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 4:08 PM, Amir Goldstein <amir73il@xxxxxxxxx> wrote:
> 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);

Minor nit: we don't even need the "err ||" since in the error case
d_inode(dentry) will be NULL.   I'll fix that up.

Thanks,
Miklos
--
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