Re: [PATCH v2 3/3] fs: port files to file_ref

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Oct 25, 2024 at 10:45 PM Jann Horn <jannh@xxxxxxxxxx> wrote:
> On Mon, Oct 7, 2024 at 4:23 PM Christian Brauner <brauner@xxxxxxxxxx> wrote:
> > Port files to rely on file_ref reference to improve scaling and gain
> > overflow protection.
> [...]
> > diff --git a/fs/file_table.c b/fs/file_table.c
> > index 4b23eb7b79dd9d4ec779f4c01ba2e902988895dc..3f5dc4176b21ff82cc9440ed92a0ad962fdb2046 100644
> > --- a/fs/file_table.c
> > +++ b/fs/file_table.c
> > @@ -178,7 +178,7 @@ static int init_file(struct file *f, int flags, const struct cred *cred)
> >          * fget-rcu pattern users need to be able to handle spurious
> >          * refcount bumps we should reinitialize the reused file first.
> >          */
> > -       atomic_long_set(&f->f_count, 1);
> > +       file_ref_init(&f->f_ref, 1);
>
> It is good that you use file_ref_init() here to atomically initialize
> the file_ref; however, I think it is problematic that before this,
> alloc_empty_file() uses kmem_cache_zalloc(filp_cachep, GFP_KERNEL) to
> allocate the file, because that sets __GFP_ZERO, which means that
> slab_post_alloc_hook() will use memset() to zero the file object. That
> causes trouble in two different ways:
>
>
> 1. After the memset() has changed the file ref to zero, I think
> file_ref_get() can return true? Which means __get_file_rcu() could
> believe that it acquired a reference, and we could race like this:
>
> task A                          task B
>                                 __get_file_rcu()
>                                   rcu_dereference_raw()
> close()
>   [frees file]
> alloc_empty_file()
>   kmem_cache_zalloc()
>     [reallocates same file]
>     memset(..., 0, ...)
>                                   file_ref_get()
>                                     [increments 0->1, returns true]
>   init_file()
>     file_ref_init(..., 1)
>       [sets to 0]
>                                   rcu_dereference_raw()
>                                   fput()
>                                     file_ref_put()
>                                       [decrements 0->FILE_REF_NOREF, frees file]
>   [UAF]
>
>
> 2. AFAIK the memset() is not guaranteed to atomically update an
> "unsigned long", so you could see an entirely bogus torn counter
> value.
>
> The only reason this worked in the old code is that the refcount value
> stored in freed files is 0.
>
> So I think you need to stop using kmem_cache_zalloc() to allocate
> files, and instead use a constructor function that zeroes the refcount
> field, and manually memset() the rest of the "struct file" to 0 after
> calling kmem_cache_alloc().

Actually, thinking about this again, I guess there's actually no
reason why you'd need a constructor function; if you just avoid
memset()ing the refcount field on allocation, that'd be good enough.





[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux