Re: [PATCH v3 08/16] ovl: validate lower layer uuid and root on redirect by fh

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

 



On Thu, Apr 27, 2017 at 12:35 AM, Amir Goldstein <amir73il@xxxxxxxxx> wrote:
> On copy up, we store xattr overlay.origin.uuid and
> overlay.origin.root along with overlay.origin.fh.
>
> Before decoding the file handle at overlay.origin.fh verify:
> - All lower layers are on the same fs
> - UUID of lower fs matches the stored uuid
>
> If there are more than one lower layer, find the lower layer mount
> in which origin.fh should be decoded by decoding origin.root and
> matching the result to a lower layer root dentry.
>
> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
> ---
>  fs/overlayfs/namei.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 75 insertions(+), 1 deletion(-)
>
> diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
> index 28d54e3..970d8158 100644
> --- a/fs/overlayfs/namei.c
> +++ b/fs/overlayfs/namei.c
> @@ -27,6 +27,8 @@ struct ovl_lookup_data {
>         char *redirect;         /* - path to follow */
>         bool by_fh;             /* redirect by file handle: */
>         struct ovl_fh *fh;      /* - file handle to follow */
> +       struct ovl_fh *rootfh;  /* - file handle of layer root */
> +       unsigned char uuid[16]; /* - uuid of layer filesystem */
>  };
>
>  static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
> @@ -121,8 +123,24 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
>  static int ovl_check_redirect_fh(struct dentry *dentry,
>                                  struct ovl_lookup_data *d)
>  {
> +       int res;
> +
>         kfree(d->fh);
>         d->fh = ovl_get_fh(dentry, OVL_XATTR_ORIGIN_FH);
> +       kfree(d->rootfh);
> +       d->rootfh = ovl_get_fh(dentry, OVL_XATTR_ORIGIN_ROOT);
> +
> +       res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN_UUID, d->uuid,
> +                          sizeof(d->uuid));
> +       if (res == sizeof(d->uuid))
> +               return 0;
> +
> +       if (res != -ENODATA && res != -EOPNOTSUPP) {
> +               pr_warn_ratelimited("overlayfs: failed to get %s (%i)\n",
> +                                   OVL_XATTR_ORIGIN_UUID, res);
> +       }
> +
> +       memset(d->uuid, 0, sizeof(d->uuid));
>         return 0;
>  }
>
> @@ -130,6 +148,9 @@ static void ovl_reset_redirect_fh(struct ovl_lookup_data *d)
>  {
>         kfree(d->fh);
>         d->fh = NULL;
> +       kfree(d->rootfh);
> +       d->rootfh = NULL;
> +       memset(d->uuid, 0, sizeof(d->uuid));
>  }
>
>  static bool ovl_is_opaquedir(struct dentry *dentry)
> @@ -309,6 +330,53 @@ static int ovl_lookup_layer_fh(struct vfsmount *mnt, struct ovl_lookup_data *d,
>         return ovl_lookup_data(this, d, 0, "", ret);
>  }
>
> +static int ovl_is_dir(void *ctx, struct dentry *dentry)
> +{
> +       return d_is_dir(dentry);
> +}
> +
> +/* Find lower layer index by layer root file handle and uuid */
> +static int ovl_find_layer_by_fh(struct dentry *dentry, struct ovl_lookup_data *d)
> +{
> +       struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata;
> +       struct super_block *lower_sb = ovl_same_lower_sb(dentry->d_sb);
> +       struct dentry *this;
> +       int i;
> +
> +       /*
> +        * For now, we only support lookup by fh for all lower layers on the
> +        * same sb.  Not all filesystems set sb->s_uuid.  For those who don't
> +        * this code will compare zeros, which at least ensures us that the
> +        * file handles are not crossing from filesystem with sb->s_uuid to
> +        * a filesystem without sb->s_uuid and vice versa.

Nah. On second thought, it's better to disable redirect_fh at mount time
if lower s_uuid is NULL.

> +        */
> +       if (!lower_sb || memcmp(lower_sb->s_uuid, &d->uuid, sizeof(d->uuid)))
> +               return -1;
> +
> +       /* Don't bother verifying rootfh with a single lower layer */
> +       if (roe->numlower == 1)
> +               return 0;
> +
> +       /*
> +        * Layer root dentries are pinned, there are no aliases for dirs, and
> +        * all lower layers are on the same sb.  If rootfh is correct,
> +        * exportfs_decode_fh() will find it in dcache and return the only
> +        * instance, regardless of the mnt argument and we can compare the
> +        * returned pointer with the pointers in lowerstack.
> +        */
> +       this = ovl_decode_fh(roe->lowerstack[0].mnt, d->rootfh, ovl_is_dir);
> +       if (IS_ERR(this))
> +               return -1;
> +
> +       for (i = 0; i < roe->numlower; i++) {
> +               if (this == roe->lowerstack[i].dentry)
> +                       break;
> +       }
> +
> +       dput(this);
> +       return i < roe->numlower ? i : -1;
> +}
> +
>  /*
>   * Returns next layer in stack starting from top.
>   * Returns -1 if this is the last layer.
> @@ -357,6 +425,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
>                 .redirect = NULL,
>                 .by_fh = true,
>                 .fh = NULL,
> +               .rootfh = NULL,
>         };
>
>         if (dentry->d_name.len > ofs->namelen)
> @@ -405,8 +474,13 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
>         /* Try to lookup lower layers by file handle */
>         d.by_path = false;
>         if (!d.stop && d.fh) {
> -               struct vfsmount *lowermnt = roe->lowerstack[0].mnt;
> +               struct vfsmount *lowermnt;
> +               int layer = ovl_find_layer_by_fh(dentry, &d);
> +
> +               if (layer < 0 || layer >= roe->numlower)
> +                       goto lookup_by_path;
>
> +               lowermnt = roe->lowerstack[layer].mnt;
>                 d.last = true;
>                 err = ovl_lookup_layer_fh(lowermnt, &d, &this);
>                 if (err)
> --
> 2.7.4
>



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