Commit 8f1f7eeb authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains a rather large update with Netfilter
fixes, specifically targeted to incorrect RCU usage in several spots and
the userspace conntrack helper infrastructure (nfnetlink_cthelper),
more specifically they are:

1) expect_class_max is incorrect set via cthelper, as in kernel semantics
   mandate that this represents the array of expectation classes minus 1.
   Patch from Liping Zhang.

2) Expectation policy updates via cthelper are currently broken for several
   reasons: This code allows illegal changes in the policy such as changing
   the number of expeciation classes, it is leaking the updated policy and
   such update occurs with no RCU protection at all. Fix this by adding a
   new nfnl_cthelper_update_policy() that describes what is really legal on
   the update path.

3) Fix several memory leaks in cthelper, from Jeffy Chen.

4) synchronize_rcu() is missing in the removal path of several modules,
   this may lead to races since CPU may still be running on code that has
   just gone. Also from Liping Zhang.

5) Don't use the helper hashtable from cthelper, it is not safe to walk
   over those bits without the helper mutex. Fix this by introducing a
   new independent list for userspace helpers. From Liping Zhang.

6) nf_ct_extend_unregister() needs synchronize_rcu() to make sure no
   packets are walking on any conntrack extension that is gone after
   module removal, again from Liping.

7) nf_nat_snmp may crash if we fail to unregister the helper due to
   accidental leftover code, from Gao Feng.

