copy_struct_to_user(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

✨ 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 be ksize bytes long, no cap.
  • usize β€” the (alleged) size of the dst struct. "alleged" is doing so much work in that sentence, i'm obsessed.
  • src β€” source address, down here in kernel space.
  • ksize β€” the size of the src struct.
  • ignored_trailing β€” gets set to true if there was a non-zero byte in src that 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 -EMSGSIZE if 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 about ignored_trailing you 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:

  1. If usize == ksize, then it's copied verbatim. slay, no notes.
  2. 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 β€” but ignored_trailing gets set to true if any of the trailing (ksize - usize) bytes are non-zero. πŸ‘€
  3. 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:

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