Commit 52d1aa8b authored by Daniel Xu's avatar Daniel Xu Committed by Pablo Neira Ayuso
Browse files

netfilter: conntrack: Fix data-races around ct mark



nf_conn:mark can be read from and written to in parallel. Use
READ_ONCE()/WRITE_ONCE() for reads and writes to prevent unwanted
compiler optimizations.

Fixes: 1da177e4 ("Linux-2.6.12-rc2")
Signed-off-by: default avatarDaniel Xu <dxu@dxuuu.xyz>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 40b9d1ab
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -296,7 +296,7 @@ skb_flow_dissect_ct(const struct sk_buff *skb,
	key->ct_zone = ct->zone.id;
#endif
#if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)
	key->ct_mark = ct->mark;
	key->ct_mark = READ_ONCE(ct->mark);
#endif

	cl = nf_ct_labels_find(ct);
+2 −2
Original line number Diff line number Diff line
@@ -435,7 +435,7 @@ clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par)

	switch (ctinfo) {
	case IP_CT_NEW:
		ct->mark = hash;
		WRITE_ONCE(ct->mark, hash);
		break;
	case IP_CT_RELATED:
	case IP_CT_RELATED_REPLY:
@@ -452,7 +452,7 @@ clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par)
#ifdef DEBUG
	nf_ct_dump_tuple_ip(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
#endif
	pr_debug("hash=%u ct_hash=%u ", hash, ct->mark);
	pr_debug("hash=%u ct_hash=%u ", hash, READ_ONCE(ct->mark));
	if (!clusterip_responsible(cipinfo->config, hash)) {
		pr_debug("not responsible\n");
		return NF_DROP;
+1 −1
Original line number Diff line number Diff line
@@ -1781,7 +1781,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
			}

#ifdef CONFIG_NF_CONNTRACK_MARK
			ct->mark = exp->master->mark;
			ct->mark = READ_ONCE(exp->master->mark);
#endif
#ifdef CONFIG_NF_CONNTRACK_SECMARK
			ct->secmark = exp->master->secmark;
+14 −10
Original line number Diff line number Diff line
@@ -328,9 +328,9 @@ ctnetlink_dump_timestamp(struct sk_buff *skb, const struct nf_conn *ct)
}

#ifdef CONFIG_NF_CONNTRACK_MARK
static int ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct)
static int ctnetlink_dump_mark(struct sk_buff *skb, u32 mark)
{
	if (nla_put_be32(skb, CTA_MARK, htonl(ct->mark)))
	if (nla_put_be32(skb, CTA_MARK, htonl(mark)))
		goto nla_put_failure;
	return 0;

@@ -543,7 +543,7 @@ static int ctnetlink_dump_extinfo(struct sk_buff *skb,
static int ctnetlink_dump_info(struct sk_buff *skb, struct nf_conn *ct)
{
	if (ctnetlink_dump_status(skb, ct) < 0 ||
	    ctnetlink_dump_mark(skb, ct) < 0 ||
	    ctnetlink_dump_mark(skb, READ_ONCE(ct->mark)) < 0 ||
	    ctnetlink_dump_secctx(skb, ct) < 0 ||
	    ctnetlink_dump_id(skb, ct) < 0 ||
	    ctnetlink_dump_use(skb, ct) < 0 ||
@@ -722,6 +722,7 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item)
	struct sk_buff *skb;
	unsigned int type;
	unsigned int flags = 0, group;
	u32 mark;
	int err;

	if (events & (1 << IPCT_DESTROY)) {
@@ -826,8 +827,9 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item)
	}

#ifdef CONFIG_NF_CONNTRACK_MARK
	if ((events & (1 << IPCT_MARK) || ct->mark)
	    && ctnetlink_dump_mark(skb, ct) < 0)
	mark = READ_ONCE(ct->mark);
	if ((events & (1 << IPCT_MARK) || mark) &&
	    ctnetlink_dump_mark(skb, mark) < 0)
		goto nla_put_failure;
#endif
	nlmsg_end(skb, nlh);
@@ -1154,7 +1156,7 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
	}

#ifdef CONFIG_NF_CONNTRACK_MARK
	if ((ct->mark & filter->mark.mask) != filter->mark.val)
	if ((READ_ONCE(ct->mark) & filter->mark.mask) != filter->mark.val)
		goto ignore_entry;
#endif
	status = (u32)READ_ONCE(ct->status);
@@ -2002,9 +2004,9 @@ static void ctnetlink_change_mark(struct nf_conn *ct,
		mask = ~ntohl(nla_get_be32(cda[CTA_MARK_MASK]));

	mark = ntohl(nla_get_be32(cda[CTA_MARK]));
	newmark = (ct->mark & mask) ^ mark;
	if (newmark != ct->mark)
		ct->mark = newmark;
	newmark = (READ_ONCE(ct->mark) & mask) ^ mark;
	if (newmark != READ_ONCE(ct->mark))
		WRITE_ONCE(ct->mark, newmark);
}
#endif

@@ -2669,6 +2671,7 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct)
{
	const struct nf_conntrack_zone *zone;
	struct nlattr *nest_parms;
	u32 mark;

	zone = nf_ct_zone(ct);

@@ -2730,7 +2733,8 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct)
		goto nla_put_failure;

#ifdef CONFIG_NF_CONNTRACK_MARK
	if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0)
	mark = READ_ONCE(ct->mark);
	if (mark && ctnetlink_dump_mark(skb, mark) < 0)
		goto nla_put_failure;
#endif
	if (ctnetlink_dump_labels(skb, ct) < 0)
+1 −1
Original line number Diff line number Diff line
@@ -366,7 +366,7 @@ static int ct_seq_show(struct seq_file *s, void *v)
		goto release;

#if defined(CONFIG_NF_CONNTRACK_MARK)
	seq_printf(s, "mark=%u ", ct->mark);
	seq_printf(s, "mark=%u ", READ_ONCE(ct->mark));
#endif

	ct_show_secctx(s, ct);
Loading