Every character device driver eventually needs to move data between an application running in user space and the driver running in kernel space. This is exactly where copy_from_user() and copy_to_user() come in, and in this lecture of our free Linux kernel development course you will learn why a driver can never simply dereference a user space pointer directly, how these two functions safely move buffers across the user-kernel boundary, and how the lighter-weight put_user() and get_user() macros handle single values efficiently on modern Linux kernel 6.x systems.
free linux kernel development course
free embedded linux course
copy_from_user copy_to_user
put_user get_user
What You Will Learn
copy_to_user() and copy_from_user() function signatures and return values
put_user() and get_user() macros for single variables
An original misc character driver using both APIs
Common bugs, security risks, and performance notes
Testing the driver from the command line
Prerequisites
A Linux 6.x kernel build environment with headers installed
Comfort compiling and inserting kernel modules with insmod / rmmod
Why User Space Pointers Cannot Be Dereferenced Directly
When an application calls read() or write() on your device file, it passes a pointer into its own virtual address space. That pointer is meaningless inside the kernel for two reasons. First, the kernel runs with its own page tables active during a syscall, so a raw user address may not even be mapped the same way. Second, and more importantly, user space is untrusted. An application could pass a bad pointer, a pointer into someone else’s memory, or a pointer designed to trick the driver into leaking kernel memory. Because of this, the kernel never lets a driver use memcpy() or a plain assignment on a user space address. Instead, every driver must go through the dedicated user-copy interface, and that is the whole reason copy_from_user() and copy_to_user() exist.
Application buffer
read()/write() call
copy_to_user() / copy_from_user()
put_user() / get_user()
Driver’s private buffer
file_operations callback
copy_to_user() and copy_from_user() API
These two functions handle bulk buffer transfers of any size and are the workhorses behind almost every read() and write() implementation:
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
The __user annotation is a sparse checker hint marking a pointer as belonging to user space, and it is also a signal to you as the driver author that this pointer must never be dereferenced directly. n is the byte count you intend to transfer. Both functions return the number of bytes that could NOT be copied — a return value of 0 means complete success, and any non-zero value means a partial or failed copy that your driver must treat as an error.
| Function | Direction | On Partial Failure |
|---|---|---|
| copy_to_user() | Kernel → User | Copies only what fit; return value shows uncopied bytes |
| copy_from_user() | User → Kernel | Uncopied kernel bytes are zero-padded for safety |
Original Example: Buffer Copy in a Misc Driver
Here is an original driver snippet, written for kernel 6.x, showing copy_from_user() and copy_to_user() inside a simple misc device’s write and read callbacks:
static char ep_kbuf[EP_BUF_SIZE];
static size_t ep_kbuf_len;static ssize_t ep_userbuf_write(struct file *filp, const char __user *ubuf,
size_t count, loff_t *pos)
{
if (count > EP_BUF_SIZE)
count = EP_BUF_SIZE;if (copy_from_user(ep_kbuf, ubuf, count))
return -EFAULT;ep_kbuf_len = count;
pr_info(“ep_userbuf: stored %zu bytes from user space\n”, count);
return count;
}static ssize_t ep_userbuf_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *pos)
{
if (*pos >= ep_kbuf_len)
return 0;if (count > ep_kbuf_len – *pos)
count = ep_kbuf_len – *pos;if (copy_to_user(ubuf, ep_kbuf + *pos, count))
return -EFAULT;*pos += count;
return count;
}
Notice the pattern: check the copy function’s return value, and if it is non-zero, return -EFAULT immediately. This single habit prevents an entire class of driver bugs where a partially copied buffer is silently treated as complete.
put_user() and get_user(): Copying Single Values
Copying a whole buffer through copy_to_user() for a single integer or character is wasteful. The kernel provides two macros, put_user() and get_user(), purpose-built for scalar types like char, int, and long:
get_user(kernel_var, user_ptr); /* user -> kernel, returns 0 or -EFAULT */
get_user() sets the destination kernel variable to 0 automatically if the copy fails, so you never end up operating on uninitialized data. Both macros type-check at compile time: the kernel variable and the dereferenced user pointer must be assignable to each other, which catches size mismatches long before they become runtime bugs.
Original Example: A Single-Value Status Register
static int ep_status_value = 42;
static ssize_t ep_status_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *pos)
{
if (*pos > 0 || count < sizeof(int))
return 0;
if (put_user(ep_status_value, (int __user *)ubuf))
return -EFAULT;
*pos += sizeof(int);
return sizeof(int);
}
static ssize_t ep_status_write(struct file *filp, const char __user *ubuf,
size_t count, loff_t *pos)
{
int new_value;
if (count < sizeof(int))
return -EINVAL;
if (get_user(new_value, (int __user *)ubuf))
return -EFAULT;
ep_status_value = new_value;
return sizeof(int);
}
Common Mistakes When Using copy_from_user() and copy_to_user()
| Mistake | Why It Breaks |
|---|---|
| Ignoring the return value | A partial copy is treated as a full success, corrupting data silently |
| Not clamping count against buffer size | Leads to kernel buffer overflow before the copy even happens |
| Calling copy_from_user() with a kernel pointer as the source | Undefined behavior; the API assumes the source is a real user address |
| Reusing get_user()’s output before checking its return code | On failure the variable is zeroed, which can mask the real error |
Security Considerations for copy_to_user copy_from_user Code
Because copy_to_user() can leak kernel memory to an application if misused, always zero or fully initialize a kernel buffer before copying it out, and never copy more bytes than you have actually written. Modern kernels also enable CONFIG_HARDENED_USERCOPY, which performs additional bounds checking against the target slab object at copy time and panics on an out-of-bounds copy instead of silently corrupting memory — another reason to always pass the correct, clamped length rather than trusting an unchecked count field.
Performance Notes
put_user() and get_user() compile down to a single inline access with fault handling, so they are noticeably cheaper than copy_to_user()/copy_from_user() for one scalar value. For anything larger than a few bytes, however, the bulk functions are the right tool since they are optimized internally for larger transfers.
Testing the Driver From the Command Line
$ sudo insmod ep_userbuf.ko
$ echo -n “hello kernel” | sudo tee /dev/ep_userbuf
$ sudo cat /dev/ep_userbuf
hello kernel
$ dmesg | tail -n 3
[ 1234.567890] ep_userbuf: stored 12 bytes from user space
$ sudo rmmod ep_userbuf
Best Practices Checklist
Always check the return value of copy_to_user / copy_from_user / put_user / get_user
Use put_user/get_user only for single scalar values, never structures or arrays
Never store or dereference a __user pointer outside the syscall context that gave it to you
Zero-initialize kernel buffers before copying them out to avoid leaking stale memory
Real-World Use Cases
Almost every character driver you will ever write depends on this API: sensor drivers returning readings to a user application, EEPROM or NVRAM drivers exposing raw storage, configuration interfaces that accept tuning parameters, and even simple logging drivers all rely on copy_to_user() and copy_from_user() to move data safely. Anytime your driver implements read() or write() on a device node, you will reach for this exact pair of functions.
Summary and Key Takeaways
copy_to_user() and copy_from_user() safely move buffers of any size across that boundary
put_user() and get_user() are lightweight macros for single scalar values
A non-zero return value always means a partial or failed copy that must be handled
CONFIG_HARDENED_USERCOPY adds kernel-side bounds checking on modern kernels
Conclusion
Understanding copy_to_user(), copy_from_user(), put_user(), and get_user() is one of the most important steps in this free Linux device drivers course, because every character driver that talks to user space applications depends on getting this boundary right. Get comfortable clamping lengths, checking return values, and choosing the right function for the size of data you are moving, and you will avoid the majority of bugs that new kernel driver developers run into. In the next lecture of this free Linux kernel development course, we will use these exact functions inside a complete driver’s open() and release() methods.
Frequently Asked Questions
Why can’t a driver just use memcpy() on a user space pointer?
A user space pointer may be invalid, unmapped, or deliberately malicious. memcpy() performs no validation, while copy_to_user()/copy_from_user() include fault-handling logic that safely catches bad addresses instead of crashing the kernel.
What does a non-zero return value from copy_from_user() mean?
It reports how many bytes could NOT be copied. Any non-zero value should be treated as a failure, typically by returning -EFAULT from your file operation.
When should I use put_user() instead of copy_to_user()?
Use put_user()/get_user() only when transferring a single scalar value such as an int or char. For buffers, structures, or arrays, use copy_to_user()/copy_from_user() instead.
Does copy_from_user() zero out the kernel buffer on failure?
Yes. If the copy is incomplete, the remaining destination bytes in kernel memory are zero-padded so no stale or leftover kernel data is exposed.
Is access_ok() still required before calling copy_to_user()?
On modern Linux kernel 6.x, copy_to_user() and copy_from_user() perform the necessary address-range validation internally, so manually calling access_ok() beforehand is generally unnecessary in ordinary driver code.
What is CONFIG_HARDENED_USERCOPY?
It is a kernel hardening option that validates the size and bounds of a copy against the actual kernel object being copied to or from, causing the kernel to reject or panic on suspicious out-of-bounds copies rather than allow silent corruption.
Can put_user() and get_user() be used with structures?
No. They only work with scalar types that fit in a machine word such as char, short, int, or long. Structures and arrays must use copy_to_user() and copy_from_user().
Continue the Free Linux Kernel Development Course
Next up: implementing open() and release() for a real character device driver.
