net: Fix skb_set_peeked use-after-free bug
The commit738ac1ebb9
("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:738ac1ebb9
("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>
This commit is contained in:
parent
14d2b7c1a9
commit
a0a2a66024
|
@ -131,12 +131,12 @@ out_noerr:
|
||||||
goto out;
|
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;
|
struct sk_buff *nskb;
|
||||||
|
|
||||||
if (skb->peeked)
|
if (skb->peeked)
|
||||||
return 0;
|
return skb;
|
||||||
|
|
||||||
/* We have to unshare an skb before modifying it. */
|
/* We have to unshare an skb before modifying it. */
|
||||||
if (!skb_shared(skb))
|
if (!skb_shared(skb))
|
||||||
|
@ -144,7 +144,7 @@ static int skb_set_peeked(struct sk_buff *skb)
|
||||||
|
|
||||||
nskb = skb_clone(skb, GFP_ATOMIC);
|
nskb = skb_clone(skb, GFP_ATOMIC);
|
||||||
if (!nskb)
|
if (!nskb)
|
||||||
return -ENOMEM;
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
skb->prev->next = nskb;
|
skb->prev->next = nskb;
|
||||||
skb->next->prev = nskb;
|
skb->next->prev = nskb;
|
||||||
|
@ -157,7 +157,7 @@ static int skb_set_peeked(struct sk_buff *skb)
|
||||||
done:
|
done:
|
||||||
skb->peeked = 1;
|
skb->peeked = 1;
|
||||||
|
|
||||||
return 0;
|
return skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -229,8 +229,9 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = skb_set_peeked(skb);
|
skb = skb_set_peeked(skb);
|
||||||
if (error)
|
error = PTR_ERR(skb);
|
||||||
|
if (IS_ERR(skb))
|
||||||
goto unlock_err;
|
goto unlock_err;
|
||||||
|
|
||||||
atomic_inc(&skb->users);
|
atomic_inc(&skb->users);
|
||||||
|
|
Loading…
Reference in New Issue