Almost every character or misc device driver moves data between kernel space and user space
using copy_to_user() and copy_from_user(). These helper functions look
simple, but careless use of them is one of the most common sources of real security bugs in
Linux kernel drivers. In this lesson of our free Linux kernel development course
we explain, at a conceptual and defensive level, exactly why this copy_to_user()
vulnerability class matters, and how to write driver code that is safe against it.
What You Will Learn
- What
copy_to_user()andcopy_from_user()actually do under the hood - Why blindly trusting a user-supplied pointer or length is dangerous
- The difference between a user-space virtual address (UVA) and a kernel virtual address (KVA)
- How to validate untrusted input defensively in your driver’s read/write callbacks
- Practical best practices used in production-grade kernel drivers
Prerequisites
- Basic character/misc device driver experience (file_operations, read/write callbacks)
- Understanding of virtual memory basics — user space vs kernel space address ranges
- Comfort reading small C code snippets
What copy_to_user() and copy_from_user() Actually Do
When your driver’s read() callback runs, it executes with full kernel privileges,
but the destination buffer supplied by the calling application lives in that process’s user-space
address range. The kernel cannot simply use memcpy() to write there directly, because
that address range is only valid, and only safely accessible, through special helper routines.
copy_to_user() and its counterpart copy_from_user() exist exactly for
this: they safely move bytes across the user/kernel boundary, checking that the destination
range is a legitimate part of the calling process’s address space before touching it.
| Kernel Buffer trusted driver data |
→ | copy_to_user() validates destination range |
→ | User Buffer calling process’s memory |
Where the Risk Comes From
The safety of copy_to_user() depends entirely on the driver passing it a genuine,
untampered destination address and a length that matches the buffer the application actually
allocated. Both of those values typically arrive as arguments straight from user space (the
ubuf pointer and count parameter of your read()
callback). If a driver forwards these values to lower-level copy routines without validation, or
worse, if it performs its own pointer arithmetic on them without bounds checking, it opens the
door to memory corruption. In the worst case, a driver bug in this area can let an attacker
influence what gets overwritten in either user or kernel memory, which is why this class of bug
is treated so seriously in kernel security reviews.
Importantly, this is not a flaw in copy_to_user() itself — the helper does its
address-range validation correctly. The vulnerability is introduced by driver code that
mismanages the pointer or length before ever calling it, for example by not clamping
count to the driver’s real buffer size, or by computing a destination address using
untrusted arithmetic instead of trusting only the pointer the kernel handed it.
Writing a Defensively Safe read() Callback
The fix is straightforward once you internalise the rule: never trust the length the caller gives you beyond your own driver’s known buffer size, and never derive a destination address through your own arithmetic — always copy to the pointer the kernel passed you, unmodified.
// safe_read.c - defensive pattern for a character device read()
#define DRV_BUF_SIZE 128
static char drv_buffer[DRV_BUF_SIZE] = "driver default data";
static ssize_t safe_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *ppos)
{
size_t avail = strlen(drv_buffer) + 1;
/* Never trust count blindly - clamp to what we actually have */
if (*ppos >= avail)
return 0; /* EOF */
size_t to_copy = min(count, avail - (size_t)*ppos);
/* Always copy to the untouched pointer the kernel gave us */
if (copy_to_user(ubuf, drv_buffer + *ppos, to_copy))
return -EFAULT; /* copy_to_user failed validation */
*ppos += to_copy;
return to_copy;
}
Notice three deliberate choices in this pattern: the copy length is clamped against the driver’s
own known buffer size, the offset is validated before use, and ubuf is passed to
copy_to_user() exactly as received — never modified, never recalculated.
Safe vs. Risky Patterns at a Glance
| Practice | Risky | Safe |
|---|---|---|
| Destination pointer | Recomputed via driver-side arithmetic | Used exactly as received from the kernel |
| Copy length | Trusts user-supplied count directly | Clamped to driver’s known buffer size |
| Return value handling | Ignored | Checked; non-zero return treated as -EFAULT |
Real-World Relevance
- Kernel code review checklists: reviewers specifically look for unclamped lengths around every
copy_to_user()/copy_from_user()call - Fuzzing: tools like syzkaller routinely surface exactly this class of bug in out-of-tree and staging drivers
- Static analysis: smatch and Coccinelle checks in the kernel build process specifically flag unchecked copy lengths
Common Mistakes and Troubleshooting
Always clamp against your own buffer size first — never allocate or copy using a caller-supplied length without an upper bound check.
Avoid recalculating the destination pointer; if you must offset it, validate the resulting range still lies within the original buffer bounds.
A non-zero return means only part of the data was copied — always propagate this as -EFAULT rather than assuming success.
Best Practices
- Clamp every user-supplied length against your driver’s real, fixed-size buffer
- Never trust or recompute the destination/source pointer given by the VFS layer
- Always check the return value of
copy_to_user()/copy_from_user() - Run kernel static analysis (
smatch,sparse) regularly on driver code - Keep driver read/write buffers fixed-size where possible, to make bounds checking trivial
Performance Considerations
Bounds checking here is essentially free — a couple of comparisons per call — compared to the cost of the copy itself. There is no legitimate performance reason to skip validation, so this is one security practice with effectively zero trade-off.
Security Considerations
Treat every value that originates from a user-space system call argument as untrusted input, including lengths, offsets, and flags — not just pointers. Defensive validation at the very start of your read/write callbacks is the single highest-value security habit a driver author can build.
Summary / Key Takeaways
copy_to_user()/copy_from_user()safely bridge kernel and user address spaces, but only when driver code feeds them validated inputs- The real risk lies in driver-side length and pointer mismanagement, not in the helper functions themselves
- Clamp lengths to your own buffer size and never recompute the destination pointer
- Always check the return value and propagate failures as
-EFAULT
Conclusion
Data transfer between user space and kernel space is one of the most security-sensitive parts of any driver. The good news is that writing it safely costs almost nothing in performance — it just requires discipline: validate lengths, never trust pointer arithmetic on user-supplied addresses, and always check return values. Apply this pattern consistently across every driver you write in this free Linux device drivers course, and you eliminate one of the most common classes of kernel security bugs before it ever ships.
Frequently Asked Questions
No — the function correctly validates that the destination lies within the calling process’s address space. The risk comes from driver code that mismanages the length or pointer before calling it.
It returns the number of bytes that could not be copied; a well-written driver checks this and returns -EFAULT to the caller.
memcpy() performs no validation of the destination address; copy_to_user() specifically checks that the address range belongs to the calling process before touching it.
Yes — the same length-clamping and pointer-handling discipline applies equally to copy_from_user() in your write() callback.
Use kernel static analysis tools such as smatch and sparse, enable fuzzing where feasible, and make length/pointer validation part of every driver code review.
No — access_ok() checks that an address range is a valid user-space range, but it does not know your driver’s actual buffer size, so length clamping is still your responsibility.
Explore more lessons on device driver security, IPC, and embedded systems on EmbeddedPathashala.
Browse All Lectures
2 Comments