Skip to content

Commit d00551b

Browse files
committed
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
Steffen Klassert says: ==================== pull request (net): ipsec 2021-08-04 1) Fix a sysbot reported memory leak in xfrm_user_rcv_msg. From Pavel Skripkin. 2) Revert "xfrm: policy: Read seqcount outside of rcu-read side in xfrm_policy_lookup_bytype". This commit tried to fix a lockin bug, but only cured some of the symptoms. A proper fix is applied on top of this revert. 3) Fix a locking bug on xfrm state hash resize. A recent change on sequence counters accidentally repaced a spinlock by a mutex. Fix from Frederic Weisbecker. 4) Fix possible user-memory-access in xfrm_user_rcv_msg_compat(). From Dmitry Safonov. 5) Add initialiation sefltest fot xfrm_spdattr_type_t. From Dmitry Safonov. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents ff0ee9d + 480e93e commit d00551b

File tree

6 files changed

+231
-28
lines changed

6 files changed

+231
-28
lines changed

include/net/netns/xfrm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct netns_xfrm {
7575
#endif
7676
spinlock_t xfrm_state_lock;
7777
seqcount_spinlock_t xfrm_state_hash_generation;
78+
seqcount_spinlock_t xfrm_policy_hash_generation;
7879

7980
spinlock_t xfrm_policy_lock;
8081
struct mutex xfrm_cfg_mutex;

net/xfrm/xfrm_compat.c

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,16 @@ static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src)
298298
len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]);
299299

300300
nla_for_each_attr(nla, attrs, len, remaining) {
301-
int err = xfrm_xlate64_attr(dst, nla);
301+
int err;
302302

303+
switch (type) {
304+
case XFRM_MSG_NEWSPDINFO:
305+
err = xfrm_nla_cpy(dst, nla, nla_len(nla));
306+
break;
307+
default:
308+
err = xfrm_xlate64_attr(dst, nla);
309+
break;
310+
}
303311
if (err)
304312
return err;
305313
}
@@ -341,7 +349,8 @@ static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src
341349

342350
/* Calculates len of translated 64-bit message. */
343351
static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src,
344-
struct nlattr *attrs[XFRMA_MAX+1])
352+
struct nlattr *attrs[XFRMA_MAX + 1],
353+
int maxtype)
345354
{
346355
size_t len = nlmsg_len(src);
347356

@@ -358,10 +367,20 @@ static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src,
358367
case XFRM_MSG_POLEXPIRE:
359368
len += 8;
360369
break;
370+
case XFRM_MSG_NEWSPDINFO:
371+
/* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */
372+
return len;
361373
default:
362374
break;
363375
}
364376

377+
/* Unexpected for anything, but XFRM_MSG_NEWSPDINFO, please
378+
* correct both 64=>32-bit and 32=>64-bit translators to copy
379+
* new attributes.
380+
*/
381+
if (WARN_ON_ONCE(maxtype))
382+
return len;
383+
365384
if (attrs[XFRMA_SA])
366385
len += 4;
367386
if (attrs[XFRMA_POLICY])
@@ -440,7 +459,8 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
440459

441460
static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
442461
struct nlattr *attrs[XFRMA_MAX+1],
443-
size_t size, u8 type, struct netlink_ext_ack *extack)
462+
size_t size, u8 type, int maxtype,
463+
struct netlink_ext_ack *extack)
444464
{
445465
size_t pos;
446466
int i;
@@ -520,6 +540,25 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
520540
}
521541
pos = dst->nlmsg_len;
522542

543+
if (maxtype) {
544+
/* attirbutes are xfrm_spdattr_type_t, not xfrm_attr_type_t */
545+
WARN_ON_ONCE(src->nlmsg_type != XFRM_MSG_NEWSPDINFO);
546+
547+
for (i = 1; i <= maxtype; i++) {
548+
int err;
549+
550+
if (!attrs[i])
551+
continue;
552+
553+
/* just copy - no need for translation */
554+
err = xfrm_attr_cpy32(dst, &pos, attrs[i], size,
555+
nla_len(attrs[i]), nla_len(attrs[i]));
556+
if (err)
557+
return err;
558+
}
559+
return 0;
560+
}
561+
523562
for (i = 1; i < XFRMA_MAX + 1; i++) {
524563
int err;
525564

@@ -564,7 +603,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
564603
if (err < 0)
565604
return ERR_PTR(err);
566605

567-
len = xfrm_user_rcv_calculate_len64(h32, attrs);
606+
len = xfrm_user_rcv_calculate_len64(h32, attrs, maxtype);
568607
/* The message doesn't need translation */
569608
if (len == nlmsg_len(h32))
570609
return NULL;
@@ -574,7 +613,7 @@ static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32,
574613
if (!h64)
575614
return ERR_PTR(-ENOMEM);
576615

577-
err = xfrm_xlate32(h64, h32, attrs, len, type, extack);
616+
err = xfrm_xlate32(h64, h32, attrs, len, type, maxtype, extack);
578617
if (err < 0) {
579618
kvfree(h64);
580619
return ERR_PTR(err);

net/xfrm/xfrm_ipcomp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ static void ipcomp_free_tfms(struct crypto_comp * __percpu *tfms)
241241
break;
242242
}
243243

244-
WARN_ON(!pos);
244+
WARN_ON(list_entry_is_head(pos, &ipcomp_tfms_list, list));
245245

246246
if (--pos->users)
247247
return;

net/xfrm/xfrm_policy.c

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
155155
__read_mostly;
156156

157157
static struct kmem_cache *xfrm_dst_cache __ro_after_init;
158-
static __read_mostly seqcount_mutex_t xfrm_policy_hash_generation;
159158

160159
static struct rhashtable xfrm_policy_inexact_table;
161160
static const struct rhashtable_params xfrm_pol_inexact_params;
@@ -585,7 +584,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)
585584
return;
586585

