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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
4164cda8ca
commit
5fa39f1603
+7
-6
@@ -128,12 +128,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))
|
||||
@@ -141,7 +141,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;
|
||||
@@ -154,7 +154,7 @@ static int skb_set_peeked(struct sk_buff *skb)
|
||||
done:
|
||||
skb->peeked = 1;
|
||||
|
||||
return 0;
|
||||
return skb;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,8 +226,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);
|
||||
|
||||
Reference in New Issue
Block a user