On Thu, Apr 30, 2020 at 12:46:08PM -0700, Andrii Nakryiko wrote:
> If bpf_link_prime() succeeds to allocate new anon file, but then fails to
> allocate ID for it, link priming is considered to be failed and user is
> supposed ot be able to directly kfree() bpf_link, because it was never exposed
> to user-space.
> 
> But at that point file already keeps a pointer to bpf_link and will eventually
> call bpf_link_release(), so if bpf_link was kfree()'d by caller, that would
> lead to use-after-free.
> 
> Fix this by creating file with NULL private_data until ID allocation succeeds.
> Only then set private_data to bpf_link. Teach bpf_link_release() to recognize
> such situation and do nothing.
> 
> Fixes: a3b80e107894 ("bpf: Allocate ID for bpf_link")
> Reported-by: syzbot+39b64425f91b5aab7...@syzkaller.appspotmail.com
> Signed-off-by: Andrii Nakryiko <andr...@fb.com>
> ---
>  kernel/bpf/syscall.c | 16 +++++++++++++---
>  1 file changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index c75b2dd2459c..ce00df64a4d4 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -2267,7 +2267,12 @@ static int bpf_link_release(struct inode *inode, 
> struct file *filp)
>  {
>       struct bpf_link *link = filp->private_data;
>  
> -     bpf_link_put(link);
> +     /* if bpf_link_prime() allocated file, but failed to allocate ID,
> +      * file->private_data will be null and by now link itself is kfree()'d
> +      * directly, so just do nothing in such case.
> +      */
> +     if (link)
> +             bpf_link_put(link);
>       return 0;
>  }
>  
> @@ -2348,7 +2353,7 @@ int bpf_link_prime(struct bpf_link *link, struct 
> bpf_link_primer *primer)
>       if (fd < 0)
>               return fd;
>  
> -     file = anon_inode_getfile("bpf_link", &bpf_link_fops, link, O_CLOEXEC);
> +     file = anon_inode_getfile("bpf_link", &bpf_link_fops, NULL, O_CLOEXEC);
>       if (IS_ERR(file)) {
>               put_unused_fd(fd);
>               return PTR_ERR(file);
> @@ -2357,10 +2362,15 @@ int bpf_link_prime(struct bpf_link *link, struct 
> bpf_link_primer *primer)
>       id = bpf_link_alloc_id(link);
>       if (id < 0) {
>               put_unused_fd(fd);
> -             fput(file);
> +             fput(file); /* won't put link, so user can kfree() it */
>               return id;
>       }
>  
> +     /* Link priming succeeded, point file's private data to link now.
> +      * After this caller has to call bpf_link_cleanup() to free link.
> +      */
> +     file->private_data = link;
Instead of switching private_data back and forth, how about calling getfile() 
at end
(i.e. after alloc_id())?

> +
>       primer->link = link;
>       primer->file = file;
>       primer->fd = fd;
> -- 
> 2.24.1
> 

Reply via email to