587586
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
588-
write_seqcount_begin(&xfrm_policy_hash_generation);
587+
write_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation);
589588

590589
odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table,
591590
lockdep_is_held(&net->xfrm.xfrm_policy_lock));
@@ -596,7 +595,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)
596595
rcu_assign_pointer(net->xfrm.policy_bydst[dir].table, ndst);
597596
net->xfrm.policy_bydst[dir].hmask = nhashmask;
598597

599-
write_seqcount_end(&xfrm_policy_hash_generation);
598+
write_seqcount_end(&net->xfrm.xfrm_policy_hash_generation);
600599
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
601600

602601
synchronize_rcu();
@@ -1245,7 +1244,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
12451244
} while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq));
12461245

12471246
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
1248-
write_seqcount_begin(&xfrm_policy_hash_generation);
1247+
write_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation);
12491248

12501249
/* make sure that we can insert the indirect policies again before
12511250
* we start with destructive action.
@@ -1354,7 +1353,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
13541353

13551354
out_unlock:
13561355
__xfrm_policy_inexact_flush(net);
1357-
write_seqcount_end(&xfrm_policy_hash_generation);
1356+
write_seqcount_end(&net->xfrm.xfrm_policy_hash_generation);
13581357
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
13591358

13601359
mutex_unlock(&hash_resize_mutex);
@@ -2091,15 +2090,12 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
20912090
if (unlikely(!daddr || !saddr))
20922091
return NULL;
20932092

2094-
retry:
2095-
sequence = read_seqcount_begin(&xfrm_policy_hash_generation);
20962093
rcu_read_lock();
2097-
2098-
chain = policy_hash_direct(net, daddr, saddr, family, dir);
2099-
if (read_seqcount_retry(&xfrm_policy_hash_generation, sequence)) {
2100-
rcu_read_unlock();
2101-
goto retry;
2102-
}
2094+
retry:
2095+
do {
2096+
sequence = read_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation);
2097+
chain = policy_hash_direct(net, daddr, saddr, family, dir);
2098+
} while (read_seqcount_retry(&net->xfrm.xfrm_policy_hash_generation, sequence));
21032099

21042100
ret = NULL;
21052101
hlist_for_each_entry_rcu(pol, chain, bydst) {
@@ -2130,15 +2126,11 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
21302126
}
21312127

21322128
skip_inexact:
2133-
if (read_seqcount_retry(&xfrm_policy_hash_generation, sequence)) {
2134-
rcu_read_unlock();
2129+
if (read_seqcount_retry(&net->xfrm.xfrm_policy_hash_generation, sequence))
21352130
goto retry;
2136-
}
21372131

2138-
if (ret && !xfrm_pol_hold_rcu(ret)) {
2139-
rcu_read_unlock();
2132+
if (ret && !xfrm_pol_hold_rcu(ret))
21402133
goto retry;
2141-
}
21422134
fail:
21432135
rcu_read_unlock();
21442136

@@ -4089,6 +4081,7 @@ static int __net_init xfrm_net_init(struct net *net)
40894081
/* Initialize the per-net locks here */
40904082
spin_lock_init(&net->xfrm.xfrm_state_lock);
40914083
spin_lock_init(&net->xfrm.xfrm_policy_lock);
4084+
seqcount_spinlock_init(&net->xfrm.xfrm_policy_hash_generation, &net->xfrm.xfrm_policy_lock);
40924085
mutex_init(&net->xfrm.xfrm_cfg_mutex);
40934086

40944087
rv = xfrm_statistics_init(net);
@@ -4133,7 +4126,6 @@ void __init xfrm_init(void)
41334126
{
41344127
register_pernet_subsys(&xfrm_net_ops);
41354128
xfrm_dev_init();
4136-
seqcount_mutex_init(&xfrm_policy_hash_generation, &hash_resize_mutex);
41374129
xfrm_input_init();
41384130

41394131
#ifdef CONFIG_XFRM_ESPINTCP

net/xfrm/xfrm_user.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2811,6 +2811,16 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
28112811

28122812
err = link->doit(skb, nlh, attrs);
28132813

2814+
/* We need to free skb allocated in xfrm_alloc_compat() before
2815+
* returning from this function, because consume_skb() won't take
2816+
* care of frag_list since netlink destructor sets
2817+
* sbk->head to NULL. (see netlink_skb_destructor())
2818+
*/
2819+
if (skb_has_frag_list(skb)) {
2820+
kfree_skb(skb_shinfo(skb)->frag_list);
2821+
skb_shinfo(skb)->frag_list = NULL;
2822+
}
2823+
28142824
err:
28152825
kvfree(nlh64);
28162826
return err;

0 commit comments

Comments
 (0)