lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:   Fri, 1 May 2020 10:46:30 -0700
From:   Andrii Nakryiko <andrii.nakryiko@...il.com>
To:     Martin KaFai Lau <kafai@...com>
Cc:     Andrii Nakryiko <andriin@...com>, bpf <bpf@...r.kernel.org>,
        Networking <netdev@...r.kernel.org>,
        Alexei Starovoitov <ast@...com>,
        Daniel Borkmann <daniel@...earbox.net>,
        Kernel Team <kernel-team@...com>,
        syzbot <syzbot+39b64425f91b5aab714d@...kaller.appspotmail.com>
Subject: Re: [Potential Spoof] [PATCH bpf-next] bpf: fix use-after-free of
 bpf_link when priming half-fails

On Fri, May 1, 2020 at 12:09 AM Martin KaFai Lau <kafai@...com> wrote:
>
> On Thu, Apr 30, 2020 at 11:32:59PM -0700, Andrii Nakryiko wrote:
> > On Thu, Apr 30, 2020 at 11:25 PM Martin KaFai Lau <kafai@...com> wrote:
> > >
> > > 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+39b64425f91b5aab714d@...kaller.appspotmail.com
> > > > Signed-off-by: Andrii Nakryiko <andriin@...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())?
> > >
> >
> > Because once ID is allocated, user-space might have bumped bpf_link
> > refcnt already, so we can't just kfree() it after that.
> link->id is not set, so refcnt should not be bumped.
>
> Calling bpf_link_free_id(id) at the getfile() error path should be enough?
>

You are totally right! This was my initial instinct to do it this way,
but I somehow convinced myself it wouldn't work due to possible refcnt
bump. But you are right, there is `if (link->id)` check, which will
make all this safe. Thanks for suggestion! I'll re-work this patch.

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

Powered by blists - more mailing lists