Re: [PATCH v4 9/9] ovl: use iget5_prealloc() to hash a newly created inode

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

 



On Fri, May 18, 2018 at 1:14 PM, Miklos Szeredi <miklos@xxxxxxxxxx> wrote:
> On Fri, May 18, 2018 at 10:29 AM, Amir Goldstein <amir73il@xxxxxxxxx> wrote:
>> Currently, there is a small window where ovl_obtain_alias() can
>> race with ovl_instantiate() and create two different overlay inodes
>> with the same underlying real non-dir non-hardlink inode.
>>
>> The race requires an adversary to guess the file handle of the
>> yet to be created upper inode and decode the guessed file handle
>> after ovl_creat_real(), but before ovl_instantiate().
>> This race does not affect overlay directory inodes, because those
>> are decoded via ovl_lookup_real() and not with ovl_obtain_alias().
>>
>> This patch fixes the race, by using iget5_prealloc() to add a newly
>> created inode to cache.
>>
>> If the newly created inode apears to already exist in cache (hashed
>> by the same real upper inode), we instantiate the dentry with the old
>> inode and drop the new inode, instead of silently not hashing the new
>> inode.
>>
>> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
>> ---
>>  fs/overlayfs/dir.c       | 81 +++++++++++++++++++++++++++++++++++-------------
>>  fs/overlayfs/inode.c     | 12 +++++--
>>  fs/overlayfs/overlayfs.h |  1 +
>>  3 files changed, 70 insertions(+), 24 deletions(-)
>>
>> diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
>> index 6f7400be3fc8..833528d3e351 100644
>> --- a/fs/overlayfs/dir.c
>> +++ b/fs/overlayfs/dir.c
>> @@ -217,24 +217,57 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
>>         return ovl_set_opaque_xerr(dentry, upperdentry, -EIO);
>>  }
>>
>> -/* Common operations required to be done after creation of file on upper */
>> -static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
>> -                           struct dentry *newdentry, bool hardlink)
>> +/*
>> + * Common operations required to be done after creation of file on upper.
>> + * If @hardlink is false, then @inode is a pre-allocated inode, not yet on
>> + * overlay's inode_sb_list.  If @newdentry is instantiated with @inode it
>> + * takes a reference on @inode.
>> + */
>> +static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
>> +                          struct dentry *newdentry, bool hardlink)
>>  {
>> +       struct ovl_inode_params oip = {
>> +               .upperdentry = newdentry,
>> +               .newinode = inode,
>> +       };
>> +
>>         ovl_dir_modified(dentry->d_parent, false);
>> -       ovl_copyattr(d_inode(newdentry), inode);
>>         ovl_dentry_set_upper_alias(dentry);
>>         if (!hardlink) {
>> -               ovl_inode_update(inode, newdentry);
>> +               /*
>> +                * ovl_obtain_alias() can be called after ovl_create_real()
>> +                * and before we get here, so we may get an inode from cache
>> +                * with the same real upperdentry that is not the inode we
>> +                * pre-allocated.  In this case we will use the cached inode
>> +                * to instantiate the new dentry.
>> +                *
>> +                * XXX: if we ever use ovl_obtain_alias() to decode directory
>> +                * file handles, need to use ovl_get_inode_locked() and
>> +                * d_instantiate_new() here to prevent from creating two
>> +                * hashed directory inode aliases.
>> +                */
>> +               inode = ovl_get_inode(dentry->d_sb, &oip);
>> +               if (IS_ERR(inode))
>> +                       return PTR_ERR(inode);
>>         } else {
>>                 WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
>>                 dput(newdentry);
>>                 inc_nlink(inode);
>>         }
>> +
>> +       ihold(inode);
>
> I don't get it.   We can get rid of this ihold...
>
>
>>         d_instantiate(dentry, inode);
>> +       if (inode != oip.newinode) {
>> +               pr_warn_ratelimited("overlayfs: newly created inode found in cache (%pd2)\n",
>> +                                   dentry);
>> +               iput(inode);
>
> ... and this iput ...
>
>> +       }
>> +
>>         /* Force lookup of new upper hardlink to find its lower */
>>         if (hardlink)
>>                 d_drop(dentry);
>> +
>> +       return 0;
>>  }
>>
>>  static bool ovl_type_merge(struct dentry *dentry)
>> @@ -273,11 +306,17 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
>>                 ovl_set_opaque(dentry, newdentry);
>>         }
>>
>> -       ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
>> -       err = 0;
>> +       err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
>> +       if (err)
>> +               goto out_cleanup;
>>  out_unlock:
>>         inode_unlock(udir);
>>         return err;
>> +
>> +out_cleanup:
>> +       ovl_cleanup(udir, newdentry);
>> +       dput(newdentry);
>> +       goto out_unlock;
>>  }
>>
>>  static struct dentry *ovl_clear_empty(struct dentry *dentry,
>> @@ -462,10 +501,9 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
>>                 if (err)
>>                         goto out_cleanup;
>>         }
>> -       ovl_instantiate(dentry, inode, newdentry, hardlink);
>> -       newdentry = NULL;
>> -out_dput2:
>> -       dput(newdentry);
>> +       err = ovl_instantiate(dentry, inode, newdentry, hardlink);
>> +       if (err)
>> +               goto out_cleanup;
>>  out_dput:
>>         dput(upper);
>>  out_unlock:
>> @@ -479,7 +517,8 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
>>
>>  out_cleanup:
>>         ovl_cleanup(wdir, newdentry);
>> -       goto out_dput2;
>> +       dput(newdentry);
>> +       goto out_dput;
>>  }
>>
>>  static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
>> @@ -547,8 +586,9 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
>>         if (err)
>>                 goto out;
>>
>> +       /* Preallocate inode to be used by ovl_get_inode() */
>>         err = -ENOMEM;
>> -       inode = ovl_new_inode(dentry->d_sb, mode, rdev);
>> +       inode = alloc_inode(dentry->d_sb);
>>         if (!inode)
>>                 goto out_drop_write;
>>
>> @@ -556,9 +596,12 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
>>         attr.mode = inode->i_mode;
>>
>>         err = ovl_create_or_link(dentry, inode, &attr, false);
>> -       if (err)
>> -               iput(inode);
>>
>> +       /* Did we ended up using the preallocated inode? */
>> +       if (inode == d_inode(dentry))
>> +               iput(inode);
>
> ... and this iput ...
>

-       if (err)
-               iput(inode);
+       /* Did we end up using the preallocated inode? */
+       if (err || inode != d_inode(dentry))
+               destroy_inode(inode);

Pushed to ovl-fixes.

Thanks,
Amir.
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux