diff options
author | Liping Zhang <zlpnobody@gmail.com> | 2017-05-07 10:01:56 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2017-05-15 06:42:29 -0400 |
commit | 9338d7b4418e9996a7642867d8f6b482a6040ed6 (patch) | |
tree | 5bf37ab11070434ed04c1bcfe12194925bfffd25 | |
parent | d91fc59cd77c719f33eda65c194ad8f95a055190 (diff) |
netfilter: nfnl_cthelper: reject del request if helper obj is in use
We can still delete the ct helper even if it is in use, this will cause
a use-after-free error. In more detail, I mean:
# nfct helper add ssdp inet udp
# iptables -t raw -A OUTPUT -p udp -j CT --helper ssdp
# nfct helper delete ssdp //--> oops, succeed!
BUG: unable to handle kernel paging request at 000026ca
IP: 0x26ca
[...]
Call Trace:
? ipv4_helper+0x62/0x80 [nf_conntrack_ipv4]
nf_hook_slow+0x21/0xb0
ip_output+0xe9/0x100
? ip_fragment.constprop.54+0xc0/0xc0
ip_local_out+0x33/0x40
ip_send_skb+0x16/0x80
udp_send_skb+0x84/0x240
udp_sendmsg+0x35d/0xa50
So add reference count to fix this issue, if ct helper is used by
others, reject the delete request.
Apply this patch:
# nfct helper delete ssdp
nfct v1.4.3: netlink error: Device or resource busy
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/net/netfilter/nf_conntrack_helper.h | 2 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_helper.c | 6 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_cthelper.c | 17 |
3 files changed, 19 insertions, 6 deletions
diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index c1c12411103a..c519bb5b5bb8 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #ifndef _NF_CONNTRACK_HELPER_H | 10 | #ifndef _NF_CONNTRACK_HELPER_H |
11 | #define _NF_CONNTRACK_HELPER_H | 11 | #define _NF_CONNTRACK_HELPER_H |
12 | #include <linux/refcount.h> | ||
12 | #include <net/netfilter/nf_conntrack.h> | 13 | #include <net/netfilter/nf_conntrack.h> |
13 | #include <net/netfilter/nf_conntrack_extend.h> | 14 | #include <net/netfilter/nf_conntrack_extend.h> |
14 | #include <net/netfilter/nf_conntrack_expect.h> | 15 | #include <net/netfilter/nf_conntrack_expect.h> |
@@ -26,6 +27,7 @@ struct nf_conntrack_helper { | |||
26 | struct hlist_node hnode; /* Internal use. */ | 27 | struct hlist_node hnode; /* Internal use. */ |
27 | 28 | ||
28 | char name[NF_CT_HELPER_NAME_LEN]; /* name of the module */ | 29 | char name[NF_CT_HELPER_NAME_LEN]; /* name of the module */ |
30 | refcount_t refcnt; | ||
29 | struct module *me; /* pointer to self */ | 31 | struct module *me; /* pointer to self */ |
30 | const struct nf_conntrack_expect_policy *expect_policy; | 32 | const struct nf_conntrack_expect_policy *expect_policy; |
31 | 33 | ||
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index e17006b6e434..7f6100ca63be 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c | |||
@@ -174,6 +174,10 @@ nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum) | |||
174 | #endif | 174 | #endif |
175 | if (h != NULL && !try_module_get(h->me)) | 175 | if (h != NULL && !try_module_get(h->me)) |
176 | h = NULL; | 176 | h = NULL; |
177 | if (h != NULL && !refcount_inc_not_zero(&h->refcnt)) { | ||
178 | module_put(h->me); | ||
179 | h = NULL; | ||
180 | } | ||
177 | 181 | ||
178 | rcu_read_unlock(); | 182 | rcu_read_unlock(); |
179 | 183 | ||
@@ -183,6 +187,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get); | |||
183 | 187 | ||
184 | void nf_conntrack_helper_put(struct nf_conntrack_helper *helper) | 188 | void nf_conntrack_helper_put(struct nf_conntrack_helper *helper) |
185 | { | 189 | { |
190 | refcount_dec(&helper->refcnt); | ||
186 | module_put(helper->me); | 191 | module_put(helper->me); |
187 | } | 192 | } |
188 | EXPORT_SYMBOL_GPL(nf_conntrack_helper_put); | 193 | EXPORT_SYMBOL_GPL(nf_conntrack_helper_put); |
@@ -423,6 +428,7 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) | |||
423 | } | 428 | } |
424 | } | 429 | } |
425 | } | 430 | } |
431 | refcount_set(&me->refcnt, 1); | ||
426 | hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); | 432 | hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); |
427 | nf_ct_helper_count++; | 433 | nf_ct_helper_count++; |
428 | out: | 434 | out: |
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 950bf6eadc65..be678a323598 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c | |||
@@ -686,6 +686,7 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl, | |||
686 | tuple_set = true; | 686 | tuple_set = true; |
687 | } | 687 | } |
688 | 688 | ||
689 | ret = -ENOENT; | ||
689 | list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { | 690 | list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { |
690 | cur = &nlcth->helper; | 691 | cur = &nlcth->helper; |
691 | j++; | 692 | j++; |
@@ -699,16 +700,20 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl, | |||
699 | tuple.dst.protonum != cur->tuple.dst.protonum)) | 700 | tuple.dst.protonum != cur->tuple.dst.protonum)) |
700 | continue; | 701 | continue; |
701 | 702 | ||
702 | found = true; | 703 | if (refcount_dec_if_one(&cur->refcnt)) { |
703 | nf_conntrack_helper_unregister(cur); | 704 | found = true; |
704 | kfree(cur->expect_policy); | 705 | nf_conntrack_helper_unregister(cur); |
706 | kfree(cur->expect_policy); | ||
705 | 707 | ||
706 | list_del(&nlcth->list); | 708 | list_del(&nlcth->list); |
707 | kfree(nlcth); | 709 | kfree(nlcth); |
710 | } else { | ||
711 | ret = -EBUSY; | ||
712 | } | ||
708 | } | 713 | } |
709 | 714 | ||
710 | /* Make sure we return success if we flush and there is no helpers */ | 715 | /* Make sure we return success if we flush and there is no helpers */ |
711 | return (found || j == 0) ? 0 : -ENOENT; | 716 | return (found || j == 0) ? 0 : ret; |
712 | } | 717 | } |
713 | 718 | ||
714 | static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = { | 719 | static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = { |