copy_struct_to_user(9)
β¨ copy_struct_to_user(9) β¨
okay so bestie, welcome to copy_struct_to_user(9) β literally the kernel-side glow-up for handing a struct over to userspace. sit down, grab a strawberry π, we're doing this.
πΈ NAME πΈ
copy_struct_to_user β copy a struct to userspace. that's it, that's the vibe. π
β¨ SYNOPSIS β¨
int copy_struct_to_user (void __user *dst , size_t usize , const void
*src , size_t ksize , bool *ignored_trailing );
look at her. five arguments. main character energy fr. π
π ARGUMENTS π
dstβ destination address, up in userspace. heads up: this buffer has to beksizebytes long, no cap.usizeβ the (alleged) size of thedststruct. "alleged" is doing so much work in that sentence, i'm obsessed.srcβ source address, down here in kernel space.ksizeβ the size of thesrcstruct.ignored_trailingβ gets set totrueif there was a non-zero byte insrcthat userspace literally cannot see, because they're using a smaller struct. π«Ά
π¦ DESCRIPTION π¦
So what does she actually do. She copies a struct from kernel space to userspace in a way that guarantees backwards-compatibility for struct syscall arguments β and yes that guarantee 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. periodt. β¨
Some syscalls wanna be extra sure that userspace knows about everything in the struct. So if there's a non-zero value that userspace doesn't know about, they want to return an error (like -EMSGSIZE) or have some other fallback (like adding a "you're missing some information" flag). If ignored_trailing is non-NULL, it gets set to true if there was a non-zero byte that couldn't be copied to userspace (ie. it was past usize). π
okay but here's the nuance: while unconditionally returning an error in this case is the simplest solution, for maximum backward compatibility you should try to only return-EMSGSIZEif the user explicitly requested the data that couldn't be copied. note that structure sizes can change due to header changes and simple recompilations without code changes(!) β so if you care aboutignored_trailingyou probably wanna make sure any new field data is associated with a flag. otherwise you might assume a program knows about data it does not. π that's the trap, bestie. don't fall in.
ksize is just sizeof(*src), and usize should've been passed by userspace. the recommended usage is something like this:
SYSCALL_DEFINE2(foobar, struct foo __user *, uarg, size_t, usize) {
int err;
bool ignored_trailing;
struct foo karg = {};
if (usize > PAGE_SIZE)
return -E2BIG;
if (usize < FOO_SIZE_VER0)
return -EINVAL;
// ... modify karg somehow ...
err = copy_struct_to_user(uarg, usize, karg, sizeof(karg), ignored_trailing);
if (err)
return err;
if (ignored_trailing)
return -EMSGSIZE:
// ...
}
not me copying that colon after -EMSGSIZE verbatim π i don't make the rules, i just relay them.
There are three cases to consider β a whole little trio, a vibe check:
- If
usize == ksize, then it's copied verbatim. slay, no notes. - If
usize < ksize, then the kernel is trying to pass userspace a newer struct than it supports. so we only copy the interoperable portions (usize) and ignore the rest β butignored_trailinggets set totrueif any of the trailing(ksize - usize)bytes are non-zero. π - If
usize > ksize, then the kernel is trying to pass userspace an older struct than userspace supports. and in order to make sure the unknown-to-the-kernel fields don't contain garbage values, we zero the trailing(usize - ksize)bytes. sparkle clean. β¨
Returns (and in all cases, some data may have been copied β yes, even on the sad path, keep that in mind):
-EFAULT: access to userspace failed. π
π SEE ALSO π
Kernel file ./include/linux/uaccess.h:
__copy_to_user_inatomic(9)probe_subpage_writeable(9)copy_struct_from_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)
a whole friend group honestly. iykyk π§
and that's the page!! π source: ./include/linux, dated October 2026 β mwah, go forth and copy your structs responsibly, bestie π
π¦ 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