copy_struct_from_user(9)
π copy_struct_from_user β Kernel API Manual (yes, the 9 one) π
okay bestie, so this page lives in the Kernel API Manual and it's about one tiny little function that is carrying the entire weight of struct syscall backwards-compatibility on its shoulders. icon behavior honestly. π«Ά
β¨ NAME β¨
copy_struct_from_user β copy a struct from userspace. that's it, that's the whole name, and it's giving exactly what it says on the tin. π
πΈ SYNOPSIS πΈ
int copy_struct_from_user (void *dst , size_t ksize , const void __user
* src , size_t usize );
π¦ ARGUMENTS π¦
dstβ destination address, but in kernel space. heads up: this buffer must beksizebytes long. no sneaky short buffers, she will notice. πksizeβ size of thedststruct. yes it's justsizeof(*dst), we'll get there.srcβ source address, over in userspace where things are scary and untrusted. πusizeβ the (alleged) size of thesrcstruct. alleged!! because userspace is out here lying for sport. π
π DESCRIPTION π
Copies a struct from userspace into kernel space, in a way that guarantees backwards-compatibility for struct syscall arguments. and like, that guarantee is the whole point, so listen up:
it only holds 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. so, append-only, zeroes mean "not set", no cap. π§
ksize is literally just sizeof(*dst), and usize should've been passed by userspace. the recommended usage? something like this, bestie, take notes: π
SYSCALL_DEFINE2(foobar, const struct foo __user *, uarg, size_t, usize)
{ int err; struct foo karg = {};
if (usize > PAGE_SIZE) return -E2BIG; if (usize < FOO_SIZE_VER0) return
-EINVAL;
err = copy_struct_from_user(karg, sizeof(karg), uarg, usize); if (err)
return err;
// ... }
There are three cases to consider, and honestly each one is a whole vibe:
- If
usize == ksize, then it's copied verbatim. same size, same struct, no drama. slay. π - If
usize < ksize, then userspace has passed an old struct to a newer kernel. the rest of the trailing bytes indst(ksize - usize) are to be zero-filled. old soul, new kernel, we make it work. π₯Ί - If
usize > ksize, then userspace has passed a new struct to an older kernel. those trailing bytes the kernel doesn't know about (usize - ksize) get checked to make sure they're zeroed β otherwise-E2BIGcomes back at you. so: unknown bytes must be zeroes or you're getting yeeted. π
Returns (and in all of these cases, some data may have been copied β no takebacks):
-E2BIG: (usize > ksize) and there are non-zero trailing bytes insrc. the audacity.-EFAULT: access to userspace failed. it happens to the best of us. π
π₯ SEE ALSO π₯
Kernel file ./include/linux/uaccess.h β plus a whole friend group of friends:
__copy_to_user_inatomic(9)probe_subpage_writeable(9)copy_struct_to_user(9)get_kernel_nofault(9)__scoped_user_access_begin(9)__scoped_user_access(9)scoped_user_read_access_size(9)scoped_user_read_access(9)scoped_user_write_access_size(9)scoped_user_write_access(9)scoped_user_rw_access_size(9)scoped_user_rw_access(9)get_user_inline(9)put_user_inline(9)
go read them, they're all iconic in their own way π
that's the whole page, bestie!! straight from ./include/linux, cheerfully dated October 2026 β so yeah, from the future, we love that for us β follow for more uaccess lore, mwah π
π
π¦ where this page comes from
- nixpkgs unstable: linux-manual 7.3.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms