* [PATCH nf V2] netfilter: ctnetlink: make it safer when updating ct->status
@ 2017-04-13 12:53 Liping Zhang
2017-04-13 23:25 ` Pablo Neira Ayuso
0 siblings, 1 reply; 3+ messages in thread
From: Liping Zhang @ 2017-04-13 12:53 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel, cernekee, fgao, Liping Zhang
From: Liping Zhang <zlpnobody@gmail.com>
User can update the ct->status via nfnetlink, but using a non-atomic
operation "ct->status |= status;". This is unsafe, and may clear
IPS_DYING_BIT bit set by another CPU unexpectedly. For example:
CPU0 CPU1
ctnetlink_change_status __nf_conntrack_find_get
old = ct->status nf_ct_gc_expired
- nf_ct_kill
- test_and_set_bit(IPS_DYING_BIT
new = old | status; -
ct->status = new; <-- oops, _DYING_ is cleared!
So using a series of atomic bit operation to solve the above issue.
Also note, user shouldn't set IPS_TEMPLATE, IPS_SEQ_ADJUST directly.
If we set the IPS_TEMPLATE_BIT, ct will be freed by nf_ct_tmpl_free,
but actually it is alloced by nf_conntrack_alloc.
If we set the IPS_SEQ_ADJUST_BIT, this may cause the NULL pointer
deference, as the nfct_seqadj(ct) maybe NULL.
So make these two bits be unchangable too.
Last, add some comments to describe the logic change due to the
commit a963d710f367 ("netfilter: ctnetlink: Fix regression in CTA_STATUS
processing"), which makes me feel a little confusing.
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
---
V2: make __ctnetlink_change_status more clean, per Gao Feng
include/uapi/linux/netfilter/nf_conntrack_common.h | 13 ++++++---
net/netfilter/nf_conntrack_netlink.c | 33 ++++++++++++++++------
2 files changed, 33 insertions(+), 13 deletions(-)
diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h
index 6a8e33d..38fc383 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_common.h
@@ -82,10 +82,6 @@ enum ip_conntrack_status {
IPS_DYING_BIT = 9,
IPS_DYING = (1 << IPS_DYING_BIT),
- /* Bits that cannot be altered from userland. */
- IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK |
- IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING),
-
/* Connection has fixed timeout. */
IPS_FIXED_TIMEOUT_BIT = 10,
IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
@@ -101,6 +97,15 @@ enum ip_conntrack_status {
/* Conntrack got a helper explicitly attached via CT target. */
IPS_HELPER_BIT = 13,
IPS_HELPER = (1 << IPS_HELPER_BIT),
+
+ /* Be careful here, modifying these bits can make things messy,
+ * so don't let users modify them directly.
+ */
+ IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK |
+ IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING |
+ IPS_SEQ_ADJUST | IPS_TEMPLATE),
+
+ __IPS_MAX_BIT = 14,
};
/* Connection tracking event types */
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 908d858..c16641e 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1419,6 +1419,24 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
}
#endif
+static void
+__ctnetlink_change_status(struct nf_conn *ct, unsigned long on,
+ unsigned long off)
+{
+ unsigned int bit;
+
+ /* Ignore these unchangable bits */
+ on &= ~IPS_UNCHANGEABLE_MASK;
+ off &= ~IPS_UNCHANGEABLE_MASK;
+
+ for (bit = 0; bit < __IPS_MAX_BIT; bit++) {
+ if (on & (1 << bit))
+ set_bit(bit, &ct->status);
+ else if (off & (1 << bit))
+ clear_bit(bit, &ct->status);
+ }
+}
+
static int
ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[])
{
@@ -1438,10 +1456,7 @@ ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[])
/* ASSURED bit can only be set */
return -EBUSY;
- /* Be careful here, modifying NAT bits can screw up things,
- * so don't let users modify them directly if they don't pass
- * nf_nat_range. */
- ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK);
+ __ctnetlink_change_status(ct, status, 0);
return 0;
}
@@ -1632,7 +1647,7 @@ ctnetlink_change_seq_adj(struct nf_conn *ct,
if (ret < 0)
return ret;
- ct->status |= IPS_SEQ_ADJUST;
+ set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
}
if (cda[CTA_SEQ_ADJ_REPLY]) {
@@ -1641,7 +1656,7 @@ ctnetlink_change_seq_adj(struct nf_conn *ct,
if (ret < 0)
return ret;
- ct->status |= IPS_SEQ_ADJUST;
+ set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
}
return 0;
@@ -2295,10 +2310,10 @@ ctnetlink_update_status(struct nf_conn *ct, const struct nlattr * const cda[])
/* This check is less strict than ctnetlink_change_status()
* because callers often flip IPS_EXPECTED bits when sending
* an NFQA_CT attribute to the kernel. So ignore the
- * unchangeable bits but do not error out.
+ * unchangeable bits but do not error out. Also user programs
+ * are allowed to clear the bits that they are allowed to change.
*/
- ct->status = (status & ~IPS_UNCHANGEABLE_MASK) |
- (ct->status & IPS_UNCHANGEABLE_MASK);
+ __ctnetlink_change_status(ct, status, ~status);
return 0;
}
--
2.5.5
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH nf V2] netfilter: ctnetlink: make it safer when updating ct->status
2017-04-13 12:53 [PATCH nf V2] netfilter: ctnetlink: make it safer when updating ct->status Liping Zhang
@ 2017-04-13 23:25 ` Pablo Neira Ayuso
2017-04-14 2:43 ` Liping Zhang
0 siblings, 1 reply; 3+ messages in thread
From: Pablo Neira Ayuso @ 2017-04-13 23:25 UTC (permalink / raw)
To: Liping Zhang; +Cc: netfilter-devel, cernekee, fgao, Liping Zhang
On Thu, Apr 13, 2017 at 08:53:47PM +0800, Liping Zhang wrote:
> From: Liping Zhang <zlpnobody@gmail.com>
>
> User can update the ct->status via nfnetlink, but using a non-atomic
> operation "ct->status |= status;". This is unsafe, and may clear
> IPS_DYING_BIT bit set by another CPU unexpectedly. For example:
> CPU0 CPU1
> ctnetlink_change_status __nf_conntrack_find_get
> old = ct->status nf_ct_gc_expired
> - nf_ct_kill
> - test_and_set_bit(IPS_DYING_BIT
> new = old | status; -
> ct->status = new; <-- oops, _DYING_ is cleared!
This is fixing an issue that was introduced in ca7433df3a67.
So I would like this comes in a patch batch including the several
patches that we need to fix the conntrack update path from ctnetlink.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH nf V2] netfilter: ctnetlink: make it safer when updating ct->status
2017-04-13 23:25 ` Pablo Neira Ayuso
@ 2017-04-14 2:43 ` Liping Zhang
0 siblings, 0 replies; 3+ messages in thread
From: Liping Zhang @ 2017-04-14 2:43 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: Liping Zhang, Netfilter Developer Mailing List, cernekee, Gao Feng
Hi Pablo,
2017-04-14 7:25 GMT+08:00 Pablo Neira Ayuso <pablo@netfilter.org>:
> On Thu, Apr 13, 2017 at 08:53:47PM +0800, Liping Zhang wrote:
>> From: Liping Zhang <zlpnobody@gmail.com>
>>
>> User can update the ct->status via nfnetlink, but using a non-atomic
>> operation "ct->status |= status;". This is unsafe, and may clear
>> IPS_DYING_BIT bit set by another CPU unexpectedly. For example:
>> CPU0 CPU1
>> ctnetlink_change_status __nf_conntrack_find_get
>> old = ct->status nf_ct_gc_expired
>> - nf_ct_kill
>> - test_and_set_bit(IPS_DYING_BIT
>> new = old | status; -
>> ct->status = new; <-- oops, _DYING_ is cleared!
>
> This is fixing an issue that was introduced in ca7433df3a67.
Actually, even holding the global nf_conntrack_lock, this won't help anything.
Because data processing path is protected by rcu, so they can manipulate
the same _ct_ at the same time.
After having a close investigation, I found the above issue was
introduced by commit 76507f69c44e ("[NETFILTER]: nf_conntrack: use RCU
for conntrack hash"):
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76507f69c44ed199a1a68086145398459e55835d
The commit ca7433df3a67 ("netfilter: conntrack: seperate expect
locking from nf_conntrack_lock")
only introduced the dead lock indeed.
> So I would like this comes in a patch batch including the several
> patches that we need to fix the conntrack update path from ctnetlink.
OK, I will try to send a patch set.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2017-04-14 2:43 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-13 12:53 [PATCH nf V2] netfilter: ctnetlink: make it safer when updating ct->status Liping Zhang
2017-04-13 23:25 ` Pablo Neira Ayuso
2017-04-14 2:43 ` Liping Zhang
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.