Skip to content

Commit 49ca022

Browse files
Florian Westphalummakynes
Florian Westphal
authored andcommitted
netfilter: ctnetlink: don't dump ct extensions of unconfirmed conntracks
When dumping the unconfirmed lists, the cpu that is processing the ct entry can reallocate ct->ext at any time. Right now accessing the extensions from another CPU is ok provided we're holding rcu read lock: extension reallocation does use rcu. Once RCU isn't used anymore this becomes unsafe, so skip extensions for the unconfirmed list. Dumping the extension area for confirmed or dying conntracks is fine: no reallocations are allowed and list iteration holds appropriate locks that prevent ct (and this ct->ext) from getting free'd. v2: fix compiler warnings due to misue of 'const' and missing return statement (kbuild robot). Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 5ccbf89 commit 49ca022

File tree

1 file changed

+50
-26
lines changed

1 file changed

+50
-26
lines changed

net/netfilter/nf_conntrack_netlink.c

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,45 @@ static int ctnetlink_dump_use(struct sk_buff *skb, const struct nf_conn *ct)
506506
return -1;
507507
}
508508

509+
/* all these functions access ct->ext. Caller must either hold a reference
510+
* on ct or prevent its deletion by holding either the bucket spinlock or
511+
* pcpu dying list lock.
512+
*/
513+
static int ctnetlink_dump_extinfo(struct sk_buff *skb,
514+
struct nf_conn *ct, u32 type)
515+
{
516+
if (ctnetlink_dump_acct(skb, ct, type) < 0 ||
517+
ctnetlink_dump_timestamp(skb, ct) < 0 ||
518+
ctnetlink_dump_helpinfo(skb, ct) < 0 ||
519+
ctnetlink_dump_labels(skb, ct) < 0 ||
520+
ctnetlink_dump_ct_seq_adj(skb, ct) < 0 ||
521+
ctnetlink_dump_ct_synproxy(skb, ct) < 0)
522+
return -1;
523+
524+
return 0;
525+
}
526+
527+
static int ctnetlink_dump_info(struct sk_buff *skb, struct nf_conn *ct)
528+
{
529+
if (ctnetlink_dump_status(skb, ct) < 0 ||
530+
ctnetlink_dump_mark(skb, ct) < 0 ||
531+
ctnetlink_dump_secctx(skb, ct) < 0 ||
532+
ctnetlink_dump_id(skb, ct) < 0 ||
533+
ctnetlink_dump_use(skb, ct) < 0 ||
534+
ctnetlink_dump_master(skb, ct) < 0)
535+
return -1;
536+
537+
if (!test_bit(IPS_OFFLOAD_BIT, &ct->status) &&
538+
(ctnetlink_dump_timeout(skb, ct) < 0 ||
539+
ctnetlink_dump_protoinfo(skb, ct) < 0))
540+
return -1;
541+
542+
return 0;
543+
}
544+
509545
static int
510546
ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
511-
struct nf_conn *ct)
547+
struct nf_conn *ct, bool extinfo)
512548
{
513549
const struct nf_conntrack_zone *zone;
514550
struct nlmsghdr *nlh;
@@ -552,23 +588,9 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
552588
NF_CT_DEFAULT_ZONE_DIR) < 0)
553589
goto nla_put_failure;
554590

555-
if (ctnetlink_dump_status(skb, ct) < 0 ||
556-
ctnetlink_dump_acct(skb, ct, type) < 0 ||
557-
ctnetlink_dump_timestamp(skb, ct) < 0 ||
558-
ctnetlink_dump_helpinfo(skb, ct) < 0 ||
559-
ctnetlink_dump_mark(skb, ct) < 0 ||
560-
ctnetlink_dump_secctx(skb, ct) < 0 ||
561-
ctnetlink_dump_labels(skb, ct) < 0 ||
562-
ctnetlink_dump_id(skb, ct) < 0 ||
563-
ctnetlink_dump_use(skb, ct) < 0 ||
564-
ctnetlink_dump_master(skb, ct) < 0 ||
565-
ctnetlink_dump_ct_seq_adj(skb, ct) < 0 ||
566-
ctnetlink_dump_ct_synproxy(skb, ct) < 0)
591+
if (ctnetlink_dump_info(skb, ct) < 0)
567592
goto nla_put_failure;
568-
569-
if (!test_bit(IPS_OFFLOAD_BIT, &ct->status) &&
570-
(ctnetlink_dump_timeout(skb, ct) < 0 ||
571-
ctnetlink_dump_protoinfo(skb, ct) < 0))
593+
if (extinfo && ctnetlink_dump_extinfo(skb, ct, type) < 0)
572594
goto nla_put_failure;
573595

574596
nlmsg_end(skb, nlh);
@@ -953,13 +975,11 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
953975
if (!ctnetlink_filter_match(ct, cb->data))
954976
continue;
955977

956-
rcu_read_lock();
957978
res =
958979
ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid,
959980
cb->nlh->nlmsg_seq,
960981
NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
961-
ct);
962-
rcu_read_unlock();
982+
ct, true);
963983
if (res < 0) {
964984
nf_conntrack_get(&ct->ct_general);
965985
cb->args[1] = (unsigned long)ct;
@@ -1364,10 +1384,8 @@ static int ctnetlink_get_conntrack(struct net *net, struct sock *ctnl,
13641384
return -ENOMEM;
13651385
}
13661386

1367-
rcu_read_lock();
13681387
err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
1369-
NFNL_MSG_TYPE(nlh->nlmsg_type), ct);
1370-
rcu_read_unlock();
1388+
NFNL_MSG_TYPE(nlh->nlmsg_type), ct, true);
13711389
nf_ct_put(ct);
13721390
if (err <= 0)
13731391
goto free;
@@ -1429,12 +1447,18 @@ ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying
14291447
continue;
14301448
cb->args[1] = 0;
14311449
}
1432-
rcu_read_lock();
1450+
1451+
/* We can't dump extension info for the unconfirmed
1452+
* list because unconfirmed conntracks can have
1453+
* ct->ext reallocated (and thus freed).
1454+
*
1455+
* In the dying list case ct->ext can't be free'd
1456+
* until after we drop pcpu->lock.
1457+
*/
14331458
res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid,
14341459
cb->nlh->nlmsg_seq,
14351460
NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
1436-
ct);
1437-
rcu_read_unlock();
1461+
ct, dying ? true : false);
14381462
if (res < 0) {
14391463
if (!atomic_inc_not_zero(&ct->ct_general.use))
14401464
continue;

0 commit comments

Comments
 (0)