diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2015-08-04 15:42:47 +0800 |
---|---|---|
committer | Sasha Levin <sasha.levin@oracle.com> | 2016-04-12 08:47:23 -0400 |
commit | fae6f76ce1a42ed2f7f6ec1ba02e29602b0ebc16 (patch) | |
tree | 78b36491e63b5e6c74237b32cf1b8be5461aec03 /net | |
parent | 9f0b813dfce98f289ca6511f20c9ab38c5670027 (diff) | |
download | lwn-fae6f76ce1a42ed2f7f6ec1ba02e29602b0ebc16.tar.gz lwn-fae6f76ce1a42ed2f7f6ec1ba02e29602b0ebc16.zip |
net: Fix skb_set_peeked use-after-free bug
[ Upstream commit a0a2a6602496a45ae838a96db8b8173794b5d398 ]
The commit 738ac1ebb96d02e0d23bc320302a6ea94c612dec ("net: Clone
skb before setting peeked flag") introduced a use-after-free bug
in skb_recv_datagram. This is because skb_set_peeked may create
a new skb and free the existing one. As it stands the caller will
continue to use the old freed skb.
This patch fixes it by making skb_set_peeked return the new skb
(or the old one if unchanged).
Fixes: 738ac1ebb96d ("net: Clone skb before setting peeked flag")
Reported-by: Brenden Blanco <bblanco@plumgrid.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Tested-by: Brenden Blanco <bblanco@plumgrid.com>
Reviewed-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/datagram.c | 13 |
1 files changed, 7 insertions, 6 deletions
diff --git a/net/core/datagram.c b/net/core/datagram.c index 61e99f315ed9..2850ab32f28a 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -130,12 +130,12 @@ out_noerr: goto out; } -static int skb_set_peeked(struct sk_buff *skb) +static struct sk_buff *skb_set_peeked(struct sk_buff *skb) { struct sk_buff *nskb; if (skb->peeked) - return 0; + return skb; /* We have to unshare an skb before modifying it. */ if (!skb_shared(skb)) @@ -143,7 +143,7 @@ static int skb_set_peeked(struct sk_buff *skb) nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); skb->prev->next = nskb; skb->next->prev = nskb; @@ -156,7 +156,7 @@ static int skb_set_peeked(struct sk_buff *skb) done: skb->peeked = 1; - return 0; + return skb; } /** @@ -228,8 +228,9 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, continue; } - error = skb_set_peeked(skb); - if (error) + skb = skb_set_peeked(skb); + error = PTR_ERR(skb); + if (IS_ERR(skb)) goto unlock_err; atomic_inc(&skb->users); |