Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipsec: Fix aborted xfrm policy dump crash

An independent security researcher, Mohamed Ghannam, has reported
this vulnerability to Beyond Security's SecuriTeam Secure Disclosure
program.

The xfrm_dump_policy_done function expects xfrm_dump_policy to
have been called at least once or it will crash. This can be
triggered if a dump fails because the target socket's receive
buffer is full.

This patch fixes it by using the cb->start mechanism to ensure that
the initialisation is always done regardless of the buffer situation.

Fixes: 12a169e7d8f4 ("ipsec: Put dumpers on the dump list")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

authored by

Herbert Xu and committed by
Steffen Klassert
1137b5e2 10a7ef33

+15 -10
+15 -10
net/xfrm/xfrm_user.c
··· 1693 1693 1694 1694 static int xfrm_dump_policy_done(struct netlink_callback *cb) 1695 1695 { 1696 - struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 1696 + struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args; 1697 1697 struct net *net = sock_net(cb->skb->sk); 1698 1698 1699 1699 xfrm_policy_walk_done(walk, net); 1700 1700 return 0; 1701 1701 } 1702 1702 1703 + static int xfrm_dump_policy_start(struct netlink_callback *cb) 1704 + { 1705 + struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args; 1706 + 1707 + BUILD_BUG_ON(sizeof(*walk) > sizeof(cb->args)); 1708 + 1709 + xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 1710 + return 0; 1711 + } 1712 + 1703 1713 static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 1704 1714 { 1705 1715 struct net *net = sock_net(skb->sk); 1706 - struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 1716 + struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args; 1707 1717 struct xfrm_dump_info info; 1708 - 1709 - BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 1710 - sizeof(cb->args) - sizeof(cb->args[0])); 1711 1718 1712 1719 info.in_skb = cb->skb; 1713 1720 info.out_skb = skb; 1714 1721 info.nlmsg_seq = cb->nlh->nlmsg_seq; 1715 1722 info.nlmsg_flags = NLM_F_MULTI; 1716 - 1717 - if (!cb->args[0]) { 1718 - cb->args[0] = 1; 1719 - xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 1720 - } 1721 1723 1722 1724 (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 1723 1725 ··· 2476 2474 2477 2475 static const struct xfrm_link { 2478 2476 int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 2477 + int (*start)(struct netlink_callback *); 2479 2478 int (*dump)(struct sk_buff *, struct netlink_callback *); 2480 2479 int (*done)(struct netlink_callback *); 2481 2480 const struct nla_policy *nla_pol; ··· 2490 2487 [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2491 2488 [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2492 2489 [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 2490 + .start = xfrm_dump_policy_start, 2493 2491 .dump = xfrm_dump_policy, 2494 2492 .done = xfrm_dump_policy_done }, 2495 2493 [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, ··· 2543 2539 2544 2540 { 2545 2541 struct netlink_dump_control c = { 2542 + .start = link->start, 2546 2543 .dump = link->dump, 2547 2544 .done = link->done, 2548 2545 };