Linux Kernel Usercopy
Description
I was playing around with a kernel module that I built to practice buffer overflows on. I used regular usercopy to move data between user land and kernel land when I realized that normal usercopy employs a size check on the kernel buffer and the size provided, preventing out of bounds read or write.
You can read a little more about it here at Red Hat Documentation. Although I am using regular Ubuntu, it seems that this hardened usercopy is also default on this system. Even though hardened usercopy is nothing new, I wanted to write this blog post to show you the assembly concerning the bounds check.
Kernel Module
This is the read function in a kernel module that has no apparent bounds check on the buffer that is being read to the user. An attacker could provide an arbitrary value in the size field:
/**
* Called when a process reads from the device
*/
static ssize_t hackme_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
size_t bytes_read = 0;
char vuln_buf[100];
bytes_read = copy_to_user(buffer, vuln_buf, length);
return bytes_read;
}
Disassembly
Taking a look inside the hackme_read function, there is a bounds check before copy_to_user is called.
The first picture shows the value of the rdx register: 0x78
The following picture is inside of the hackme_read function, where there clearly is a compare that will jump past the call to copy_to_user(underlined with white):
Conclusion
If you want to experiment with kernel buffer overflows or make a CTF challenge for example, you could use the function raw_copy_to_user which will not check the size field by default.