Commit 02a3231b authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso
Browse files

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: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent bffcaad9
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -22,10 +22,16 @@ struct nf_conntrack_expect {
	/* Hash member */
	struct hlist_node hnode;

	/* Network namespace */
	possible_net_t net;

	/* We expect this tuple, with the following mask */
	struct nf_conntrack_tuple tuple;
	struct nf_conntrack_tuple_mask mask;

#ifdef CONFIG_NF_CONNTRACK_ZONES
	struct nf_conntrack_zone zone;
#endif
	/* Usage count. */
	refcount_t use;

@@ -62,7 +68,17 @@ struct nf_conntrack_expect {

static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
{
	return nf_ct_net(exp->master);
	return read_pnet(&exp->net);
}

static inline bool nf_ct_exp_zone_equal_any(const struct nf_conntrack_expect *a,
					    const struct nf_conntrack_zone *b)
{
#ifdef CONFIG_NF_CONNTRACK_ZONES
	return a->zone.id == b->id;
#else
	return true;
#endif
}

#define NF_CT_EXP_POLICY_NAME_LEN	16
+5 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
				unsigned int timeout)
{
	const struct nf_conntrack_helper *helper;
	struct net *net = read_pnet(&ct->ct_net);
	struct nf_conntrack_expect *exp;
	struct iphdr *iph = ip_hdr(skb);
	struct rtable *rt = skb_rtable(skb);
@@ -71,7 +72,10 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
	exp->flags                = NF_CT_EXPECT_PERMANENT;
	exp->class		  = NF_CT_EXPECT_CLASS_DEFAULT;
	rcu_assign_pointer(exp->helper, helper);

	write_pnet(&exp->net, net);
#ifdef CONFIG_NF_CONNTRACK_ZONES
	exp->zone = ct->zone;
#endif
	nf_ct_expect_related(exp, 0);
	nf_ct_expect_put(exp);

+7 −2
Original line number Diff line number Diff line
@@ -113,8 +113,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
		const struct net *net)
{
	return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
	       net_eq(net, nf_ct_net(i->master)) &&
	       nf_ct_zone_equal_any(i->master, zone);
	       net_eq(net, read_pnet(&i->net)) &&
	       nf_ct_exp_zone_equal_any(i, zone);
}

bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
@@ -326,6 +326,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
{
	struct nf_conntrack_helper *helper = NULL;
	struct nf_conn *ct = exp->master;
	struct net *net = read_pnet(&ct->ct_net);
	struct nf_conn_help *help;
	int len;

@@ -343,6 +344,10 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
		helper = rcu_dereference(help->helper);

	rcu_assign_pointer(exp->helper, helper);
	write_pnet(&exp->net, net);
#ifdef CONFIG_NF_CONNTRACK_ZONES
	exp->zone = ct->zone;
#endif
	exp->tuple.src.l3num = family;
	exp->tuple.dst.protonum = proto;

+5 −0
Original line number Diff line number Diff line
@@ -3538,6 +3538,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
		       struct nf_conntrack_tuple *tuple,
		       struct nf_conntrack_tuple *mask)
{
	struct net *net = read_pnet(&ct->ct_net);
	struct nf_conntrack_expect *exp;
	struct nf_conn_help *help;
	u32 class = 0;
@@ -3577,6 +3578,10 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,

	exp->class = class;
	exp->master = ct;
	write_pnet(&exp->net, net);
#ifdef CONFIG_NF_CONNTRACK_ZONES
	exp->zone = ct->zone;
#endif
	if (!helper)
		helper = rcu_dereference(help->helper);
	rcu_assign_pointer(exp->helper, helper);