On Fri, May 1, 2020 at 12:09 AM Martin KaFai Lau <kafai@xxxxxx> 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@xxxxxx> 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@xxxxxxxxxxxxxxxxxxxxxxxxx > > > > Signed-off-by: Andrii Nakryiko <andriin@xxxxxx> > > > > --- > > > > 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 > > > >