* Re: [PATCH 4/6] vfs: Allow mount information to be queried by fsinfo() [ver #15]
From: Ian Kent @ 2019-07-03 1:42 UTC (permalink / raw)
To: christian, David Howells, viro
Cc: mszeredi, linux-api, linux-fsdevel, linux-kernel
In-Reply-To: <2daf229272884deaf139be510f5842f0689c18a6.camel@themaw.net>
On Wed, 2019-07-03 at 09:24 +0800, Ian Kent wrote:
> On Wed, 2019-07-03 at 09:09 +0800, Ian Kent wrote:
> > Hi Christian,
> >
> > About the propagation attributes you mentioned ...
>
> Umm ... how did you work out if a mount is unbindable from proc
> mountinfo?
>
> I didn't notice anything that could be used for that when I was
> looking at this.
Oh wait, fs/proc_namespace.c:show_mountinfo() has:
if (IS_MNT_UNBINDABLE(r))
seq_puts(m, " unbindable");
I missed that, probably because I didn't have any unbindable mounts
at the time I was looking at it, oops!
That's missing and probably should be added too.
>
> > On Fri, 2019-06-28 at 16:47 +0100, David Howells wrote:
> >
> > snip ...
> >
> > > +
> > > +#ifdef CONFIG_FSINFO
> > > +int fsinfo_generic_mount_info(struct path *path, struct fsinfo_kparams
> > > *params)
> > > +{
> > > + struct fsinfo_mount_info *p = params->buffer;
> > > + struct super_block *sb;
> > > + struct mount *m;
> > > + struct path root;
> > > + unsigned int flags;
> > > +
> > > + if (!path->mnt)
> > > + return -ENODATA;
> > > +
> > > + m = real_mount(path->mnt);
> > > + sb = m->mnt.mnt_sb;
> > > +
> > > + p->f_sb_id = sb->s_unique_id;
> > > + p->mnt_id = m->mnt_id;
> > > + p->parent_id = m->mnt_parent->mnt_id;
> > > + p->change_counter = atomic_read(&m->mnt_change_counter);
> > > +
> > > + get_fs_root(current->fs, &root);
> > > + if (path->mnt == root.mnt) {
> > > + p->parent_id = p->mnt_id;
> > > + } else {
> > > + rcu_read_lock();
> > > + if (!are_paths_connected(&root, path))
> > > + p->parent_id = p->mnt_id;
> > > + rcu_read_unlock();
> > > + }
> > > + if (IS_MNT_SHARED(m))
> > > + p->group_id = m->mnt_group_id;
> > > + if (IS_MNT_SLAVE(m)) {
> > > + int master = m->mnt_master->mnt_group_id;
> > > + int dom = get_dominating_id(m, &root);
> > > + p->master_id = master;
> > > + if (dom && dom != master)
> > > + p->from_id = dom;
> >
> > This provides information about mount propagation (well mostly).
> >
> > My understanding of this was that:
> > "If a mount is propagation private (or slave) the group_id will
> > be zero otherwise it's propagation shared and it's group id will
> > be non-zero.
> >
> > If a mount is propagation slave and propagation peers exist then
> > the mount field mnt_master will be non-NULL. Then mnt_master
> > (slave's master) can be used to set master_id. If the group id
> > of the propagation source is not that of the master then set
> > the from_id group as well."
> >
> > This parallels the way in which these values are reported in
> > the proc pseudo file system.
> >
> > Perhaps adding flags as well as setting the fields would be
> > useful too, since interpreting the meaning of the structure
> > fields isn't obvious, ;)
> >
> > David, Al, thoughts?
> >
> > Ian
^ permalink raw reply
* Re: [PATCH v6 17/17] f2fs: add fs-verity support
From: Chao Yu @ 2019-07-03 1:25 UTC (permalink / raw)
To: Eric Biggers, linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-18-ebiggers@kernel.org>
On 2019/7/1 23:32, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@google.com>
>
> Add fs-verity support to f2fs. fs-verity is a filesystem feature that
> enables transparent integrity protection and authentication of read-only
> files. It uses a dm-verity like mechanism at the file level: a Merkle
> tree is used to verify any block in the file in log(filesize) time. It
> is implemented mainly by helper functions in fs/verity/. See
> Documentation/filesystems/fsverity.rst for the full documentation.
>
> The f2fs support for fs-verity consists of:
>
> - Adding a filesystem feature flag and an inode flag for fs-verity.
>
> - Implementing the fsverity_operations to support enabling verity on an
> inode and reading/writing the verity metadata.
>
> - Updating ->readpages() to verify data as it's read from verity files
> and to support reading verity metadata pages.
>
> - Updating ->write_begin(), ->write_end(), and ->writepages() to support
> writing verity metadata pages.
>
> - Calling the fs-verity hooks for ->open(), ->setattr(), and ->ioctl().
>
> Like ext4, f2fs stores the verity metadata (Merkle tree and
> fsverity_descriptor) past the end of the file, starting at the first 64K
> boundary beyond i_size. This approach works because (a) verity files
> are readonly, and (b) pages fully beyond i_size aren't visible to
> userspace but can be read/written internally by f2fs with only some
> relatively small changes to f2fs. Extended attributes cannot be used
> because (a) f2fs limits the total size of an inode's xattr entries to
> 4096 bytes, which wouldn't be enough for even a single Merkle tree
> block, and (b) f2fs encryption doesn't encrypt xattrs, yet the verity
> metadata *must* be encrypted when the file is because it contains hashes
> of the plaintext data.
>
> Acked-by: Jaegeuk Kim <jaegeuk@kernel.org>
> Signed-off-by: Eric Biggers <ebiggers@google.com>
Acked-by: Chao Yu <yuchao0@huawei.com>
Thanks,
^ permalink raw reply
* Re: [PATCH 4/6] vfs: Allow mount information to be queried by fsinfo() [ver #15]
From: Ian Kent @ 2019-07-03 1:24 UTC (permalink / raw)
To: christian, David Howells, viro
Cc: mszeredi, linux-api, linux-fsdevel, linux-kernel
In-Reply-To: <8c70abf248d5ac07f334730af70d64235185b109.camel@themaw.net>
On Wed, 2019-07-03 at 09:09 +0800, Ian Kent wrote:
> Hi Christian,
>
> About the propagation attributes you mentioned ...
Umm ... how did you work out if a mount is unbindable from proc
mountinfo?
I didn't notice anything that could be used for that when I was
looking at this.
>
> On Fri, 2019-06-28 at 16:47 +0100, David Howells wrote:
>
> snip ...
>
> > +
> > +#ifdef CONFIG_FSINFO
> > +int fsinfo_generic_mount_info(struct path *path, struct fsinfo_kparams
> > *params)
> > +{
> > + struct fsinfo_mount_info *p = params->buffer;
> > + struct super_block *sb;
> > + struct mount *m;
> > + struct path root;
> > + unsigned int flags;
> > +
> > + if (!path->mnt)
> > + return -ENODATA;
> > +
> > + m = real_mount(path->mnt);
> > + sb = m->mnt.mnt_sb;
> > +
> > + p->f_sb_id = sb->s_unique_id;
> > + p->mnt_id = m->mnt_id;
> > + p->parent_id = m->mnt_parent->mnt_id;
> > + p->change_counter = atomic_read(&m->mnt_change_counter);
> > +
> > + get_fs_root(current->fs, &root);
> > + if (path->mnt == root.mnt) {
> > + p->parent_id = p->mnt_id;
> > + } else {
> > + rcu_read_lock();
> > + if (!are_paths_connected(&root, path))
> > + p->parent_id = p->mnt_id;
> > + rcu_read_unlock();
> > + }
> > + if (IS_MNT_SHARED(m))
> > + p->group_id = m->mnt_group_id;
> > + if (IS_MNT_SLAVE(m)) {
> > + int master = m->mnt_master->mnt_group_id;
> > + int dom = get_dominating_id(m, &root);
> > + p->master_id = master;
> > + if (dom && dom != master)
> > + p->from_id = dom;
>
> This provides information about mount propagation (well mostly).
>
> My understanding of this was that:
> "If a mount is propagation private (or slave) the group_id will
> be zero otherwise it's propagation shared and it's group id will
> be non-zero.
>
> If a mount is propagation slave and propagation peers exist then
> the mount field mnt_master will be non-NULL. Then mnt_master
> (slave's master) can be used to set master_id. If the group id
> of the propagation source is not that of the master then set
> the from_id group as well."
>
> This parallels the way in which these values are reported in
> the proc pseudo file system.
>
> Perhaps adding flags as well as setting the fields would be
> useful too, since interpreting the meaning of the structure
> fields isn't obvious, ;)
>
> David, Al, thoughts?
>
> Ian
^ permalink raw reply
* Re: [PATCH 4/6] vfs: Allow mount information to be queried by fsinfo() [ver #15]
From: Ian Kent @ 2019-07-03 1:09 UTC (permalink / raw)
To: christian, David Howells, viro
Cc: mszeredi, linux-api, linux-fsdevel, linux-kernel
In-Reply-To: <156173685496.14728.9606180227161368035.stgit@warthog.procyon.org.uk>
Hi Christian,
About the propagation attributes you mentioned ...
On Fri, 2019-06-28 at 16:47 +0100, David Howells wrote:
snip ...
> +
> +#ifdef CONFIG_FSINFO
> +int fsinfo_generic_mount_info(struct path *path, struct fsinfo_kparams
> *params)
> +{
> + struct fsinfo_mount_info *p = params->buffer;
> + struct super_block *sb;
> + struct mount *m;
> + struct path root;
> + unsigned int flags;
> +
> + if (!path->mnt)
> + return -ENODATA;
> +
> + m = real_mount(path->mnt);
> + sb = m->mnt.mnt_sb;
> +
> + p->f_sb_id = sb->s_unique_id;
> + p->mnt_id = m->mnt_id;
> + p->parent_id = m->mnt_parent->mnt_id;
> + p->change_counter = atomic_read(&m->mnt_change_counter);
> +
> + get_fs_root(current->fs, &root);
> + if (path->mnt == root.mnt) {
> + p->parent_id = p->mnt_id;
> + } else {
> + rcu_read_lock();
> + if (!are_paths_connected(&root, path))
> + p->parent_id = p->mnt_id;
> + rcu_read_unlock();
> + }
> + if (IS_MNT_SHARED(m))
> + p->group_id = m->mnt_group_id;
> + if (IS_MNT_SLAVE(m)) {
> + int master = m->mnt_master->mnt_group_id;
> + int dom = get_dominating_id(m, &root);
> + p->master_id = master;
> + if (dom && dom != master)
> + p->from_id = dom;
This provides information about mount propagation (well mostly).
My understanding of this was that:
"If a mount is propagation private (or slave) the group_id will
be zero otherwise it's propagation shared and it's group id will
be non-zero.
If a mount is propagation slave and propagation peers exist then
the mount field mnt_master will be non-NULL. Then mnt_master
(slave's master) can be used to set master_id. If the group id
of the propagation source is not that of the master then set
the from_id group as well."
This parallels the way in which these values are reported in
the proc pseudo file system.
Perhaps adding flags as well as setting the fields would be
useful too, since interpreting the meaning of the structure
fields isn't obvious, ;)
David, Al, thoughts?
Ian
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Song Liu @ 2019-07-02 23:48 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Kees Cook, linux-security@vger.kernel.org, Networking, bpf,
Alexei Starovoitov, Daniel Borkmann, Kernel Team, Lorenz Bauer,
Jann Horn, Greg KH, Linux API
In-Reply-To: <CALCETrXTta26CTtEDnzvtd03-WOGdXcnsAogP8JjLkcj4-mHvg@mail.gmail.com>
> On Jul 3, 2019, at 5:32 AM, Andy Lutomirski <luto@kernel.org> wrote:
>
> On Tue, Jul 2, 2019 at 2:04 PM Kees Cook <keescook@chromium.org> wrote:
>>
>> On Mon, Jul 01, 2019 at 06:59:13PM -0700, Andy Lutomirski wrote:
>>> I think I'm understanding your motivation. You're not trying to make
>>> bpf() generically usable without privilege -- you're trying to create
>>> a way to allow certain users to access dangerous bpf functionality
>>> within some limits.
>>>
>>> That's a perfectly fine goal, but I think you're reinventing the
>>> wheel, and the wheel you're reinventing is quite complicated and
>>> already exists. I think you should teach bpftool to be secure when
>>> installed setuid root or with fscaps enabled and put your policy in
>>> bpftool. If you want to harden this a little bit, it would seem
>>> entirely reasonable to add a new CAP_BPF_ADMIN and change some, but
>>> not all, of the capable() checks to check CAP_BPF_ADMIN instead of the
>>> capabilities that they currently check.
>>
>> If finer grained controls are wanted, it does seem like the /dev/bpf
>> path makes the most sense. open, request abilities, use fd. The open can
>> be mediated by DAC and LSM. The request can be mediated by LSM. This
>> provides a way to add policy at the LSM level and at the tool level.
>> (i.e. For tool-level controls: leave LSM wide open, make /dev/bpf owned
>> by "bpfadmin" and bpftool becomes setuid "bpfadmin". For fine-grained
>> controls, leave /dev/bpf wide open and add policy to SELinux, etc.)
>>
>> With only a new CAP, you don't get the fine-grained controls. (The
>> "request abilities" part is the key there.)
>
> Sure you do: the effective set. It has somewhat bizarre defaults, but
> I don't think that's a real problem. Also, this wouldn't be like
> CAP_DAC_READ_SEARCH -- you can't accidentally use your BPF caps.
>
> I think that a /dev capability-like object isn't totally nuts, but I
> think we should do it well, and this patch doesn't really achieve
> that. But I don't think bpf wants fine-grained controls like this at
> all -- as I pointed upthread, a fine-grained solution really wants
> different treatment for the different capable() checks, and a bunch of
> them won't resemble capabilities or /dev/bpf at all.
Thanks everyone again for great inputs. We will discuss this again and
respin the set.
Best,
Song
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Andy Lutomirski @ 2019-07-02 21:32 UTC (permalink / raw)
To: Kees Cook
Cc: Andy Lutomirski, Song Liu, linux-security@vger.kernel.org,
Networking, bpf, Alexei Starovoitov, Daniel Borkmann, Kernel Team,
Lorenz Bauer, Jann Horn, Greg KH, Linux API
In-Reply-To: <201907021115.DCD56BBABB@keescook>
On Tue, Jul 2, 2019 at 2:04 PM Kees Cook <keescook@chromium.org> wrote:
>
> On Mon, Jul 01, 2019 at 06:59:13PM -0700, Andy Lutomirski wrote:
> > I think I'm understanding your motivation. You're not trying to make
> > bpf() generically usable without privilege -- you're trying to create
> > a way to allow certain users to access dangerous bpf functionality
> > within some limits.
> >
> > That's a perfectly fine goal, but I think you're reinventing the
> > wheel, and the wheel you're reinventing is quite complicated and
> > already exists. I think you should teach bpftool to be secure when
> > installed setuid root or with fscaps enabled and put your policy in
> > bpftool. If you want to harden this a little bit, it would seem
> > entirely reasonable to add a new CAP_BPF_ADMIN and change some, but
> > not all, of the capable() checks to check CAP_BPF_ADMIN instead of the
> > capabilities that they currently check.
>
> If finer grained controls are wanted, it does seem like the /dev/bpf
> path makes the most sense. open, request abilities, use fd. The open can
> be mediated by DAC and LSM. The request can be mediated by LSM. This
> provides a way to add policy at the LSM level and at the tool level.
> (i.e. For tool-level controls: leave LSM wide open, make /dev/bpf owned
> by "bpfadmin" and bpftool becomes setuid "bpfadmin". For fine-grained
> controls, leave /dev/bpf wide open and add policy to SELinux, etc.)
>
> With only a new CAP, you don't get the fine-grained controls. (The
> "request abilities" part is the key there.)
Sure you do: the effective set. It has somewhat bizarre defaults, but
I don't think that's a real problem. Also, this wouldn't be like
CAP_DAC_READ_SEARCH -- you can't accidentally use your BPF caps.
I think that a /dev capability-like object isn't totally nuts, but I
think we should do it well, and this patch doesn't really achieve
that. But I don't think bpf wants fine-grained controls like this at
all -- as I pointed upthread, a fine-grained solution really wants
different treatment for the different capable() checks, and a bunch of
them won't resemble capabilities or /dev/bpf at all.
^ permalink raw reply
* fsinfo(2) manpage
From: David Howells @ 2019-07-02 19:10 UTC (permalink / raw)
To: Eric Biggers
Cc: dhowells, viro, raven, mszeredi, christian, linux-api,
linux-fsdevel, linux-kernel
In-Reply-To: <20190702181539.GA110306@gmail.com>
Here's a manpage for fsinfo(). It needs a little updating, but I've applied
some review comments that will require this updating anyway.
David
---
'\" t
.\" Copyright (c) 2018 David Howells <dhowells@redhat.com>
.\"
.\" %%%LICENSE_START(VERBATIM)
.\" Permission is granted to make and distribute verbatim copies of this
.\" manual provided the copyright notice and this permission notice are
.\" preserved on all copies.
.\"
.\" Permission is granted to copy and distribute modified versions of this
.\" manual under the conditions for verbatim copying, provided that the
.\" entire resulting derived work is distributed under the terms of a
.\" permission notice identical to this one.
.\"
.\" Since the Linux kernel and libraries are constantly changing, this
.\" manual page may be incorrect or out-of-date. The author(s) assume no
.\" responsibility for errors or omissions, or for damages resulting from
.\" the use of the information contained herein. The author(s) may not
.\" have taken the same level of care in the production of this manual,
.\" which is licensed free of charge, as they might when working
.\" professionally.
.\"
.\" Formatted or processed versions of this manual, if unaccompanied by
.\" the source, must acknowledge the copyright and authors of this work.
.\" %%%LICENSE_END
.\"
.TH FSINFO 2 2018-06-06 "Linux" "Linux Programmer's Manual"
.SH NAME
fsinfo \- Get filesystem information
.SH SYNOPSIS
.nf
.B #include <sys/types.h>
.br
.B #include <sys/fsinfo.h>
.br
.B #include <unistd.h>
.br
.BR "#include <fcntl.h> " "/* Definition of AT_* constants */"
.PP
.BI "int fsinfo(int " dirfd ", const char *" pathname ","
.BI " struct fsinfo_params *" params ","
.BI " void *" buffer ", size_t " buf_size );
.fi
.PP
.IR Note :
There is no glibc wrapper for
.BR fsinfo ();
see NOTES.
.SH DESCRIPTION
.PP
fsinfo() retrieves the desired filesystem attribute, as selected by the
parameters pointed to by
.IR params ,
and stores its value in the buffer pointed to by
.IR buffer .
.PP
The parameter structure is optional, defaulting to all the parameters being 0
if the pointer is NULL. The structure looks like the following:
.PP
.in +4n
.nf
struct fsinfo_params {
__u32 at_flags; /* AT_SYMLINK_NOFOLLOW and similar flags */
__u32 request; /* Requested attribute */
__u32 Nth; /* Instance of attribute */
__u32 Mth; /* Subinstance of Nth instance */
__u32 __reserved[6]; /* Reserved params; all must be 0 */
};
.fi
.in
.PP
The filesystem to be queried is looked up using a combination of
.IR dfd ", " pathname " and " params->at_flags.
This is discussed in more detail below.
.PP
The desired attribute is indicated by
.IR params->request .
If
.I params
is NULL, this will default to
.BR FSINFO_ATTR_STATFS ,
which retrieves some of the information returned by
.BR statfs ().
The available attributes are described below in the "THE ATTRIBUTES" section.
.PP
Some attributes can have multiple values and some can even have multiple
instances with multiple values. For example, a network filesystem might use
multiple servers. The names of each of these servers can be retrieved by
using
.I params->Nth
to iterate through all the instances until error
.B ENODATA
occurs, indicating the end of the list. Further, each server might have
multiple addresses available; these can be enumerated using
.I params->Nth
to iterate the servers and
.I params->Mth
to iterate the addresses of the Nth server.
.PP
The amount of data written into the buffer depends on the attribute selected.
Some attributes return variable-length strings and some return fixed-size
structures. If either
.IR buffer " is NULL or " buf_size " is 0"
then the size of the attribute value will be returned and nothing will be
written into the buffer.
.PP
The
.I params->__reserved
parameters must all be 0.
.\"_______________________________________________________
.SS
Allowance for Future Attribute Expansion
.PP
To allow for the future expansion and addition of fields to any fixed-size
structure attribute,
.BR fsinfo ()
makes the following guarantees:
.RS 4m
.IP (1) 4m
It will always clear any excess space in the buffer.
.IP (2) 4m
It will always return the actual size of the data.
.IP (3) 4m
It will truncate the data to fit it into the buffer rather than giving an
error.
.IP (4) 4m
Any new version of a structure will incorporate all the fields from the old
version at same offsets.
.RE
.PP
So, for example, if the caller is running on an older version of the kernel
with an older, smaller version of the structure than was asked for, the kernel
will write the smaller version into the buffer and will clear the remainder of
the buffer to make sure any additional fields are set to 0. The function will
return the actual size of the data.
.PP
On the other hand, if the caller is running on a newer version of the kernel
with a newer version of the structure that is larger than the buffer, the write
to the buffer will be truncated to fit as necessary and the actual size of the
data will be returned.
.PP
Note that this doesn't apply to variable-length string attributes.
.\"_______________________________________________________
.SS
Invoking \fBfsinfo\fR():
.PP
To access a file's status, no permissions are required on the file itself, but
in the case of
.BR fsinfo ()
with a path, execute (search) permission is required on all of the directories
in
.I pathname
that lead to the file.
.PP
.BR fsinfo ()
uses
.IR pathname ", " dirfd " and " params->at_flags
to locate the target file in one of a variety of ways:
.TP
[*] By absolute path.
.I pathname
points to an absolute path and
.I dirfd
is ignored. The file is looked up by name, starting from the root of the
filesystem as seen by the calling process.
.TP
[*] By cwd-relative path.
.I pathname
points to a relative path and
.IR dirfd " is " AT_FDCWD .
The file is looked up by name, starting from the current working directory.
.TP
[*] By dir-relative path.
.I pathname
points to relative path and
.I dirfd
indicates a file descriptor pointing to a directory. The file is looked up by
name, starting from the directory specified by
.IR dirfd .
.TP
[*] By file descriptor.
.IR pathname " is " NULL " and " dirfd
indicates a file descriptor. The file attached to the file descriptor is
queried directly. The file descriptor may point to any type of file, not just
a directory.
.PP
.I flags
can be used to influence a path-based lookup. A value for
.I flags
is constructed by OR'ing together zero or more of the following constants:
.TP
.BR AT_EMPTY_PATH
.\" commit 65cfc6722361570bfe255698d9cd4dccaf47570d
If
.I pathname
is an empty string, operate on the file referred to by
.IR dirfd
(which may have been obtained using the
.BR open (2)
.B O_PATH
flag).
If
.I dirfd
is
.BR AT_FDCWD ,
the call operates on the current working directory.
In this case,
.I dirfd
can refer to any type of file, not just a directory.
This flag is Linux-specific; define
.B _GNU_SOURCE
.\" Before glibc 2.16, defining _ATFILE_SOURCE sufficed
to obtain its definition.
.TP
.BR AT_NO_AUTOMOUNT
Don't automount the terminal ("basename") component of
.I pathname
if it is a directory that is an automount point. This allows the caller to
gather attributes of the filesystem holding an automount point (rather than
the filesystem it would mount). This flag can be used in tools that scan
directories to prevent mass-automounting of a directory of automount points.
The
.B AT_NO_AUTOMOUNT
flag has no effect if the mount point has already been mounted over.
This flag is Linux-specific; define
.B _GNU_SOURCE
.\" Before glibc 2.16, defining _ATFILE_SOURCE sufficed
to obtain its definition.
.TP
.B AT_SYMLINK_NOFOLLOW
If
.I pathname
is a symbolic link, do not dereference it:
instead return information about the link itself, like
.BR lstat ().
.SH THE ATTRIBUTES
.PP
There is a range of attributes that can be selected from. These are:
.\" __________________ FSINFO_ATTR_STATFS __________________
.TP
.B fsinfo_attr_statfs
This retrieves the "dynamic"
.B statfs
information, such as block and file counts, that are expected to change whilst
a filesystem is being used. This fills in the following structure:
.PP
.RS
.in +4n
.nf
struct fsinfo_statfs {
__u64 f_blocks; /* Total number of blocks in fs */
__u64 f_bfree; /* Total number of free blocks */
__u64 f_bavail; /* Number of free blocks available to ordinary user */
__u64 f_files; /* Total number of file nodes in fs */
__u64 f_ffree; /* Number of free file nodes */
__u64 f_favail; /* Number of free file nodes available to ordinary user */
__u32 f_bsize; /* Optimal block size */
__u32 f_frsize; /* Fragment size */
};
.fi
.in
.RE
.IP
The fields correspond to those of the same name returned by
.BR statfs ().
.\" __________________ FSINFO_ATTR_FSINFO __________________
.TP
.B FSINFO_ATTR_FSINFO
This retrieves information about the
.BR fsinfo ()
system call itself. This fills in the following structure:
.PP
.RS
.in +4n
.nf
struct fsinfo_fsinfo {
__u32 max_attr;
__u32 max_cap;
};
.fi
.in
.RE
.IP
The
.I max_attr
value indicates the number of attributes supported by the
.BR fsinfo ()
system call, and
.I max_cap
indicates the number of capability bits supported by the
.B FSINFO_ATTR_CAPABILITIES
attribute. The first corresponds to
.I fsinfo_attr__nr
and the second to
.I fsinfo_cap__nr
in the header file.
.\" __________________ FSINFO_ATTR_IDS __________________
.TP
.B FSINFO_ATTR_IDS
This retrieves a number of fixed IDs and other static information otherwise
available through
.BR statfs ().
The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_ids {
char f_fs_name[15 + 1]; /* Filesystem name */
__u64 f_flags; /* Filesystem mount flags (MS_*) */
__u64 f_fsid; /* Short 64-bit Filesystem ID */
__u64 f_sb_id; /* Internal superblock ID */
__u32 f_fstype; /* Filesystem type from linux/magic.h */
__u32 f_dev_major; /* As st_dev_* from struct statx */
__u32 f_dev_minor;
};
.fi
.in
.RE
.IP
Most of these are filled in as for
.BR statfs (),
with the addition of the filesystem's symbolic name in
.I f_fs_name
and an identifier for use in notifications in
.IR f_sb_id .
.\" __________________ FSINFO_ATTR_LIMITS __________________
.TP
.B FSINFO_ATTR_LIMITS
This retrieves information about the limits of what a filesystem can support.
The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_limits {
__u64 max_file_size;
__u64 max_uid;
__u64 max_gid;
__u64 max_projid;
__u32 max_dev_major;
__u32 max_dev_minor;
__u32 max_hard_links;
__u32 max_xattr_body_len;
__u16 max_xattr_name_len;
__u16 max_filename_len;
__u16 max_symlink_len;
__u16 __reserved[1];
};
.fi
.in
.RE
.IP
These indicate the maximum supported sizes for a variety of filesystem objects,
including the file size, the extended attribute name length and body length,
the filename length and the symlink body length.
.IP
It also indicates the maximum representable values for a User ID, a Group ID,
a Project ID, a device major number and a device minor number.
.IP
And finally, it indicates the maximum number of hard links that can be made to
a file.
.IP
Note that some of these values may be zero if the underlying object or concept
is not supported by the filesystem or the medium.
.\" __________________ FSINFO_ATTR_SUPPORTS __________________
.TP
.B FSINFO_ATTR_SUPPORTS
This retrieves information about what bits a filesystem supports in various
masks. The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_supports {
__u64 stx_attributes;
__u32 stx_mask;
__u32 ioc_flags;
__u32 win_file_attrs;
__u32 __reserved[1];
};
.fi
.in
.RE
.IP
The
.IR stx_attributes " and " stx_mask
fields indicate what bits in the struct statx fields of the matching names
are supported by the filesystem.
.IP
The
.I ioc_flags
field indicates what FS_*_FL flag bits as used through the FS_IOC_GET/SETFLAGS
ioctls are supported by the filesystem.
.IP
The
.I win_file_attrs
indicates what DOS/Windows file attributes a filesystem supports, if any.
.\" __________________ FSINFO_ATTR_CAPABILITIES __________________
.TP
.B FSINFO_ATTR_CAPABILITIES
This retrieves information about what features a filesystem supports as a
series of single bit indicators. The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_capabilities {
__u8 capabilities[(fsinfo_cap__nr + 7) / 8];
};
.fi
.in
.RE
.IP
where the bit of interest can be found by:
.PP
.RS
.in +4n
.nf
p->capabilities[bit / 8] & (1 << (bit % 8)))
.fi
.in
.RE
.IP
The bits are listed by
.I enum fsinfo_capability
and
.B fsinfo_cap__nr
is one more than the last capability bit listed in the header file.
.IP
Note that the number of capability bits actually supported by the kernel can be
found using the
.B FSINFO_ATTR_FSINFO
attribute.
.IP
The capability bits and their meanings are listed below in the "THE
CAPABILITIES" section.
.\" __________________ FSINFO_ATTR_TIMESTAMP_INFO __________________
.TP
.B FSINFO_ATTR_TIMESTAMP_INFO
This retrieves information about what timestamp resolution and scope is
supported by a filesystem for each of the file timestamps. The following
structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_timestamp_info {
__s64 minimum_timestamp;
__s64 maximum_timestamp;
__u16 atime_gran_mantissa;
__u16 btime_gran_mantissa;
__u16 ctime_gran_mantissa;
__u16 mtime_gran_mantissa;
__s8 atime_gran_exponent;
__s8 btime_gran_exponent;
__s8 ctime_gran_exponent;
__s8 mtime_gran_exponent;
__u32 __reserved[1];
};
.fi
.in
.RE
.IP
where
.IR minimum_timestamp " and " maximum_timestamp
are the limits on the timestamps that the filesystem supports and
.IR *time_gran_mantissa " and " *time_gran_exponent
indicate the granularity of each timestamp in terms of seconds, using the
formula:
.PP
.RS
.in +4n
.nf
mantissa * pow(10, exponent) Seconds
.fi
.in
.RE
.IP
where exponent may be negative and the result may be a fraction of a second.
.IP
Four timestamps are detailed: \fBA\fPccess time, \fBB\fPirth/creation time,
\fBC\fPhange time and \fBM\fPodification time. Capability bits are defined
that specify whether each of these exist in the filesystem or not.
.IP
Note that the timestamp description may be approximated or inaccurate if the
file is actually remote or is the union of multiple objects.
.\" __________________ FSINFO_ATTR_VOLUME_ID __________________
.TP
.B FSINFO_ATTR_VOLUME_ID
This retrieves the system's superblock volume identifier as a variable-length
string. This does not necessarily represent a value stored in the medium but
might be constructed on the fly.
.IP
For instance, for a block device this is the block device identifier
(eg. "sdb2"); for AFS this would be the numeric volume identifier.
.\" __________________ FSINFO_ATTR_VOLUME_UUID __________________
.TP
.B FSINFO_ATTR_VOLUME_UUID
This retrieves the volume UUID, if there is one, as a little-endian binary
UUID. This fills in the following structure:
.PP
.RS
.in +4n
.nf
struct fsinfo_volume_uuid {
__u8 uuid[16];
};
.fi
.in
.RE
.IP
.\" __________________ FSINFO_ATTR_VOLUME_NAME __________________
.TP
.B FSINFO_ATTR_VOLUME_NAME
This retrieves the filesystem's volume name as a variable-length string. This
is expected to represent a name stored in the medium.
.IP
For a block device, this might be a label stored in the superblock. For a
network filesystem, this might be a logical volume name of some sort.
.\" __________________ FSINFO_ATTR_CELL/DOMAIN __________________
.PP
.B FSINFO_ATTR_CELL_NAME
.br
.B FSINFO_ATTR_DOMAIN_NAME
.br
.IP
These two attributes are variable-length string attributes that may be used to
obtain information about network filesystems. An AFS volume, for instance,
belongs to a named cell. CIFS shares may belong to a domain.
.\" __________________ FSINFO_ATTR_REALM_NAME __________________
.TP
.B FSINFO_ATTR_REALM_NAME
This attribute is variable-length string that indicates the Kerberos realm that
a filesystem's authentication tokens should come from.
.\" __________________ FSINFO_ATTR_SERVER_NAME __________________
.TP
.B FSINFO_ATTR_SERVER_NAME
This attribute is a multiple-value attribute that lists the names of the
servers that are backing a network filesystem. Each value is a variable-length
string. The values are enumerated by calling
.BR fsinfo ()
multiple times, incrementing
.I params->Nth
each time until an ENODATA error occurs, thereby indicating the end of the
list.
.\" __________________ FSINFO_ATTR_SERVER_ADDRESS __________________
.TP
.B FSINFO_ATTR_SERVER_ADDRESS
This attribute is a multiple-instance, multiple-value attribute that lists the
addresses of the servers that are backing a network filesystem. Each value is
a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_server_address {
struct __kernel_sockaddr_storage address;
};
.fi
.in
.RE
.IP
Where the address may be AF_INET, AF_INET6, AF_RXRPC or any other type as
appropriate to the filesystem.
.IP
The values are enumerated by calling
.IR fsinfo ()
multiple times, incrementing
.I params->Nth
to step through the servers and
.I params->Mth
to step through the addresses of the Nth server each time until ENODATA errors
occur, thereby indicating either the end of a server's address list or the end
of the server list.
.IP
Barring the server list changing whilst being accessed, it is expected that the
.I params->Nth
will correspond to
.I params->Nth
for
.BR FSINFO_ATTR_SERVER_NAME .
.\" __________________ FSINFO_ATTR_PARAMETER __________________
.TP
.B FSINFO_ATTR_PARAMETER
This attribute is a 2D multiple-value attribute that lists the values of the
mount parameters for a filesystem as variable-length strings.
.IP
The parameters are enumerated by calling
.BR fsinfo ()
multiple times, incrementing
.IR params->Nth and params->Mth
to step through them until error ENODATA is given.
.IP
Parameter strings are presented in a form akin to the way they're passed to the
context created by the
.BR fsopen ()
system call. For example, straight text parameters will be rendered as
something like:
.PP
.RS
.in +4n
.nf
"source=/dev/sda1"
"data=journal"
"noquota"
.fi
.in
.RE
.IP
where the first parameters correspond on a 1-to-1 basis by
.I params->Nth
with the parameters defined by
.IR FSINFO_ATTR_PARAM_SPECIFICATION .
Additional parameters may also be presented. Further, any particular parameter
may have multiple values (multiple sources for example). These can be
enumerated with params->Mth.
.\" __________________ FSINFO_ATTR_NAME_ENCODING __________________
.TP
.B FSINFO_ATTR_NAME_ENCODING
This attribute is variable-length string that indicates the filename encoding
used by the filesystem. The default is "utf8". Note that this may indicate a
non-8-bit encoding if that's what the underlying filesystem actually supports.
.\" __________________ FSINFO_ATTR_NAME_CODEPAGE __________________
.TP
.B FSINFO_ATTR_NAME_CODEPAGE
This attribute is variable-length string that indicates the codepage used to
translate filenames from the filesystem to the system if this is applicable to
the filesystem.
.\" __________________ FSINFO_ATTR_IO_SIZE __________________
.TP
.B FSINFO_ATTR_IO_SIZE
This retrieves information about the I/O sizes supported by the filesystem.
The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_io_size {
__u32 dio_size_gran;
__u32 dio_mem_align;
};
.fi
.in
.RE
.IP
Where
.I dio_size_gran
indicate the fundamental I/O block size that the size of O_DIRECT read/write
calls must be a multiple of and
.I dio_mem_align
indicates the memory alignment requirements of the data buffer in any O_DIRECT
read/write call.
.IP
Note that any of these may be zero if inapplicable or indeterminable.
.\" __________________ FSINFO_ATTR_PARAM_DESCRIPTION __________________
.TP
.B FSINFO_ATTR_PARAM_DESCRIPTION
This retrieves basic information about the superblock configuration parameters
used by the filesystem. The value returned is of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_description {
__u32 nr_params; /* Number of individual parameters */
__u32 nr_names; /* Number of parameter names */
__u32 nr_enum_names; /* Number of enum names */
};
.fi
.in
.RE
.IP
Where
.I nr_params indicates the number of described parameters (it's possible for
the configuration to take more than this - cgroup-v1 for example);
.I nr_names
indicates the number of parameter names that there are defined (nr_names can be
more than nr_params if there are synonyms); and
.I nr_enum_names
indicates the number of enum value names that there are defined.
.\" __________________ FSINFO_ATTR_PARAM_SPECIFICATION __________________
.TP
.B FSINFO_ATTR_PARAM_SPECIFICATION
This retrieves information about the Nth superblock configuration parameter
available in the filesystem. This is enumerated by incrementing
.I params->Nth
each time. Each value is a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_specification {
__u32 type;
__u32 flags;
};
.fi
.in
.RE
.IP
Where
.I type
indicates the type of value by way of one of the following constants:
.PP
.RS
.in +4n
.nf
FSINFO_PARAM_SPEC_NOT_DEFINED
FSINFO_PARAM_SPEC_TAKES_NO_VALUE
FSINFO_PARAM_SPEC_IS_BOOL
FSINFO_PARAM_SPEC_IS_U32
FSINFO_PARAM_SPEC_IS_U32_OCTAL
FSINFO_PARAM_SPEC_IS_U32_HEX
FSINFO_PARAM_SPEC_IS_S32
FSINFO_PARAM_SPEC_IS_ENUM
FSINFO_PARAM_SPEC_IS_STRING
FSINFO_PARAM_SPEC_IS_BLOB
FSINFO_PARAM_SPEC_IS_BLOCKDEV
FSINFO_PARAM_SPEC_IS_PATH
FSINFO_PARAM_SPEC_IS_FD
.fi
.in
.RE
.IP
depending on whether the kernel (incorrectly) didn't define the type, the
parameter takes no value, or takes a bool, one of a number of integers, a named
enum value, a string, a binary blob, a blockdev, an arbitrary path or a file
descriptor.
.PP
.I flags
qualifies the form of the value accepted:
.PP
.RS
.in +4n
.nf
FSINFO_PARAM_SPEC_VALUE_IS_OPTIONAL
FSINFO_PARAM_SPEC_PREFIX_NO_IS_NEG
FSINFO_PARAM_SPEC_EMPTY_STRING_IS_NEG
FSINFO_PARAM_SPEC_DEPRECATED
.fi
.in
.RE
.IP
These indicate whether the value is optional, the parameter can be made
negative by prefixing with 'no' or giving it an empty value or whether the
parameter is deprecated and a warning issued if it used.
.\" __________________ FSINFO_ATTR_PARAM_NAME __________________
.TP
.B FSINFO_ATTR_PARAM_NAME
This retrieves information about the Nth superblock configuration parameter
available in the filesystem. This is enumerated by incrementing
.I params->Nth
each time. Each value is a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_name {
__u32 param_index;
char name[252];
};
.fi
.in
.RE
.IP
Where
.I param_index
is refers to the Nth parameter returned by FSINFO_ATTR_PARAM_SPECIFICATION and
.I name
is a name string that maps to the specified parameter.
.\" __________________ FSINFO_ATTR_PARAM_ENUMs __________________
.TP
.B FSINFO_ATTR_PARAM_ENUM
This can be used to list all the enum value symbols available for all the
configuration parameters available in the filesystem. This is enumerated by
incrementing
.I params->Nth
each time. Each value is a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_enum {
__u32 param_index; /* Index of the relevant parameter specification */
char name[252]; /* Name of the enum value */
};
.fi
.in
.RE
.IP
Where
.I param_index
indicates the enumeration-type parameter to which this value corresponds and
.I name
is the symbolic name. Note that all the enum values from all the enum
parameters are in one list together
.SH THE CAPABILITIES
.PP
There are number of capability bits in a bit array that can be retrieved using
.BR fsinfo_attr_capabilities .
These give information about features of the filesystem driver and the specific
filesystem.
.\" __________________ FSINFO_CAP_IS_*_FS __________________
.PP
.B FSINFO_CAP_IS_KERNEL_FS
.br
.B FSINFO_CAP_IS_BLOCK_FS
.br
.B FSINFO_CAP_IS_FLASH_FS
.br
.B FSINFO_CAP_IS_NETWORK_FS
.br
.B FSINFO_CAP_IS_AUTOMOUNTER_FS
.IP
These indicate the primary type of the filesystem.
.B kernel
filesystems are special communication interfaces that substitute files for
system calls; examples include procfs and sysfs.
.B block
filesystems require a block device on which to operate; examples include ext4
and XFS.
.B flash
filesystems require an MTD device on which to operate; examples include JFFS2.
.B network
filesystems require access to the network and contact one or more servers;
examples include NFS and AFS.
.B automounter
filesystems are kernel special filesystems that host automount points and
triggers to dynamically create automount points. Examples include autofs and
AFS's dynamic root.
.\" __________________ FSINFO_CAP_AUTOMOUNTS __________________
.TP
.B FSINFO_CAP_AUTOMOUNTS
The filesystem may have automount points that can be triggered by pathwalk.
.\" __________________ FSINFO_CAP_ADV_LOCKS __________________
.TP
.B FSINFO_CAP_ADV_LOCKS
The filesystem supports advisory file locks. For a network filesystem, this
indicates that the advisory file locks are cross-client (and also between
server and its local filesystem on something like NFS).
.\" __________________ FSINFO_CAP_MAND_LOCKS __________________
.TP
.B FSINFO_CAP_MAND_LOCKS
The filesystem supports mandatory file locks. For a network filesystem, this
indicates that the mandatory file locks are cross-client (and also between
server and its local filesystem on something like NFS).
.\" __________________ FSINFO_CAP_LEASES __________________
.TP
.B FSINFO_CAP_LEASES
The filesystem supports leases. For a network filesystem, this means that the
server will tell the client to clean up its state on a file before passing the
lease to another client.
.\" __________________ FSINFO_CAP_*IDS __________________
.PP
.B FSINFO_CAP_UIDS
.br
.B FSINFO_CAP_GIDS
.br
.B FSINFO_CAP_PROJIDS
.IP
These indicate that the filesystem supports numeric user IDs, group IDs and
project IDs respectively.
.\" __________________ FSINFO_CAP_ID_* __________________
.PP
.B FSINFO_CAP_ID_NAMES
.br
.B FSINFO_CAP_ID_GUIDS
.IP
These indicate that the filesystem employs textual names and/or GUIDs as
identifiers.
.\" __________________ FSINFO_CAP_WINDOWS_ATTRS __________________
.TP
.B FSINFO_CAP_WINDOWS_ATTRS
Indicates that the filesystem supports some Windows FILE_* attributes.
.\" __________________ FSINFO_CAP_*_QUOTAS __________________
.PP
.B FSINFO_CAP_USER_QUOTAS
.br
.B FSINFO_CAP_GROUP_QUOTAS
.br
.B FSINFO_CAP_PROJECT_QUOTAS
.IP
These indicate that the filesystem supports quotas for users, groups and
projects respectively.
.\" __________________ FSINFO_CAP_XATTRS/FILETYPES __________________
.PP
.B FSINFO_CAP_XATTRS
.br
.B FSINFO_CAP_SYMLINKS
.br
.B FSINFO_CAP_HARD_LINKS
.br
.B FSINFO_CAP_HARD_LINKS_1DIR
.br
.B FSINFO_CAP_DEVICE_FILES
.br
.B FSINFO_CAP_UNIX_SPECIALS
.IP
These indicate that the filesystem supports respectively extended attributes;
symbolic links; hard links spanning direcories; hard links, but only within a
directory; block and character device files; and UNIX special files, such as
FIFO and socket.
.\" __________________ FSINFO_CAP_*JOURNAL* __________________
.PP
.B FSINFO_CAP_JOURNAL
.br
.B FSINFO_CAP_DATA_IS_JOURNALLED
.IP
The first of these indicates that the filesystem has a journal and the second
that the file data changes are being journalled.
.\" __________________ FSINFO_CAP_O_* __________________
.PP
.B FSINFO_CAP_O_SYNC
.br
.B FSINFO_CAP_O_DIRECT
.IP
These indicate that O_SYNC and O_DIRECT are supported respectively.
.\" __________________ FSINFO_CAP_* for names __________________
.PP
.B FSINFO_CAP_VOLUME_ID
.br
.B FSINFO_CAP_VOLUME_UUID
.br
.B FSINFO_CAP_VOLUME_NAME
.br
.B FSINFO_CAP_VOLUME_FSID
.br
.B FSINFO_CAP_CELL_NAME
.br
.B FSINFO_CAP_DOMAIN_NAME
.br
.B FSINFO_CAP_REALM_NAME
.IP
These indicate if various attributes are supported by the filesystem, where
.B FSINFO_CAP_X
here corresponds to
.BR fsinfo_attr_X .
.\" __________________ FSINFO_CAP_IVER_* __________________
.PP
.B FSINFO_CAP_IVER_ALL_CHANGE
.br
.B FSINFO_CAP_IVER_DATA_CHANGE
.br
.B FSINFO_CAP_IVER_MONO_INCR
.IP
These indicate if
.I i_version
on an inode in the filesystem is supported and
how it behaves.
.B all_change
indicates that i_version is incremented on metadata changes as well as data
changes.
.B data_change
indicates that i_version is only incremented on data changes, including
truncation.
.B mono_incr
indicates that i_version is incremented by exactly 1 for each change made.
.\" __________________ FSINFO_CAP_RESOURCE_FORKS __________________
.TP
.B FSINFO_CAP_RESOURCE_FORKS
This indicates that the filesystem supports some sort of resource fork or
alternate data stream on a file. This isn't the same as an extended attribute.
.\" __________________ FSINFO_CAP_NAME_* __________________
.PP
.B FSINFO_CAP_NAME_CASE_INDEP
.br
.B FSINFO_CAP_NAME_NON_UTF8
.br
.B FSINFO_CAP_NAME_HAS_CODEPAGE
.IP
These indicate certain facts about the filenames in a filesystem: whether
they're case-independent; if they're not UTF-8; and if there's a codepage
employed to map the names.
.\" __________________ FSINFO_CAP_SPARSE __________________
.TP
.B FSINFO_CAP_SPARSE
This indicates that the filesystem supports sparse files.
.\" __________________ FSINFO_CAP_NOT_PERSISTENT __________________
.TP
.B FSINFO_CAP_NOT_PERSISTENT
This indicates that the filesystem is not persistent, and that any data stored
here will not be saved in the event that the filesystem is unmounted, the
machine is rebooted or the machine loses power.
.\" __________________ FSINFO_CAP_NO_UNIX_MODE __________________
.TP
.B FSINFO_CAP_NO_UNIX_MODE
This indicates that the filesystem doesn't support the UNIX mode permissions
bits.
.\" __________________ FSINFO_CAP_HAS_*TIME __________________
.PP
.B FSINFO_CAP_HAS_ATIME
.br
.B FSINFO_CAP_HAS_BTIME
.br
.B FSINFO_CAP_HAS_CTIME
.br
.B FSINFO_CAP_HAS_MTIME
.IP
These indicate as to what timestamps a filesystem supports, including: Access
time, Birth/creation time, Change time (metadata and data) and Modification
time (data only).
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.SH RETURN VALUE
On success, the size of the value that the kernel has available is returned,
irrespective of whether the buffer is large enough to hold that. The data
written to the buffer will be truncated if it is not. On error, \-1 is
returned, and
.I errno
is set appropriately.
.SH ERRORS
.TP
.B EACCES
Search permission is denied for one of the directories
in the path prefix of
.IR pathname .
(See also
.BR path_resolution (7).)
.TP
.B EBADF
.I dirfd
is not a valid open file descriptor.
.TP
.B EFAULT
.I pathname
is NULL or
.IR pathname ", " params " or " buffer
point to a location outside the process's accessible address space.
.TP
.B EINVAL
Reserved flag specified in
.IR params->at_flags " or one of " params->__reserved[]
is not 0.
.TP
.B EOPNOTSUPP
Unsupported attribute requested in
.IR params->request .
This may be beyond the limit of the supported attribute set or may just not be
one that's supported by the filesystem.
.TP
.B ENODATA
Unavailable attribute value requested by
.IR params->Nth " and/or " params->Mth .
.TP
.B ELOOP
Too many symbolic links encountered while traversing the pathname.
.TP
.B ENAMETOOLONG
.I pathname
is too long.
.TP
.B ENOENT
A component of
.I pathname
does not exist, or
.I pathname
is an empty string and
.B AT_EMPTY_PATH
was not specified in
.IR params->at_flags .
.TP
.B ENOMEM
Out of memory (i.e., kernel memory).
.TP
.B ENOTDIR
A component of the path prefix of
.I pathname
is not a directory or
.I pathname
is relative and
.I dirfd
is a file descriptor referring to a file other than a directory.
.SH VERSIONS
.BR fsinfo ()
was added to Linux in kernel 4.18.
.SH CONFORMING TO
.BR fsinfo ()
is Linux-specific.
.SH NOTES
Glibc does not (yet) provide a wrapper for the
.BR fsinfo ()
system call; call it using
.BR syscall (2).
.SH SEE ALSO
.BR ioctl_iflags (2),
.BR statx (2),
.BR statfs (2)
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Kees Cook @ 2019-07-02 18:24 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Song Liu, linux-security@vger.kernel.org, Networking, bpf,
Alexei Starovoitov, Daniel Borkmann, Kernel Team, Lorenz Bauer,
Jann Horn, Greg KH, Linux API
In-Reply-To: <CALCETrWBWbNFJvsTCeUchu3BZJ3SH3dvtXLUB2EhnPrzFfsLNA@mail.gmail.com>
On Mon, Jul 01, 2019 at 06:59:13PM -0700, Andy Lutomirski wrote:
> I think I'm understanding your motivation. You're not trying to make
> bpf() generically usable without privilege -- you're trying to create
> a way to allow certain users to access dangerous bpf functionality
> within some limits.
>
> That's a perfectly fine goal, but I think you're reinventing the
> wheel, and the wheel you're reinventing is quite complicated and
> already exists. I think you should teach bpftool to be secure when
> installed setuid root or with fscaps enabled and put your policy in
> bpftool. If you want to harden this a little bit, it would seem
> entirely reasonable to add a new CAP_BPF_ADMIN and change some, but
> not all, of the capable() checks to check CAP_BPF_ADMIN instead of the
> capabilities that they currently check.
If finer grained controls are wanted, it does seem like the /dev/bpf
path makes the most sense. open, request abilities, use fd. The open can
be mediated by DAC and LSM. The request can be mediated by LSM. This
provides a way to add policy at the LSM level and at the tool level.
(i.e. For tool-level controls: leave LSM wide open, make /dev/bpf owned
by "bpfadmin" and bpftool becomes setuid "bpfadmin". For fine-grained
controls, leave /dev/bpf wide open and add policy to SELinux, etc.)
With only a new CAP, you don't get the fine-grained controls. (The
"request abilities" part is the key there.)
--
Kees Cook
^ permalink raw reply
* Re: [PATCH 00/11] VFS: Introduce filesystem information query syscall [ver #15]
From: Eric Biggers @ 2019-07-02 18:15 UTC (permalink / raw)
To: David Howells
Cc: viro, raven, mszeredi, christian, linux-api, linux-fsdevel,
linux-kernel
In-Reply-To: <156173661696.14042.17822154531324224780.stgit@warthog.procyon.org.uk>
On Fri, Jun 28, 2019 at 04:43:37PM +0100, David Howells wrote:
>
> Here are a set of patches that adds a syscall, fsinfo(), that allows
> attributes of a filesystem/superblock to be queried. Attribute values are
> of four basic types:
>
> (1) Version dependent-length structure (size defined by type).
>
> (2) Variable-length string (up to 4096, no NUL).
>
> (3) Array of fixed-length structures (up to INT_MAX size).
>
> (4) Opaque blob (up to INT_MAX size).
>
> Attributes can have multiple values either as a sequence of values or a
> sequence-of-sequences of values and all the values of a particular
> attribute must be of the same type.
>
> Note that the values of an attribute *are* allowed to vary between dentries
> within a single superblock, depending on the specific dentry that you're
> looking at.
>
> I've tried to make the interface as light as possible, so integer/enum
> attribute selector rather than string and the core does all the allocation
> and extensibility support work rather than leaving that to the filesystems.
> That means that for the first two attribute types, sb->s_op->fsinfo() may
> assume that the provided buffer is always present and always big enough.
>
> Further, this removes the possibility of the filesystem gaining access to the
> userspace buffer.
>
>
> fsinfo() allows a variety of information to be retrieved about a filesystem
> and the mount topology:
>
> (1) General superblock attributes:
>
> - The amount of space/free space in a filesystem (as statfs()).
> - Filesystem identifiers (UUID, volume label, device numbers, ...)
> - The limits on a filesystem's capabilities
> - Information on supported statx fields and attributes and IOC flags.
> - A variety single-bit flags indicating supported capabilities.
> - Timestamp resolution and range.
> - Sources (as per mount(2), but fsconfig() allows multiple sources).
> - In-filesystem filename format information.
> - Filesystem parameters ("mount -o xxx"-type things).
> - LSM parameters (again "mount -o xxx"-type things).
>
> (2) Filesystem-specific superblock attributes:
>
> - Server names and addresses.
> - Cell name.
>
> (3) Filesystem configuration metadata attributes:
>
> - Filesystem parameter type descriptions.
> - Name -> parameter mappings.
> - Simple enumeration name -> value mappings.
>
> (4) Information about what the fsinfo() syscall itself supports, including
> the number of attibutes supported and the number of capability bits
> supported.
>
> (5) Future patches will include information about the mount topology.
>
> The system is extensible:
>
> (1) New attributes can be added. There is no requirement that a
> filesystem implement every attribute. Note that the core VFS keeps a
> table of types and sizes so it can handle future extensibility rather
> than delegating this to the filesystems.
>
> (2) Version length-dependent structure attributes can be made larger and
> have additional information tacked on the end, provided it keeps the
> layout of the existing fields. If an older process asks for a shorter
> structure, it will only be given the bits it asks for. If a newer
> process asks for a longer structure on an older kernel, the extra
> space will be set to 0. In all cases, the size of the data actually
> available is returned.
>
> In essence, the size of a structure is that structure's version: a
> smaller size is an earlier version and a later version includes
> everything that the earlier version did.
>
> (3) New single-bit capability flags can be added. This is a structure-typed
> attribute and, as such, (2) applies. Any bits you wanted but the kernel
> doesn't support are automatically set to 0.
>
> If a filesystem-specific attribute is added, it should just take up the next
> number in the enumeration. Currently, I do not intend that the number space
> should be subdivided between interested parties.
>
>
> fsinfo() may be called like the following, for example:
>
> struct fsinfo_params params = {
> .at_flags = AT_SYMLINK_NOFOLLOW,
> .request = FSINFO_ATTR_SERVER_ADDRESS;
> .Nth = 2;
> .Mth = 1;
> };
> struct fsinfo_server_address address;
>
> len = fsinfo(AT_FDCWD, "/afs/grand.central.org/doc", ¶ms,
> &address, sizeof(address));
>
> The above example would query a network filesystem, such as AFS or NFS, and
> ask what the 2nd address (Mth) of the 3rd server (Nth) that the superblock is
> using is. Whereas:
>
> struct fsinfo_params params = {
> .at_flags = AT_SYMLINK_NOFOLLOW,
> .request = FSINFO_ATTR_AFS_CELL_NAME;
> };
> char cell_name[256];
>
> len = fsinfo(AT_FDCWD, "/afs/grand.central.org/doc", ¶ms,
> &cell_name, sizeof(cell_name));
>
> would retrieve the name of an AFS cell as a string.
>
> fsinfo() can also be used to query a context from fsopen() or fspick():
>
> fd = fsopen("ext4", 0);
> struct fsinfo_params params = {
> .request = FSINFO_ATTR_PARAM_DESCRIPTION;
> };
> struct fsinfo_param_description desc;
> fsinfo(fd, NULL, ¶ms, &desc, sizeof(desc));
>
> even if that context doesn't currently have a superblock attached (though if
> there's no superblock attached, only filesystem-specific things like parameter
> descriptions can be accessed).
>
> The patches can be found here also:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git
>
> on branch:
>
> fsinfo-core
>
>
Where are the tests and man page for this system call? "Tests" meaning actual
automated tests in a commonly used test suite (e.g. LTP, kselftests, or
xfstests), not just a sample program.
- Eric
^ permalink raw reply
* Re: [RFC PATCH] binfmt_elf: Extract .note.gnu.property from an ELF file
From: Dave Martin @ 2019-07-02 14:20 UTC (permalink / raw)
To: Yu-cheng Yu
Cc: x86, H. Peter Anvin, Thomas Gleixner, Ingo Molnar, linux-kernel,
linux-doc, linux-mm, linux-arch, linux-api, Arnd Bergmann,
Andy Lutomirski, Balbir Singh, Borislav Petkov, Cyrill Gorcunov,
Dave Hansen, Eugene Syromiatnikov, Florian Weimer, H.J. Lu,
Jann Horn, Jonathan Corbet, Kees Cook, Mike Kravetz, Nadav Amit
In-Reply-To: <20190628172203.797-1-yu-cheng.yu@intel.com>
On Fri, Jun 28, 2019 at 10:22:03AM -0700, Yu-cheng Yu wrote:
> This patch was part of the Intel Control-flow Enforcement (CET) series at:
>
> https://lkml.org/lkml/2019/6/6/1014.
>
> In the discussion, we decided to look at only an ELF header's
> PT_GNU_PROPERTY, which is a shortcut pointing to the file's
> .note.gnu.property.
>
> The Linux gABI extension draft is here:
>
> https://github.com/hjl-tools/linux-abi/wiki/linux-abi-draft.pdf.
>
> A few existing CET-enabled binary files were built without
> PT_GNU_PROPERTY; but those files' .note.gnu.property are checked by
> ld-linux, not Linux. The compatibility impact from this change is
> therefore managable.
That's convenient :)
> An ELF file's .note.gnu.property indicates features the executable file
> can support. For example, the property GNU_PROPERTY_X86_FEATURE_1_AND
> indicates the file supports GNU_PROPERTY_X86_FEATURE_1_IBT and/or
> GNU_PROPERTY_X86_FEATURE_1_SHSTK.
>
> With this patch, if an arch needs to setup features from ELF properties,
> it needs CONFIG_ARCH_USE_GNU_PROPERTY to be set, and specific
> arch_parse_property() and arch_setup_property().
>
> This work is derived from code provided by H.J. Lu <hjl.tools@gmail.com>.
Thanks for reworking this ... comments below.
> Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com>
> ---
> fs/Kconfig.binfmt | 3 +
> fs/Makefile | 1 +
> fs/binfmt_elf.c | 20 +++
> fs/gnu_property.c | 279 +++++++++++++++++++++++++++++++++++++++
> include/linux/elf.h | 11 ++
> include/uapi/linux/elf.h | 14 ++
> 6 files changed, 328 insertions(+)
> create mode 100644 fs/gnu_property.c
>
> diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
> index f87ddd1b6d72..397138ab305b 100644
> --- a/fs/Kconfig.binfmt
> +++ b/fs/Kconfig.binfmt
> @@ -36,6 +36,9 @@ config COMPAT_BINFMT_ELF
> config ARCH_BINFMT_ELF_STATE
> bool
>
> +config ARCH_USE_GNU_PROPERTY
> + bool
> +
> config BINFMT_ELF_FDPIC
> bool "Kernel support for FDPIC ELF binaries"
> default y if !BINFMT_ELF
> diff --git a/fs/Makefile b/fs/Makefile
> index c9aea23aba56..b69f18c14e09 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o
> obj-$(CONFIG_COMPAT_BINFMT_ELF) += compat_binfmt_elf.o
> obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o
> obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o
> +obj-$(CONFIG_ARCH_USE_GNU_PROPERTY) += gnu_property.o
>
> obj-$(CONFIG_FS_MBCACHE) += mbcache.o
> obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o
> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
> index 8264b468f283..cbc6d68f4a18 100644
> --- a/fs/binfmt_elf.c
> +++ b/fs/binfmt_elf.c
> @@ -852,6 +852,21 @@ static int load_elf_binary(struct linux_binprm *bprm)
> }
> }
>
> + if (interpreter) {
> + retval = arch_parse_property(&loc->interp_elf_ex,
> + interp_elf_phdata,
> + interpreter, true,
> + &arch_state);
> + } else {
> + retval = arch_parse_property(&loc->elf_ex,
> + elf_phdata,
> + bprm->file, false,
> + &arch_state);
> + }
> +
> + if (retval)
> + goto out_free_dentry;
> +
> /*
> * Allow arch code to reject the ELF at this point, whilst it's
> * still possible to return an error to the code that invoked
> @@ -1080,6 +1095,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
> goto out_free_dentry;
> }
>
> + retval = arch_setup_property(&arch_state);
> +
> + if (retval < 0)
> + goto out_free_dentry;
> +
> if (interpreter) {
> unsigned long interp_map_addr = 0;
>
> diff --git a/fs/gnu_property.c b/fs/gnu_property.c
> new file mode 100644
> index 000000000000..37cd503a0c48
> --- /dev/null
> +++ b/fs/gnu_property.c
> @@ -0,0 +1,279 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Extract an ELF file's .note.gnu.property.
> + *
> + * The path from the ELF header to the note section is the following:
> + * elfhdr->elf_phdr->elf_note->property[].
> + */
> +
> +#include <uapi/linux/elf-em.h>
> +#include <linux/processor.h>
> +#include <linux/binfmts.h>
> +#include <linux/elf.h>
> +#include <linux/slab.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/string.h>
> +#include <linux/compat.h>
> +
> +/*
> + * The .note.gnu.property layout:
> + *
> + * struct elf_note {
> + * u32 n_namesz; --> sizeof(n_name[]); always (4)
> + * u32 n_ndescsz;--> sizeof(property[])
> + * u32 n_type; --> always NT_GNU_PROPERTY_TYPE_0 (5)
> + * };
> + * char n_name[4]; --> always 'GNU\0'
> + *
> + * struct {
> + * struct gnu_property {
> + * u32 pr_type;
> + * u32 pr_datasz;
> + * };
> + * u8 pr_data[pr_datasz];
> + * }[];
> + */
> +
> +typedef bool (test_item_fn)(void *buf, u32 *arg, u32 type);
> +typedef void *(next_item_fn)(void *buf, u32 *arg, u32 type);
> +
> +static bool test_property(void *buf, u32 *max_type, u32 pr_type)
> +{
> + struct gnu_property *pr = buf;
> +
> + /*
> + * Property types must be in ascending order.
> + * Keep track of the max when testing each.
> + */
> + if (pr->pr_type > *max_type)
> + *max_type = pr->pr_type;
> +
> + return (pr->pr_type == pr_type);
> +}
> +
> +static void *next_property(void *buf, u32 *max_type, u32 pr_type)
> +{
> + struct gnu_property *pr = buf;
> +
> + if ((buf + sizeof(*pr) + pr->pr_datasz < buf) ||
> + (pr->pr_type > pr_type) ||
> + (pr->pr_type > *max_type))
> + return NULL;
> + else
> + return (buf + sizeof(*pr) + pr->pr_datasz);
> +}
> +
> +/*
> + * Scan 'buf' for a pattern; return true if found.
> + * *pos is the distance from the beginning of buf to where
> + * the searched item or the next item is located.
> + */
> +static int scan(u8 *buf, u32 buf_size, int item_size, test_item_fn test_item,
> + next_item_fn next_item, u32 *arg, u32 type, u32 *pos)
> +{
> + int found = 0;
> + u8 *p, *max;
> +
> + max = buf + buf_size;
> + if (max < buf)
> + return 0;
> +
> + p = buf;
> +
> + while ((p + item_size < max) && (p + item_size > buf)) {
> + if (test_item(p, arg, type)) {
> + found = 1;
> + break;
> + }
> +
> + p = next_item(p, arg, type);
> + }
> +
> + *pos = (p + item_size <= buf) ? 0 : (u32)(p - buf);
> + return found;
> +}
> +
> +/*
> + * Search an NT_GNU_PROPERTY_TYPE_0 for the property of 'pr_type'.
> + */
> +static int find_property(u32 pr_type, u32 *property, struct file *file,
> + loff_t file_offset, unsigned long desc_size)
> +{
> + u8 *buf;
> + int buf_size;
> +
> + u32 buf_pos;
> + unsigned long read_size;
> + unsigned long done;
> + int found = 0;
> + int ret = 0;
> + u32 last_pr = 0;
> +
> + *property = 0;
> + buf_pos = 0;
> +
> + buf_size = (desc_size > PAGE_SIZE) ? PAGE_SIZE : desc_size;
> + buf = kmalloc(buf_size, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + for (done = 0; done < desc_size; done += buf_pos) {
> + read_size = desc_size - done;
> + if (read_size > buf_size)
> + read_size = buf_size;
> +
> + ret = kernel_read(file, buf, read_size, &file_offset);
This can be simpler if we just read the whole PT_GNU_PROPERTY segment
before hand.
We should set some sanity limit on the size we accept, but I don't think
it's realistically going to be very big.
> +
> + if (ret != read_size)
> + return (ret < 0) ? ret : -EIO;
> +
> + ret = 0;
> + found = scan(buf, read_size, sizeof(struct gnu_property),
> + test_property, next_property,
> + &last_pr, pr_type, &buf_pos);
> +
> + if ((!buf_pos) || found)
> + break;
> +
> + file_offset += buf_pos - read_size;
> + }
> +
> + if (found) {
> + struct gnu_property *pr =
> + (struct gnu_property *)(buf + buf_pos);
> +
> + if (pr->pr_datasz == 4) {
> + u32 *max = (u32 *)(buf + read_size);
> + u32 *data = (u32 *)((u8 *)pr + sizeof(*pr));
> +
> + if (data + 1 <= max) {
> + *property = *data;
> + } else {
> + file_offset += buf_pos - read_size;
> + file_offset += sizeof(*pr);
> + ret = kernel_read(file, property, 4,
> + &file_offset);
> + }
> + }
> + }
> +
> + kfree(buf);
> + return ret;
> +}
> +
> +/*
> + * Look at an ELF file's PT_GNU_PROPERTY for the property of pr_type.
> + *
> + * Input:
> + * file: the file to search;
> + * phdr: the file's elf header;
> + * phnum: number of entries in phdr;
> + * pr_type: the property type.
> + *
> + * Output:
> + * The property found.
> + *
> + * Return:
> + * Zero or error.
> + */
> +
> +static int scan_segments_64(struct file *file, struct elf64_phdr *phdr,
> + int phnum, u32 pr_type, u32 *property)
> +{
> + int i, err;
> +
> + err = 0;
> +
> + for (i = 0; i < phnum; i++, phdr++) {
> + if (phdr->p_align != 8)
> + continue;
> +
> + if (phdr->p_type == PT_GNU_PROPERTY) {
> + struct elf64_note n;
> + loff_t pos;
> +
> + /* read note header */
> + pos = phdr->p_offset;
> + err = kernel_read(file, &n, sizeof(n), &pos);
> + if (err < sizeof(n))
> + return -EIO;
Should we check n_type and n_name?
Maybe we don't need to bother if we trust the tools not to put garbage
on PT_GNU_PROPERTY. I'm a little concerned that hjl's spec is pretty
vague on what the PT_GNU_PROPERTY segment should contain (even it it's
"obvious").
> +
> + /* find note payload offset */
> + pos = phdr->p_offset + round_up(sizeof(n) + n.n_namesz,
> + phdr->p_align);
> +
> + err = find_property(pr_type, property, file,
> + pos, n.n_descsz);
> + break;
> + }
> + }
> +
> + return err;
> +}
> +
> +static int scan_segments_32(struct file *file, struct elf32_phdr *phdr,
> + int phnum, u32 pr_type, u32 *property)
> +{
> + int i, err;
> +
> + err = 0;
> +
> + for (i = 0; i < phnum; i++, phdr++) {
> + if (phdr->p_align != 4)
> + continue;
> +
> + if (phdr->p_type == PT_GNU_PROPERTY) {
I wonder whether we should stick a printk_once here, along the lines of
"malformed PT_GNU_PROPERTY note ignored, go fix your toolchain".
Otherwise, maybe we don't need to bother to check this at all: if the
toolchain generates bad binaries, it's arguably not our problem?
(For example, we don't even bother to check that e_ident[EI_DATA]
matches the host endianness..., and we don't look at e_ident[EI_VERSION]
etc.)
if (phdr->p_type != PT_GNU_PROPERTY)
continue;
if (phdr->p_align != 4) {
/* complaining printk */
break;
}
/* handle PT_GNU_PROPERTY */
> + struct elf32_note n;
> + loff_t pos;
> +
> + /* read note header */
> + pos = phdr->p_offset;
> + err = kernel_read(file, &n, sizeof(n), &pos);
Would it be simpler just to load the whole segment using phdr->p_memsz?
This would allow us to do just a single kernel_read()?
> + if (err < sizeof(n))
> + return -EIO;
> +
> + /* find note payload offset */
> + pos = phdr->p_offset + round_up(sizeof(n) + n.n_namesz,
> + phdr->p_align);
> +
> + err = find_property(pr_type, property, file,
> + pos, n.n_descsz);
> + break;
> + }
> + }
> +
> + return err;
> +}
These two functions look the same except for trivial details.
Can we pass in a pointer to the ELF header, and a void * or union
pointer for the phdrs? We already do those tricks for calling
get_gnu_property() anyway.
> +
> +int get_gnu_property(void *ehdr_p, void *phdr_p, struct file *f,
> + u32 pr_type, u32 *property)
Do we have to call this every time we want to fetch a property?
This will be costly if there are several properties we want to
look at. I can also imagine that some properties will be generic
while others are arch-specific.
So, if the arch or generic code wants properties, we call this
from the generic code, and call out to arch and generic hooks to
handle any properties found. That way we would only need to do
this scan once.
> +{
> + struct elf64_hdr *ehdr64 = ehdr_p;
> + int err = 0;
> +
> + *property = 0;
> +
> + if (ehdr64->e_ident[EI_CLASS] == ELFCLASS64) {
> + struct elf64_phdr *phdr64 = phdr_p;
> +
> + err = scan_segments_64(f, phdr64, ehdr64->e_phnum,
> + pr_type, property);
> + if (err < 0)
> + goto out;
> + } else {
> + struct elf32_hdr *ehdr32 = ehdr_p;
> +
> + if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) {
> + struct elf32_phdr *phdr32 = phdr_p;
> +
> + err = scan_segments_32(f, phdr32, ehdr32->e_phnum,
> + pr_type, property);
> + if (err < 0)
> + goto out;
> + }
> + }
We still do nothing and return 0 if e_ident->[EI_CLASS] is neither
ELFCLASS32 or ELFCLASS64, which seems a bit odd.
If we think this should never happen, it might be worth sticking a
WARN() in here and returning an error just in case.
> +
> +out:
> + return err;
> +}
> diff --git a/include/linux/elf.h b/include/linux/elf.h
> index e3649b3e970e..c86cbfd17382 100644
> --- a/include/linux/elf.h
> +++ b/include/linux/elf.h
> @@ -56,4 +56,15 @@ static inline int elf_coredump_extra_notes_write(struct coredump_params *cprm) {
> extern int elf_coredump_extra_notes_size(void);
> extern int elf_coredump_extra_notes_write(struct coredump_params *cprm);
> #endif
> +
> +#ifdef CONFIG_ARCH_USE_GNU_PROPERTY
> +extern int arch_parse_property(void *ehdr, void *phdr, struct file *f,
> + bool inter, struct arch_elf_state *state);
> +extern int arch_setup_property(struct arch_elf_state *state);
> +extern int get_gnu_property(void *ehdr_p, void *phdr_p, struct file *f,
> + u32 pr_type, u32 *feature);
> +#else
> +#define arch_parse_property(ehdr, phdr, file, inter, state) (0)
> +#define arch_setup_property(state) (0)
Can we make these fallbacks into static inlines, so that we still get
argument type checking?
> +#endif
> #endif /* _LINUX_ELF_H */
> diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
> index 34c02e4290fe..530ce08467c2 100644
> --- a/include/uapi/linux/elf.h
> +++ b/include/uapi/linux/elf.h
> @@ -36,6 +36,7 @@ typedef __s64 Elf64_Sxword;
> #define PT_LOPROC 0x70000000
> #define PT_HIPROC 0x7fffffff
> #define PT_GNU_EH_FRAME 0x6474e550
> +#define PT_GNU_PROPERTY 0x6474e553
>
> #define PT_GNU_STACK (PT_LOOS + 0x474e551)
>
> @@ -443,4 +444,17 @@ typedef struct elf64_note {
> Elf64_Word n_type; /* Content type */
> } Elf64_Nhdr;
>
> +/* NT_GNU_PROPERTY_TYPE_0 header */
> +struct gnu_property {
> + __u32 pr_type;
> + __u32 pr_datasz;
Would it make sense to have
__u8 pr_data[];
here?
We should also be using the Elf types here for pr_type and pr_datasz.
Maybe we can follow hjl's lead on the definition of the type...
In linux-abi-draft.pdf, we already have
typedef struct {
Elf_Word pr_type;
Elf_Word pr_datasz;
unsigned char pr_data[PR_DATASZ];
unsigned char pr_padding[PR_PADDING];
} ElF_Prop;
This doesn't work as a generic definition due to the variable-sized
arrays, but we can omit pr_padding. For Linux purposes, __u8 is
probably preferable to unsigned char for pd_data, which we can leave as
a flexible array member.
I see no reason not to introduce
typedef __u32 Elf_Word;
somewhere so that we don't have to pointlessly special-case Elf_Prop for
the 32- and 64-bit cases.
> +};
> +
> +/* .note.gnu.property types */
> +#define GNU_PROPERTY_X86_FEATURE_1_AND 0xc0000002
> +
> +/* Bits of GNU_PROPERTY_X86_FEATURE_1_AND */
> +#define GNU_PROPERTY_X86_FEATURE_1_IBT 0x00000001
> +#define GNU_PROPERTY_X86_FEATURE_1_SHSTK 0x00000002
> +
[...]
Cheers
---Dave
^ permalink raw reply
* Re: [PATCH 01/11] vfs: syscall: Add fsinfo() to query filesystem information [ver #15]
From: Christian Brauner @ 2019-07-02 13:02 UTC (permalink / raw)
To: David Howells
Cc: viro, raven, mszeredi, linux-api, linux-fsdevel, linux-kernel
In-Reply-To: <156173662509.14042.3867242748127323502.stgit@warthog.procyon.org.uk>
On Fri, Jun 28, 2019 at 04:43:45PM +0100, David Howells 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.
>
> ===============
> NEW SYSTEM CALL
> ===============
>
> The new system call looks like:
>
> int ret = fsinfo(int dfd,
> const char *filename,
> const struct fsinfo_params *params,
> void *buffer,
> size_t buf_size);
>
> The params parameter optionally points to a block of parameters:
>
> struct fsinfo_params {
> __u32 at_flags;
> __u32 request;
> __u32 Nth;
> __u32 Mth;
> __u64 __reserved[3];
> };
>
> If params is NULL, it is assumed params->request should be
> fsinfo_attr_statfs, params->Nth should be 0, params->Mth should be 0 and
> params->at_flags should be 0.
>
> If params is given, all of params->__reserved[] must be 0.
>
> dfd, filename and params->at_flags indicate the file to query. There is no
> equivalent of lstat() as that can be emulated with fsinfo() by setting
> AT_SYMLINK_NOFOLLOW in params->at_flags. There is also no equivalent of
> fstat() as that can be emulated by passing a NULL filename to fsinfo() with
> the fd of interest in dfd. AT_NO_AUTOMOUNT can also be used to an allow
> automount point to be queried without triggering it.
>
> params->request indicates the attribute/attributes to be queried. This can
> be one of:
>
> FSINFO_ATTR_STATFS - statfs-style info
> FSINFO_ATTR_FSINFO - Information about fsinfo()
> FSINFO_ATTR_IDS - Filesystem IDs
> FSINFO_ATTR_LIMITS - Filesystem limits
> FSINFO_ATTR_SUPPORTS - What's supported in statx(), IOC flags
> FSINFO_ATTR_CAPABILITIES - Filesystem capabilities
> FSINFO_ATTR_TIMESTAMP_INFO - Inode timestamp info
> FSINFO_ATTR_VOLUME_ID - Volume ID (string)
> FSINFO_ATTR_VOLUME_UUID - Volume UUID
> FSINFO_ATTR_VOLUME_NAME - Volume name (string)
> FSINFO_ATTR_NAME_ENCODING - Filename encoding (string)
> FSINFO_ATTR_NAME_CODEPAGE - Filename codepage (string)
>
> Some attributes (such as the servers backing a network filesystem) can have
> multiple values. These can be enumerated by setting params->Nth and
> params->Mth to 0, 1, ... until ENODATA is returned.
>
> buffer and buf_size point to the reply buffer. The buffer is filled up to
> the specified size, even if this means truncating the reply. The full size
> of the reply is returned. In future versions, this will allow extra fields
> to be tacked on to the end of the reply, but anyone not expecting them will
> only get the subset they're expecting. If either buffer of buf_size are 0,
> no copy will take place and the data size will be returned.
>
> At the moment, this will only work on x86_64 and i386 as it requires the
> system call to be wired up.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: linux-api@vger.kernel.org
> ---
>
> arch/x86/entry/syscalls/syscall_32.tbl | 1
> arch/x86/entry/syscalls/syscall_64.tbl | 1
> fs/Kconfig | 7
> fs/Makefile | 1
> fs/fsinfo.c | 545 ++++++++++++++++++++++++++++++++
> include/linux/fs.h | 5
> include/linux/fsinfo.h | 65 ++++
> include/linux/syscalls.h | 4
> include/uapi/asm-generic/unistd.h | 4
> include/uapi/linux/fsinfo.h | 219 +++++++++++++
> kernel/sys_ni.c | 1
> samples/vfs/Makefile | 4
> samples/vfs/test-fsinfo.c | 551 ++++++++++++++++++++++++++++++++
> 13 files changed, 1407 insertions(+), 1 deletion(-)
> create mode 100644 fs/fsinfo.c
> create mode 100644 include/linux/fsinfo.h
> create mode 100644 include/uapi/linux/fsinfo.h
> create mode 100644 samples/vfs/test-fsinfo.c
>
> diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
> index ad968b7bac72..03decae51513 100644
> --- a/arch/x86/entry/syscalls/syscall_32.tbl
> +++ b/arch/x86/entry/syscalls/syscall_32.tbl
> @@ -438,3 +438,4 @@
> 431 i386 fsconfig sys_fsconfig __ia32_sys_fsconfig
> 432 i386 fsmount sys_fsmount __ia32_sys_fsmount
> 433 i386 fspick sys_fspick __ia32_sys_fspick
> +434 i386 fsinfo sys_fsinfo __ia32_sys_fsinfo
> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
> index b4e6f9e6204a..ea63df9a1020 100644
> --- a/arch/x86/entry/syscalls/syscall_64.tbl
> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
> @@ -355,6 +355,7 @@
> 431 common fsconfig __x64_sys_fsconfig
> 432 common fsmount __x64_sys_fsmount
> 433 common fspick __x64_sys_fspick
> +434 common fsinfo __x64_sys_fsinfo
>
> #
> # x32-specific system call numbers start at 512 to avoid cache impact
> diff --git a/fs/Kconfig b/fs/Kconfig
> index cbbffc8b9ef5..9e7d2f2c0111 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -15,6 +15,13 @@ config VALIDATE_FS_PARSER
> Enable this to perform validation of the parameter description for a
> filesystem when it is registered.
>
> +config FSINFO
> + bool "Enable the fsinfo() system call"
> + help
> + Enable the file system information querying system call to allow
> + comprehensive information to be retrieved about a filesystem,
> + superblock or mount object.
> +
> if BLOCK
>
> config FS_IOMAP
> diff --git a/fs/Makefile b/fs/Makefile
> index c9aea23aba56..26eaeae4b9a1 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -53,6 +53,7 @@ obj-$(CONFIG_SYSCTL) += drop_caches.o
>
> obj-$(CONFIG_FHANDLE) += fhandle.o
> obj-$(CONFIG_FS_IOMAP) += iomap.o
> +obj-$(CONFIG_FSINFO) += fsinfo.o
>
> obj-y += quota/
>
> diff --git a/fs/fsinfo.c b/fs/fsinfo.c
> new file mode 100644
> index 000000000000..09e743b16235
> --- /dev/null
> +++ b/fs/fsinfo.c
> @@ -0,0 +1,545 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Filesystem information query.
> + *
> + * Copyright (C) 2019 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + */
> +#include <linux/syscalls.h>
> +#include <linux/fs.h>
> +#include <linux/file.h>
> +#include <linux/mount.h>
> +#include <linux/namei.h>
> +#include <linux/statfs.h>
> +#include <linux/security.h>
> +#include <linux/uaccess.h>
> +#include <linux/fsinfo.h>
> +#include <uapi/linux/mount.h>
> +#include "internal.h"
> +
> +static u32 calc_mount_attrs(u32 mnt_flags)
I totally forgot to mention this:
I had a patchset that extended statfs to also report back when a
mountpoint is shared, slave, private, or unbindable to avoid parsing
/proc/1/mountinfo which is unreliable and slow. I've given a lengthier
argument in the patchset I sent more than a year ago:
https://lkml.org/lkml/2018/5/25/397
Pretty please, make it possible to retrieve propagation attributes with
fsinfo(). We desperately need this and it's trivial to add imho.
> +{
> + u32 attrs = 0;
> +
> + if (mnt_flags & MNT_READONLY)
> + attrs |= MOUNT_ATTR_RDONLY;
> + if (mnt_flags & MNT_NOSUID)
> + attrs |= MOUNT_ATTR_NOSUID;
> + if (mnt_flags & MNT_NODEV)
> + attrs |= MOUNT_ATTR_NODEV;
> + if (mnt_flags & MNT_NOEXEC)
> + attrs |= MOUNT_ATTR_NOEXEC;
> + if (mnt_flags & MNT_NODIRATIME)
> + attrs |= MOUNT_ATTR_NODIRATIME;
> +
> + if (mnt_flags & MNT_NOATIME)
> + attrs |= MOUNT_ATTR_NOATIME;
> + else if (mnt_flags & MNT_RELATIME)
> + attrs |= MOUNT_ATTR_RELATIME;
> + else
> + attrs |= MOUNT_ATTR_STRICTATIME;
> + return attrs;
> +}
^ permalink raw reply
* Re: [PATCH 01/11] vfs: syscall: Add fsinfo() to query filesystem information [ver #15]
From: Christian Brauner @ 2019-07-02 12:58 UTC (permalink / raw)
To: David Howells
Cc: viro, raven, mszeredi, linux-api, linux-fsdevel, linux-kernel,
Rasmus Villemoes
In-Reply-To: <27313.1561986796@warthog.procyon.org.uk>
On Mon, Jul 01, 2019 at 02:13:16PM +0100, David Howells wrote:
> Christian Brauner <christian@brauner.io> wrote:
>
> > > +config FSINFO
> >
> > Hm, any reason why we would hide that syscalls under a config option?
>
> Rasmus Villemoes asked for it to be made conditional.
Ah, ok. I guess this is another case of "what about embedded users".
Fair enough.
>
> https://lore.kernel.org/lkml/f3646774-ee9e-d5b7-8a11-670012034d59@rasmusvillemoes.dk/
>
> > Do we, not have any dumb helpers for scenarios like this?:
> >
> > #define strlen_literal(x) (sizeof(""x"") - 1)
> > #define strlen_array(x) (sizeof(x) - 1)
>
> git grep doesn't find them under this name.
Yeah, than we don't have that. Might be worth having such helpers at
some point.
>
> > > + while (!signal_pending(current)) {
> > > + params->usage = 0;
> > > + ret = fsinfo(path, params);
> > > + if (IS_ERR_VALUE((long)ret))
> > > + return ret; /* Error */
> > > + if ((unsigned int)ret <= params->buf_size)
> >
> > if ((size_t)ret ...? Just for the sake of clarity if for nothing else.
> >
> > > + return ret; /* It fitted */
> >
> > Ok, a little confused here, tbh. params->buf_size is size_t
>
> It's "unsigned int".
Ok, good.
>
> > and this function returns an int. Forgot whether you mentioned this before,
> > buf_size exceed can't exceed INT_MAX?
>
> It's mentioned in the documentation (ie. fsinfo.rst). I'll mention it in the
> comments adjacent to the attribute definition table also.
Thanks! I missed that apparently.
>
> > Is it really wort it to have this code generating stuff in there?
>
> From a readability PoV, yes, tabulation is awesome, IMO;-). Up to 5 lines per
> attribute is too much vertical space and expanding it makes the whole thing
> much less readable. Add to that that not all attributes will be the same
> number of lines.
>
> It would be easier if the I could get away with making the constant names
> lower case, but the thou-shalt-capitalise-constantists dislike that, so, given
> that I don't know of a way to make the C preprocessor change the case of a
> symbol, I have to include both parts.
>
> I have four pieces of information: type, depth, constant name, struct name (if
> applicable), and I can fit them on one line this way.
>
> You really find this:
>
> static const struct fsinfo_attr_info fsinfo_buffer_info[FSINFO_ATTR__NR] = {
> [FSINFO_ATTR_STATFS] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_statfs)
> },
> [FSINFO_ATTR_FSINFO] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_fsinfo)
> },
> [FSINFO_ATTR_IDS] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_ids)
> },
> [FSINFO_ATTR_LIMITS] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_limits)
> },
> [FSINFO_ATTR_CAPABILITIES] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_capabilities)
> },
> [FSINFO_ATTR_SUPPORTS] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_supports)
> },
> [FSINFO_ATTR_TIMESTAMP_INFO] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_timestamp_info)
> },
> [FSINFO_ATTR_VOLUME_ID] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_VOLUME_UUID] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_volume_uuid)
> },
> [FSINFO_ATTR_VOLUME_NAME] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_NAME_ENCODING] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_NAME_CODEPAGE] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_PARAM_DESCRIPTION] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_param_description)
> },
> [FSINFO_ATTR_PARAM_SPECIFICATION] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_N,
> .size = sizeof(struct fsinfo_param_specification)
> },
> [FSINFO_ATTR_PARAM_ENUM] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_N,
> .size = sizeof(struct fsinfo_param_enum)
> },
> [FSINFO_ATTR_PARAMETERS] = {
> .type = __FSINFO_OPAQUE,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_LSM_PARAMETERS] = {
> .type = __FSINFO_OPAQUE,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_SERVER_NAME] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_N,
> },
> [FSINFO_ATTR_SERVER_ADDRESS] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_NM,
> .size = sizeof(struct fsinfo_server_address)
> },
> [FSINFO_ATTR_AFS_CELL_NAME] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_MOUNT_INFO] = {
> .type = __FSINFO_STRUCT,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_mount_info)
> },
> [FSINFO_ATTR_MOUNT_DEVNAME] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_SINGLE,
> },
> [FSINFO_ATTR_MOUNT_CHILDREN] = {
> .type = __FSINFO_STRUCT_ARRAY,
> .flags = __FSINFO_SINGLE,
> .size = sizeof(struct fsinfo_mount_child)
> },
> [FSINFO_ATTR_MOUNT_SUBMOUNT] = {
> .type = __FSINFO_STRING,
> .flags = __FSINFO_N,
> },
> };
>
> is easier to read than this?:
Yes, very much so imho. :)
>
> static const struct fsinfo_attr_info fsinfo_buffer_info[FSINFO_ATTR__NR] = {
> FSINFO_STRUCT (STATFS, statfs),
> FSINFO_STRUCT (FSINFO, fsinfo),
> FSINFO_STRUCT (IDS, ids),
> FSINFO_STRUCT (LIMITS, limits),
> FSINFO_STRUCT (CAPABILITIES, capabilities),
> FSINFO_STRUCT (SUPPORTS, supports),
> FSINFO_STRUCT (TIMESTAMP_INFO, timestamp_info),
> FSINFO_STRING (VOLUME_ID),
> FSINFO_STRUCT (VOLUME_UUID, volume_uuid),
> FSINFO_STRING (VOLUME_NAME),
> FSINFO_STRING (NAME_ENCODING),
> FSINFO_STRING (NAME_CODEPAGE),
> FSINFO_STRUCT (PARAM_DESCRIPTION, param_description),
> FSINFO_STRUCT_N (PARAM_SPECIFICATION, param_specification),
> FSINFO_STRUCT_N (PARAM_ENUM, param_enum),
> FSINFO_OPAQUE (PARAMETERS),
> FSINFO_OPAQUE (LSM_PARAMETERS),
> FSINFO_STRING_N (SERVER_NAME),
> FSINFO_STRUCT_NM (SERVER_ADDRESS, server_address),
> FSINFO_STRING (AFS_CELL_NAME),
> FSINFO_STRUCT (MOUNT_INFO, mount_info),
> FSINFO_STRING (MOUNT_DEVNAME),
> FSINFO_STRUCT_ARRAY (MOUNT_CHILDREN, mount_child),
> FSINFO_STRING_N (MOUNT_SUBMOUNT),
> };
>
> The latter also has the advantage that I can take this and drop it into the
> test program and change the helper macros to make it do other things. With
> the fully expanded code, that isn't possible.
>
> One thing I will grant you, though, I can simplify:
>
> #define __FSINFO_STRUCT 0
> #define __FSINFO_STRING 1
> #define __FSINFO_OPAQUE 2
> #define __FSINFO_STRUCT_ARRAY 3
> #define __FSINFO_0 0
> #define __FSINFO_N 0x0001
> #define __FSINFO_NM 0x0002
>
> #define _Z(T, F, S) { .type = __FSINFO_##T, .flags = __FSINFO_##F, .size = S }
> #define FSINFO_STRING(X) [FSINFO_ATTR_##X] = _Z(STRING, 0, 0)
> #define FSINFO_STRUCT(X,Y) [FSINFO_ATTR_##X] = _Z(STRUCT, 0, sizeof(struct fsinfo_##Y))
> #define FSINFO_STRING_N(X) [FSINFO_ATTR_##X] = _Z(STRING, N, 0)
> #define FSINFO_STRUCT_N(X,Y) [FSINFO_ATTR_##X] = _Z(STRUCT, N, sizeof(struct fsinfo_##Y))
> #define FSINFO_STRING_NM(X) [FSINFO_ATTR_##X] = _Z(STRING, NM, 0)
> #define FSINFO_STRUCT_NM(X,Y) [FSINFO_ATTR_##X] = _Z(STRUCT, NM, sizeof(struct fsinfo_##Y))
> #define FSINFO_OPAQUE(X) [FSINFO_ATTR_##X] = _Z(OPAQUE, 0, 0)
> #define FSINFO_STRUCT_ARRAY(X,Y) [FSINFO_ATTR_##X] = _Z(STRUCT_ARRAY, 0, sizeof(struct fsinfo_##Y))
>
> a bit:
>
> #define __FSINFO_STRUCT 0
> #define __FSINFO_STRING 1
> #define __FSINFO_OPAQUE 2
> #define __FSINFO_STRUCT_ARRAY 3
> #define __FSINFO_N 0x01
> #define __FSINFO_NM 0x02
>
> #define _Z(T, S) { .type = __FSINFO_##T, .flags = 0, .size = S }
> #define _Z_N(T, S) { .type = __FSINFO_##T, .flags = __FSINFO_N, .size = S }
> #define _Z_NM(T, S) { .type = __FSINFO_##T, .flags = __FSINFO_NM, .size = S }
> #define FSINFO_STRING(X) [FSINFO_ATTR_##X] = _Z(STRING, 0)
> #define FSINFO_STRUCT(X,Y) [FSINFO_ATTR_##X] = _Z(STRUCT, sizeof(struct fsinfo_##Y))
> #define FSINFO_STRING_N(X) [FSINFO_ATTR_##X] = _Z_N(STRING, 0)
> #define FSINFO_STRUCT_N(X,Y) [FSINFO_ATTR_##X] = _Z_N(STRUCT, sizeof(struct fsinfo_##Y))
> #define FSINFO_STRING_NM(X) [FSINFO_ATTR_##X] = _Z_NM(STRING, 0)
> #define FSINFO_STRUCT_NM(X,Y) [FSINFO_ATTR_##X] = _Z_NM(STRUCT, sizeof(struct fsinfo_##Y))
> #define FSINFO_OPAQUE(X) [FSINFO_ATTR_##X] = _Z(OPAQUE, 0)
> #define FSINFO_STRUCT_ARRAY(X,Y) [FSINFO_ATTR_##X] = _Z(STRUCT_ARRAY, sizeof(struct fsinfo_##Y))
>
> > I urge you to think about git grep users. For them this is an absolute
> > nightmare. :)
>
> That's a valid point, but it's a problem all over the kernel. We use
> macroisation everywhere. See all the declaration and define macros that nest
> layers deep.
Well maybe we can stop doing it (at least for some stuff). :)
>
> If that's your main worry, The attribute type name could be fully expanded in
> the table, eg.:
>
> FSINFO_STRUCT (FSINFO_ATTR_CAPABILITIES, capabilities),
> FSINFO_STRING_N (FSINFO_ATTR_MOUNT_SUBMOUNT),
>
> > > + unsigned int result_size;
> >
> > Wouldn't it be better if this could be a size_t?
>
> Why? size_t takes more space on a 64-bit system, but I'm not allowing the
> filesystem to return that much data, mainly because I don't really want to be
> allocating a >2G buffer.
>
> In fact, for large objects there's something to be said for writing directly
> to userspace rather than going through a buffer, but for the fact that I want
> to hold, say, the RCU readlock across the entire transaction in some
> instances.
>
> > > + if (!user_buffer || !user_buf_size) {
> >
> > Maybe we could be a little more strict and require both be set to their
> > respective zero values, i.e. only support reporting the size if
> > !user_buffer && user_buf_size = 0 for that to work. If only one of them
> > is set to their zero value we report EINVAL.
>
> That's an option, certainly.
Ok, up to you. I find my suggestion a little cleaner.
>
> > Hm, I'm not sure that "capabilities" is a good name here. This is
> > potentially misleading because of other uses of "capabilities" we
> > already have. Like, I don't want thes capabilities to pop up when I do
> > git grep capabilities. Just a short way until someone also speaks of
> > "fscaps" or "fsinfocaps" and then confusion is basically guaranteed. :)
> >
> > Maybe "features" would be better?
>
> Yeah - that's probably better. The only issue is that it doesn't have a nice
> short hypocoristicon like "cap", though I could use "feat" I guess.
FEAT is probably ok. Not pretty but "cap" isn't either.
>
> > > +#define _ATFILE_SOURCE
> >
> > nit: Defining fsinfoat() implicitly or what's that supposed to do? If that's
> > the case wouldn't it be nicer to just explicitly declare fsinfoat()
>
> Um... fsinfo() takes AT_* flags. It's fsinfoat(), ffsinfo() and lfsinfo()
> all rolled into one, plus a couple of extra bits. It doesn't really need an
> at-suffix on the name as there's no at-less original.
Ah, seems like a very ancient macro...
^ permalink raw reply
* Re: [PATCH v5 2/3] fpga: dfl: fme: add thermal management support
From: Moritz Fischer @ 2019-07-02 4:26 UTC (permalink / raw)
To: Wu Hao
Cc: mdf, linux-fpga, linux-kernel, linux-api, linux-hwmon, jdelvare,
linux, atull, gregkh, Luwei Kang, Russ Weight, Xu Yilun
In-Reply-To: <1561963027-4213-3-git-send-email-hao.wu@intel.com>
Hi Hao,
On Mon, Jul 01, 2019 at 02:37:06PM +0800, Wu Hao wrote:
> This patch adds support to thermal management private feature for DFL
> FPGA Management Engine (FME). This private feature driver registers
> a hwmon for thermal/temperature monitoring (hwmon temp1_input).
> If hardware automatic throttling is supported by this hardware, then
> driver also exposes sysfs interfaces under hwmon for thresholds
> (temp1_max/ crit/ emergency), threshold alarms (temp1_max_alarm/
> temp1_crit_alarm) and throttling policy (temp1_max_policy).
>
> Signed-off-by: Luwei Kang <luwei.kang@intel.com>
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> Signed-off-by: Xu Yilun <yilun.xu@intel.com>
> Signed-off-by: Wu Hao <hao.wu@intel.com>
> Acked-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Moritz Fischer <mdf@kernel.org>
> ---
> v2: create a dfl_fme_thermal hwmon to expose thermal information.
> move all sysfs interfaces under hwmon
> tempareture --> hwmon temp1_input
> threshold1 --> hwmon temp1_alarm
> threshold2 --> hwmon temp1_crit
> trip_threshold --> hwmon temp1_emergency
> threshold1_status --> hwmon temp1_alarm_status
> threshold2_status --> hwmon temp1_crit_status
> threshold1_policy --> hwmon temp1_alarm_policy
> v3: rename some hwmon sysfs interfaces to follow hwmon ABI.
> temp1_alarm --> temp1_max
> temp1_alarm_status --> temp1_max_alarm
> temp1_crit_status --> temp1_crit_alarm
> temp1_alarm_policy --> temp1_max_policy
> update sysfs doc for above sysfs interface changes.
> replace scnprintf with sprintf in sysfs interface.
> v4: use HWMON_CHANNEL_INFO.
> rebase, and update date in sysfs doc.
> v5: no change.
> ---
> Documentation/ABI/testing/sysfs-platform-dfl-fme | 64 ++++++++
> drivers/fpga/Kconfig | 2 +-
> drivers/fpga/dfl-fme-main.c | 187 +++++++++++++++++++++++
> 3 files changed, 252 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-fme b/Documentation/ABI/testing/sysfs-platform-dfl-fme
> index 86eef83..2cd17dc 100644
> --- a/Documentation/ABI/testing/sysfs-platform-dfl-fme
> +++ b/Documentation/ABI/testing/sysfs-platform-dfl-fme
> @@ -119,3 +119,67 @@ Description: Write-only. Write error code to this file to clear all errors
> logged in errors, first_error and next_error. Write fails with
> -EINVAL if input parsing fails or input error code doesn't
> match.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/name
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. Read this file to get the name of hwmon device, it
> + supports values:
> + 'dfl_fme_thermal' - thermal hwmon device name
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_input
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. It returns FPGA device temperature in millidegrees
> + Celsius.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. It returns hardware threshold1 temperature in
> + millidegrees Celsius. If temperature rises at or above this
> + threshold, hardware starts 50% or 90% throttling (see
> + 'temp1_max_policy').
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_crit
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. It returns hardware threshold2 temperature in
> + millidegrees Celsius. If temperature rises at or above this
> + threshold, hardware starts 100% throttling.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_emergency
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. It returns hardware trip threshold temperature in
> + millidegrees Celsius. If temperature rises at or above this
> + threshold, a fatal event will be triggered to board management
> + controller (BMC) to shutdown FPGA.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max_alarm
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns 1 if temperature is currently at or above
> + hardware threshold1 (see 'temp1_max'), otherwise 0.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_crit_alarm
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns 1 if temperature is currently at or above
> + hardware threshold2 (see 'temp1_crit'), otherwise 0.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max_policy
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. Read this file to get the policy of hardware threshold1
> + (see 'temp1_max'). It only supports two values (policies):
> + 0 - AP2 state (90% throttling)
> + 1 - AP1 state (50% throttling)
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 8072c19..48f6224 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -155,7 +155,7 @@ config FPGA_DFL
>
> config FPGA_DFL_FME
> tristate "FPGA DFL FME Driver"
> - depends on FPGA_DFL
> + depends on FPGA_DFL && HWMON
> help
> The FPGA Management Engine (FME) is a feature device implemented
> under Device Feature List (DFL) framework. Select this option to
> diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
> index 4490cf4..59ff9f1 100644
> --- a/drivers/fpga/dfl-fme-main.c
> +++ b/drivers/fpga/dfl-fme-main.c
> @@ -14,6 +14,8 @@
> * Henry Mitchel <henry.mitchel@intel.com>
> */
>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/uaccess.h>
> @@ -217,6 +219,187 @@ static long fme_hdr_ioctl(struct platform_device *pdev,
> .ioctl = fme_hdr_ioctl,
> };
>
> +#define FME_THERM_THRESHOLD 0x8
> +#define TEMP_THRESHOLD1 GENMASK_ULL(6, 0)
> +#define TEMP_THRESHOLD1_EN BIT_ULL(7)
> +#define TEMP_THRESHOLD2 GENMASK_ULL(14, 8)
> +#define TEMP_THRESHOLD2_EN BIT_ULL(15)
> +#define TRIP_THRESHOLD GENMASK_ULL(30, 24)
> +#define TEMP_THRESHOLD1_STATUS BIT_ULL(32) /* threshold1 reached */
> +#define TEMP_THRESHOLD2_STATUS BIT_ULL(33) /* threshold2 reached */
> +/* threshold1 policy: 0 - AP2 (90% throttle) / 1 - AP1 (50% throttle) */
> +#define TEMP_THRESHOLD1_POLICY BIT_ULL(44)
> +
> +#define FME_THERM_RDSENSOR_FMT1 0x10
> +#define FPGA_TEMPERATURE GENMASK_ULL(6, 0)
> +
> +#define FME_THERM_CAP 0x20
> +#define THERM_NO_THROTTLE BIT_ULL(0)
> +
> +#define MD_PRE_DEG
> +
> +static bool fme_thermal_throttle_support(void __iomem *base)
> +{
> + u64 v = readq(base + FME_THERM_CAP);
> +
> + return FIELD_GET(THERM_NO_THROTTLE, v) ? false : true;
> +}
> +
> +static umode_t thermal_hwmon_attrs_visible(const void *drvdata,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + const struct dfl_feature *feature = drvdata;
> +
> + /* temperature is always supported, and check hardware cap for others */
> + if (attr == hwmon_temp_input)
> + return 0444;
> +
> + return fme_thermal_throttle_support(feature->ioaddr) ? 0444 : 0;
> +}
> +
> +static int thermal_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> + u64 v;
> +
> + switch (attr) {
> + case hwmon_temp_input:
> + v = readq(feature->ioaddr + FME_THERM_RDSENSOR_FMT1);
> + *val = (long)(FIELD_GET(FPGA_TEMPERATURE, v) * 1000);
> + break;
> + case hwmon_temp_max:
> + v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
> + *val = (long)(FIELD_GET(TEMP_THRESHOLD1, v) * 1000);
> + break;
> + case hwmon_temp_crit:
> + v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
> + *val = (long)(FIELD_GET(TEMP_THRESHOLD2, v) * 1000);
> + break;
> + case hwmon_temp_emergency:
> + v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
> + *val = (long)(FIELD_GET(TRIP_THRESHOLD, v) * 1000);
> + break;
> + case hwmon_temp_max_alarm:
> + v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
> + *val = (long)FIELD_GET(TEMP_THRESHOLD1_STATUS, v);
> + break;
> + case hwmon_temp_crit_alarm:
> + v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
> + *val = (long)FIELD_GET(TEMP_THRESHOLD2_STATUS, v);
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static const struct hwmon_ops thermal_hwmon_ops = {
> + .is_visible = thermal_hwmon_attrs_visible,
> + .read = thermal_hwmon_read,
> +};
> +
> +static const struct hwmon_channel_info *thermal_hwmon_info[] = {
> + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_EMERGENCY |
> + HWMON_T_MAX | HWMON_T_MAX_ALARM |
> + HWMON_T_CRIT | HWMON_T_CRIT_ALARM),
> + NULL
> +};
> +
> +static const struct hwmon_chip_info thermal_hwmon_chip_info = {
> + .ops = &thermal_hwmon_ops,
> + .info = thermal_hwmon_info,
> +};
> +
> +static ssize_t temp1_max_policy_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> + u64 v;
> +
> + v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
> +
> + return sprintf(buf, "%u\n",
> + (unsigned int)FIELD_GET(TEMP_THRESHOLD1_POLICY, v));
> +}
> +
> +static DEVICE_ATTR_RO(temp1_max_policy);
> +
> +static struct attribute *thermal_extra_attrs[] = {
> + &dev_attr_temp1_max_policy.attr,
> + NULL,
> +};
> +
> +static umode_t thermal_extra_attrs_visible(struct kobject *kobj,
> + struct attribute *attr, int index)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> +
> + return fme_thermal_throttle_support(feature->ioaddr) ? attr->mode : 0;
> +}
> +
> +static const struct attribute_group thermal_extra_group = {
> + .attrs = thermal_extra_attrs,
> + .is_visible = thermal_extra_attrs_visible,
> +};
> +__ATTRIBUTE_GROUPS(thermal_extra);
> +
> +static int fme_thermal_mgmt_init(struct platform_device *pdev,
> + struct dfl_feature *feature)
> +{
> + struct device *hwmon;
> +
> + dev_dbg(&pdev->dev, "FME Thermal Management Init.\n");
> +
> + /*
> + * create hwmon to allow userspace monitoring temperature and other
> + * threshold information.
> + *
> + * temp1_input -> FPGA device temperature
> + * temp1_max -> hardware threshold 1 -> 50% or 90% throttling
> + * temp1_crit -> hardware threshold 2 -> 100% throttling
> + * temp1_emergency -> hardware trip_threshold to shutdown FPGA
> + * temp1_max_alarm -> hardware threshold 1 alarm
> + * temp1_crit_alarm -> hardware threshold 2 alarm
> + *
> + * create device specific sysfs interfaces, e.g. read temp1_max_policy
> + * to understand the actual hardware throttling action (50% vs 90%).
> + *
> + * If hardware doesn't support automatic throttling per thresholds,
> + * then all above sysfs interfaces are not visible except temp1_input
> + * for temperature.
> + */
> + hwmon = devm_hwmon_device_register_with_info(&pdev->dev,
> + "dfl_fme_thermal", feature,
> + &thermal_hwmon_chip_info,
> + thermal_extra_groups);
> + if (IS_ERR(hwmon)) {
> + dev_err(&pdev->dev, "Fail to register thermal hwmon\n");
> + return PTR_ERR(hwmon);
> + }
> +
> + return 0;
> +}
> +
> +static void fme_thermal_mgmt_uinit(struct platform_device *pdev,
> + struct dfl_feature *feature)
> +{
> + dev_dbg(&pdev->dev, "FME Thermal Management UInit.\n");
> +}
> +
> +static const struct dfl_feature_id fme_thermal_mgmt_id_table[] = {
> + {.id = FME_FEATURE_ID_THERMAL_MGMT,},
> + {0,}
> +};
> +
> +static const struct dfl_feature_ops fme_thermal_mgmt_ops = {
> + .init = fme_thermal_mgmt_init,
> + .uinit = fme_thermal_mgmt_uinit,
> +};
> +
> static struct dfl_feature_driver fme_feature_drvs[] = {
> {
> .id_table = fme_hdr_id_table,
> @@ -231,6 +414,10 @@ static long fme_hdr_ioctl(struct platform_device *pdev,
> .ops = &fme_global_err_ops,
> },
> {
> + .id_table = fme_thermal_mgmt_id_table,
> + .ops = &fme_thermal_mgmt_ops,
> + },
> + {
> .ops = NULL,
> },
> };
> --
> 1.8.3.1
>
^ permalink raw reply
* Re: [PATCH v5 3/3] fpga: dfl: fme: add power management support
From: Moritz Fischer @ 2019-07-02 4:25 UTC (permalink / raw)
To: Wu Hao
Cc: mdf, linux-fpga, linux-kernel, linux-api, linux-hwmon, jdelvare,
linux, atull, gregkh, Luwei Kang, Xu Yilun
In-Reply-To: <1561963027-4213-4-git-send-email-hao.wu@intel.com>
Hi Hao,
On Mon, Jul 01, 2019 at 02:37:07PM +0800, Wu Hao wrote:
> This patch adds support for power management private feature under
> FPGA Management Engine (FME). This private feature driver registers
> a hwmon for power (power1_input), thresholds information, e.g.
> (power1_max / crit / max_alarm / crit_alarm) and also read-only sysfs
> interfaces for other power management information. For configuration,
> user could write threshold values via above power1_max / crit sysfs
> interface under hwmon too.
>
> Signed-off-by: Luwei Kang <luwei.kang@intel.com>
> Signed-off-by: Xu Yilun <yilun.xu@intel.com>
> Signed-off-by: Wu Hao <hao.wu@intel.com>
Reviewed-by: Moritz Fischer <mdf@kernel.org>
> ---
> v2: create a dfl_fme_power hwmon to expose power sysfs interfaces.
> move all sysfs interfaces under hwmon
> consumed --> hwmon power1_input
> threshold1 --> hwmon power1_cap
> threshold2 --> hwmon power1_crit
> threshold1_status --> hwmon power1_cap_status
> threshold2_status --> hwmon power1_crit_status
> xeon_limit --> hwmon power1_xeon_limit
> fpga_limit --> hwmon power1_fpga_limit
> ltr --> hwmon power1_ltr
> v3: rename some hwmon sysfs interfaces to follow hwmon ABI.
> power1_cap --> power1_max
> power1_cap_status --> power1_max_alarm
> power1_crit_status --> power1_crit_alarm
> update sysfs doc for above sysfs interface changes.
> replace scnprintf with sprintf in sysfs interface.
> v4: use HWMON_CHANNEL_INFO.
> update date in sysfs doc.
> v5: clamp threshold inputs in power_hwmon_write function.
> update sysfs doc as threshold inputs are clamped now.
> add more descriptions to ltr sysfs interface.
> ---
> Documentation/ABI/testing/sysfs-platform-dfl-fme | 68 +++++++
> drivers/fpga/dfl-fme-main.c | 216 +++++++++++++++++++++++
> 2 files changed, 284 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-fme b/Documentation/ABI/testing/sysfs-platform-dfl-fme
> index 2cd17dc..5c2e49d 100644
> --- a/Documentation/ABI/testing/sysfs-platform-dfl-fme
> +++ b/Documentation/ABI/testing/sysfs-platform-dfl-fme
> @@ -127,6 +127,7 @@ Contact: Wu Hao <hao.wu@intel.com>
> Description: Read-Only. Read this file to get the name of hwmon device, it
> supports values:
> 'dfl_fme_thermal' - thermal hwmon device name
> + 'dfl_fme_power' - power hwmon device name
>
> What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_input
> Date: June 2019
> @@ -183,3 +184,70 @@ Description: Read-Only. Read this file to get the policy of hardware threshold1
> (see 'temp1_max'). It only supports two values (policies):
> 0 - AP2 state (90% throttling)
> 1 - AP1 state (50% throttling)
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_input
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. It returns current FPGA power consumption in uW.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_max
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Write. Read this file to get current hardware power
> + threshold1 in uW. If power consumption rises at or above
> + this threshold, hardware starts 50% throttling.
> + Write this file to set current hardware power threshold1 in uW.
> + As hardware only accepts values in Watts, so input value will
> + be round down per Watts (< 1 watts part will be discarded) and
> + clamped within the range from 0 to 127 Watts. Write fails with
> + -EINVAL if input parsing fails.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_crit
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Write. Read this file to get current hardware power
> + threshold2 in uW. If power consumption rises at or above
> + this threshold, hardware starts 90% throttling.
> + Write this file to set current hardware power threshold2 in uW.
> + As hardware only accepts values in Watts, so input value will
> + be round down per Watts (< 1 watts part will be discarded) and
> + clamped within the range from 0 to 127 Watts. Write fails with
> + -EINVAL if input parsing fails.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_max_alarm
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns 1 if power consumption is currently at or
> + above hardware threshold1 (see 'power1_max'), otherwise 0.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_crit_alarm
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns 1 if power consumption is currently at or
> + above hardware threshold2 (see 'power1_crit'), otherwise 0.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_xeon_limit
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. It returns power limit for XEON in uW.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_fpga_limit
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-Only. It returns power limit for FPGA in uW.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_ltr
> +Date: June 2019
> +KernelVersion: 5.3
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. Read this file to get current Latency Tolerance
> + Reporting (ltr) value. It returns 1 if all Accelerated
> + Function Units (AFUs) can tolerate latency >= 40us for memory
> + access or 0 if any AFU is latency sensitive (< 40us).
> diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
> index 59ff9f1..1ff386d 100644
> --- a/drivers/fpga/dfl-fme-main.c
> +++ b/drivers/fpga/dfl-fme-main.c
> @@ -400,6 +400,218 @@ static void fme_thermal_mgmt_uinit(struct platform_device *pdev,
> .uinit = fme_thermal_mgmt_uinit,
> };
>
> +#define FME_PWR_STATUS 0x8
> +#define FME_LATENCY_TOLERANCE BIT_ULL(18)
> +#define PWR_CONSUMED GENMASK_ULL(17, 0)
> +
> +#define FME_PWR_THRESHOLD 0x10
> +#define PWR_THRESHOLD1 GENMASK_ULL(6, 0) /* in Watts */
> +#define PWR_THRESHOLD2 GENMASK_ULL(14, 8) /* in Watts */
> +#define PWR_THRESHOLD_MAX 0x7f /* in Watts */
> +#define PWR_THRESHOLD1_STATUS BIT_ULL(16)
> +#define PWR_THRESHOLD2_STATUS BIT_ULL(17)
> +
> +#define FME_PWR_XEON_LIMIT 0x18
> +#define XEON_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */
> +#define XEON_PWR_EN BIT_ULL(15)
> +#define FME_PWR_FPGA_LIMIT 0x20
> +#define FPGA_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */
> +#define FPGA_PWR_EN BIT_ULL(15)
> +
> +static int power_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> + u64 v;
> +
> + switch (attr) {
> + case hwmon_power_input:
> + v = readq(feature->ioaddr + FME_PWR_STATUS);
> + *val = (long)(FIELD_GET(PWR_CONSUMED, v) * 1000000);
> + break;
> + case hwmon_power_max:
> + v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
> + *val = (long)(FIELD_GET(PWR_THRESHOLD1, v) * 1000000);
> + break;
> + case hwmon_power_crit:
> + v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
> + *val = (long)(FIELD_GET(PWR_THRESHOLD2, v) * 1000000);
> + break;
> + case hwmon_power_max_alarm:
> + v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
> + *val = (long)FIELD_GET(PWR_THRESHOLD1_STATUS, v);
> + break;
> + case hwmon_power_crit_alarm:
> + v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
> + *val = (long)FIELD_GET(PWR_THRESHOLD2_STATUS, v);
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long val)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev->parent);
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> + int ret = 0;
> + u64 v;
> +
> + val = clamp_val(val / 1000000, 0, PWR_THRESHOLD_MAX);
> +
> + mutex_lock(&pdata->lock);
> +
> + switch (attr) {
> + case hwmon_power_max:
> + v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
> + v &= ~PWR_THRESHOLD1;
> + v |= FIELD_PREP(PWR_THRESHOLD1, val);
> + writeq(v, feature->ioaddr + FME_PWR_THRESHOLD);
> + break;
> + case hwmon_power_crit:
> + v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
> + v &= ~PWR_THRESHOLD2;
> + v |= FIELD_PREP(PWR_THRESHOLD2, val);
> + writeq(v, feature->ioaddr + FME_PWR_THRESHOLD);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + }
> +
> + mutex_unlock(&pdata->lock);
> +
> + return ret;
> +}
> +
> +static umode_t power_hwmon_attrs_visible(const void *drvdata,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + switch (attr) {
> + case hwmon_power_input:
> + case hwmon_power_max_alarm:
> + case hwmon_power_crit_alarm:
> + return 0444;
> + case hwmon_power_max:
> + case hwmon_power_crit:
> + return 0644;
> + }
> +
> + return 0;
> +}
> +
> +static const struct hwmon_ops power_hwmon_ops = {
> + .is_visible = power_hwmon_attrs_visible,
> + .read = power_hwmon_read,
> + .write = power_hwmon_write,
> +};
> +
> +static const struct hwmon_channel_info *power_hwmon_info[] = {
> + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT |
> + HWMON_P_MAX | HWMON_P_MAX_ALARM |
> + HWMON_P_CRIT | HWMON_P_CRIT_ALARM),
> + NULL
> +};
> +
> +static const struct hwmon_chip_info power_hwmon_chip_info = {
> + .ops = &power_hwmon_ops,
> + .info = power_hwmon_info,
> +};
> +
> +static ssize_t power1_xeon_limit_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> + u16 xeon_limit = 0;
> + u64 v;
> +
> + v = readq(feature->ioaddr + FME_PWR_XEON_LIMIT);
> +
> + if (FIELD_GET(XEON_PWR_EN, v))
> + xeon_limit = FIELD_GET(XEON_PWR_LIMIT, v);
> +
> + return sprintf(buf, "%u\n", xeon_limit * 100000);
> +}
> +
> +static ssize_t power1_fpga_limit_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> + u16 fpga_limit = 0;
> + u64 v;
> +
> + v = readq(feature->ioaddr + FME_PWR_FPGA_LIMIT);
> +
> + if (FIELD_GET(FPGA_PWR_EN, v))
> + fpga_limit = FIELD_GET(FPGA_PWR_LIMIT, v);
> +
> + return sprintf(buf, "%u\n", fpga_limit * 100000);
> +}
> +
> +static ssize_t power1_ltr_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct dfl_feature *feature = dev_get_drvdata(dev);
> + u64 v;
> +
> + v = readq(feature->ioaddr + FME_PWR_STATUS);
> +
> + return sprintf(buf, "%u\n",
> + (unsigned int)FIELD_GET(FME_LATENCY_TOLERANCE, v));
> +}
> +
> +static DEVICE_ATTR_RO(power1_xeon_limit);
> +static DEVICE_ATTR_RO(power1_fpga_limit);
> +static DEVICE_ATTR_RO(power1_ltr);
> +
> +static struct attribute *power_extra_attrs[] = {
> + &dev_attr_power1_xeon_limit.attr,
> + &dev_attr_power1_fpga_limit.attr,
> + &dev_attr_power1_ltr.attr,
> + NULL
> +};
> +
> +ATTRIBUTE_GROUPS(power_extra);
> +
> +static int fme_power_mgmt_init(struct platform_device *pdev,
> + struct dfl_feature *feature)
> +{
> + struct device *hwmon;
> +
> + dev_dbg(&pdev->dev, "FME Power Management Init.\n");
> +
> + hwmon = devm_hwmon_device_register_with_info(&pdev->dev,
> + "dfl_fme_power", feature,
> + &power_hwmon_chip_info,
> + power_extra_groups);
> + if (IS_ERR(hwmon)) {
> + dev_err(&pdev->dev, "Fail to register power hwmon\n");
> + return PTR_ERR(hwmon);
> + }
> +
> + return 0;
> +}
> +
> +static void fme_power_mgmt_uinit(struct platform_device *pdev,
> + struct dfl_feature *feature)
> +{
> + dev_dbg(&pdev->dev, "FME Power Management UInit.\n");
> +}
> +
> +static const struct dfl_feature_id fme_power_mgmt_id_table[] = {
> + {.id = FME_FEATURE_ID_POWER_MGMT,},
> + {0,}
> +};
> +
> +static const struct dfl_feature_ops fme_power_mgmt_ops = {
> + .init = fme_power_mgmt_init,
> + .uinit = fme_power_mgmt_uinit,
> +};
> +
> static struct dfl_feature_driver fme_feature_drvs[] = {
> {
> .id_table = fme_hdr_id_table,
> @@ -418,6 +630,10 @@ static void fme_thermal_mgmt_uinit(struct platform_device *pdev,
> .ops = &fme_thermal_mgmt_ops,
> },
> {
> + .id_table = fme_power_mgmt_id_table,
> + .ops = &fme_power_mgmt_ops,
> + },
> + {
> .ops = NULL,
> },
> };
> --
> 1.8.3.1
>
Thanks,
Moritz
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Andy Lutomirski @ 2019-07-02 1:59 UTC (permalink / raw)
To: Song Liu
Cc: Andy Lutomirski, linux-security@vger.kernel.org, Networking, bpf,
Alexei Starovoitov, Daniel Borkmann, Kernel Team, Lorenz Bauer,
Jann Horn, Greg KH, Linux API, Kees Cook
In-Reply-To: <0DE7F23E-9CD2-4F03-82B5-835506B59056@fb.com>
On Mon, Jul 1, 2019 at 2:03 AM Song Liu <songliubraving@fb.com> wrote:
>
> Hi Andy,
>
> Thanks for these detailed analysis.
>
> > On Jun 30, 2019, at 8:12 AM, Andy Lutomirski <luto@kernel.org> wrote:
> >
> > On Fri, Jun 28, 2019 at 12:05 PM Song Liu <songliubraving@fb.com> wrote:
> >>
> >> Hi Andy,
> >>
> >>> On Jun 27, 2019, at 4:40 PM, Andy Lutomirski <luto@kernel.org> wrote:
> >>>
> >>> On 6/27/19 1:19 PM, Song Liu wrote:
> >>>> This patch introduce unprivileged BPF access. The access control is
> >>>> achieved via device /dev/bpf. Users with write access to /dev/bpf are able
> >>>> to call sys_bpf().
> >>>> Two ioctl command are added to /dev/bpf:
> >>>> The two commands enable/disable permission to call sys_bpf() for current
> >>>> task. This permission is noted by bpf_permitted in task_struct. This
> >>>> permission is inherited during clone(CLONE_THREAD).
> >>>> Helper function bpf_capable() is added to check whether the task has got
> >>>> permission via /dev/bpf.
> >>>
> >>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> >>>> index 0e079b2298f8..79dc4d641cf3 100644
> >>>> --- a/kernel/bpf/verifier.c
> >>>> +++ b/kernel/bpf/verifier.c
> >>>> @@ -9134,7 +9134,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
> >>>> env->insn_aux_data[i].orig_idx = i;
> >>>> env->prog = *prog;
> >>>> env->ops = bpf_verifier_ops[env->prog->type];
> >>>> - is_priv = capable(CAP_SYS_ADMIN);
> >>>> + is_priv = bpf_capable(CAP_SYS_ADMIN);
> >>>
> >>> Huh? This isn't a hardening measure -- the "is_priv" verifier mode allows straight-up leaks of private kernel state to user mode.
> >>>
> >>> (For that matter, the pending lockdown stuff should possibly consider this a "confidentiality" issue.)
> >>>
> >>>
> >>> I have a bigger issue with this patch, though: it's a really awkward way to pretend to have capabilities. For bpf, it seems like you could make this be a *real* capability without too much pain since there's only one syscall there. Just find a way to pass an fd to /dev/bpf into the syscall. If this means you need a new bpf_with_cap() syscall that takes an extra argument, so be it. The old bpf() syscall can just translate to bpf_with_cap(..., -1).
> >>>
> >>> For a while, I've considered a scheme I call "implicit rights". There would be a directory in /dev called /dev/implicit_rights. This would either be part of devtmpfs or a whole new filesystem -- it would *not* be any other filesystem. The contents would be files that can't be read or written and exist only in memory. You create them with a privileged syscall. Certain actions that are sensitive but not at the level of CAP_SYS_ADMIN (use of large-attack-surface bpf stuff, creation of user namespaces, profiling the kernel, etc) could require an "implicit right". When you do them, if you don't have CAP_SYS_ADMIN, the kernel would do a path walk for, say, /dev/implicit_rights/bpf and, if the object exists, can be opened, and actually refers to the "bpf" rights object, then the action is allowed. Otherwise it's denied.
> >>>
> >>> This is extensible, and it doesn't require the rather ugly per-task state of whether it's enabled.
> >>>
> >>> For things like creation of user namespaces, there's an existing API, and the default is that it works without privilege. Switching it to an implicit right has the benefit of not requiring code changes to programs that already work as non-root.
> >>>
> >>> But, for BPF in particular, this type of compatibility issue doesn't exist now. You already can't use most eBPF functionality without privilege. New bpf-using programs meant to run without privilege are *new*, so they can use a new improved API. So, rather than adding this obnoxious ioctl, just make the API explicit, please.
> >>>
> >>> Also, please cc: linux-abi next time.
> >>
> >> Thanks for your inputs.
> >>
> >> I think we need to clarify the use case here. In this case, we are NOT
> >> thinking about creating new tools for unprivileged users. Instead, we
> >> would like to use existing tools without root.
> >
> > I read patch 4, and I interpret it very differently. Patches 2-4 are
> > creating a new version of libbpf and a new version of bpftool. Given
> > this, I see no real justification for adding a new in-kernel per-task
> > state instead of just pushing the complexity into libbpf.
>
> I am not sure whether we are on the same page. Let me try an example,
> say we have application A, which calls sys_bpf().
>
> Before the series: we have to run A with root;
> After the series: we add a special user with access to /dev/bpf, and
> run A with this special user.
>
> If we look at the whole system, I would say we are more secure after
> the series.
>
> I am not trying to make an extreme example here, because this use case
> is the motivation here.
>
> To stay safe, we have to properly manage the permission of /dev/bpf.
> This is just like we need to properly manage access to /etc/sudoers and
> /dev/mem.
>
> Does this make sense?
>
I think I'm understanding your motivation. You're not trying to make
bpf() generically usable without privilege -- you're trying to create
a way to allow certain users to access dangerous bpf functionality
within some limits.
That's a perfectly fine goal, but I think you're reinventing the
wheel, and the wheel you're reinventing is quite complicated and
already exists. I think you should teach bpftool to be secure when
installed setuid root or with fscaps enabled and put your policy in
bpftool. If you want to harden this a little bit, it would seem
entirely reasonable to add a new CAP_BPF_ADMIN and change some, but
not all, of the capable() checks to check CAP_BPF_ADMIN instead of the
capabilities that they currently check.
Your example of /etc/sudoers is apt, and it does not involve any
kernel support :)
^ permalink raw reply
* Re: [RFC PATCH] binfmt_elf: Extract .note.gnu.property from an ELF file
From: Yu-cheng Yu @ 2019-07-01 19:49 UTC (permalink / raw)
To: Jann Horn
Cc: the arch/x86 maintainers, H. Peter Anvin, Thomas Gleixner,
Ingo Molnar, kernel list, linux-doc, Linux-MM, linux-arch,
Linux API, Arnd Bergmann, Andy Lutomirski, Balbir Singh,
Borislav Petkov, Cyrill Gorcunov, Dave Hansen,
Eugene Syromiatnikov, Florian Weimer, H.J. Lu, Jonathan Corbet,
Kees Cook, Mike Kravetz <mike.krav>
In-Reply-To: <CAG48ez0rHHfcRgiVZf5FP0YOzxsXigvpg6ci790cmiN6PBwkhQ@mail.gmail.com>
On Mon, 2019-07-01 at 21:49 +0200, Jann Horn wrote:
> On Fri, Jun 28, 2019 at 7:30 PM Yu-cheng Yu <yu-cheng.yu@intel.com> wrote:
> [...]
> > In the discussion, we decided to look at only an ELF header's
> > PT_GNU_PROPERTY, which is a shortcut pointing to the file's
> > .note.gnu.property.
> >
> > The Linux gABI extension draft is here:
> >
> > https://github.com/hjl-tools/linux-abi/wiki/linux-abi-draft.pdf.
> >
> > A few existing CET-enabled binary files were built without
> > PT_GNU_PROPERTY; but those files' .note.gnu.property are checked by
> > ld-linux, not Linux. The compatibility impact from this change is
> > therefore managable.
> >
> > An ELF file's .note.gnu.property indicates features the executable file
> > can support. For example, the property GNU_PROPERTY_X86_FEATURE_1_AND
> > indicates the file supports GNU_PROPERTY_X86_FEATURE_1_IBT and/or
> > GNU_PROPERTY_X86_FEATURE_1_SHSTK.
> >
> > With this patch, if an arch needs to setup features from ELF properties,
> > it needs CONFIG_ARCH_USE_GNU_PROPERTY to be set, and specific
> > arch_parse_property() and arch_setup_property().
>
> [...]
> > +typedef bool (test_item_fn)(void *buf, u32 *arg, u32 type);
> > +typedef void *(next_item_fn)(void *buf, u32 *arg, u32 type);
> > +
> > +static bool test_property(void *buf, u32 *max_type, u32 pr_type)
> > +{
> > + struct gnu_property *pr = buf;
> > +
> > + /*
> > + * Property types must be in ascending order.
> > + * Keep track of the max when testing each.
> > + */
> > + if (pr->pr_type > *max_type)
> > + *max_type = pr->pr_type;
> > +
> > + return (pr->pr_type == pr_type);
> > +}
> > +
> > +static void *next_property(void *buf, u32 *max_type, u32 pr_type)
> > +{
> > + struct gnu_property *pr = buf;
> > +
> > + if ((buf + sizeof(*pr) + pr->pr_datasz < buf) ||
>
> This looks like UB to me, see below.
>
> > + (pr->pr_type > pr_type) ||
> > + (pr->pr_type > *max_type))
> > + return NULL;
> > + else
> > + return (buf + sizeof(*pr) + pr->pr_datasz);
> > +}
> > +
> > +/*
> > + * Scan 'buf' for a pattern; return true if found.
> > + * *pos is the distance from the beginning of buf to where
> > + * the searched item or the next item is located.
> > + */
> > +static int scan(u8 *buf, u32 buf_size, int item_size, test_item_fn
> > test_item,
> > + next_item_fn next_item, u32 *arg, u32 type, u32 *pos)
> > +{
> > + int found = 0;
> > + u8 *p, *max;
> > +
> > + max = buf + buf_size;
> > + if (max < buf)
> > + return 0;
>
> How can this ever legitimately happen? If it can't, perhaps you meant
> to put a WARN_ON_ONCE() or something like that here?
> Also, computing out-of-bounds pointers is UB (section 6.5.6 of C99:
> "If both the pointer operand and the result point to elements of the
> same array object, or one past the last element of the array object,
> the evaluation shall not produce an overflow; otherwise, the behavior
> is undefined."), and if the addition makes the pointer wrap, that's
> certainly out of bounds; so I don't think this condition can trigger
> without UB.
>
> > +
> > + p = buf;
> > +
> > + while ((p + item_size < max) && (p + item_size > buf)) {
>
> Again, as far as I know, this is technically UB. Please rewrite this.
> For example, you could do something like:
>
> while (max - p >= item_size) {
>
> and then make sure that next_item() never computes OOB pointers.
>
> > + if (test_item(p, arg, type)) {
> > + found = 1;
> > + break;
> > + }
> > +
> > + p = next_item(p, arg, type);
> > + }
> > +
> > + *pos = (p + item_size <= buf) ? 0 : (u32)(p - buf);
> > + return found;
> > +}
> > +
> > +/*
> > + * Search an NT_GNU_PROPERTY_TYPE_0 for the property of 'pr_type'.
> > + */
> > +static int find_property(u32 pr_type, u32 *property, struct file *file,
> > + loff_t file_offset, unsigned long desc_size)
> > +{
> > + u8 *buf;
> > + int buf_size;
> > +
> > + u32 buf_pos;
> > + unsigned long read_size;
> > + unsigned long done;
> > + int found = 0;
> > + int ret = 0;
> > + u32 last_pr = 0;
> > +
> > + *property = 0;
> > + buf_pos = 0;
> > +
> > + buf_size = (desc_size > PAGE_SIZE) ? PAGE_SIZE : desc_size;
>
> open-coded min(desc_size, PAGE_SIZE)
>
> > + buf = kmalloc(buf_size, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + for (done = 0; done < desc_size; done += buf_pos) {
> > + read_size = desc_size - done;
> > + if (read_size > buf_size)
> > + read_size = buf_size;
> > +
> > + ret = kernel_read(file, buf, read_size, &file_offset);
> > +
> > + if (ret != read_size)
> > + return (ret < 0) ? ret : -EIO;
>
> This leaks the memory allocated for `buf`.
>
> > +
> > + ret = 0;
> > + found = scan(buf, read_size, sizeof(struct gnu_property),
> > + test_property, next_property,
> > + &last_pr, pr_type, &buf_pos);
> > +
> > + if ((!buf_pos) || found)
> > + break;
> > +
> > + file_offset += buf_pos - read_size;
> > + }
>
> [...]
> > + kfree(buf);
> > + return ret;
> > +}
I will fix these.
Thanks,
Yu-cheng
^ permalink raw reply
* Re: [RFC PATCH] binfmt_elf: Extract .note.gnu.property from an ELF file
From: Jann Horn @ 2019-07-01 19:49 UTC (permalink / raw)
To: Yu-cheng Yu
Cc: the arch/x86 maintainers, H. Peter Anvin, Thomas Gleixner,
Ingo Molnar, kernel list, linux-doc, Linux-MM, linux-arch,
Linux API, Arnd Bergmann, Andy Lutomirski, Balbir Singh,
Borislav Petkov, Cyrill Gorcunov, Dave Hansen,
Eugene Syromiatnikov, Florian Weimer, H.J. Lu, Jonathan Corbet,
Kees Cook, Mike Kravetz <mike.kr>
In-Reply-To: <20190628172203.797-1-yu-cheng.yu@intel.com>
On Fri, Jun 28, 2019 at 7:30 PM Yu-cheng Yu <yu-cheng.yu@intel.com> wrote:
[...]
> In the discussion, we decided to look at only an ELF header's
> PT_GNU_PROPERTY, which is a shortcut pointing to the file's
> .note.gnu.property.
>
> The Linux gABI extension draft is here:
>
> https://github.com/hjl-tools/linux-abi/wiki/linux-abi-draft.pdf.
>
> A few existing CET-enabled binary files were built without
> PT_GNU_PROPERTY; but those files' .note.gnu.property are checked by
> ld-linux, not Linux. The compatibility impact from this change is
> therefore managable.
>
> An ELF file's .note.gnu.property indicates features the executable file
> can support. For example, the property GNU_PROPERTY_X86_FEATURE_1_AND
> indicates the file supports GNU_PROPERTY_X86_FEATURE_1_IBT and/or
> GNU_PROPERTY_X86_FEATURE_1_SHSTK.
>
> With this patch, if an arch needs to setup features from ELF properties,
> it needs CONFIG_ARCH_USE_GNU_PROPERTY to be set, and specific
> arch_parse_property() and arch_setup_property().
[...]
> +typedef bool (test_item_fn)(void *buf, u32 *arg, u32 type);
> +typedef void *(next_item_fn)(void *buf, u32 *arg, u32 type);
> +
> +static bool test_property(void *buf, u32 *max_type, u32 pr_type)
> +{
> + struct gnu_property *pr = buf;
> +
> + /*
> + * Property types must be in ascending order.
> + * Keep track of the max when testing each.
> + */
> + if (pr->pr_type > *max_type)
> + *max_type = pr->pr_type;
> +
> + return (pr->pr_type == pr_type);
> +}
> +
> +static void *next_property(void *buf, u32 *max_type, u32 pr_type)
> +{
> + struct gnu_property *pr = buf;
> +
> + if ((buf + sizeof(*pr) + pr->pr_datasz < buf) ||
This looks like UB to me, see below.
> + (pr->pr_type > pr_type) ||
> + (pr->pr_type > *max_type))
> + return NULL;
> + else
> + return (buf + sizeof(*pr) + pr->pr_datasz);
> +}
> +
> +/*
> + * Scan 'buf' for a pattern; return true if found.
> + * *pos is the distance from the beginning of buf to where
> + * the searched item or the next item is located.
> + */
> +static int scan(u8 *buf, u32 buf_size, int item_size, test_item_fn test_item,
> + next_item_fn next_item, u32 *arg, u32 type, u32 *pos)
> +{
> + int found = 0;
> + u8 *p, *max;
> +
> + max = buf + buf_size;
> + if (max < buf)
> + return 0;
How can this ever legitimately happen? If it can't, perhaps you meant
to put a WARN_ON_ONCE() or something like that here?
Also, computing out-of-bounds pointers is UB (section 6.5.6 of C99:
"If both the pointer operand and the result point to elements of the
same array object, or one past the last element of the array object,
the evaluation shall not produce an overflow; otherwise, the behavior
is undefined."), and if the addition makes the pointer wrap, that's
certainly out of bounds; so I don't think this condition can trigger
without UB.
> +
> + p = buf;
> +
> + while ((p + item_size < max) && (p + item_size > buf)) {
Again, as far as I know, this is technically UB. Please rewrite this.
For example, you could do something like:
while (max - p >= item_size) {
and then make sure that next_item() never computes OOB pointers.
> + if (test_item(p, arg, type)) {
> + found = 1;
> + break;
> + }
> +
> + p = next_item(p, arg, type);
> + }
> +
> + *pos = (p + item_size <= buf) ? 0 : (u32)(p - buf);
> + return found;
> +}
> +
> +/*
> + * Search an NT_GNU_PROPERTY_TYPE_0 for the property of 'pr_type'.
> + */
> +static int find_property(u32 pr_type, u32 *property, struct file *file,
> + loff_t file_offset, unsigned long desc_size)
> +{
> + u8 *buf;
> + int buf_size;
> +
> + u32 buf_pos;
> + unsigned long read_size;
> + unsigned long done;
> + int found = 0;
> + int ret = 0;
> + u32 last_pr = 0;
> +
> + *property = 0;
> + buf_pos = 0;
> +
> + buf_size = (desc_size > PAGE_SIZE) ? PAGE_SIZE : desc_size;
open-coded min(desc_size, PAGE_SIZE)
> + buf = kmalloc(buf_size, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + for (done = 0; done < desc_size; done += buf_pos) {
> + read_size = desc_size - done;
> + if (read_size > buf_size)
> + read_size = buf_size;
> +
> + ret = kernel_read(file, buf, read_size, &file_offset);
> +
> + if (ret != read_size)
> + return (ret < 0) ? ret : -EIO;
This leaks the memory allocated for `buf`.
> +
> + ret = 0;
> + found = scan(buf, read_size, sizeof(struct gnu_property),
> + test_property, next_property,
> + &last_pr, pr_type, &buf_pos);
> +
> + if ((!buf_pos) || found)
> + break;
> +
> + file_offset += buf_pos - read_size;
> + }
[...]
> + kfree(buf);
> + return ret;
> +}
^ permalink raw reply
* Re: [RFC PATCH 3/3] Prevent user from writing to IBT bitmap.
From: Yu-cheng Yu @ 2019-07-01 19:48 UTC (permalink / raw)
To: Andy Lutomirski
Cc: X86 ML, H. Peter Anvin, Thomas Gleixner, Ingo Molnar, LKML,
open list:DOCUMENTATION, Linux-MM, linux-arch, Linux API,
Arnd Bergmann, Balbir Singh, Borislav Petkov, Cyrill Gorcunov,
Dave Hansen, Eugene Syromiatnikov, Florian Weimer, H.J. Lu,
Jann Horn, Jonathan Corbet, Kees Cook, Mike Kravetz
In-Reply-To: <CALCETrXsXXJWTSJxUO8YxHUo=QJKmHyJa7iz+jOBjWMRhno4rA@mail.gmail.com>
On Sat, 2019-06-29 at 16:44 -0700, Andy Lutomirski wrote:
> On Fri, Jun 28, 2019 at 12:50 PM Yu-cheng Yu <yu-cheng.yu@intel.com> wrote:
> >
> > The IBT bitmap is visiable from user-mode, but not writable.
> >
> > Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com>
> >
> > ---
> > arch/x86/mm/fault.c | 7 +++++++
> > 1 file changed, 7 insertions(+)
> >
> > diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> > index 59f4f66e4f2e..231196abb62e 100644
> > --- a/arch/x86/mm/fault.c
> > +++ b/arch/x86/mm/fault.c
> > @@ -1454,6 +1454,13 @@ void do_user_addr_fault(struct pt_regs *regs,
> > * we can handle it..
> > */
> > good_area:
> > +#define USER_MODE_WRITE (FAULT_FLAG_WRITE | FAULT_FLAG_USER)
> > + if (((flags & USER_MODE_WRITE) == USER_MODE_WRITE) &&
> > + (vma->vm_flags & VM_IBT)) {
> > + bad_area_access_error(regs, hw_error_code, address, vma);
> > + return;
> > + }
> > +
>
> Just make the VMA have VM_WRITE and VM_MAYWRITE clear. No new code
> like this should be required.
Ok, I will work on that.
Thanks,
Yu-cheng
^ permalink raw reply
* [PATCH v6 17/17] f2fs: add fs-verity support
From: Eric Biggers @ 2019-07-01 15:32 UTC (permalink / raw)
To: linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add fs-verity support to f2fs. fs-verity is a filesystem feature that
enables transparent integrity protection and authentication of read-only
files. It uses a dm-verity like mechanism at the file level: a Merkle
tree is used to verify any block in the file in log(filesize) time. It
is implemented mainly by helper functions in fs/verity/. See
Documentation/filesystems/fsverity.rst for the full documentation.
The f2fs support for fs-verity consists of:
- Adding a filesystem feature flag and an inode flag for fs-verity.
- Implementing the fsverity_operations to support enabling verity on an
inode and reading/writing the verity metadata.
- Updating ->readpages() to verify data as it's read from verity files
and to support reading verity metadata pages.
- Updating ->write_begin(), ->write_end(), and ->writepages() to support
writing verity metadata pages.
- Calling the fs-verity hooks for ->open(), ->setattr(), and ->ioctl().
Like ext4, f2fs stores the verity metadata (Merkle tree and
fsverity_descriptor) past the end of the file, starting at the first 64K
boundary beyond i_size. This approach works because (a) verity files
are readonly, and (b) pages fully beyond i_size aren't visible to
userspace but can be read/written internally by f2fs with only some
relatively small changes to f2fs. Extended attributes cannot be used
because (a) f2fs limits the total size of an inode's xattr entries to
4096 bytes, which wouldn't be enough for even a single Merkle tree
block, and (b) f2fs encryption doesn't encrypt xattrs, yet the verity
metadata *must* be encrypted when the file is because it contains hashes
of the plaintext data.
Acked-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/f2fs/Makefile | 1 +
fs/f2fs/data.c | 72 ++++++++++++--
fs/f2fs/f2fs.h | 23 ++++-
fs/f2fs/file.c | 40 ++++++++
fs/f2fs/inode.c | 5 +-
fs/f2fs/super.c | 3 +
fs/f2fs/sysfs.c | 11 +++
fs/f2fs/verity.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++
fs/f2fs/xattr.h | 2 +
9 files changed, 388 insertions(+), 14 deletions(-)
create mode 100644 fs/f2fs/verity.c
diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile
index 776c4b936504..2aaecc63834f 100644
--- a/fs/f2fs/Makefile
+++ b/fs/f2fs/Makefile
@@ -8,3 +8,4 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
+f2fs-$(CONFIG_FS_VERITY) += verity.o
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index eda4181d2092..8f175d47291d 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -73,6 +73,7 @@ static enum count_type __read_io_type(struct page *page)
enum bio_post_read_step {
STEP_INITIAL = 0,
STEP_DECRYPT,
+ STEP_VERITY,
};
struct bio_post_read_ctx {
@@ -119,8 +120,23 @@ static void decrypt_work(struct work_struct *work)
bio_post_read_processing(ctx);
}
+static void verity_work(struct work_struct *work)
+{
+ struct bio_post_read_ctx *ctx =
+ container_of(work, struct bio_post_read_ctx, work);
+
+ fsverity_verify_bio(ctx->bio);
+
+ bio_post_read_processing(ctx);
+}
+
static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
{
+ /*
+ * We use different work queues for decryption and for verity because
+ * verity may require reading metadata pages that need decryption, and
+ * we shouldn't recurse to the same workqueue.
+ */
switch (++ctx->cur_step) {
case STEP_DECRYPT:
if (ctx->enabled_steps & (1 << STEP_DECRYPT)) {
@@ -130,6 +146,14 @@ static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
}
ctx->cur_step++;
/* fall-through */
+ case STEP_VERITY:
+ if (ctx->enabled_steps & (1 << STEP_VERITY)) {
+ INIT_WORK(&ctx->work, verity_work);
+ fsverity_enqueue_verify_work(&ctx->work);
+ return;
+ }
+ ctx->cur_step++;
+ /* fall-through */
default:
__read_end_io(ctx->bio);
}
@@ -553,8 +577,15 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio)
up_write(&io->io_rwsem);
}
+static inline bool f2fs_need_verity(const struct inode *inode, pgoff_t idx)
+{
+ return fsverity_active(inode) &&
+ idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
+}
+
static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
- unsigned nr_pages, unsigned op_flag)
+ unsigned nr_pages, unsigned op_flag,
+ pgoff_t first_idx)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct bio *bio;
@@ -570,6 +601,10 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
if (f2fs_encrypted_file(inode))
post_read_steps |= 1 << STEP_DECRYPT;
+
+ if (f2fs_need_verity(inode, first_idx))
+ post_read_steps |= 1 << STEP_VERITY;
+
if (post_read_steps) {
ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
if (!ctx) {
@@ -591,7 +626,7 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page,
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct bio *bio;
- bio = f2fs_grab_read_bio(inode, blkaddr, 1, 0);
+ bio = f2fs_grab_read_bio(inode, blkaddr, 1, 0, page->index);
if (IS_ERR(bio))
return PTR_ERR(bio);
@@ -1514,6 +1549,15 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
return ret;
}
+static inline loff_t f2fs_readpage_limit(struct inode *inode)
+{
+ if (IS_ENABLED(CONFIG_FS_VERITY) &&
+ (IS_VERITY(inode) || f2fs_verity_in_progress(inode)))
+ return inode->i_sb->s_maxbytes;
+
+ return i_size_read(inode);
+}
+
static int f2fs_read_single_page(struct inode *inode, struct page *page,
unsigned nr_pages,
struct f2fs_map_blocks *map,
@@ -1532,7 +1576,7 @@ static int f2fs_read_single_page(struct inode *inode, struct page *page,
block_in_file = (sector_t)page->index;
last_block = block_in_file + nr_pages;
- last_block_in_file = (i_size_read(inode) + blocksize - 1) >>
+ last_block_in_file = (f2fs_readpage_limit(inode) + blocksize - 1) >>
blkbits;
if (last_block > last_block_in_file)
last_block = last_block_in_file;
@@ -1576,6 +1620,11 @@ static int f2fs_read_single_page(struct inode *inode, struct page *page,
} else {
zero_out:
zero_user_segment(page, 0, PAGE_SIZE);
+ if (f2fs_need_verity(inode, page->index) &&
+ !fsverity_verify_page(page)) {
+ ret = -EIO;
+ goto out;
+ }
if (!PageUptodate(page))
SetPageUptodate(page);
unlock_page(page);
@@ -1594,7 +1643,7 @@ static int f2fs_read_single_page(struct inode *inode, struct page *page,
}
if (bio == NULL) {
bio = f2fs_grab_read_bio(inode, block_nr, nr_pages,
- is_readahead ? REQ_RAHEAD : 0);
+ is_readahead ? REQ_RAHEAD : 0, page->index);
if (IS_ERR(bio)) {
ret = PTR_ERR(bio);
bio = NULL;
@@ -1991,7 +2040,7 @@ static int __write_data_page(struct page *page, bool *submitted,
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
goto redirty_out;
- if (page->index < end_index)
+ if (page->index < end_index || f2fs_verity_in_progress(inode))
goto write;
/*
@@ -2383,7 +2432,8 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
* the block addresses when there is no need to fill the page.
*/
if (!f2fs_has_inline_data(inode) && len == PAGE_SIZE &&
- !is_inode_flag_set(inode, FI_NO_PREALLOC))
+ !is_inode_flag_set(inode, FI_NO_PREALLOC) &&
+ !f2fs_verity_in_progress(inode))
return 0;
/* f2fs_lock_op avoids race between write CP and convert_inline_page */
@@ -2522,7 +2572,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
if (len == PAGE_SIZE || PageUptodate(page))
return 0;
- if (!(pos & (PAGE_SIZE - 1)) && (pos + len) >= i_size_read(inode)) {
+ if (!(pos & (PAGE_SIZE - 1)) && (pos + len) >= i_size_read(inode) &&
+ !f2fs_verity_in_progress(inode)) {
zero_user_segment(page, len, PAGE_SIZE);
return 0;
}
@@ -2585,7 +2636,8 @@ static int f2fs_write_end(struct file *file,
set_page_dirty(page);
- if (pos + copied > i_size_read(inode))
+ if (pos + copied > i_size_read(inode) &&
+ !f2fs_verity_in_progress(inode))
f2fs_i_size_write(inode, pos + copied);
unlock_out:
f2fs_put_page(page, 1);
@@ -2906,7 +2958,9 @@ void f2fs_clear_page_cache_dirty_tag(struct page *page)
int __init f2fs_init_post_read_processing(void)
{
- bio_post_read_ctx_cache = KMEM_CACHE(bio_post_read_ctx, 0);
+ bio_post_read_ctx_cache =
+ kmem_cache_create("f2fs_bio_post_read_ctx",
+ sizeof(struct bio_post_read_ctx), 0, 0, NULL);
if (!bio_post_read_ctx_cache)
goto fail;
bio_post_read_ctx_pool =
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 06b89a9862ab..8477191ad1c9 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -25,6 +25,7 @@
#include <crypto/hash.h>
#include <linux/fscrypt.h>
+#include <linux/fsverity.h>
#ifdef CONFIG_F2FS_CHECK_FS
#define f2fs_bug_on(sbi, condition) BUG_ON(condition)
@@ -148,7 +149,7 @@ struct f2fs_mount_info {
#define F2FS_FEATURE_QUOTA_INO 0x0080
#define F2FS_FEATURE_INODE_CRTIME 0x0100
#define F2FS_FEATURE_LOST_FOUND 0x0200
-#define F2FS_FEATURE_VERITY 0x0400 /* reserved */
+#define F2FS_FEATURE_VERITY 0x0400
#define F2FS_FEATURE_SB_CHKSUM 0x0800
#define __F2FS_HAS_FEATURE(raw_super, mask) \
@@ -626,7 +627,7 @@ enum {
#define FADVISE_ENC_NAME_BIT 0x08
#define FADVISE_KEEP_SIZE_BIT 0x10
#define FADVISE_HOT_BIT 0x20
-#define FADVISE_VERITY_BIT 0x40 /* reserved */
+#define FADVISE_VERITY_BIT 0x40
#define FADVISE_MODIFIABLE_BITS (FADVISE_COLD_BIT | FADVISE_HOT_BIT)
@@ -646,6 +647,8 @@ enum {
#define file_is_hot(inode) is_file(inode, FADVISE_HOT_BIT)
#define file_set_hot(inode) set_file(inode, FADVISE_HOT_BIT)
#define file_clear_hot(inode) clear_file(inode, FADVISE_HOT_BIT)
+#define file_is_verity(inode) is_file(inode, FADVISE_VERITY_BIT)
+#define file_set_verity(inode) set_file(inode, FADVISE_VERITY_BIT)
#define DEF_DIR_LEVEL 0
@@ -2344,6 +2347,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
#define F2FS_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
#define F2FS_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
#define F2FS_EXTENTS_FL 0x00080000 /* Inode uses extents */
+#define F2FS_VERITY_FL 0x00100000 /* Verity protected inode */
#define F2FS_EA_INODE_FL 0x00200000 /* Inode used for large EA */
#define F2FS_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */
#define F2FS_NOCOW_FL 0x00800000 /* Do not cow file */
@@ -2351,7 +2355,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
#define F2FS_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
-#define F2FS_FL_USER_VISIBLE 0x30CBDFFF /* User visible flags */
+#define F2FS_FL_USER_VISIBLE 0x30DBDFFF /* User visible flags */
#define F2FS_FL_USER_MODIFIABLE 0x204BC0FF /* User modifiable flags */
/* Flags we can manipulate with through F2FS_IOC_FSSETXATTR */
@@ -2417,6 +2421,7 @@ enum {
FI_PROJ_INHERIT, /* indicate file inherits projectid */
FI_PIN_FILE, /* indicate file should not be gced */
FI_ATOMIC_REVOKE_REQUEST, /* request to drop atomic data */
+ FI_VERITY_IN_PROGRESS, /* building fs-verity Merkle tree */
};
static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2456,6 +2461,12 @@ static inline void clear_inode_flag(struct inode *inode, int flag)
__mark_inode_dirty_flag(inode, flag, false);
}
+static inline bool f2fs_verity_in_progress(struct inode *inode)
+{
+ return IS_ENABLED(CONFIG_FS_VERITY) &&
+ is_inode_flag_set(inode, FI_VERITY_IN_PROGRESS);
+}
+
static inline void set_acl_inode(struct inode *inode, umode_t mode)
{
F2FS_I(inode)->i_acl_mode = mode;
@@ -3524,6 +3535,9 @@ void f2fs_exit_sysfs(void);
int f2fs_register_sysfs(struct f2fs_sb_info *sbi);
void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi);
+/* verity.c */
+extern const struct fsverity_operations f2fs_verityops;
+
/*
* crypto support
*/
@@ -3546,7 +3560,7 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode)
*/
static inline bool f2fs_post_read_required(struct inode *inode)
{
- return f2fs_encrypted_file(inode);
+ return f2fs_encrypted_file(inode) || fsverity_active(inode);
}
#define F2FS_FEATURE_FUNCS(name, flagname) \
@@ -3564,6 +3578,7 @@ F2FS_FEATURE_FUNCS(flexible_inline_xattr, FLEXIBLE_INLINE_XATTR);
F2FS_FEATURE_FUNCS(quota_ino, QUOTA_INO);
F2FS_FEATURE_FUNCS(inode_crtime, INODE_CRTIME);
F2FS_FEATURE_FUNCS(lost_found, LOST_FOUND);
+F2FS_FEATURE_FUNCS(verity, VERITY);
F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM);
#ifdef CONFIG_BLK_DEV_ZONED
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..6706c2081941 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -493,6 +493,10 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)
{
int err = fscrypt_file_open(inode, filp);
+ if (err)
+ return err;
+
+ err = fsverity_file_open(inode, filp);
if (err)
return err;
@@ -781,6 +785,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
if (err)
return err;
+ err = fsverity_prepare_setattr(dentry, attr);
+ if (err)
+ return err;
+
if (is_quota_modification(inode, attr)) {
err = dquot_initialize(inode);
if (err)
@@ -1656,6 +1664,8 @@ static int f2fs_ioc_getflags(struct file *filp, unsigned long arg)
if (IS_ENCRYPTED(inode))
flags |= F2FS_ENCRYPT_FL;
+ if (IS_VERITY(inode))
+ flags |= F2FS_VERITY_FL;
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))
flags |= F2FS_INLINE_DATA_FL;
if (is_inode_flag_set(inode, FI_PIN_FILE))
@@ -2980,6 +2990,30 @@ static int f2fs_ioc_precache_extents(struct file *filp, unsigned long arg)
return f2fs_precache_extents(file_inode(filp));
}
+static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg)
+{
+ struct inode *inode = file_inode(filp);
+
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+
+ if (!f2fs_sb_has_verity(F2FS_I_SB(inode))) {
+ f2fs_msg(inode->i_sb, KERN_WARNING,
+ "Can't enable fs-verity on inode %lu: the verity feature is not enabled on this filesystem.\n",
+ inode->i_ino);
+ return -EOPNOTSUPP;
+ }
+
+ return fsverity_ioctl_enable(filp, (const void __user *)arg);
+}
+
+static int f2fs_ioc_measure_verity(struct file *filp, unsigned long arg)
+{
+ if (!f2fs_sb_has_verity(F2FS_I_SB(file_inode(filp))))
+ return -EOPNOTSUPP;
+
+ return fsverity_ioctl_measure(filp, (void __user *)arg);
+}
+
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -3036,6 +3070,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return f2fs_ioc_set_pin_file(filp, arg);
case F2FS_IOC_PRECACHE_EXTENTS:
return f2fs_ioc_precache_extents(filp, arg);
+ case FS_IOC_ENABLE_VERITY:
+ return f2fs_ioc_enable_verity(filp, arg);
+ case FS_IOC_MEASURE_VERITY:
+ return f2fs_ioc_measure_verity(filp, arg);
default:
return -ENOTTY;
}
@@ -3149,6 +3187,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case F2FS_IOC_GET_PIN_FILE:
case F2FS_IOC_SET_PIN_FILE:
case F2FS_IOC_PRECACHE_EXTENTS:
+ case FS_IOC_ENABLE_VERITY:
+ case FS_IOC_MEASURE_VERITY:
break;
default:
return -ENOIOCTLCMD;
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index ccb02226dd2c..b2f945b1afe5 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -46,9 +46,11 @@ void f2fs_set_inode_flags(struct inode *inode)
new_fl |= S_DIRSYNC;
if (file_is_encrypt(inode))
new_fl |= S_ENCRYPTED;
+ if (file_is_verity(inode))
+ new_fl |= S_VERITY;
inode_set_flags(inode, new_fl,
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|
- S_ENCRYPTED);
+ S_ENCRYPTED|S_VERITY);
}
static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
@@ -749,6 +751,7 @@ void f2fs_evict_inode(struct inode *inode)
}
out_clear:
fscrypt_put_encryption_info(inode);
+ fsverity_cleanup_inode(inode);
clear_inode(inode);
}
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 6b959bbb336a..ea4a247d6ed6 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -3177,6 +3177,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_op = &f2fs_sops;
#ifdef CONFIG_FS_ENCRYPTION
sb->s_cop = &f2fs_cryptops;
+#endif
+#ifdef CONFIG_FS_VERITY
+ sb->s_vop = &f2fs_verityops;
#endif
sb->s_xattr = f2fs_xattr_handlers;
sb->s_export_op = &f2fs_export_ops;
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index 729f46a3c9ee..b3e28467db72 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -117,6 +117,9 @@ static ssize_t features_show(struct f2fs_attr *a,
if (f2fs_sb_has_lost_found(sbi))
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "lost_found");
+ if (f2fs_sb_has_verity(sbi))
+ len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
+ len ? ", " : "", "verity");
if (f2fs_sb_has_sb_chksum(sbi))
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "sb_checksum");
@@ -350,6 +353,7 @@ enum feat_id {
FEAT_QUOTA_INO,
FEAT_INODE_CRTIME,
FEAT_LOST_FOUND,
+ FEAT_VERITY,
FEAT_SB_CHECKSUM,
};
@@ -367,6 +371,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
case FEAT_QUOTA_INO:
case FEAT_INODE_CRTIME:
case FEAT_LOST_FOUND:
+ case FEAT_VERITY:
case FEAT_SB_CHECKSUM:
return snprintf(buf, PAGE_SIZE, "supported\n");
}
@@ -455,6 +460,9 @@ F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR);
F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO);
F2FS_FEATURE_RO_ATTR(inode_crtime, FEAT_INODE_CRTIME);
F2FS_FEATURE_RO_ATTR(lost_found, FEAT_LOST_FOUND);
+#ifdef CONFIG_FS_VERITY
+F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY);
+#endif
F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM);
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
@@ -517,6 +525,9 @@ static struct attribute *f2fs_feat_attrs[] = {
ATTR_LIST(quota_ino),
ATTR_LIST(inode_crtime),
ATTR_LIST(lost_found),
+#ifdef CONFIG_FS_VERITY
+ ATTR_LIST(verity),
+#endif
ATTR_LIST(sb_checksum),
NULL,
};
diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
new file mode 100644
index 000000000000..91184cecbade
--- /dev/null
+++ b/fs/f2fs/verity.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * fs/f2fs/verity.c: fs-verity support for f2fs
+ *
+ * Copyright 2019 Google LLC
+ */
+
+/*
+ * Implementation of fsverity_operations for f2fs.
+ *
+ * Like ext4, f2fs stores the verity metadata (Merkle tree and
+ * fsverity_descriptor) past the end of the file, starting at the first 64K
+ * boundary beyond i_size. This approach works because (a) verity files are
+ * readonly, and (b) pages fully beyond i_size aren't visible to userspace but
+ * can be read/written internally by f2fs with only some relatively small
+ * changes to f2fs. Extended attributes cannot be used because (a) f2fs limits
+ * the total size of an inode's xattr entries to 4096 bytes, which wouldn't be
+ * enough for even a single Merkle tree block, and (b) f2fs encryption doesn't
+ * encrypt xattrs, yet the verity metadata *must* be encrypted when the file is
+ * because it contains hashes of the plaintext data.
+ *
+ * Using a 64K boundary rather than a 4K one keeps things ready for
+ * architectures with 64K pages, and it doesn't necessarily waste space on-disk
+ * since there can be a hole between i_size and the start of the Merkle tree.
+ */
+
+#include <linux/f2fs_fs.h>
+
+#include "f2fs.h"
+#include "xattr.h"
+
+static inline loff_t f2fs_verity_metadata_pos(const struct inode *inode)
+{
+ return round_up(inode->i_size, 65536);
+}
+
+/*
+ * Read some verity metadata from the inode. __vfs_read() can't be used because
+ * we need to read beyond i_size.
+ */
+static int pagecache_read(struct inode *inode, void *buf, size_t count,
+ loff_t pos)
+{
+ while (count) {
+ size_t n = min_t(size_t, count,
+ PAGE_SIZE - offset_in_page(pos));
+ struct page *page;
+ void *addr;
+
+ page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT,
+ NULL);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
+
+ addr = kmap_atomic(page);
+ memcpy(buf, addr + offset_in_page(pos), n);
+ kunmap_atomic(addr);
+
+ put_page(page);
+
+ buf += n;
+ pos += n;
+ count -= n;
+ }
+ return 0;
+}
+
+/*
+ * Write some verity metadata to the inode for FS_IOC_ENABLE_VERITY.
+ * kernel_write() can't be used because the file descriptor is readonly.
+ */
+static int pagecache_write(struct inode *inode, const void *buf, size_t count,
+ loff_t pos)
+{
+ if (pos + count > inode->i_sb->s_maxbytes)
+ return -EFBIG;
+
+ while (count) {
+ size_t n = min_t(size_t, count,
+ PAGE_SIZE - offset_in_page(pos));
+ struct page *page;
+ void *fsdata;
+ void *addr;
+ int res;
+
+ res = pagecache_write_begin(NULL, inode->i_mapping, pos, n, 0,
+ &page, &fsdata);
+ if (res)
+ return res;
+
+ addr = kmap_atomic(page);
+ memcpy(addr + offset_in_page(pos), buf, n);
+ kunmap_atomic(addr);
+
+ res = pagecache_write_end(NULL, inode->i_mapping, pos, n, n,
+ page, fsdata);
+ if (res < 0)
+ return res;
+ if (res != n)
+ return -EIO;
+
+ buf += n;
+ pos += n;
+ count -= n;
+ }
+ return 0;
+}
+
+/*
+ * Format of f2fs verity xattr. This points to the location of the verity
+ * descriptor within the file data rather than containing it directly because
+ * the verity descriptor *must* be encrypted when f2fs encryption is used. But,
+ * f2fs encryption does not encrypt xattrs.
+ */
+struct fsverity_descriptor_location {
+ __le32 version;
+ __le32 size;
+ __le64 pos;
+};
+
+static int f2fs_begin_enable_verity(struct file *filp)
+{
+ struct inode *inode = file_inode(filp);
+ int err;
+
+ if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode))
+ return -EOPNOTSUPP;
+
+ /*
+ * Since the file was opened readonly, we have to initialize the quotas
+ * here and not rely on ->open() doing it. This must be done before
+ * evicting the inline data.
+ */
+ err = dquot_initialize(inode);
+ if (err)
+ return err;
+
+ err = f2fs_convert_inline_inode(inode);
+ if (err)
+ return err;
+
+ set_inode_flag(inode, FI_VERITY_IN_PROGRESS);
+ return 0;
+}
+
+static int f2fs_end_enable_verity(struct file *filp, const void *desc,
+ size_t desc_size, u64 merkle_tree_size)
+{
+ struct inode *inode = file_inode(filp);
+ u64 desc_pos = f2fs_verity_metadata_pos(inode) + merkle_tree_size;
+ struct fsverity_descriptor_location dloc = {
+ .version = cpu_to_le32(1),
+ .size = cpu_to_le32(desc_size),
+ .pos = cpu_to_le64(desc_pos),
+ };
+ int err = 0;
+
+ if (desc != NULL) {
+ /* Succeeded; write the verity descriptor. */
+ err = pagecache_write(inode, desc, desc_size, desc_pos);
+
+ /* Write all pages before clearing FI_VERITY_IN_PROGRESS. */
+ if (!err)
+ err = filemap_write_and_wait(inode->i_mapping);
+ }
+
+ /* If we failed, truncate anything we wrote past i_size. */
+ if (desc == NULL || err)
+ f2fs_truncate(inode);
+
+ clear_inode_flag(inode, FI_VERITY_IN_PROGRESS);
+
+ if (desc != NULL && !err) {
+ err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY,
+ F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
+ NULL, XATTR_CREATE);
+ if (!err) {
+ file_set_verity(inode);
+ f2fs_set_inode_flags(inode);
+ f2fs_mark_inode_dirty_sync(inode, true);
+ }
+ }
+ return err;
+}
+
+static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
+ size_t buf_size)
+{
+ struct fsverity_descriptor_location dloc;
+ int res;
+ u32 size;
+ u64 pos;
+
+ /* Get the descriptor location */
+ res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_VERITY,
+ F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc), NULL);
+ if (res < 0 && res != -ERANGE)
+ return res;
+ if (res != sizeof(dloc) || dloc.version != cpu_to_le32(1)) {
+ f2fs_msg(inode->i_sb, KERN_WARNING,
+ "unknown verity xattr format");
+ return -EINVAL;
+ }
+ size = le32_to_cpu(dloc.size);
+ pos = le64_to_cpu(dloc.pos);
+
+ /* Get the descriptor */
+ if (pos + size < pos || pos + size > inode->i_sb->s_maxbytes ||
+ pos < f2fs_verity_metadata_pos(inode) || size > INT_MAX) {
+ f2fs_msg(inode->i_sb, KERN_WARNING, "invalid verity xattr");
+ return -EUCLEAN; /* EFSCORRUPTED */
+ }
+ if (buf_size) {
+ if (size > buf_size)
+ return -ERANGE;
+ res = pagecache_read(inode, buf, size, pos);
+ if (res)
+ return res;
+ }
+ return size;
+}
+
+static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
+ pgoff_t index)
+{
+ index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT;
+
+ return read_mapping_page(inode->i_mapping, index, NULL);
+}
+
+static int f2fs_write_merkle_tree_block(struct inode *inode, const void *buf,
+ u64 index, int log_blocksize)
+{
+ loff_t pos = f2fs_verity_metadata_pos(inode) + (index << log_blocksize);
+
+ return pagecache_write(inode, buf, 1 << log_blocksize, pos);
+}
+
+const struct fsverity_operations f2fs_verityops = {
+ .begin_enable_verity = f2fs_begin_enable_verity,
+ .end_enable_verity = f2fs_end_enable_verity,
+ .get_verity_descriptor = f2fs_get_verity_descriptor,
+ .read_merkle_tree_page = f2fs_read_merkle_tree_page,
+ .write_merkle_tree_block = f2fs_write_merkle_tree_block,
+};
diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index a90920e2f949..de0c600b9cab 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -34,8 +34,10 @@
#define F2FS_XATTR_INDEX_ADVISE 7
/* Should be same as EXT4_XATTR_INDEX_ENCRYPTION */
#define F2FS_XATTR_INDEX_ENCRYPTION 9
+#define F2FS_XATTR_INDEX_VERITY 11
#define F2FS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
+#define F2FS_XATTR_NAME_VERITY "v"
struct f2fs_xattr_header {
__le32 h_magic; /* magic number for identification */
--
2.22.0
^ permalink raw reply related
* [PATCH v6 16/17] ext4: update on-disk format documentation for fs-verity
From: Eric Biggers @ 2019-07-01 15:32 UTC (permalink / raw)
To: linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Document the format of verity files on ext4, and the corresponding inode
and superblock flags.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
Documentation/filesystems/ext4/inodes.rst | 6 ++-
Documentation/filesystems/ext4/overview.rst | 1 +
Documentation/filesystems/ext4/super.rst | 2 +
Documentation/filesystems/ext4/verity.rst | 41 +++++++++++++++++++++
4 files changed, 48 insertions(+), 2 deletions(-)
create mode 100644 Documentation/filesystems/ext4/verity.rst
diff --git a/Documentation/filesystems/ext4/inodes.rst b/Documentation/filesystems/ext4/inodes.rst
index 6bd35e506b6f..e851e6ca31fa 100644
--- a/Documentation/filesystems/ext4/inodes.rst
+++ b/Documentation/filesystems/ext4/inodes.rst
@@ -277,6 +277,8 @@ The ``i_flags`` field is a combination of these values:
- This is a huge file (EXT4\_HUGE\_FILE\_FL).
* - 0x80000
- Inode uses extents (EXT4\_EXTENTS\_FL).
+ * - 0x100000
+ - Verity protected file (EXT4\_VERITY\_FL).
* - 0x200000
- Inode stores a large extended attribute value in its data blocks
(EXT4\_EA\_INODE\_FL).
@@ -299,9 +301,9 @@ The ``i_flags`` field is a combination of these values:
- Reserved for ext4 library (EXT4\_RESERVED\_FL).
* -
- Aggregate flags:
- * - 0x4BDFFF
+ * - 0x705BDFFF
- User-visible flags.
- * - 0x4B80FF
+ * - 0x604BC0FF
- User-modifiable flags. Note that while EXT4\_JOURNAL\_DATA\_FL and
EXT4\_EXTENTS\_FL can be set with setattr, they are not in the kernel's
EXT4\_FL\_USER\_MODIFIABLE mask, since it needs to handle the setting of
diff --git a/Documentation/filesystems/ext4/overview.rst b/Documentation/filesystems/ext4/overview.rst
index cbab18baba12..123ebfde47ee 100644
--- a/Documentation/filesystems/ext4/overview.rst
+++ b/Documentation/filesystems/ext4/overview.rst
@@ -24,3 +24,4 @@ order.
.. include:: bigalloc.rst
.. include:: inlinedata.rst
.. include:: eainode.rst
+.. include:: verity.rst
diff --git a/Documentation/filesystems/ext4/super.rst b/Documentation/filesystems/ext4/super.rst
index 04ff079a2acf..6eae92054827 100644
--- a/Documentation/filesystems/ext4/super.rst
+++ b/Documentation/filesystems/ext4/super.rst
@@ -696,6 +696,8 @@ the following:
(RO\_COMPAT\_READONLY)
* - 0x2000
- Filesystem tracks project quotas. (RO\_COMPAT\_PROJECT)
+ * - 0x8000
+ - Verity inodes may be present on the filesystem. (RO\_COMPAT\_VERITY)
.. _super_def_hash:
diff --git a/Documentation/filesystems/ext4/verity.rst b/Documentation/filesystems/ext4/verity.rst
new file mode 100644
index 000000000000..3e4c0ee0e068
--- /dev/null
+++ b/Documentation/filesystems/ext4/verity.rst
@@ -0,0 +1,41 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Verity files
+------------
+
+ext4 supports fs-verity, which is a filesystem feature that provides
+Merkle tree based hashing for individual readonly files. Most of
+fs-verity is common to all filesystems that support it; see
+:ref:`Documentation/filesystems/fsverity.rst <fsverity>` for the
+fs-verity documentation. However, the on-disk layout of the verity
+metadata is filesystem-specific. On ext4, the verity metadata is
+stored after the end of the file data itself, in the following format:
+
+- Zero-padding to the next 65536-byte boundary. This padding need not
+ actually be allocated on-disk, i.e. it may be a hole.
+
+- The Merkle tree, as documented in
+ :ref:`Documentation/filesystems/fsverity.rst
+ <fsverity_merkle_tree>`, with the tree levels stored in order from
+ root to leaf, and the tree blocks within each level stored in their
+ natural order.
+
+- Zero-padding to the next filesystem block boundary.
+
+- The verity descriptor, as documented in
+ :ref:`Documentation/filesystems/fsverity.rst <fsverity_descriptor>`,
+ with optionally appended signature blob.
+
+- Zero-padding to the next offset that is 4 bytes before a filesystem
+ block boundary.
+
+- The size of the verity descriptor in bytes, as a 4-byte little
+ endian integer.
+
+Verity inodes have EXT4_VERITY_FL set, and they must use extents, i.e.
+EXT4_EXTENTS_FL must be set and EXT4_INLINE_DATA_FL must be clear.
+They can have EXT4_ENCRYPT_FL set, in which case the verity metadata
+is encrypted as well as the data itself.
+
+Verity files cannot have blocks allocated past the end of the verity
+metadata.
--
2.22.0
^ permalink raw reply related
* [PATCH v6 15/17] ext4: add fs-verity read support
From: Eric Biggers @ 2019-07-01 15:32 UTC (permalink / raw)
To: linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Make ext4_mpage_readpages() verify data as it is read from fs-verity
files, using the helper functions from fs/verity/.
To support both encryption and verity simultaneously, this required
refactoring the decryption workflow into a generic "post-read
processing" workflow which can do decryption, verification, or both.
The case where the ext4 block size is not equal to the PAGE_SIZE is not
supported yet, since in that case ext4_mpage_readpages() sometimes falls
back to block_read_full_page(), which does not support fs-verity yet.
Co-developed-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/ext4/ext4.h | 2 +
fs/ext4/inode.c | 2 +
fs/ext4/readpage.c | 207 ++++++++++++++++++++++++++++++++++++++-------
fs/ext4/super.c | 9 +-
4 files changed, 190 insertions(+), 30 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5a1deea3fb3e..3c0d491c4970 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3158,6 +3158,8 @@ static inline void ext4_set_de_type(struct super_block *sb,
extern int ext4_mpage_readpages(struct address_space *mapping,
struct list_head *pages, struct page *page,
unsigned nr_pages, bool is_readahead);
+extern int __init ext4_init_post_read_processing(void);
+extern void ext4_exit_post_read_processing(void);
/* symlink.c */
extern const struct inode_operations ext4_encrypted_symlink_inode_operations;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 514e24f88f90..37571d080b3c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3893,6 +3893,8 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode))
return 0;
#endif
+ if (fsverity_active(inode))
+ return 0;
/*
* If we are doing data journalling we don't support O_DIRECT
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index c916017db334..84152b686e49 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -47,6 +47,11 @@
#include "ext4.h"
+#define NUM_PREALLOC_POST_READ_CTXS 128
+
+static struct kmem_cache *bio_post_read_ctx_cache;
+static mempool_t *bio_post_read_ctx_pool;
+
static inline bool ext4_bio_encrypted(struct bio *bio)
{
#ifdef CONFIG_FS_ENCRYPTION
@@ -56,6 +61,100 @@ static inline bool ext4_bio_encrypted(struct bio *bio)
#endif
}
+/* postprocessing steps for read bios */
+enum bio_post_read_step {
+ STEP_INITIAL = 0,
+ STEP_DECRYPT,
+ STEP_VERITY,
+};
+
+struct bio_post_read_ctx {
+ struct bio *bio;
+ struct work_struct work;
+ unsigned int cur_step;
+ unsigned int enabled_steps;
+};
+
+static void __read_end_io(struct bio *bio)
+{
+ struct page *page;
+ struct bio_vec *bv;
+ struct bvec_iter_all iter_all;
+
+ bio_for_each_segment_all(bv, bio, iter_all) {
+ page = bv->bv_page;
+
+ /* PG_error was set if any post_read step failed */
+ if (bio->bi_status || PageError(page)) {
+ ClearPageUptodate(page);
+ /* will re-read again later */
+ ClearPageError(page);
+ } else {
+ SetPageUptodate(page);
+ }
+ unlock_page(page);
+ }
+ if (bio->bi_private)
+ mempool_free(bio->bi_private, bio_post_read_ctx_pool);
+ bio_put(bio);
+}
+
+static void bio_post_read_processing(struct bio_post_read_ctx *ctx);
+
+static void decrypt_work(struct work_struct *work)
+{
+ struct bio_post_read_ctx *ctx =
+ container_of(work, struct bio_post_read_ctx, work);
+
+ fscrypt_decrypt_bio(ctx->bio);
+
+ bio_post_read_processing(ctx);
+}
+
+static void verity_work(struct work_struct *work)
+{
+ struct bio_post_read_ctx *ctx =
+ container_of(work, struct bio_post_read_ctx, work);
+
+ fsverity_verify_bio(ctx->bio);
+
+ bio_post_read_processing(ctx);
+}
+
+static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
+{
+ /*
+ * We use different work queues for decryption and for verity because
+ * verity may require reading metadata pages that need decryption, and
+ * we shouldn't recurse to the same workqueue.
+ */
+ switch (++ctx->cur_step) {
+ case STEP_DECRYPT:
+ if (ctx->enabled_steps & (1 << STEP_DECRYPT)) {
+ INIT_WORK(&ctx->work, decrypt_work);
+ fscrypt_enqueue_decrypt_work(&ctx->work);
+ return;
+ }
+ ctx->cur_step++;
+ /* fall-through */
+ case STEP_VERITY:
+ if (ctx->enabled_steps & (1 << STEP_VERITY)) {
+ INIT_WORK(&ctx->work, verity_work);
+ fsverity_enqueue_verify_work(&ctx->work);
+ return;
+ }
+ ctx->cur_step++;
+ /* fall-through */
+ default:
+ __read_end_io(ctx->bio);
+ }
+}
+
+static bool bio_post_read_required(struct bio *bio)
+{
+ return bio->bi_private && !bio->bi_status;
+}
+
/*
* I/O completion handler for multipage BIOs.
*
@@ -70,30 +169,53 @@ static inline bool ext4_bio_encrypted(struct bio *bio)
*/
static void mpage_end_io(struct bio *bio)
{
- struct bio_vec *bv;
- struct bvec_iter_all iter_all;
+ if (bio_post_read_required(bio)) {
+ struct bio_post_read_ctx *ctx = bio->bi_private;
- if (ext4_bio_encrypted(bio)) {
- if (bio->bi_status) {
- fscrypt_release_ctx(bio->bi_private);
- } else {
- fscrypt_enqueue_decrypt_bio(bio->bi_private, bio);
- return;
- }
+ ctx->cur_step = STEP_INITIAL;
+ bio_post_read_processing(ctx);
+ return;
}
- bio_for_each_segment_all(bv, bio, iter_all) {
- struct page *page = bv->bv_page;
+ __read_end_io(bio);
+}
- if (!bio->bi_status) {
- SetPageUptodate(page);
- } else {
- ClearPageUptodate(page);
- SetPageError(page);
- }
- unlock_page(page);
+static inline bool ext4_need_verity(const struct inode *inode, pgoff_t idx)
+{
+ return fsverity_active(inode) &&
+ idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
+}
+
+static struct bio_post_read_ctx *get_bio_post_read_ctx(struct inode *inode,
+ struct bio *bio,
+ pgoff_t first_idx)
+{
+ unsigned int post_read_steps = 0;
+ struct bio_post_read_ctx *ctx = NULL;
+
+ if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode))
+ post_read_steps |= 1 << STEP_DECRYPT;
+
+ if (ext4_need_verity(inode, first_idx))
+ post_read_steps |= 1 << STEP_VERITY;
+
+ if (post_read_steps) {
+ ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+ ctx->bio = bio;
+ ctx->enabled_steps = post_read_steps;
+ bio->bi_private = ctx;
}
+ return ctx;
+}
- bio_put(bio);
+static inline loff_t ext4_readpage_limit(struct inode *inode)
+{
+ if (IS_ENABLED(CONFIG_FS_VERITY) &&
+ (IS_VERITY(inode) || ext4_verity_in_progress(inode)))
+ return inode->i_sb->s_maxbytes;
+
+ return i_size_read(inode);
}
int ext4_mpage_readpages(struct address_space *mapping,
@@ -141,7 +263,8 @@ int ext4_mpage_readpages(struct address_space *mapping,
block_in_file = (sector_t)page->index << (PAGE_SHIFT - blkbits);
last_block = block_in_file + nr_pages * blocks_per_page;
- last_block_in_file = (i_size_read(inode) + blocksize - 1) >> blkbits;
+ last_block_in_file = (ext4_readpage_limit(inode) +
+ blocksize - 1) >> blkbits;
if (last_block > last_block_in_file)
last_block = last_block_in_file;
page_block = 0;
@@ -218,6 +341,9 @@ int ext4_mpage_readpages(struct address_space *mapping,
zero_user_segment(page, first_hole << blkbits,
PAGE_SIZE);
if (first_hole == 0) {
+ if (ext4_need_verity(inode, page->index) &&
+ !fsverity_verify_page(page))
+ goto set_error_page;
SetPageUptodate(page);
unlock_page(page);
goto next_page;
@@ -241,18 +367,15 @@ int ext4_mpage_readpages(struct address_space *mapping,
bio = NULL;
}
if (bio == NULL) {
- struct fscrypt_ctx *ctx = NULL;
+ struct bio_post_read_ctx *ctx;
- if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)) {
- ctx = fscrypt_get_ctx(GFP_NOFS);
- if (IS_ERR(ctx))
- goto set_error_page;
- }
bio = bio_alloc(GFP_KERNEL,
min_t(int, nr_pages, BIO_MAX_PAGES));
- if (!bio) {
- if (ctx)
- fscrypt_release_ctx(ctx);
+ if (!bio)
+ goto set_error_page;
+ ctx = get_bio_post_read_ctx(inode, bio, page->index);
+ if (IS_ERR(ctx)) {
+ bio_put(bio);
goto set_error_page;
}
bio_set_dev(bio, bdev);
@@ -293,3 +416,29 @@ int ext4_mpage_readpages(struct address_space *mapping,
submit_bio(bio);
return 0;
}
+
+int __init ext4_init_post_read_processing(void)
+{
+ bio_post_read_ctx_cache =
+ kmem_cache_create("ext4_bio_post_read_ctx",
+ sizeof(struct bio_post_read_ctx), 0, 0, NULL);
+ if (!bio_post_read_ctx_cache)
+ goto fail;
+ bio_post_read_ctx_pool =
+ mempool_create_slab_pool(NUM_PREALLOC_POST_READ_CTXS,
+ bio_post_read_ctx_cache);
+ if (!bio_post_read_ctx_pool)
+ goto fail_free_cache;
+ return 0;
+
+fail_free_cache:
+ kmem_cache_destroy(bio_post_read_ctx_cache);
+fail:
+ return -ENOMEM;
+}
+
+void ext4_exit_post_read_processing(void)
+{
+ mempool_destroy(bio_post_read_ctx_pool);
+ kmem_cache_destroy(bio_post_read_ctx_cache);
+}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 05a9874687c3..23e7acd43e4e 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -6103,6 +6103,10 @@ static int __init ext4_init_fs(void)
return err;
err = ext4_init_pending();
+ if (err)
+ goto out7;
+
+ err = ext4_init_post_read_processing();
if (err)
goto out6;
@@ -6144,8 +6148,10 @@ static int __init ext4_init_fs(void)
out4:
ext4_exit_pageio();
out5:
- ext4_exit_pending();
+ ext4_exit_post_read_processing();
out6:
+ ext4_exit_pending();
+out7:
ext4_exit_es();
return err;
@@ -6162,6 +6168,7 @@ static void __exit ext4_exit_fs(void)
ext4_exit_sysfs();
ext4_exit_system_zone();
ext4_exit_pageio();
+ ext4_exit_post_read_processing();
ext4_exit_es();
ext4_exit_pending();
}
--
2.22.0
^ permalink raw reply related
* [PATCH v6 14/17] ext4: add basic fs-verity support
From: Eric Biggers @ 2019-07-01 15:32 UTC (permalink / raw)
To: linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add most of fs-verity support to ext4. fs-verity is a filesystem
feature that enables transparent integrity protection and authentication
of read-only files. It uses a dm-verity like mechanism at the file
level: a Merkle tree is used to verify any block in the file in
log(filesize) time. It is implemented mainly by helper functions in
fs/verity/. See Documentation/filesystems/fsverity.rst for the full
documentation.
This commit adds all of ext4 fs-verity support except for the actual
data verification, including:
- Adding a filesystem feature flag and an inode flag for fs-verity.
- Implementing the fsverity_operations to support enabling verity on an
inode and reading/writing the verity metadata.
- Updating ->write_begin(), ->write_end(), and ->writepages() to support
writing verity metadata pages.
- Calling the fs-verity hooks for ->open(), ->setattr(), and ->ioctl().
ext4 stores the verity metadata (Merkle tree and fsverity_descriptor)
past the end of the file, starting at the first 64K boundary beyond
i_size. This approach works because (a) verity files are readonly, and
(b) pages fully beyond i_size aren't visible to userspace but can be
read/written internally by ext4 with only some relatively small changes
to ext4. This approach avoids having to depend on the EA_INODE feature
and on rearchitecturing ext4's xattr support to support paging
multi-gigabyte xattrs into memory, and to support encrypting xattrs.
Note that the verity metadata *must* be encrypted when the file is,
since it contains hashes of the plaintext data.
This patch incorporates work by Theodore Ts'o and Chandan Rajendra.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/ext4/Makefile | 1 +
fs/ext4/ext4.h | 21 ++-
fs/ext4/file.c | 4 +
fs/ext4/inode.c | 46 ++++--
fs/ext4/ioctl.c | 12 ++
fs/ext4/super.c | 9 ++
fs/ext4/sysfs.c | 6 +
fs/ext4/verity.c | 364 +++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 448 insertions(+), 15 deletions(-)
create mode 100644 fs/ext4/verity.c
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 8fdfcd3c3e04..b17ddc229ac5 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -13,3 +13,4 @@ ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
+ext4-$(CONFIG_FS_VERITY) += verity.o
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 1cb67859e051..5a1deea3fb3e 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -41,6 +41,7 @@
#endif
#include <linux/fscrypt.h>
+#include <linux/fsverity.h>
#include <linux/compiler.h>
@@ -395,6 +396,7 @@ struct flex_groups {
#define EXT4_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
+#define EXT4_VERITY_FL 0x00100000 /* Verity protected inode */
#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */
#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */
#define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */
@@ -402,7 +404,7 @@ struct flex_groups {
#define EXT4_CASEFOLD_FL 0x40000000 /* Casefolded file */
#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
-#define EXT4_FL_USER_VISIBLE 0x704BDFFF /* User visible flags */
+#define EXT4_FL_USER_VISIBLE 0x705BDFFF /* User visible flags */
#define EXT4_FL_USER_MODIFIABLE 0x604BC0FF /* User modifiable flags */
/* Flags we can manipulate with through EXT4_IOC_FSSETXATTR */
@@ -466,6 +468,7 @@ enum {
EXT4_INODE_TOPDIR = 17, /* Top of directory hierarchies*/
EXT4_INODE_HUGE_FILE = 18, /* Set to each huge file */
EXT4_INODE_EXTENTS = 19, /* Inode uses extents */
+ EXT4_INODE_VERITY = 20, /* Verity protected inode */
EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */
EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */
EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */
@@ -511,6 +514,7 @@ static inline void ext4_check_flag_values(void)
CHECK_FLAG_VALUE(TOPDIR);
CHECK_FLAG_VALUE(HUGE_FILE);
CHECK_FLAG_VALUE(EXTENTS);
+ CHECK_FLAG_VALUE(VERITY);
CHECK_FLAG_VALUE(EA_INODE);
CHECK_FLAG_VALUE(EOFBLOCKS);
CHECK_FLAG_VALUE(INLINE_DATA);
@@ -1559,6 +1563,7 @@ enum {
EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */
EXT4_STATE_EXT_PRECACHED, /* extents have been precached */
EXT4_STATE_LUSTRE_EA_INODE, /* Lustre-style ea_inode */
+ EXT4_STATE_VERITY_IN_PROGRESS, /* building fs-verity Merkle tree */
};
#define EXT4_INODE_BIT_FNS(name, field, offset) \
@@ -1609,6 +1614,12 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
#define EXT4_SB(sb) (sb)
#endif
+static inline bool ext4_verity_in_progress(struct inode *inode)
+{
+ return IS_ENABLED(CONFIG_FS_VERITY) &&
+ ext4_test_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
+}
+
#define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
/*
@@ -1661,6 +1672,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000
#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000
+#define EXT4_FEATURE_RO_COMPAT_VERITY 0x8000
#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
@@ -1755,6 +1767,7 @@ EXT4_FEATURE_RO_COMPAT_FUNCS(bigalloc, BIGALLOC)
EXT4_FEATURE_RO_COMPAT_FUNCS(metadata_csum, METADATA_CSUM)
EXT4_FEATURE_RO_COMPAT_FUNCS(readonly, READONLY)
EXT4_FEATURE_RO_COMPAT_FUNCS(project, PROJECT)
+EXT4_FEATURE_RO_COMPAT_FUNCS(verity, VERITY)
EXT4_FEATURE_INCOMPAT_FUNCS(compression, COMPRESSION)
EXT4_FEATURE_INCOMPAT_FUNCS(filetype, FILETYPE)
@@ -1812,7 +1825,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(casefold, CASEFOLD)
EXT4_FEATURE_RO_COMPAT_BIGALLOC |\
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
EXT4_FEATURE_RO_COMPAT_QUOTA |\
- EXT4_FEATURE_RO_COMPAT_PROJECT)
+ EXT4_FEATURE_RO_COMPAT_PROJECT |\
+ EXT4_FEATURE_RO_COMPAT_VERITY)
#define EXTN_FEATURE_FUNCS(ver) \
static inline bool ext4_has_unknown_ext##ver##_compat_features(struct super_block *sb) \
@@ -3250,6 +3264,9 @@ extern int ext4_bio_write_page(struct ext4_io_submit *io,
/* mmp.c */
extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t);
+/* verity.c */
+extern const struct fsverity_operations ext4_verityops;
+
/*
* Add new method to test whether block and inode bitmaps are properly
* initialized. With uninit_bg reading the block from disk is not enough
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 2c5baa5e8291..ed59fb8f268e 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -451,6 +451,10 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
if (ret)
return ret;
+ ret = fsverity_file_open(inode, filp);
+ if (ret)
+ return ret;
+
/*
* Set up the jbd2_inode if we are opening the inode for
* writing and the journal is present
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index c7f77c643008..514e24f88f90 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1390,6 +1390,7 @@ static int ext4_write_end(struct file *file,
int ret = 0, ret2;
int i_size_changed = 0;
int inline_data = ext4_has_inline_data(inode);
+ bool verity = ext4_verity_in_progress(inode);
trace_ext4_write_end(inode, pos, len, copied);
if (inline_data) {
@@ -1407,12 +1408,16 @@ static int ext4_write_end(struct file *file,
/*
* it's important to update i_size while still holding page lock:
* page writeout could otherwise come in and zero beyond i_size.
+ *
+ * If FS_IOC_ENABLE_VERITY is running on this inode, then Merkle tree
+ * blocks are being written past EOF, so skip the i_size update.
*/
- i_size_changed = ext4_update_inode_size(inode, pos + copied);
+ if (!verity)
+ i_size_changed = ext4_update_inode_size(inode, pos + copied);
unlock_page(page);
put_page(page);
- if (old_size < pos)
+ if (old_size < pos && !verity)
pagecache_isize_extended(inode, old_size, pos);
/*
* Don't mark the inode dirty under page lock. First, it unnecessarily
@@ -1423,7 +1428,7 @@ static int ext4_write_end(struct file *file,
if (i_size_changed || inline_data)
ext4_mark_inode_dirty(handle, inode);
- if (pos + len > inode->i_size && ext4_can_truncate(inode))
+ if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode))
/* if we have allocated more blocks and copied
* less. We will have blocks allocated outside
* inode->i_size. So truncate them
@@ -1434,7 +1439,7 @@ static int ext4_write_end(struct file *file,
if (!ret)
ret = ret2;
- if (pos + len > inode->i_size) {
+ if (pos + len > inode->i_size && !verity) {
ext4_truncate_failed_write(inode);
/*
* If truncate failed early the inode might still be
@@ -1495,6 +1500,7 @@ static int ext4_journalled_write_end(struct file *file,
unsigned from, to;
int size_changed = 0;
int inline_data = ext4_has_inline_data(inode);
+ bool verity = ext4_verity_in_progress(inode);
trace_ext4_journalled_write_end(inode, pos, len, copied);
from = pos & (PAGE_SIZE - 1);
@@ -1524,13 +1530,14 @@ static int ext4_journalled_write_end(struct file *file,
if (!partial)
SetPageUptodate(page);
}
- size_changed = ext4_update_inode_size(inode, pos + copied);
+ if (!verity)
+ size_changed = ext4_update_inode_size(inode, pos + copied);
ext4_set_inode_state(inode, EXT4_STATE_JDATA);
EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
unlock_page(page);
put_page(page);
- if (old_size < pos)
+ if (old_size < pos && !verity)
pagecache_isize_extended(inode, old_size, pos);
if (size_changed || inline_data) {
@@ -1539,7 +1546,7 @@ static int ext4_journalled_write_end(struct file *file,
ret = ret2;
}
- if (pos + len > inode->i_size && ext4_can_truncate(inode))
+ if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode))
/* if we have allocated more blocks and copied
* less. We will have blocks allocated outside
* inode->i_size. So truncate them
@@ -1550,7 +1557,7 @@ static int ext4_journalled_write_end(struct file *file,
ret2 = ext4_journal_stop(handle);
if (!ret)
ret = ret2;
- if (pos + len > inode->i_size) {
+ if (pos + len > inode->i_size && !verity) {
ext4_truncate_failed_write(inode);
/*
* If truncate failed early the inode might still be
@@ -2146,7 +2153,8 @@ static int ext4_writepage(struct page *page,
trace_ext4_writepage(page);
size = i_size_read(inode);
- if (page->index == size >> PAGE_SHIFT)
+ if (page->index == size >> PAGE_SHIFT &&
+ !ext4_verity_in_progress(inode))
len = size & ~PAGE_MASK;
else
len = PAGE_SIZE;
@@ -2230,7 +2238,8 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
* after page tables are updated.
*/
size = i_size_read(mpd->inode);
- if (page->index == size >> PAGE_SHIFT)
+ if (page->index == size >> PAGE_SHIFT &&
+ !ext4_verity_in_progress(mpd->inode))
len = size & ~PAGE_MASK;
else
len = PAGE_SIZE;
@@ -2329,6 +2338,9 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd,
ext4_lblk_t blocks = (i_size_read(inode) + i_blocksize(inode) - 1)
>> inode->i_blkbits;
+ if (ext4_verity_in_progress(inode))
+ blocks = EXT_MAX_BLOCKS;
+
do {
BUG_ON(buffer_locked(bh));
@@ -3045,8 +3057,8 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
index = pos >> PAGE_SHIFT;
- if (ext4_nonda_switch(inode->i_sb) ||
- S_ISLNK(inode->i_mode)) {
+ if (ext4_nonda_switch(inode->i_sb) || S_ISLNK(inode->i_mode) ||
+ ext4_verity_in_progress(inode)) {
*fsdata = (void *)FALL_BACK_TO_NONDELALLOC;
return ext4_write_begin(file, mapping, pos,
len, flags, pagep, fsdata);
@@ -4720,6 +4732,8 @@ static bool ext4_should_use_dax(struct inode *inode)
return false;
if (ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT))
return false;
+ if (ext4_test_inode_flag(inode, EXT4_INODE_VERITY))
+ return false;
return true;
}
@@ -4744,9 +4758,11 @@ void ext4_set_inode_flags(struct inode *inode)
new_fl |= S_ENCRYPTED;
if (flags & EXT4_CASEFOLD_FL)
new_fl |= S_CASEFOLD;
+ if (flags & EXT4_VERITY_FL)
+ new_fl |= S_VERITY;
inode_set_flags(inode, new_fl,
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX|
- S_ENCRYPTED|S_CASEFOLD);
+ S_ENCRYPTED|S_CASEFOLD|S_VERITY);
}
static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
@@ -5528,6 +5544,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
if (error)
return error;
+ error = fsverity_prepare_setattr(dentry, attr);
+ if (error)
+ return error;
+
if (is_quota_modification(inode, attr)) {
error = dquot_initialize(inode);
if (error)
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..93b63697f5dc 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -1092,6 +1092,16 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case EXT4_IOC_GET_ENCRYPTION_POLICY:
return fscrypt_ioctl_get_policy(filp, (void __user *)arg);
+ case FS_IOC_ENABLE_VERITY:
+ if (!ext4_has_feature_verity(sb))
+ return -EOPNOTSUPP;
+ return fsverity_ioctl_enable(filp, (const void __user *)arg);
+
+ case FS_IOC_MEASURE_VERITY:
+ if (!ext4_has_feature_verity(sb))
+ return -EOPNOTSUPP;
+ return fsverity_ioctl_measure(filp, (void __user *)arg);
+
case EXT4_IOC_FSGETXATTR:
{
struct fsxattr fa;
@@ -1210,6 +1220,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case EXT4_IOC_SET_ENCRYPTION_POLICY:
case EXT4_IOC_GET_ENCRYPTION_PWSALT:
case EXT4_IOC_GET_ENCRYPTION_POLICY:
+ case FS_IOC_ENABLE_VERITY:
+ case FS_IOC_MEASURE_VERITY:
case EXT4_IOC_SHUTDOWN:
case FS_IOC_GETFSMAP:
break;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 4079605d437a..05a9874687c3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1179,6 +1179,7 @@ void ext4_clear_inode(struct inode *inode)
EXT4_I(inode)->jinode = NULL;
}
fscrypt_put_encryption_info(inode);
+ fsverity_cleanup_inode(inode);
}
static struct inode *ext4_nfs_get_inode(struct super_block *sb,
@@ -4272,6 +4273,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
#ifdef CONFIG_FS_ENCRYPTION
sb->s_cop = &ext4_cryptops;
#endif
+#ifdef CONFIG_FS_VERITY
+ sb->s_vop = &ext4_verityops;
+#endif
#ifdef CONFIG_QUOTA
sb->dq_op = &ext4_quota_operations;
if (ext4_has_feature_quota(sb))
@@ -4419,6 +4423,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
goto failed_mount_wq;
}
+ if (ext4_has_feature_verity(sb) && blocksize != PAGE_SIZE) {
+ ext4_msg(sb, KERN_ERR, "Unsupported blocksize for fs-verity");
+ goto failed_mount_wq;
+ }
+
if (DUMMY_ENCRYPTION_ENABLED(sbi) && !sb_rdonly(sb) &&
!ext4_has_feature_encrypt(sb)) {
ext4_set_feature_encrypt(sb);
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index 04b4f53f0659..534531747bf1 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -241,6 +241,9 @@ EXT4_ATTR_FEATURE(encryption);
#ifdef CONFIG_UNICODE
EXT4_ATTR_FEATURE(casefold);
#endif
+#ifdef CONFIG_FS_VERITY
+EXT4_ATTR_FEATURE(verity);
+#endif
EXT4_ATTR_FEATURE(metadata_csum_seed);
static struct attribute *ext4_feat_attrs[] = {
@@ -252,6 +255,9 @@ static struct attribute *ext4_feat_attrs[] = {
#endif
#ifdef CONFIG_UNICODE
ATTR_LIST(casefold),
+#endif
+#ifdef CONFIG_FS_VERITY
+ ATTR_LIST(verity),
#endif
ATTR_LIST(metadata_csum_seed),
NULL,
diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
new file mode 100644
index 000000000000..dd0d1093e362
--- /dev/null
+++ b/fs/ext4/verity.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * fs/ext4/verity.c: fs-verity support for ext4
+ *
+ * Copyright 2019 Google LLC
+ */
+
+/*
+ * Implementation of fsverity_operations for ext4.
+ *
+ * ext4 stores the verity metadata (Merkle tree and fsverity_descriptor) past
+ * the end of the file, starting at the first 64K boundary beyond i_size. This
+ * approach works because (a) verity files are readonly, and (b) pages fully
+ * beyond i_size aren't visible to userspace but can be read/written internally
+ * by ext4 with only some relatively small changes to ext4. This approach
+ * avoids having to depend on the EA_INODE feature and on rearchitecturing
+ * ext4's xattr support to support paging multi-gigabyte xattrs into memory, and
+ * to support encrypting xattrs. Note that the verity metadata *must* be
+ * encrypted when the file is, since it contains hashes of the plaintext data.
+ *
+ * Using a 64K boundary rather than a 4K one keeps things ready for
+ * architectures with 64K pages, and it doesn't necessarily waste space on-disk
+ * since there can be a hole between i_size and the start of the Merkle tree.
+ */
+
+#include <linux/quotaops.h>
+
+#include "ext4.h"
+#include "ext4_extents.h"
+#include "ext4_jbd2.h"
+
+static inline loff_t ext4_verity_metadata_pos(const struct inode *inode)
+{
+ return round_up(inode->i_size, 65536);
+}
+
+/*
+ * Read some verity metadata from the inode. __vfs_read() can't be used because
+ * we need to read beyond i_size.
+ */
+static int pagecache_read(struct inode *inode, void *buf, size_t count,
+ loff_t pos)
+{
+ while (count) {
+ size_t n = min_t(size_t, count,
+ PAGE_SIZE - offset_in_page(pos));
+ struct page *page;
+ void *addr;
+
+ page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT,
+ NULL);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
+
+ addr = kmap_atomic(page);
+ memcpy(buf, addr + offset_in_page(pos), n);
+ kunmap_atomic(addr);
+
+ put_page(page);
+
+ buf += n;
+ pos += n;
+ count -= n;
+ }
+ return 0;
+}
+
+/*
+ * Write some verity metadata to the inode for FS_IOC_ENABLE_VERITY.
+ * kernel_write() can't be used because the file descriptor is readonly.
+ */
+static int pagecache_write(struct inode *inode, const void *buf, size_t count,
+ loff_t pos)
+{
+ if (pos + count > inode->i_sb->s_maxbytes)
+ return -EFBIG;
+
+ while (count) {
+ size_t n = min_t(size_t, count,
+ PAGE_SIZE - offset_in_page(pos));
+ struct page *page;
+ void *fsdata;
+ void *addr;
+ int res;
+
+ res = pagecache_write_begin(NULL, inode->i_mapping, pos, n, 0,
+ &page, &fsdata);
+ if (res)
+ return res;
+
+ addr = kmap_atomic(page);
+ memcpy(addr + offset_in_page(pos), buf, n);
+ kunmap_atomic(addr);
+
+ res = pagecache_write_end(NULL, inode->i_mapping, pos, n, n,
+ page, fsdata);
+ if (res < 0)
+ return res;
+ if (res != n)
+ return -EIO;
+
+ buf += n;
+ pos += n;
+ count -= n;
+ }
+ return 0;
+}
+
+static int ext4_begin_enable_verity(struct file *filp)
+{
+ struct inode *inode = file_inode(filp);
+ const int credits = 2; /* superblock and inode for ext4_orphan_add() */
+ handle_t *handle;
+ int err;
+
+ /*
+ * Since the file was opened readonly, we have to initialize the jbd
+ * inode and quotas here and not rely on ->open() doing it. This must
+ * be done before evicting the inline data.
+ */
+
+ err = ext4_inode_attach_jinode(inode);
+ if (err)
+ return err;
+
+ err = dquot_initialize(inode);
+ if (err)
+ return err;
+
+ err = ext4_convert_inline_data(inode);
+ if (err)
+ return err;
+
+ if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+ ext4_warning_inode(inode,
+ "verity is only allowed on extent-based files");
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * ext4 uses the last allocated block to find the verity descriptor, so
+ * we must remove any other blocks past EOF which might confuse things.
+ */
+ err = ext4_truncate(inode);
+ if (err)
+ return err;
+
+ handle = ext4_journal_start(inode, EXT4_HT_INODE, credits);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ err = ext4_orphan_add(handle, inode);
+ if (err == 0)
+ ext4_set_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
+
+ ext4_journal_stop(handle);
+ return err;
+}
+
+/*
+ * ext4 stores the verity descriptor beginning on the next filesystem block
+ * boundary after the Merkle tree. Then, the descriptor size is stored in the
+ * last 4 bytes of the last allocated filesystem block --- which is either the
+ * block in which the descriptor ends, or the next block after that if there
+ * weren't at least 4 bytes remaining.
+ *
+ * We can't simply store the descriptor in an xattr because it *must* be
+ * encrypted when ext4 encryption is used, but ext4 encryption doesn't encrypt
+ * xattrs. Also, if the descriptor includes a large signature blob it may be
+ * too large to store in an xattr without the EA_INODE feature.
+ */
+static int ext4_write_verity_descriptor(struct inode *inode, const void *desc,
+ size_t desc_size, u64 merkle_tree_size)
+{
+ const u64 desc_pos = round_up(ext4_verity_metadata_pos(inode) +
+ merkle_tree_size, i_blocksize(inode));
+ const u64 desc_end = desc_pos + desc_size;
+ const __le32 desc_size_disk = cpu_to_le32(desc_size);
+ const u64 desc_size_pos = round_up(desc_end + sizeof(desc_size_disk),
+ i_blocksize(inode)) -
+ sizeof(desc_size_disk);
+ int err;
+
+ err = pagecache_write(inode, desc, desc_size, desc_pos);
+ if (err)
+ return err;
+
+ return pagecache_write(inode, &desc_size_disk, sizeof(desc_size_disk),
+ desc_size_pos);
+}
+
+static int ext4_end_enable_verity(struct file *filp, const void *desc,
+ size_t desc_size, u64 merkle_tree_size)
+{
+ struct inode *inode = file_inode(filp);
+ const int credits = 2; /* superblock and inode for ext4_orphan_add() */
+ handle_t *handle;
+ int err = 0;
+ int err2;
+
+ if (desc != NULL) {
+ /* Succeeded; write the verity descriptor. */
+ err = ext4_write_verity_descriptor(inode, desc, desc_size,
+ merkle_tree_size);
+
+ /* Write all pages before clearing VERITY_IN_PROGRESS. */
+ if (!err)
+ err = filemap_write_and_wait(inode->i_mapping);
+ }
+
+ /* If we failed, truncate anything we wrote past i_size. */
+ if (desc == NULL || err)
+ ext4_truncate(inode);
+
+ /*
+ * We must always clean up by clearing EXT4_STATE_VERITY_IN_PROGRESS and
+ * deleting the inode from the orphan list, even if something failed.
+ * If everything succeeded, we'll also set the verity bit in the same
+ * transaction.
+ */
+
+ ext4_clear_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS);
+
+ handle = ext4_journal_start(inode, EXT4_HT_INODE, credits);
+ if (IS_ERR(handle)) {
+ ext4_orphan_del(NULL, inode);
+ return PTR_ERR(handle);
+ }
+
+ err2 = ext4_orphan_del(handle, inode);
+ if (err2)
+ goto out_stop;
+
+ if (desc != NULL && !err) {
+ struct ext4_iloc iloc;
+
+ err = ext4_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ goto out_stop;
+ ext4_set_inode_flag(inode, EXT4_INODE_VERITY);
+ ext4_set_inode_flags(inode);
+ err = ext4_mark_iloc_dirty(handle, inode, &iloc);
+ }
+out_stop:
+ ext4_journal_stop(handle);
+ return err ?: err2;
+}
+
+static int ext4_get_verity_descriptor_location(struct inode *inode,
+ size_t *desc_size_ret,
+ u64 *desc_pos_ret)
+{
+ struct ext4_ext_path *path;
+ struct ext4_extent *last_extent;
+ u32 end_lblk;
+ u64 desc_size_pos;
+ __le32 desc_size_disk;
+ u32 desc_size;
+ u64 desc_pos;
+ int err;
+
+ /*
+ * Descriptor size is in last 4 bytes of last allocated block.
+ * See ext4_write_verity_descriptor().
+ */
+
+ if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+ EXT4_ERROR_INODE(inode, "verity file doesn't use extents");
+ return -EFSCORRUPTED;
+ }
+
+ path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0);
+ if (IS_ERR(path))
+ return PTR_ERR(path);
+
+ last_extent = path[path->p_depth].p_ext;
+ if (!last_extent) {
+ EXT4_ERROR_INODE(inode, "verity file has no extents");
+ ext4_ext_drop_refs(path);
+ kfree(path);
+ return -EFSCORRUPTED;
+ }
+
+ end_lblk = le32_to_cpu(last_extent->ee_block) +
+ ext4_ext_get_actual_len(last_extent);
+ desc_size_pos = (u64)end_lblk << inode->i_blkbits;
+ ext4_ext_drop_refs(path);
+ kfree(path);
+
+ if (desc_size_pos < sizeof(desc_size_disk))
+ goto bad;
+ desc_size_pos -= sizeof(desc_size_disk);
+
+ err = pagecache_read(inode, &desc_size_disk, sizeof(desc_size_disk),
+ desc_size_pos);
+ if (err)
+ return err;
+ desc_size = le32_to_cpu(desc_size_disk);
+
+ /*
+ * The descriptor is stored just before the desc_size_disk, but starting
+ * on a filesystem block boundary.
+ */
+
+ if (desc_size > INT_MAX || desc_size > desc_size_pos)
+ goto bad;
+
+ desc_pos = round_down(desc_size_pos - desc_size, i_blocksize(inode));
+ if (desc_pos < ext4_verity_metadata_pos(inode))
+ goto bad;
+
+ *desc_size_ret = desc_size;
+ *desc_pos_ret = desc_pos;
+ return 0;
+
+bad:
+ EXT4_ERROR_INODE(inode, "verity file corrupted; can't find descriptor");
+ return -EFSCORRUPTED;
+}
+
+static int ext4_get_verity_descriptor(struct inode *inode, void *buf,
+ size_t buf_size)
+{
+ size_t desc_size = 0;
+ u64 desc_pos = 0;
+ int err;
+
+ err = ext4_get_verity_descriptor_location(inode, &desc_size, &desc_pos);
+ if (err)
+ return err;
+
+ if (buf_size) {
+ if (desc_size > buf_size)
+ return -ERANGE;
+ err = pagecache_read(inode, buf, desc_size, desc_pos);
+ if (err)
+ return err;
+ }
+ return desc_size;
+}
+
+static struct page *ext4_read_merkle_tree_page(struct inode *inode,
+ pgoff_t index)
+{
+ index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
+
+ return read_mapping_page(inode->i_mapping, index, NULL);
+}
+
+static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf,
+ u64 index, int log_blocksize)
+{
+ loff_t pos = ext4_verity_metadata_pos(inode) + (index << log_blocksize);
+
+ return pagecache_write(inode, buf, 1 << log_blocksize, pos);
+}
+
+const struct fsverity_operations ext4_verityops = {
+ .begin_enable_verity = ext4_begin_enable_verity,
+ .end_enable_verity = ext4_end_enable_verity,
+ .get_verity_descriptor = ext4_get_verity_descriptor,
+ .read_merkle_tree_page = ext4_read_merkle_tree_page,
+ .write_merkle_tree_block = ext4_write_merkle_tree_block,
+};
--
2.22.0
^ permalink raw reply related
* [PATCH v6 13/17] fs-verity: support builtin file signatures
From: Eric Biggers @ 2019-07-01 15:32 UTC (permalink / raw)
To: linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
To meet some users' needs, add optional support for having fs-verity
handle a portion of the authentication policy in the kernel. An
".fs-verity" keyring is created to which X.509 certificates can be
added; then a sysctl 'fs.verity.require_signatures' can be set to cause
the kernel to enforce that all fs-verity files contain a signature of
their file measurement by a key in this keyring.
See the "Built-in signature verification" section of
Documentation/filesystems/fsverity.rst for the full documentation.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/verity/Kconfig | 17 ++++
fs/verity/Makefile | 2 +
fs/verity/enable.c | 20 ++++-
fs/verity/fsverity_private.h | 48 ++++++++++-
fs/verity/init.c | 6 ++
fs/verity/open.c | 27 ++++--
fs/verity/signature.c | 159 +++++++++++++++++++++++++++++++++++
fs/verity/verify.c | 6 ++
8 files changed, 271 insertions(+), 14 deletions(-)
create mode 100644 fs/verity/signature.c
diff --git a/fs/verity/Kconfig b/fs/verity/Kconfig
index c2bca0b01ecf..88fb25119899 100644
--- a/fs/verity/Kconfig
+++ b/fs/verity/Kconfig
@@ -36,3 +36,20 @@ config FS_VERITY_DEBUG
Enable debugging messages related to fs-verity by default.
Say N unless you are an fs-verity developer.
+
+config FS_VERITY_BUILTIN_SIGNATURES
+ bool "FS Verity builtin signature support"
+ depends on FS_VERITY
+ select SYSTEM_DATA_VERIFICATION
+ help
+ Support verifying signatures of verity files against the X.509
+ certificates that have been loaded into the ".fs-verity"
+ kernel keyring.
+
+ This is meant as a relatively simple mechanism that can be
+ used to provide an authenticity guarantee for verity files, as
+ an alternative to IMA appraisal. Userspace programs still
+ need to check that the verity bit is set in order to get an
+ authenticity guarantee.
+
+ If unsure, say N.
diff --git a/fs/verity/Makefile b/fs/verity/Makefile
index 6f7675ae0a31..570e9136334d 100644
--- a/fs/verity/Makefile
+++ b/fs/verity/Makefile
@@ -6,3 +6,5 @@ obj-$(CONFIG_FS_VERITY) += enable.o \
measure.o \
open.o \
verify.o
+
+obj-$(CONFIG_FS_VERITY_BUILTIN_SIGNATURES) += signature.o
diff --git a/fs/verity/enable.c b/fs/verity/enable.c
index 782b2911463e..e9dca76fe510 100644
--- a/fs/verity/enable.c
+++ b/fs/verity/enable.c
@@ -153,7 +153,7 @@ static int enable_verity(struct file *filp,
const struct fsverity_operations *vops = inode->i_sb->s_vop;
struct merkle_tree_params params = { };
struct fsverity_descriptor *desc;
- size_t desc_size = sizeof(*desc);
+ size_t desc_size = sizeof(*desc) + arg->sig_size;
struct fsverity_info *vi;
int err;
@@ -175,6 +175,16 @@ static int enable_verity(struct file *filp,
}
desc->salt_size = arg->salt_size;
+ /* Get the signature if the user provided one */
+ if (arg->sig_size &&
+ copy_from_user(desc->signature,
+ (const u8 __user *)(uintptr_t)arg->sig_ptr,
+ arg->sig_size)) {
+ err = -EFAULT;
+ goto out;
+ }
+ desc->sig_size = cpu_to_le32(arg->sig_size);
+
desc->data_size = cpu_to_le64(inode->i_size);
pr_debug("Building Merkle tree...\n");
@@ -215,6 +225,10 @@ static int enable_verity(struct file *filp,
goto rollback;
}
+ if (arg->sig_size)
+ pr_debug("Storing a %u-byte PKCS#7 signature alongside the file\n",
+ arg->sig_size);
+
/* Tell the filesystem to finish enabling verity on the file */
err = vops->end_enable_verity(filp, desc, desc_size, params.tree_size);
if (err) {
@@ -274,8 +288,8 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg)
if (arg.salt_size > FIELD_SIZEOF(struct fsverity_descriptor, salt))
return -EMSGSIZE;
- if (arg.sig_size)
- return -EINVAL;
+ if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE)
+ return -EMSGSIZE;
/*
* Require a regular file with write access. But the actual fd must
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index 02a547f0667c..e74c79b64d88 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -75,23 +75,41 @@ struct fsverity_info {
};
/*
- * Merkle tree properties. The file measurement is the hash of this structure.
+ * Merkle tree properties. The file measurement is the hash of this structure
+ * excluding the signature and with the sig_size field set to 0.
*/
struct fsverity_descriptor {
__u8 version; /* must be 1 */
__u8 hash_algorithm; /* Merkle tree hash algorithm */
__u8 log_blocksize; /* log2 of size of data and tree blocks */
__u8 salt_size; /* size of salt in bytes; 0 if none */
- __le32 sig_size; /* reserved, must be 0 */
+ __le32 sig_size; /* size of signature in bytes; 0 if none */
__le64 data_size; /* size of file the Merkle tree is built over */
__u8 root_hash[64]; /* Merkle tree root hash */
__u8 salt[32]; /* salt prepended to each hashed block */
__u8 __reserved[144]; /* must be 0's */
+ __u8 signature[]; /* optional PKCS#7 signature */
};
/* Arbitrary limit to bound the kmalloc() size. Can be changed. */
#define FS_VERITY_MAX_DESCRIPTOR_SIZE 16384
+#define FS_VERITY_MAX_SIGNATURE_SIZE (FS_VERITY_MAX_DESCRIPTOR_SIZE - \
+ sizeof(struct fsverity_descriptor))
+
+/*
+ * Format in which verity file measurements are signed. This is the same as
+ * 'struct fsverity_digest', except here some magic bytes are prepended to
+ * provide some context about what is being signed in case the same key is used
+ * for non-fsverity purposes, and here the fields have fixed endianness.
+ */
+struct fsverity_signed_digest {
+ char magic[8]; /* must be "FSVerity" */
+ __le16 digest_algorithm;
+ __le16 digest_size;
+ __u8 digest[];
+};
+
/* hash_algs.c */
extern struct fsverity_hash_alg fsverity_hash_algs[];
@@ -127,7 +145,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
const u8 *salt, size_t salt_size);
struct fsverity_info *fsverity_create_info(const struct inode *inode,
- const void *desc, size_t desc_size);
+ void *desc, size_t desc_size);
void fsverity_set_info(struct inode *inode, struct fsverity_info *vi);
@@ -136,8 +154,32 @@ void fsverity_free_info(struct fsverity_info *vi);
int __init fsverity_init_info_cache(void);
void __init fsverity_exit_info_cache(void);
+/* signature.c */
+
+#ifdef CONFIG_FS_VERITY_BUILTIN_SIGNATURES
+int fsverity_verify_signature(const struct fsverity_info *vi,
+ const struct fsverity_descriptor *desc,
+ size_t desc_size);
+
+int __init fsverity_init_signature(void);
+#else /* !CONFIG_FS_VERITY_BUILTIN_SIGNATURES */
+static inline int
+fsverity_verify_signature(const struct fsverity_info *vi,
+ const struct fsverity_descriptor *desc,
+ size_t desc_size)
+{
+ return 0;
+}
+
+static inline int fsverity_init_signature(void)
+{
+ return 0;
+}
+#endif /* !CONFIG_FS_VERITY_BUILTIN_SIGNATURES */
+
/* verify.c */
int __init fsverity_init_workqueue(void);
+void __init fsverity_exit_workqueue(void);
#endif /* _FSVERITY_PRIVATE_H */
diff --git a/fs/verity/init.c b/fs/verity/init.c
index b593805aafcc..94c104e00861 100644
--- a/fs/verity/init.c
+++ b/fs/verity/init.c
@@ -45,9 +45,15 @@ static int __init fsverity_init(void)
if (err)
goto err_exit_info_cache;
+ err = fsverity_init_signature();
+ if (err)
+ goto err_exit_workqueue;
+
pr_debug("Initialized fs-verity\n");
return 0;
+err_exit_workqueue:
+ fsverity_exit_workqueue();
err_exit_info_cache:
fsverity_exit_info_cache();
return err;
diff --git a/fs/verity/open.c b/fs/verity/open.c
index 3636a1ed8e2c..63d1004b688c 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -122,22 +122,32 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
return err;
}
-/* Compute the file measurement by hashing the fsverity_descriptor. */
+/*
+ * Compute the file measurement by hashing the fsverity_descriptor excluding the
+ * signature and with the sig_size field set to 0.
+ */
static int compute_file_measurement(const struct fsverity_hash_alg *hash_alg,
- const struct fsverity_descriptor *desc,
+ struct fsverity_descriptor *desc,
u8 *measurement)
{
- return fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), measurement);
+ __le32 sig_size = desc->sig_size;
+ int err;
+
+ desc->sig_size = 0;
+ err = fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), measurement);
+ desc->sig_size = sig_size;
+
+ return err;
}
/*
* Validate the given fsverity_descriptor and create a new fsverity_info from
- * it.
+ * it. The signature (if present) is also checked.
*/
struct fsverity_info *fsverity_create_info(const struct inode *inode,
- const void *_desc, size_t desc_size)
+ void *_desc, size_t desc_size)
{
- const struct fsverity_descriptor *desc = _desc;
+ struct fsverity_descriptor *desc = _desc;
struct fsverity_info *vi;
int err;
@@ -153,8 +163,7 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
return ERR_PTR(-EINVAL);
}
- if (desc->sig_size ||
- memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) {
+ if (memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) {
fsverity_err(inode, "Reserved bits set in descriptor");
return ERR_PTR(-EINVAL);
}
@@ -198,6 +207,8 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
pr_debug("Computed file measurement: %s:%*phN\n",
vi->tree_params.hash_alg->name,
vi->tree_params.digest_size, vi->measurement);
+
+ err = fsverity_verify_signature(vi, desc, desc_size);
out:
if (err) {
fsverity_free_info(vi);
diff --git a/fs/verity/signature.c b/fs/verity/signature.c
new file mode 100644
index 000000000000..e8299eff08c7
--- /dev/null
+++ b/fs/verity/signature.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * fs/verity/signature.c: verification of builtin signatures
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include "fsverity_private.h"
+
+#include <linux/cred.h>
+#include <linux/key.h>
+#include <linux/slab.h>
+#include <linux/verification.h>
+
+/*
+ * /proc/sys/fs/verity/require_signatures
+ * If 1, all verity files must have a valid builtin signature.
+ */
+static int fsverity_require_signatures;
+
+/*
+ * Keyring that contains the trusted X.509 certificates.
+ *
+ * Only root (kuid=0) can modify this. Also, root may use
+ * keyctl_restrict_keyring() to prevent any more additions.
+ */
+static struct key *fsverity_keyring;
+
+/**
+ * fsverity_verify_signature() - check a verity file's signature
+ *
+ * If the file's fs-verity descriptor includes a signature of the file
+ * measurement, verify it against the certificates in the fs-verity keyring.
+ *
+ * Return: 0 on success (signature valid or not required); -errno on failure
+ */
+int fsverity_verify_signature(const struct fsverity_info *vi,
+ const struct fsverity_descriptor *desc,
+ size_t desc_size)
+{
+ const struct inode *inode = vi->inode;
+ const struct fsverity_hash_alg *hash_alg = vi->tree_params.hash_alg;
+ const u32 sig_size = le32_to_cpu(desc->sig_size);
+ struct fsverity_signed_digest *d;
+ int err;
+
+ if (sig_size == 0) {
+ if (fsverity_require_signatures) {
+ fsverity_err(inode,
+ "require_signatures=1, rejecting unsigned file!");
+ return -EPERM;
+ }
+ return 0;
+ }
+
+ if (sig_size > desc_size - sizeof(*desc)) {
+ fsverity_err(inode, "Signature overflows verity descriptor");
+ return -EBADMSG;
+ }
+
+ d = kzalloc(sizeof(*d) + hash_alg->digest_size, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+ memcpy(d->magic, "FSVerity", 8);
+ d->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs);
+ d->digest_size = cpu_to_le16(hash_alg->digest_size);
+ memcpy(d->digest, vi->measurement, hash_alg->digest_size);
+
+ err = verify_pkcs7_signature(d, sizeof(*d) + hash_alg->digest_size,
+ desc->signature, sig_size,
+ fsverity_keyring,
+ VERIFYING_UNSPECIFIED_SIGNATURE,
+ NULL, NULL);
+ kfree(d);
+
+ if (err) {
+ if (err == -ENOKEY)
+ fsverity_err(inode,
+ "File's signing cert isn't in the fs-verity keyring");
+ else if (err == -EKEYREJECTED)
+ fsverity_err(inode, "Incorrect file signature");
+ else if (err == -EBADMSG)
+ fsverity_err(inode, "Malformed file signature");
+ else
+ fsverity_err(inode, "Error %d verifying file signature",
+ err);
+ return err;
+ }
+
+ pr_debug("Valid signature for file measurement %s:%*phN\n",
+ hash_alg->name, hash_alg->digest_size, vi->measurement);
+ return 0;
+}
+
+#ifdef CONFIG_SYSCTL
+static int zero;
+static int one = 1;
+static struct ctl_table_header *fsverity_sysctl_header;
+
+static const struct ctl_path fsverity_sysctl_path[] = {
+ { .procname = "fs", },
+ { .procname = "verity", },
+ { }
+};
+
+static struct ctl_table fsverity_sysctl_table[] = {
+ {
+ .procname = "require_signatures",
+ .data = &fsverity_require_signatures,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ { }
+};
+
+static int __init fsverity_sysctl_init(void)
+{
+ fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path,
+ fsverity_sysctl_table);
+ if (!fsverity_sysctl_header) {
+ pr_err("sysctl registration failed!\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+#else /* !CONFIG_SYSCTL */
+static inline int __init fsverity_sysctl_init(void)
+{
+ return 0;
+}
+#endif /* !CONFIG_SYSCTL */
+
+int __init fsverity_init_signature(void)
+{
+ struct key *ring;
+ int err;
+
+ ring = keyring_alloc(".fs-verity", KUIDT_INIT(0), KGIDT_INIT(0),
+ current_cred(), KEY_POS_SEARCH |
+ KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE |
+ KEY_USR_SEARCH | KEY_USR_SETATTR,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
+ if (IS_ERR(ring))
+ return PTR_ERR(ring);
+
+ err = fsverity_sysctl_init();
+ if (err)
+ goto err_put_ring;
+
+ fsverity_keyring = ring;
+ return 0;
+
+err_put_ring:
+ key_put(ring);
+ return err;
+}
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 62ab8f6a8ea1..3e8f2de44667 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -273,3 +273,9 @@ int __init fsverity_init_workqueue(void)
return -ENOMEM;
return 0;
}
+
+void __init fsverity_exit_workqueue(void)
+{
+ destroy_workqueue(fsverity_read_workqueue);
+ fsverity_read_workqueue = NULL;
+}
--
2.22.0
^ permalink raw reply related
* [PATCH v6 12/17] fs-verity: add SHA-512 support
From: Eric Biggers @ 2019-07-01 15:32 UTC (permalink / raw)
To: linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add SHA-512 support to fs-verity. This is primarily a demonstration of
the trivial changes needed to support a new hash algorithm in fs-verity;
most users will still use SHA-256, due to the smaller space required to
store the hashes. But some users may prefer SHA-512.
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/verity/fsverity_private.h | 2 +-
fs/verity/hash_algs.c | 5 +++++
include/uapi/linux/fsverity.h | 1 +
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index eaa2b3b93bbf..02a547f0667c 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -29,7 +29,7 @@ struct ahash_request;
* Largest digest size among all hash algorithms supported by fs-verity.
* Currently assumed to be <= size of fsverity_descriptor::root_hash.
*/
-#define FS_VERITY_MAX_DIGEST_SIZE SHA256_DIGEST_SIZE
+#define FS_VERITY_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
/* A hash algorithm supported by fs-verity */
struct fsverity_hash_alg {
diff --git a/fs/verity/hash_algs.c b/fs/verity/hash_algs.c
index c0457915ca10..27cbecb86be7 100644
--- a/fs/verity/hash_algs.c
+++ b/fs/verity/hash_algs.c
@@ -17,6 +17,11 @@ struct fsverity_hash_alg fsverity_hash_algs[] = {
.digest_size = SHA256_DIGEST_SIZE,
.block_size = SHA256_BLOCK_SIZE,
},
+ [FS_VERITY_HASH_ALG_SHA512] = {
+ .name = "sha512",
+ .digest_size = SHA512_DIGEST_SIZE,
+ .block_size = SHA512_BLOCK_SIZE,
+ },
};
/**
diff --git a/include/uapi/linux/fsverity.h b/include/uapi/linux/fsverity.h
index 57d1d7fc0c34..da0daf6c193b 100644
--- a/include/uapi/linux/fsverity.h
+++ b/include/uapi/linux/fsverity.h
@@ -14,6 +14,7 @@
#include <linux/types.h>
#define FS_VERITY_HASH_ALG_SHA256 1
+#define FS_VERITY_HASH_ALG_SHA512 2
struct fsverity_enable_arg {
__u32 version;
--
2.22.0
^ permalink raw reply related
* [PATCH v6 11/17] fs-verity: implement FS_IOC_MEASURE_VERITY ioctl
From: Eric Biggers @ 2019-07-01 15:32 UTC (permalink / raw)
To: linux-fscrypt
Cc: Theodore Y . Ts'o, Darrick J . Wong, linux-api, Dave Chinner,
linux-f2fs-devel, linux-fsdevel, Jaegeuk Kim, linux-integrity,
linux-ext4, Linus Torvalds, Christoph Hellwig, Victor Hsieh
In-Reply-To: <20190701153237.1777-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add a function for filesystems to call to implement the
FS_IOC_MEASURE_VERITY ioctl. This ioctl retrieves the file measurement
that fs-verity calculated for the given file and is enforcing for reads;
i.e., reads that don't match this hash will fail. This ioctl can be
used for authentication or logging of file measurements in userspace.
See the "FS_IOC_MEASURE_VERITY" section of
Documentation/filesystems/fsverity.rst for the documentation.
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/verity/Makefile | 1 +
fs/verity/measure.c | 57 ++++++++++++++++++++++++++++++++++++++++
include/linux/fsverity.h | 11 ++++++++
3 files changed, 69 insertions(+)
create mode 100644 fs/verity/measure.c
diff --git a/fs/verity/Makefile b/fs/verity/Makefile
index 04b37475fd28..6f7675ae0a31 100644
--- a/fs/verity/Makefile
+++ b/fs/verity/Makefile
@@ -3,5 +3,6 @@
obj-$(CONFIG_FS_VERITY) += enable.o \
hash_algs.o \
init.o \
+ measure.o \
open.o \
verify.o
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
new file mode 100644
index 000000000000..05049b68c745
--- /dev/null
+++ b/fs/verity/measure.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * fs/verity/measure.c: ioctl to get a verity file's measurement
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include "fsverity_private.h"
+
+#include <linux/uaccess.h>
+
+/**
+ * fsverity_ioctl_measure() - get a verity file's measurement
+ *
+ * Retrieve the file measurement that the kernel is enforcing for reads from a
+ * verity file. See the "FS_IOC_MEASURE_VERITY" section of
+ * Documentation/filesystems/fsverity.rst for the documentation.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
+{
+ const struct inode *inode = file_inode(filp);
+ struct fsverity_digest __user *uarg = _uarg;
+ const struct fsverity_info *vi;
+ const struct fsverity_hash_alg *hash_alg;
+ struct fsverity_digest arg;
+
+ vi = fsverity_get_info(inode);
+ if (!vi)
+ return -ENODATA; /* not a verity file */
+ hash_alg = vi->tree_params.hash_alg;
+
+ /*
+ * The user specifies the digest_size their buffer has space for; we can
+ * return the digest if it fits in the available space. We write back
+ * the actual size, which may be shorter than the user-specified size.
+ */
+
+ if (get_user(arg.digest_size, &uarg->digest_size))
+ return -EFAULT;
+ if (arg.digest_size < hash_alg->digest_size)
+ return -EOVERFLOW;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.digest_algorithm = hash_alg - fsverity_hash_algs;
+ arg.digest_size = hash_alg->digest_size;
+
+ if (copy_to_user(uarg, &arg, sizeof(arg)))
+ return -EFAULT;
+
+ if (copy_to_user(uarg->digest, vi->measurement, hash_alg->digest_size))
+ return -EFAULT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index b0b1854a9450..9ebb97c174c7 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -116,6 +116,10 @@ static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
extern int fsverity_ioctl_enable(struct file *filp, const void __user *arg);
+/* measure.c */
+
+extern int fsverity_ioctl_measure(struct file *filp, void __user *arg);
+
/* open.c */
extern int fsverity_file_open(struct inode *inode, struct file *filp);
@@ -143,6 +147,13 @@ static inline int fsverity_ioctl_enable(struct file *filp,
return -EOPNOTSUPP;
}
+/* measure.c */
+
+static inline int fsverity_ioctl_measure(struct file *filp, void __user *arg)
+{
+ return -EOPNOTSUPP;
+}
+
/* open.c */
static inline int fsverity_file_open(struct inode *inode, struct file *filp)
--
2.22.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox