Linux Kernel: Strncpy_From_User() Copying Too Many Bytes
I'm Trying to Write a Character Device, and I'm Copying from User to Kernel Space Using Strncpy_From_User. However, It Almost Always Copies Too Much Data. the...
I'm trying to write a character device, and I'm copying from user to kernel space using strncpy_from_user. However, it almost always copies too much data. The way I'm doing it is:
//len is buffer length.
tmp = (struct msg_list *)kmalloc(sizeof(struct msg_list),GFP_ATOMIC);
tmp->msg = (char*)kmalloc(len,GFP_ATOMIC);
strncpy_from_user(tmp->msg,buff,len);
Buffer length generally outputs 1+characters seen, which I assume is because it is counting in the trailing NUL.
e.g. The following has buffer length 4:
echo 123 > /dev/my_chardev
strcnpy_from_user, however, might copy way over 4 Bytes.
According to the documentation, the last parameter is "The maximum numbers of bytes to copy". But this does not seem to be true.
I tried manually setting (temp->msg)[len-1] = 0, but this seems to cause problems (infinite loops and segfaults). What is the best way to safely copy a string from user to kernel space?
EDIT:
As Matteo mentioned in the comments, echo writes a \n by default, he also pointed out that a trailing NUL does indeed mean nothing to read/write syscalls. This is the solution that worked for me:
tmp = (struct msg_list *)kmalloc(sizeof(struct msg_list),GFP_ATOMIC);
tmp->msg = (char*)kmalloc(len+1,GFP_ATOMIC);
strncpy_from_user(tmp->msg,buff,len);
(tmp->msg)[len]=0;
1 Answer
As with the regular strncpy, the function you are using does not terminate the buffer if the string is as long as the maximum length you specified (or longer). If len counts the number of actual characters written to the device and you want to put them in a C string, you have to remember to add 1 in the allocation size and set the last byte to 0, otherwise you are going to have a non-terminated string around.
But please, if you are still struggling with C strings basics and NULL terminated vs counted strings stay away from kernel mode, if you want to play with virtual filesystems use FUSE.