Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_conntrack_expect: store netns and zone in expectation

__nf_ct_expect_find() and nf_ct_expect_find_get() are called under
rcu_read_lock() but they dereference the master conntrack via
exp->master.

Since the expectation does not hold a reference on the master conntrack,
this could be dying conntrack or different recycled conntrack than the
real master due to SLAB_TYPESAFE_RCU.

Store the netns, the master_tuple and the zone in struct
nf_conntrack_expect as a safety measure.

This patch is required by the follow up fix not to dump expectations
that do not belong to this netns.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+34 -4
+17 -1
include/net/netfilter/nf_conntrack_expect.h
··· 22 22 /* Hash member */ 23 23 struct hlist_node hnode; 24 24 25 + /* Network namespace */ 26 + possible_net_t net; 27 + 25 28 /* We expect this tuple, with the following mask */ 26 29 struct nf_conntrack_tuple tuple; 27 30 struct nf_conntrack_tuple_mask mask; 28 31 32 + #ifdef CONFIG_NF_CONNTRACK_ZONES 33 + struct nf_conntrack_zone zone; 34 + #endif 29 35 /* Usage count. */ 30 36 refcount_t use; 31 37 ··· 68 62 69 63 static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp) 70 64 { 71 - return nf_ct_net(exp->master); 65 + return read_pnet(&exp->net); 66 + } 67 + 68 + static inline bool nf_ct_exp_zone_equal_any(const struct nf_conntrack_expect *a, 69 + const struct nf_conntrack_zone *b) 70 + { 71 + #ifdef CONFIG_NF_CONNTRACK_ZONES 72 + return a->zone.id == b->id; 73 + #else 74 + return true; 75 + #endif 72 76 } 73 77 74 78 #define NF_CT_EXP_POLICY_NAME_LEN 16
+5 -1
net/netfilter/nf_conntrack_broadcast.c
··· 21 21 unsigned int timeout) 22 22 { 23 23 const struct nf_conntrack_helper *helper; 24 + struct net *net = read_pnet(&ct->ct_net); 24 25 struct nf_conntrack_expect *exp; 25 26 struct iphdr *iph = ip_hdr(skb); 26 27 struct rtable *rt = skb_rtable(skb); ··· 72 71 exp->flags = NF_CT_EXPECT_PERMANENT; 73 72 exp->class = NF_CT_EXPECT_CLASS_DEFAULT; 74 73 rcu_assign_pointer(exp->helper, helper); 75 - 74 + write_pnet(&exp->net, net); 75 + #ifdef CONFIG_NF_CONNTRACK_ZONES 76 + exp->zone = ct->zone; 77 + #endif 76 78 nf_ct_expect_related(exp, 0); 77 79 nf_ct_expect_put(exp); 78 80
+7 -2
net/netfilter/nf_conntrack_expect.c
··· 113 113 const struct net *net) 114 114 { 115 115 return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) && 116 - net_eq(net, nf_ct_net(i->master)) && 117 - nf_ct_zone_equal_any(i->master, zone); 116 + net_eq(net, read_pnet(&i->net)) && 117 + nf_ct_exp_zone_equal_any(i, zone); 118 118 } 119 119 120 120 bool nf_ct_remove_expect(struct nf_conntrack_expect *exp) ··· 326 326 { 327 327 struct nf_conntrack_helper *helper = NULL; 328 328 struct nf_conn *ct = exp->master; 329 + struct net *net = read_pnet(&ct->ct_net); 329 330 struct nf_conn_help *help; 330 331 int len; 331 332 ··· 344 343 helper = rcu_dereference(help->helper); 345 344 346 345 rcu_assign_pointer(exp->helper, helper); 346 + write_pnet(&exp->net, net); 347 + #ifdef CONFIG_NF_CONNTRACK_ZONES 348 + exp->zone = ct->zone; 349 + #endif 347 350 exp->tuple.src.l3num = family; 348 351 exp->tuple.dst.protonum = proto; 349 352
+5
net/netfilter/nf_conntrack_netlink.c
··· 3538 3538 struct nf_conntrack_tuple *tuple, 3539 3539 struct nf_conntrack_tuple *mask) 3540 3540 { 3541 + struct net *net = read_pnet(&ct->ct_net); 3541 3542 struct nf_conntrack_expect *exp; 3542 3543 struct nf_conn_help *help; 3543 3544 u32 class = 0; ··· 3578 3577 3579 3578 exp->class = class; 3580 3579 exp->master = ct; 3580 + write_pnet(&exp->net, net); 3581 + #ifdef CONFIG_NF_CONNTRACK_ZONES 3582 + exp->zone = ct->zone; 3583 + #endif 3581 3584 if (!helper) 3582 3585 helper = rcu_dereference(help->helper); 3583 3586 rcu_assign_pointer(exp->helper, helper);