Skip to content

Commit bffcaad

Browse files
committed
netfilter: ctnetlink: ensure safe access to master conntrack
Holding reference on the expectation is not sufficient, the master conntrack object can just go away, making exp->master invalid. To access exp->master safely: - Grab the nf_conntrack_expect_lock, this gets serialized with clean_from_lists() which also holds this lock when the master conntrack goes away. - Hold reference on master conntrack via nf_conntrack_find_get(). Not so easy since the master tuple to look up for the master conntrack is not available in the existing problematic paths. This patch goes for extending the nf_conntrack_expect_lock section to address this issue for simplicity, in the cases that are described below this is just slightly extending the lock section. The add expectation command already holds a reference to the master conntrack from ctnetlink_create_expect(). However, the delete expectation command needs to grab the spinlock before looking up for the expectation. Expand the existing spinlock section to address this to cover the expectation lookup. Note that, the nf_ct_expect_iterate_net() calls already grabs the spinlock while iterating over the expectation table, which is correct. The get expectation command needs to grab the spinlock to ensure master conntrack does not go away. This also expands the existing spinlock section to cover the expectation lookup too. I needed to move the netlink skb allocation out of the spinlock to keep it GFP_KERNEL. For the expectation events, the IPEXP_DESTROY event is already delivered under the spinlock, just move the delivery of IPEXP_NEW under the spinlock too because the master conntrack event cache is reached through exp->master. While at it, add lockdep notations to help identify what codepaths need to grab the spinlock. Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent f017941 commit bffcaad

4 files changed

Lines changed: 35 additions & 10 deletions

File tree

include/net/netfilter/nf_conntrack_core.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ void nf_conntrack_lock(spinlock_t *lock);
8383

8484
extern spinlock_t nf_conntrack_expect_lock;
8585

86+
static inline void lockdep_nfct_expect_lock_held(void)
87+
{
88+
lockdep_assert_held(&nf_conntrack_expect_lock);
89+
}
90+
8691
/* ctnetlink code shared by both ctnetlink and nf_conntrack_bpf */
8792

8893
static inline void __nf_ct_set_timeout(struct nf_conn *ct, u64 timeout)

net/netfilter/nf_conntrack_ecache.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
247247
struct nf_ct_event_notifier *notify;
248248
struct nf_conntrack_ecache *e;
249249

250+
lockdep_nfct_expect_lock_held();
251+
250252
rcu_read_lock();
251253
notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
252254
if (!notify)

net/netfilter/nf_conntrack_expect.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
5151
struct net *net = nf_ct_exp_net(exp);
5252
struct nf_conntrack_net *cnet;
5353

54+
lockdep_nfct_expect_lock_held();
5455
WARN_ON(!master_help);
5556
WARN_ON(timer_pending(&exp->timeout));
5657

@@ -118,6 +119,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
118119

119120
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
120121
{
122+
lockdep_nfct_expect_lock_held();
123+
121124
if (timer_delete(&exp->timeout)) {
122125
nf_ct_unlink_expect(exp);
123126
nf_ct_expect_put(exp);
@@ -177,6 +180,8 @@ nf_ct_find_expectation(struct net *net,
177180
struct nf_conntrack_expect *i, *exp = NULL;
178181
unsigned int h;
179182

183+
lockdep_nfct_expect_lock_held();
184+
180185
if (!cnet->expect_count)
181186
return NULL;
182187

@@ -454,6 +459,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
454459
unsigned int h;
455460
int ret = 0;
456461

462+
lockdep_nfct_expect_lock_held();
463+
457464
if (!master_help) {
458465
ret = -ESHUTDOWN;
459466
goto out;
@@ -510,8 +517,9 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
510517

511518
nf_ct_expect_insert(expect);
512519

513-
spin_unlock_bh(&nf_conntrack_expect_lock);
514520
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
521+
spin_unlock_bh(&nf_conntrack_expect_lock);
522+
515523
return 0;
516524
out:
517525
spin_unlock_bh(&nf_conntrack_expect_lock);

net/netfilter/nf_conntrack_netlink.c

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3355,31 +3355,37 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
33553355
if (err < 0)
33563356
return err;
33573357

3358+
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
3359+
if (!skb2)
3360+
return -ENOMEM;
3361+
3362+
spin_lock_bh(&nf_conntrack_expect_lock);
33583363
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
3359-
if (!exp)
3364+
if (!exp) {
3365+
spin_unlock_bh(&nf_conntrack_expect_lock);
3366+
kfree_skb(skb2);
33603367
return -ENOENT;
3368+
}
33613369

33623370
if (cda[CTA_EXPECT_ID]) {
33633371
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
33643372

33653373
if (id != nf_expect_get_id(exp)) {
33663374
nf_ct_expect_put(exp);
3375+
spin_unlock_bh(&nf_conntrack_expect_lock);
3376+
kfree_skb(skb2);
33673377
return -ENOENT;
33683378
}
33693379
}
33703380

3371-
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
3372-
if (!skb2) {
3373-
nf_ct_expect_put(exp);
3374-
return -ENOMEM;
3375-
}
3376-
33773381
rcu_read_lock();
33783382
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
33793383
info->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
33803384
exp);
33813385
rcu_read_unlock();
33823386
nf_ct_expect_put(exp);
3387+
spin_unlock_bh(&nf_conntrack_expect_lock);
3388+
33833389
if (err <= 0) {
33843390
kfree_skb(skb2);
33853391
return -ENOMEM;
@@ -3426,22 +3432,26 @@ static int ctnetlink_del_expect(struct sk_buff *skb,
34263432
if (err < 0)
34273433
return err;
34283434

3435+
spin_lock_bh(&nf_conntrack_expect_lock);
3436+
34293437
/* bump usage count to 2 */
34303438
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
3431-
if (!exp)
3439+
if (!exp) {
3440+
spin_unlock_bh(&nf_conntrack_expect_lock);
34323441
return -ENOENT;
3442+
}
34333443

34343444
if (cda[CTA_EXPECT_ID]) {
34353445
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
34363446

34373447
if (id != nf_expect_get_id(exp)) {
34383448
nf_ct_expect_put(exp);
3449+
spin_unlock_bh(&nf_conntrack_expect_lock);
34393450
return -ENOENT;
34403451
}
34413452
}
34423453

34433454
/* after list removal, usage count == 1 */
3444-
spin_lock_bh(&nf_conntrack_expect_lock);
34453455
if (timer_delete(&exp->timeout)) {
34463456
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
34473457
nlmsg_report(info->nlh));

0 commit comments

Comments
 (0)