Commit b75911b66ad508a3c3f006ce37d9f9ebee34da43
Committed by
Pablo Neira Ayuso
1 parent
12be15dd5a
netfilter: cttimeout: fix use after free error when delete netns
In general, when we want to delete a netns, cttimeout_net_exit will be called before ipt_unregister_table, i.e. before ctnl_timeout_put. But after call kfree_rcu in cttimeout_net_exit, we will still decrease the timeout object's refcnt in ctnl_timeout_put, this is incorrect, and will cause a use after free error. It is easy to reproduce this problem: # while : ; do ip netns add xxx ip netns exec xxx nfct add timeout testx inet icmp timeout 200 ip netns exec xxx iptables -t raw -p icmp -I OUTPUT -j CT --timeout testx ip netns del xxx done ======================================================================= BUG kmalloc-96 (Tainted: G B E ): Poison overwritten ----------------------------------------------------------------------- INFO: 0xffff88002b5161e8-0xffff88002b5161e8. First byte 0x6a instead of 0x6b INFO: Allocated in cttimeout_new_timeout+0xd4/0x240 [nfnetlink_cttimeout] age=104 cpu=0 pid=3330 ___slab_alloc+0x4da/0x540 __slab_alloc+0x20/0x40 __kmalloc+0x1c8/0x240 cttimeout_new_timeout+0xd4/0x240 [nfnetlink_cttimeout] nfnetlink_rcv_msg+0x21a/0x230 [nfnetlink] [ ... ] So only when the refcnt decreased to 0, we call kfree_rcu to free the timeout object. And like nfnetlink_acct do, use atomic_cmpxchg to avoid race between ctnl_timeout_try_del and ctnl_timeout_put. Signed-off-by: Liping Zhang <liping.zhang@spreadtrum.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Showing 1 changed file with 10 additions and 6 deletions Side-by-side Diff
net/netfilter/nfnetlink_cttimeout.c
... | ... | @@ -330,16 +330,16 @@ |
330 | 330 | { |
331 | 331 | int ret = 0; |
332 | 332 | |
333 | - /* we want to avoid races with nf_ct_timeout_find_get. */ | |
334 | - if (atomic_dec_and_test(&timeout->refcnt)) { | |
333 | + /* We want to avoid races with ctnl_timeout_put. So only when the | |
334 | + * current refcnt is 1, we decrease it to 0. | |
335 | + */ | |
336 | + if (atomic_cmpxchg(&timeout->refcnt, 1, 0) == 1) { | |
335 | 337 | /* We are protected by nfnl mutex. */ |
336 | 338 | list_del_rcu(&timeout->head); |
337 | 339 | nf_ct_l4proto_put(timeout->l4proto); |
338 | 340 | ctnl_untimeout(net, timeout); |
339 | 341 | kfree_rcu(timeout, rcu_head); |
340 | 342 | } else { |
341 | - /* still in use, restore reference counter. */ | |
342 | - atomic_inc(&timeout->refcnt); | |
343 | 343 | ret = -EBUSY; |
344 | 344 | } |
345 | 345 | return ret; |
... | ... | @@ -543,7 +543,9 @@ |
543 | 543 | |
544 | 544 | static void ctnl_timeout_put(struct ctnl_timeout *timeout) |
545 | 545 | { |
546 | - atomic_dec(&timeout->refcnt); | |
546 | + if (atomic_dec_and_test(&timeout->refcnt)) | |
547 | + kfree_rcu(timeout, rcu_head); | |
548 | + | |
547 | 549 | module_put(THIS_MODULE); |
548 | 550 | } |
549 | 551 | #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
... | ... | @@ -591,7 +593,9 @@ |
591 | 593 | list_for_each_entry_safe(cur, tmp, &net->nfct_timeout_list, head) { |
592 | 594 | list_del_rcu(&cur->head); |
593 | 595 | nf_ct_l4proto_put(cur->l4proto); |
594 | - kfree_rcu(cur, rcu_head); | |
596 | + | |
597 | + if (atomic_dec_and_test(&cur->refcnt)) | |
598 | + kfree_rcu(cur, rcu_head); | |
595 | 599 | } |
596 | 600 | } |
597 | 601 |