netfilter: conntrack: Fix data-races around ct mark
[ Upstream commit 52d1aa8b8249ff477aaa38b6f74a8ced780d079c ]
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: 1da177e4c3 ("Linux-2.6.12-rc2")
Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
ee3ccd1abb
commit
60387731e6
@@ -1735,7 +1735,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;
|
||||
|
||||
@@ -322,9 +322,9 @@ nla_put_failure:
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
@@ -537,7 +537,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 ||
|
||||
@@ -716,6 +716,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)) {
|
||||
@@ -820,8 +821,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);
|
||||
@@ -1148,7 +1150,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);
|
||||
@@ -2016,9 +2018,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
|
||||
|
||||
@@ -2690,6 +2692,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);
|
||||
|
||||
@@ -2751,7 +2754,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)
|
||||
|
||||
@@ -363,7 +363,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);
|
||||
|
||||
@@ -97,7 +97,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
|
||||
return;
|
||||
#ifdef CONFIG_NF_CONNTRACK_MARK
|
||||
case NFT_CT_MARK:
|
||||
*dest = ct->mark;
|
||||
*dest = READ_ONCE(ct->mark);
|
||||
return;
|
||||
#endif
|
||||
#ifdef CONFIG_NF_CONNTRACK_SECMARK
|
||||
@@ -296,8 +296,8 @@ static void nft_ct_set_eval(const struct nft_expr *expr,
|
||||
switch (priv->key) {
|
||||
#ifdef CONFIG_NF_CONNTRACK_MARK
|
||||
case NFT_CT_MARK:
|
||||
if (ct->mark != value) {
|
||||
ct->mark = value;
|
||||
if (READ_ONCE(ct->mark) != value) {
|
||||
WRITE_ONCE(ct->mark, value);
|
||||
nf_conntrack_event_cache(IPCT_MARK, ct);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -30,6 +30,7 @@ connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
|
||||
u_int32_t new_targetmark;
|
||||
struct nf_conn *ct;
|
||||
u_int32_t newmark;
|
||||
u_int32_t oldmark;
|
||||
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
if (ct == NULL)
|
||||
@@ -37,14 +38,15 @@ connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
|
||||
|
||||
switch (info->mode) {
|
||||
case XT_CONNMARK_SET:
|
||||
newmark = (ct->mark & ~info->ctmask) ^ info->ctmark;
|
||||
oldmark = READ_ONCE(ct->mark);
|
||||
newmark = (oldmark & ~info->ctmask) ^ info->ctmark;
|
||||
if (info->shift_dir == D_SHIFT_RIGHT)
|
||||
newmark >>= info->shift_bits;
|
||||
else
|
||||
newmark <<= info->shift_bits;
|
||||
|
||||
if (ct->mark != newmark) {
|
||||
ct->mark = newmark;
|
||||
if (READ_ONCE(ct->mark) != newmark) {
|
||||
WRITE_ONCE(ct->mark, newmark);
|
||||
nf_conntrack_event_cache(IPCT_MARK, ct);
|
||||
}
|
||||
break;
|
||||
@@ -55,15 +57,15 @@ connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
|
||||
else
|
||||
new_targetmark <<= info->shift_bits;
|
||||
|
||||
newmark = (ct->mark & ~info->ctmask) ^
|
||||
newmark = (READ_ONCE(ct->mark) & ~info->ctmask) ^
|
||||
new_targetmark;
|
||||
if (ct->mark != newmark) {
|
||||
ct->mark = newmark;
|
||||
if (READ_ONCE(ct->mark) != newmark) {
|
||||
WRITE_ONCE(ct->mark, newmark);
|
||||
nf_conntrack_event_cache(IPCT_MARK, ct);
|
||||
}
|
||||
break;
|
||||
case XT_CONNMARK_RESTORE:
|
||||
new_targetmark = (ct->mark & info->ctmask);
|
||||
new_targetmark = (READ_ONCE(ct->mark) & info->ctmask);
|
||||
if (info->shift_dir == D_SHIFT_RIGHT)
|
||||
new_targetmark >>= info->shift_bits;
|
||||
else
|
||||
@@ -126,7 +128,7 @@ connmark_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
if (ct == NULL)
|
||||
return false;
|
||||
|
||||
return ((ct->mark & info->mask) == info->mark) ^ info->invert;
|
||||
return ((READ_ONCE(ct->mark) & info->mask) == info->mark) ^ info->invert;
|
||||
}
|
||||
|
||||
static int connmark_mt_check(const struct xt_mtchk_param *par)
|
||||
|
||||
Reference in New Issue
Block a user