Re: [PATCH v12 01/12] lib: introduce copy_struct_{to,from}_user helpers
From: Aleksa Sarai <hidden>
Date: 2019-09-05 13:36:28
Also in:
linux-alpha, linux-arch, linux-arm-kernel, linux-fsdevel, linux-kselftest, linux-mips, linux-s390, linux-sh, linuxppc-dev, lkml, sparclinux
On 2019-09-05, Peter Zijlstra [off-list ref] wrote:
On Thu, Sep 05, 2019 at 07:26:22PM +1000, Aleksa Sarai wrote:quoted
On 2019-09-05, Peter Zijlstra [off-list ref] wrote:quoted
On Thu, Sep 05, 2019 at 06:19:22AM +1000, Aleksa Sarai wrote:quoted
+/** + * copy_struct_to_user: copy a struct to user space + * @dst: Destination address, in user space. + * @usize: Size of @dst struct. + * @src: Source address, in kernel space. + * @ksize: Size of @src struct. + * + * Copies a struct from kernel space to user space, in a way that guarantees + * backwards-compatibility for struct syscall arguments (as long as future + * struct extensions are made such that all new fields are *appended* to the + * old struct, and zeroed-out new fields have the same meaning as the old + * struct). + * + * @ksize is just sizeof(*dst), and @usize should've been passed by user space. + * The recommended usage is something like the following: + * + * SYSCALL_DEFINE2(foobar, struct foo __user *, uarg, size_t, usize) + * { + * int err; + * struct foo karg = {}; + * + * // do something with karg + * + * err = copy_struct_to_user(uarg, usize, &karg, sizeof(karg)); + * if (err) + * return err; + * + * // ... + * } + * + * There are three cases to consider: + * * If @usize == @ksize, then it's copied verbatim. + * * If @usize < @ksize, then kernel space is "returning" a newer struct to an + * older user space. In order to avoid user space getting incomplete + * information (new fields might be important), all trailing bytes in @src + * (@ksize - @usize) must be zeroreds/zerored/zero/, right?It should've been "zeroed".That reads wrong to me; that way it reads like this function must take that action and zero out the 'rest'; which is just wrong. This function must verify those bytes are zero, not make them zero.
Right, in my head I was thinking "must have been zeroed" which isn't what it says. I'll switch to "zero".
quoted
quoted
quoted
, otherwise -EFBIG is returned.'Funny' that, copy_struct_from_user() below seems to use E2BIG.This is a copy of the semantics that sched_[sg]etattr(2) uses -- E2BIG for a "too big" struct passed to the kernel, and EFBIG for a "too big" struct passed to user-space. I would personally have preferred EMSGSIZE instead of EFBIG, but felt using the existing error codes would be less confusing.Sadly a recent commit: 1251201c0d34 ("sched/core: Fix uclamp ABI bug, clean up and robustify sched_read_attr() ABI logic and code") Made the situation even 'worse'.
I hadn't seen this patch before, and I have a few questions taking a
look at it:
* An error code for a particular behaviour was changed (EFBIG ->
E2BIG). Is this not a userspace breakage (I know Linus went ballistic
about something similar a while ago[1]), or did I misunderstand what
the issue was in [1]?
* At the risk of bike-shedding -- of we are changing it, wouldn't
-EMSGSIZE be more appropriate? To be fair, picking errno values has
always been more of an art than a science, but to my ears "Argument
list too long" doesn't make too much sense in the context of
"returning" a struct back to userspace (and the cause of the error
is that the argument passed by user space *isn't big enough*). If
there was an E2SMALL that would also work. ;)
* Do you want me to write a patch based on that, to switch it to
copy_struct_to_user()?
* That patch removes the "are there non-zero bytes in the tail that
userspace won't know about" check (which I have included in mine). I
understand that this caused issues specifically with sched_getattr(2)
due to the default value not being zero -- how should we rectify that
(given that we'd hopefully want to port everyone who uses that
interface to copy_struct_{to,from}_user())?
* Given that the [uk]attr->size construct is pretty important to the
usability of the sched and perf interfaces, should we require (or
encourage) it for all struct-extension syscall setups?
quoted
quoted
quoted
+ if (unlikely(!access_ok(src, usize))) + return -EFAULT; + + /* Deal with trailing bytes. */ + if (usize < ksize) + memset(dst + size, 0, rest); + else if (usize > ksize) { + const void __user *addr = src + size; + char buffer[BUFFER_SIZE] = {};Isn't that too big for on-stack?Is a 64-byte buffer too big? I picked the number "at random" to be the size of a cache line, but I could shrink it down to 32 bytes if the size is an issue (I wanted to avoid needless allocations -- hence it being on-stack).Ah, my ctags gave me a definition of BUFFER_SIZE that was 512. I suppose 64 should be OK.
Good to know, though I'll rename it to avoid confusion. [1]: https://lore.kernel.org/lkml/CA+55aFy98A+LJK4+GWMcbzaa1zsPBRo76q+ioEjbx-uaMKH6Uw@mail.gmail.com/ (local) -- Aleksa Sarai Senior Software Engineer (Containers) SUSE Linux GmbH <https://www.cyphar.com/>
Attachments
- signature.asc [application/pgp-signature] 228 bytes