On 2019-09-05, Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote:
On Thu, Sep 05, 2019 at 07:26:22PM +1000, Aleksa Sarai wrote:On 2019-09-05, Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote:On Thu, Sep 05, 2019 at 06:19:22AM +1000, Aleksa Sarai wrote:+/** + * 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".
, 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?
+ 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@xxxxxxxxxxxxxx/ -- Aleksa Sarai Senior Software Engineer (Containers) SUSE Linux GmbH <https://www.cyphar.com/>
Attachment:
signature.asc
Description: PGP signature