linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jann Horn <jannh@google.com>
To: David Howells <dhowells@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>,
	raven@themaw.net, Miklos Szeredi <mszeredi@redhat.com>,
	Christian Brauner <christian@brauner.io>,
	Linux API <linux-api@vger.kernel.org>,
	linux-fsdevel <linux-fsdevel@vger.kernel.org>,
	kernel list <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH 01/19] vfs: syscall: Add fsinfo() to query filesystem information [ver #16]
Date: Wed, 19 Feb 2020 21:07:11 +0100	[thread overview]
Message-ID: <CAG48ez0o3iHjQJNvh8V2Ao77g0CqfqGsv6caMCOFDy7w-VdtkQ@mail.gmail.com> (raw)
In-Reply-To: <158204550281.3299825.6344518327575765653.stgit@warthog.procyon.org.uk>

On Tue, Feb 18, 2020 at 6:05 PM David Howells <dhowells@redhat.com> wrote:
> Add a system call to allow filesystem information to be queried.  A request
> value can be given to indicate the desired attribute.  Support is provided
> for enumerating multi-value attributes.
[...]
> +static const struct fsinfo_attribute fsinfo_common_attributes[];
> +
> +/**
> + * fsinfo_string - Store a string as an fsinfo attribute value.
> + * @s: The string to store (may be NULL)
> + * @ctx: The parameter context
> + */
> +int fsinfo_string(const char *s, struct fsinfo_context *ctx)
> +{
> +       int ret = 0;
> +
> +       if (s) {
> +               ret = strlen(s);
> +               memcpy(ctx->buffer, s, ret);
> +       }
> +
> +       return ret;
> +}

Please add a check here to ensure that "ret" actually fits into the
buffer (and use WARN_ON() if you think the check should never fire).
Otherwise I think this is too fragile.

[...]
> +static int fsinfo_generic_ids(struct path *path, struct fsinfo_context *ctx)
> +{
> +       struct fsinfo_ids *p = ctx->buffer;
> +       struct super_block *sb;
> +       struct kstatfs buf;
> +       int ret;
> +
> +       ret = vfs_statfs(path, &buf);
> +       if (ret < 0 && ret != -ENOSYS)
> +               return ret;

What's going on here? If vfs_statfs() returns -ENOSYS, we just use the
(AFAICS uninitialized) buf.f_fsid anyway in the memcpy() below and
return it to userspace?

> +       sb = path->dentry->d_sb;
> +       p->f_fstype     = sb->s_magic;
> +       p->f_dev_major  = MAJOR(sb->s_dev);
> +       p->f_dev_minor  = MINOR(sb->s_dev);
> +
> +       memcpy(&p->f_fsid, &buf.f_fsid, sizeof(p->f_fsid));
> +       strlcpy(p->f_fs_name, path->dentry->d_sb->s_type->name,
> +               sizeof(p->f_fs_name));
> +       return sizeof(*p);
> +}
[...]
> +static int fsinfo_attribute_info(struct path *path, struct fsinfo_context *ctx)
> +{
> +       const struct fsinfo_attribute *attr;
> +       struct fsinfo_attribute_info *info = ctx->buffer;
> +       struct dentry *dentry = path->dentry;
> +
> +       if (dentry->d_sb->s_op->fsinfo_attributes)
> +               for (attr = dentry->d_sb->s_op->fsinfo_attributes; attr->get; attr++)
> +                       if (attr->attr_id == ctx->Nth)
> +                               goto found;
> +       for (attr = fsinfo_common_attributes; attr->get; attr++)
> +               if (attr->attr_id == ctx->Nth)
> +                       goto found;
> +       return -ENODATA;
> +
> +found:
> +       info->attr_id           = attr->attr_id;
> +       info->type              = attr->type;
> +       info->flags             = attr->flags;
> +       info->size              = attr->size;
> +       info->element_size      = attr->element_size;
> +       return sizeof(*attr);

I think you meant sizeof(*info).

[...]
> +static int fsinfo_attributes(struct path *path, struct fsinfo_context *ctx)
> +{
> +       const struct fsinfo_attribute *attr;
> +       struct super_block *sb = path->dentry->d_sb;
> +
> +       if (sb->s_op->fsinfo_attributes)
> +               for (attr = sb->s_op->fsinfo_attributes; attr->get; attr++)
> +                       fsinfo_attributes_insert(ctx, attr);
> +       for (attr = fsinfo_common_attributes; attr->get; attr++)
> +               fsinfo_attributes_insert(ctx, attr);
> +       return ctx->usage;

It is kind of weird that you have to return the ctx->usage everywhere
even though the caller already has ctx...

> +}
> +
> +static const struct fsinfo_attribute fsinfo_common_attributes[] = {
> +       FSINFO_VSTRUCT  (FSINFO_ATTR_STATFS,            fsinfo_generic_statfs),
> +       FSINFO_VSTRUCT  (FSINFO_ATTR_IDS,               fsinfo_generic_ids),
> +       FSINFO_VSTRUCT  (FSINFO_ATTR_LIMITS,            fsinfo_generic_limits),
> +       FSINFO_VSTRUCT  (FSINFO_ATTR_SUPPORTS,          fsinfo_generic_supports),
> +       FSINFO_VSTRUCT  (FSINFO_ATTR_TIMESTAMP_INFO,    fsinfo_generic_timestamp_info),
> +       FSINFO_STRING   (FSINFO_ATTR_VOLUME_ID,         fsinfo_generic_volume_id),
> +       FSINFO_VSTRUCT  (FSINFO_ATTR_VOLUME_UUID,       fsinfo_generic_volume_uuid),
> +       FSINFO_VSTRUCT_N(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO, fsinfo_attribute_info),
> +       FSINFO_LIST     (FSINFO_ATTR_FSINFO_ATTRIBUTES, fsinfo_attributes),
> +       {}
> +};
> +
> +/*
> + * Retrieve large filesystem information, such as an opaque blob or array of
> + * struct elements where the value isn't limited to the size of a page.
> + */
> +static int vfs_fsinfo_large(struct path *path, struct fsinfo_context *ctx,
> +                           const struct fsinfo_attribute *attr)
> +{
> +       int ret;
> +
> +       while (!signal_pending(current)) {
> +               ctx->usage = 0;
> +               ret = attr->get(path, ctx);
> +               if (IS_ERR_VALUE((long)ret))
> +                       return ret; /* Error */
> +               if ((unsigned int)ret <= ctx->buf_size)
> +                       return ret; /* It fitted */
> +
> +               /* We need to resize the buffer */
> +               kvfree(ctx->buffer);
> +               ctx->buffer = NULL;
> +               ctx->buf_size = roundup(ret, PAGE_SIZE);
> +               if (ctx->buf_size > INT_MAX)
> +                       return -EMSGSIZE;
> +               ctx->buffer = kvmalloc(ctx->buf_size, GFP_KERNEL);

ctx->buffer is _almost_ always pre-zeroed (see vfs_do_fsinfo() below),
except if we have FSINFO_TYPE_OPAQUE or FSINFO_TYPE_LIST with a size
bigger than what the attribute's ->size field said? Is that
intentional?

> +               if (!ctx->buffer)
> +                       return -ENOMEM;
> +       }
> +
> +       return -ERESTARTSYS;
> +}
> +
> +static int vfs_do_fsinfo(struct path *path, struct fsinfo_context *ctx,
> +                        const struct fsinfo_attribute *attr)
> +{
> +       if (ctx->Nth != 0 && !(attr->flags & (FSINFO_FLAGS_N | FSINFO_FLAGS_NM)))
> +               return -ENODATA;
> +       if (ctx->Mth != 0 && !(attr->flags & FSINFO_FLAGS_NM))
> +               return -ENODATA;
> +
> +       ctx->buf_size = attr->size;
> +       if (ctx->want_size_only && attr->type == FSINFO_TYPE_VSTRUCT)
> +               return attr->size;
> +
> +       ctx->buffer = kvzalloc(ctx->buf_size, GFP_KERNEL);
> +       if (!ctx->buffer)
> +               return -ENOMEM;
> +
> +       switch (attr->type) {
> +       case FSINFO_TYPE_VSTRUCT:
> +               ctx->clear_tail = true;
> +               /* Fall through */
> +       case FSINFO_TYPE_STRING:
> +               return attr->get(path, ctx);
> +
> +       case FSINFO_TYPE_OPAQUE:
> +       case FSINFO_TYPE_LIST:
> +               return vfs_fsinfo_large(path, ctx, attr);
> +
> +       default:
> +               return -ENOPKG;
> +       }
> +}
[...]
> +SYSCALL_DEFINE5(fsinfo,
> +               int, dfd, const char __user *, pathname,
> +               struct fsinfo_params __user *, params,
> +               void __user *, user_buffer, size_t, user_buf_size)
> +{
[...]
> +       if (ret < 0)
> +               goto error;
> +
> +       result_size = ret;
> +       if (result_size > user_buf_size)
> +               result_size = user_buf_size;

This is "result_size = min_t(size_t, ret, user_buf_size)".

[...]
> +/*
> + * A filesystem information attribute definition.
> + */
> +struct fsinfo_attribute {
> +       unsigned int            attr_id;        /* The ID of the attribute */
> +       enum fsinfo_value_type  type:8;         /* The type of the attribute's value(s) */
> +       unsigned int            flags:8;
> +       unsigned int            size:16;        /* - Value size (FSINFO_STRUCT) */
> +       unsigned int            element_size:16; /* - Element size (FSINFO_LIST) */
> +       int (*get)(struct path *path, struct fsinfo_context *params);
> +};

Why the bitfields? It doesn't look like that's going to help you much,
you'll just end up with 6 bytes of holes on x86-64:

$ cat fsinfo_attribute_layout.c
enum fsinfo_value_type {
  FSINFO_TYPE_VSTRUCT     = 0,    /* Version-lengthed struct (up to
4096 bytes) */
  FSINFO_TYPE_STRING      = 1,    /* NUL-term var-length string (up to
4095 chars) */
  FSINFO_TYPE_OPAQUE      = 2,    /* Opaque blob (unlimited size) */
  FSINFO_TYPE_LIST        = 3,    /* List of ints/structs (unlimited size) */
};

struct fsinfo_attribute {
  unsigned int            attr_id;        /* The ID of the attribute */
  enum fsinfo_value_type  type:8;         /* The type of the
attribute's value(s) */
  unsigned int            flags:8;
  unsigned int            size:16;        /* - Value size (FSINFO_STRUCT) */
  unsigned int            element_size:16; /* - Element size (FSINFO_LIST) */
  void *get;
};
void *blah(struct fsinfo_attribute *p) {
  return p->get;
}
$ gcc -c -o fsinfo_attribute_layout.o fsinfo_attribute_layout.c -ggdb
$ pahole -C fsinfo_attribute -E --hex fsinfo_attribute_layout.o
struct fsinfo_attribute {
        unsigned int               attr_id;          /*     0   0x4 */
        enum fsinfo_value_type type:8;               /*   0x4: 0 0x4 */
        unsigned int               flags:8;          /*   0x4:0x8 0x4 */
        unsigned int               size:16;          /*   0x4:0x10 0x4 */
        unsigned int               element_size:16;  /*   0x8: 0 0x4 */

        /* XXX 16 bits hole, try to pack */
        /* XXX 4 bytes hole, try to pack */

        void *                     get;              /*  0x10   0x8 */

        /* size: 24, cachelines: 1, members: 6 */
        /* sum members: 12, holes: 1, sum holes: 4 */
        /* sum bitfield members: 48 bits, bit holes: 1, sum bit holes:
16 bits */
        /* last cacheline: 24 bytes */
};

  parent reply	other threads:[~2020-02-19 20:07 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-18 17:04 [PATCH 00/19] VFS: Filesystem information and notifications [ver #16] David Howells
2020-02-18 17:05 ` [PATCH 01/19] vfs: syscall: Add fsinfo() to query filesystem information " David Howells
2020-02-19 16:31   ` Darrick J. Wong
2020-02-19 20:07   ` Jann Horn [this message]
2020-02-20 10:34   ` David Howells
2020-02-20 15:48     ` Darrick J. Wong
2020-02-20 11:03   ` David Howells
2020-02-20 14:54     ` Jann Horn
2020-02-20 15:31       ` Darrick J. Wong
2020-02-18 17:05 ` [PATCH 02/19] fsinfo: Add syscalls to other arches " David Howells
2020-02-21 14:51   ` Christian Brauner
2020-02-21 18:10     ` Geert Uytterhoeven
2020-02-18 17:05 ` [PATCH 03/19] fsinfo: Provide a bitmap of supported features " David Howells
2020-02-19 16:37   ` Darrick J. Wong
2020-02-20 12:22   ` David Howells
2020-02-18 17:05 ` [PATCH 04/19] vfs: Add mount change counter " David Howells
2020-02-21 14:48   ` Christian Brauner
2020-02-18 17:05 ` [PATCH 05/19] vfs: Introduce a non-repeating system-unique superblock ID " David Howells
2020-02-19 16:53   ` Darrick J. Wong
2020-02-20 12:45   ` David Howells
2020-02-18 17:05 ` [PATCH 06/19] vfs: Allow fsinfo() to look up a mount object by " David Howells
2020-02-21 15:09   ` Christian Brauner
2020-02-18 17:05 ` [PATCH 07/19] vfs: Allow mount information to be queried by fsinfo() " David Howells
2020-02-18 17:05 ` [PATCH 08/19] vfs: fsinfo sample: Mount listing program " David Howells
2020-02-18 17:06 ` [PATCH 09/19] fsinfo: Allow the mount topology propogation flags to be retrieved " David Howells
2020-02-18 17:06 ` [PATCH 10/19] fsinfo: Add API documentation " David Howells
2020-02-18 17:06 ` [PATCH 11/19] afs: Support fsinfo() " David Howells
2020-02-19 21:01   ` Jann Horn
2020-02-20 12:58   ` David Howells
2020-02-20 14:58     ` Jann Horn
2020-02-21 13:26     ` David Howells
2020-02-18 17:06 ` [PATCH 12/19] security: Add hooks to rule on setting a superblock or mount watch " David Howells
2020-02-18 17:06 ` [PATCH 13/19] vfs: Add a mount-notification facility " David Howells
2020-02-19 22:40   ` Jann Horn
2020-02-19 22:55     ` Jann Horn
2020-02-21 12:24   ` David Howells
2020-02-21 15:49     ` Jann Horn
2020-02-21 17:06     ` David Howells
2020-02-21 17:36       ` seq_lock and lockdep_is_held() assertions Jann Horn
2020-02-21 18:02         ` John Stultz
2020-02-18 17:06 ` [PATCH 14/19] notifications: sample: Display mount tree change notifications [ver #16] David Howells
2020-02-18 17:06 ` [PATCH 15/19] vfs: Add superblock " David Howells
2020-02-19 23:08   ` Jann Horn
2020-02-21 14:23   ` David Howells
2020-02-21 15:44     ` Jann Horn
2020-02-21 16:33     ` David Howells
2020-02-21 16:41       ` Jann Horn
2020-02-21 17:11       ` David Howells
2020-02-18 17:06 ` [PATCH 16/19] fsinfo: Provide superblock notification counter " David Howells
2020-02-18 17:07 ` [PATCH 17/19] notifications: sample: Display superblock notifications " David Howells
2020-02-18 17:07 ` [PATCH 18/19] ext4: Add example fsinfo information " David Howells
2020-02-19 17:04   ` Darrick J. Wong
2020-02-20  0:53   ` kbuild test robot
2020-02-21 14:43   ` David Howells
2020-02-21 16:26     ` Darrick J. Wong
2020-02-18 17:07 ` [PATCH 19/19] nfs: Add example filesystem " David Howells
2020-02-20  2:13   ` kbuild test robot
2020-02-20  2:20   ` kbuild test robot
2020-02-18 18:12 ` David Howells
2020-02-19 10:23 ` [PATCH 00/19] VFS: Filesystem information and notifications " Stefan Metzmacher
2020-02-19 14:46 ` Christian Brauner
2020-02-19 15:50   ` Darrick J. Wong
2020-02-20  4:42   ` Ian Kent
2020-02-20  9:09     ` Christian Brauner
2020-02-20 11:30       ` Ian Kent
2020-02-19 16:16 ` David Howells
2020-02-21 12:57 ` David Howells

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAG48ez0o3iHjQJNvh8V2Ao77g0CqfqGsv6caMCOFDy7w-VdtkQ@mail.gmail.com \
    --to=jannh@google.com \
    --cc=christian@brauner.io \
    --cc=dhowells@redhat.com \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mszeredi@redhat.com \
    --cc=raven@themaw.net \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).