8) Fix leak in nfnetlink_queue with secctx support, from Liping Zhang.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 358e78b5 77c1c03c
...@@ -1260,16 +1260,6 @@ static const struct nf_conntrack_expect_policy snmp_exp_policy = { ...@@ -1260,16 +1260,6 @@ static const struct nf_conntrack_expect_policy snmp_exp_policy = {
.timeout = 180, .timeout = 180,
}; };
static struct nf_conntrack_helper snmp_helper __read_mostly = {
.me = THIS_MODULE,
.help = help,
.expect_policy = &snmp_exp_policy,
.name = "snmp",
.tuple.src.l3num = AF_INET,
.tuple.src.u.udp.port = cpu_to_be16(SNMP_PORT),
.tuple.dst.protonum = IPPROTO_UDP,
};
static struct nf_conntrack_helper snmp_trap_helper __read_mostly = { static struct nf_conntrack_helper snmp_trap_helper __read_mostly = {
.me = THIS_MODULE, .me = THIS_MODULE,
.help = help, .help = help,
...@@ -1288,22 +1278,16 @@ static struct nf_conntrack_helper snmp_trap_helper __read_mostly = { ...@@ -1288,22 +1278,16 @@ static struct nf_conntrack_helper snmp_trap_helper __read_mostly = {
static int __init nf_nat_snmp_basic_init(void) static int __init nf_nat_snmp_basic_init(void)
{ {
int ret = 0;
BUG_ON(nf_nat_snmp_hook != NULL); BUG_ON(nf_nat_snmp_hook != NULL);
RCU_INIT_POINTER(nf_nat_snmp_hook, help); RCU_INIT_POINTER(nf_nat_snmp_hook, help);
ret = nf_conntrack_helper_register(&snmp_trap_helper); return nf_conntrack_helper_register(&snmp_trap_helper);
if (ret < 0) {
nf_conntrack_helper_unregister(&snmp_helper);
return ret;
}
return ret;
} }
static void __exit nf_nat_snmp_basic_fini(void) static void __exit nf_nat_snmp_basic_fini(void)
{ {
RCU_INIT_POINTER(nf_nat_snmp_hook, NULL); RCU_INIT_POINTER(nf_nat_snmp_hook, NULL);
synchronize_rcu();
nf_conntrack_helper_unregister(&snmp_trap_helper); nf_conntrack_helper_unregister(&snmp_trap_helper);
} }
......
...@@ -290,6 +290,7 @@ void nf_conntrack_unregister_notifier(struct net *net, ...@@ -290,6 +290,7 @@ void nf_conntrack_unregister_notifier(struct net *net,
BUG_ON(notify != new); BUG_ON(notify != new);
RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL); RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL);
mutex_unlock(&nf_ct_ecache_mutex); mutex_unlock(&nf_ct_ecache_mutex);
/* synchronize_rcu() is called from ctnetlink_exit. */
} }
EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
...@@ -326,6 +327,7 @@ void nf_ct_expect_unregister_notifier(struct net *net, ...@@ -326,6 +327,7 @@ void nf_ct_expect_unregister_notifier(struct net *net,
BUG_ON(notify != new); BUG_ON(notify != new);
RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL); RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL);
mutex_unlock(&nf_ct_ecache_mutex); mutex_unlock(&nf_ct_ecache_mutex);
/* synchronize_rcu() is called from ctnetlink_exit. */
} }
EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier); EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
......
...@@ -53,7 +53,11 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, ...@@ -53,7 +53,11 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id,
rcu_read_lock(); rcu_read_lock();
t = rcu_dereference(nf_ct_ext_types[id]); t = rcu_dereference(nf_ct_ext_types[id]);
BUG_ON(t == NULL); if (!t) {
rcu_read_unlock();
return NULL;
}
off = ALIGN(sizeof(struct nf_ct_ext), t->align); off = ALIGN(sizeof(struct nf_ct_ext), t->align);
len = off + t->len + var_alloc_len; len = off + t->len + var_alloc_len;
alloc_size = t->alloc_size + var_alloc_len; alloc_size = t->alloc_size + var_alloc_len;
...@@ -88,7 +92,10 @@ void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, ...@@ -88,7 +92,10 @@ void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id,
rcu_read_lock(); rcu_read_lock();
t = rcu_dereference(nf_ct_ext_types[id]); t = rcu_dereference(nf_ct_ext_types[id]);
BUG_ON(t == NULL); if (!t) {
rcu_read_unlock();
return NULL;
}
newoff = ALIGN(old->len, t->align); newoff = ALIGN(old->len, t->align);
newlen = newoff + t->len + var_alloc_len; newlen = newoff + t->len + var_alloc_len;
...@@ -175,6 +182,6 @@ void nf_ct_extend_unregister(struct nf_ct_ext_type *type) ...@@ -175,6 +182,6 @@ void nf_ct_extend_unregister(struct nf_ct_ext_type *type)
RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL); RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL);
update_alloc_size(type); update_alloc_size(type);
mutex_unlock(&nf_ct_ext_type_mutex); mutex_unlock(&nf_ct_ext_type_mutex);
rcu_barrier(); /* Wait for completion of call_rcu()'s */ synchronize_rcu();
} }
EXPORT_SYMBOL_GPL(nf_ct_extend_unregister); EXPORT_SYMBOL_GPL(nf_ct_extend_unregister);
...@@ -3442,6 +3442,7 @@ static void __exit ctnetlink_exit(void) ...@@ -3442,6 +3442,7 @@ static void __exit ctnetlink_exit(void)
#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT #ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
RCU_INIT_POINTER(nfnl_ct_hook, NULL); RCU_INIT_POINTER(nfnl_ct_hook, NULL);
#endif #endif
synchronize_rcu();
} }
module_init(ctnetlink_init); module_init(ctnetlink_init);
......
...@@ -903,6 +903,8 @@ static void __exit nf_nat_cleanup(void) ...@@ -903,6 +903,8 @@ static void __exit nf_nat_cleanup(void)
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL); RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL);
#endif #endif
synchronize_rcu();
for (i = 0; i < NFPROTO_NUMPROTO; i++) for (i = 0; i < NFPROTO_NUMPROTO; i++)
kfree(nf_nat_l4protos[i]); kfree(nf_nat_l4protos[i]);
......
This diff is collapsed.
...@@ -646,8 +646,8 @@ static void __exit cttimeout_exit(void) ...@@ -646,8 +646,8 @@ static void __exit cttimeout_exit(void)
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL); RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL); RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
synchronize_rcu();
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
rcu_barrier();
} }
module_init(cttimeout_init); module_init(cttimeout_init);
......
...@@ -443,7 +443,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, ...@@ -443,7 +443,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
skb = alloc_skb(size, GFP_ATOMIC); skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) { if (!skb) {
skb_tx_error(entskb); skb_tx_error(entskb);
return NULL; goto nlmsg_failure;
} }
nlh = nlmsg_put(skb, 0, 0, nlh = nlmsg_put(skb, 0, 0,
...@@ -452,7 +452,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, ...@@ -452,7 +452,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
if (!nlh) { if (!nlh) {
skb_tx_error(entskb); skb_tx_error(entskb);
kfree_skb(skb); kfree_skb(skb);
return NULL; goto nlmsg_failure;
} }
nfmsg = nlmsg_data(nlh); nfmsg = nlmsg_data(nlh);
nfmsg->nfgen_family = entry->state.pf; nfmsg->nfgen_family = entry->state.pf;
...@@ -598,12 +598,17 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, ...@@ -598,12 +598,17 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
} }
nlh->nlmsg_len = skb->len; nlh->nlmsg_len = skb->len;
if (seclen)
security_release_secctx(secdata, seclen);
return skb; return skb;
nla_put_failure: nla_put_failure:
skb_tx_error(entskb); skb_tx_error(entskb);
kfree_skb(skb); kfree_skb(skb);
net_err_ratelimited("nf_queue: error creating packet message\n"); net_err_ratelimited("nf_queue: error creating packet message\n");
nlmsg_failure:
if (seclen)
security_release_secctx(secdata, seclen);
return NULL; return NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment