Linux Device Driver: Copying String from Kernel to UserSpace - c

I have a struct as so:
typedef struct
{
char* BUFFER;
int Size;
}DataTransfer;
In my IOCTL function I attempt to populate the struct and pass to userspace:
case CHAR_DRIVER_IOCQREAD:
printk(KERN_INFO "In CHAR_DRIVER_IOCQREAD");
dataTransfer.BUFFER = kmalloc(strlen_user("Hello") +1, GFP_KERNEL);
dataTransfer.Size = strlen_user("Hello") +1;
error_count = copy_to_user((DataTransfer*) arg, &dataTransfer, sizeof(dataTransfer) );
In userspace I attempt to receive the struct as so:
DataTransfer dataTransfer;
if(ioctl(fd, CHAR_DRIVER_IOCQREAD, &dataTransfer) < 0)
{
perror("ERROR in ioctl CHAR_DRIVER_IOCQREAD");
}
else
{
printf("Kernel returned size %d \n", dataTransfer.Size);
printf("Kernel returned string %s \n", dataTransfer.BUFFER);
}
What is the correct way of doing this?

A couple of problems here. First, you are copying the structure to user space but not the string it points to. The structure will point to the kernel memory you allocated which user space can not access. Second you are allocating the kernel memory but not actually putting anything there.
One way of doing this would be for user space to allocate memory for the IOCtl to write the string into and then pass a structure like your DataTransfer to the IOCtl describing the memory it allocated. The kernel would read the structure from user memory using copy_from_user and then, if the buffer allocated was large enough to hold the string, write it there using copy_to_user on the address passed to it inside the structure.
E.g. (kernel side, sketch only):
case CHAR_DRIVER_IOCQREAD:
DataTransfer dataTransfer;
if (copy_from_user(&dataTransfer, arg, sizeof(dataTransfer)))
return -EFAULT;
if (dataTransfer.Size < strlen(myString) + 1)
return -ENOMEM;
if (copy_to_user(dataTransfer.BUFFER, myString, strlen(myString) + 1))
return -EFAULT;

Related

How to pass a char *array (belonging to the user address space) to a tasklet or workqueue in a kernel module?

I’m writing a device driver. If someone calls the write operation I want it to be deferred (using tasklet or workqueue). The code should be something like that:
static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off) {
packed_work *the_task;
the_task = kzalloc(sizeof(packed_work), GFP_ATOMIC);
if (the_task == NULL) {
printk(KERN_ERR "%s: tasklet buffer allocation failure\n", MODNAME);
return -1;
}
the_task->buffer = the_task;
the_task->buff = buff;
the_task->len = len;
INIT_WORK(&(the_task->the_work), (void*)deferred_write);
schedule_work(&the_task->the_work);
return len;
}
void deferred_write(struct work_struct *data) {
printk(“the text: %s\n”, container_of(data, packed_work, the_work)->buff);
//copy_from_user(&(the_object->stream_content), container_of(data, packed_work, the_work)->buff, len);
kfree((void*)container_of(data,packed_work,the_work));
}
And the struct looks like this:
typedef struct _packed_work{
void *buffer;
const char *buff;
size_t len;
struct work_struct the_work;
} packed_work;
The problem is that the kernel crashes. It crashes even before the copy_from_user (that’s why I commented it). In the deferred_write() I can print the length of the string but not the string itself. Is it a problem because the buffer is in the user space memory?
I know that, as a workaround, I can copy the user buffer in the task struct (using the copy_from_user() in the function write()) and then use the strcpy() in the deferred_write() function. But I really would like to use the copy_from_user() in deferred_write(). Is it possible? What can I do?
Even if it is possible (and there is surely a way), the user process has probably changed the contents of the buffer in the time before deferred_write runs. Notice that user programs often allocate these buffers on the stack, so they get overwritten when the function that called write returns and calls other functions.
Even worse: the user process could have unmapped the buffer, or it could have exited.
So you should not delay reading the buffer. You should read the buffer inside the write call and not anywhere else.

Questions on scull_follow function in linux device drivers 3rd edition

I never found the definition for scull_follow in the book, so I'm trying to understand it based off a github repo(https://github.com/martinezjavier/ldd3).
Here is the code I am trying to understand:
struct scull_qset *scull_follow(struct scull_dev *dev, int n) {
struct scull_qset *qs = dev->data;
/* Allocate first qset explicitly if need be */
if (!qs) { // if NULL
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
return NULL; /* Never mind */
memset(qs, 0, sizeof(struct scull_qset));
}
/* Then follow the list */
while (n--) {
if (!qs->next) {
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs->next == NULL)
return NULL; /* Never mind */
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
continue;
}
return qs;
}
Here is struct scull_qset:
struct scull_qset {
void **data;
struct scull_qset *next;
};
Conceptually, I understand that all scull_follow does is that it follows the list up to the right position so that you know where to start reading/writing.
I'm mainly confused on this part of the code.
/* Allocate first qset explicitly if need be */
if (!qs) { // if NULL
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
return NULL; /* Never mind */
memset(qs, 0, sizeof(struct scull_qset));
}
Let's say the user opens this driver and attempts to read from it first without writing. that should mean that it should go into the if statement and allocate some memory. Then, why is there a second check for if qs is NULL or not? Won't it always be not NULL because it was allocated some memory?
After that what is the memset function for? I understand that it copies 0 into qs, but what is the point of that other than initializing this memory region? Is it so that when you call the copy_to_user function in your read function it'll know that because it is filled with 0s, nothing of 'value' has been written to it so you will just get a blank output when reading, assuming the first operation you do is reading?
Thanks for answering my questions.
This:
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
return NULL;
Is standard C programming good practice: whenever a function can fail, you always need to check the return value. This is true for any kind of function that can fail, not only malloc() and friends. In this case, kmalloc() can fail to allocate memory, returning NULL, so the code is checking for that error. If that happens, the function safely aborts execution by doing a return NULL;, and the caller will then handle that as needed.
This:
memset(qs, 0, sizeof(struct scull_qset));
Is standard kernel programming good practice: whenever you allocate uninitialized memory (like kmalloc() does), it could contain sensitive kernel data. You never want uninitialized data to reach userspace through a copy_to_user() or similar calls. In order to avoid this, you need to make sure to initialize it before making it available to userspace. Filling it with zeroes using memset() is one of the simplest way to do this.
In the case an user program does a read as the first syscall on the scull driver, it would just read a bunch of 0 bytes.

C: memcpy segfault with void* pointers

Code -- Main loop:
int handleClient (struct clientData* clientData)
{
void* Buffer = malloc (INET_BUFFER_SIZE); <-- VOID* BUFFER FOR RECV()
int listenSocket = clientData->listenSocket;
struct sockaddr_in clientAddress = clientData->clientAddress;
printf("Received connection from client %s:%d.\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
while (1)
{
int packetSize;
if ((packetSize = recv(listenSocket, &Buffer, INET_BUFFER_SIZE, 0)) > 0)
{
if (packetSize == ECHO_SIZE)
{
handleEchoPacket(Buffer);
continue;
}
if (packetSize == MESSAGE_SIZE) <---THIS IS TRIGGERED BECAUSE OF PACKET SIZE
{
handleMessagePacket(Buffer);
continue;
}
}
}
Code -- handleMessagePacket(void* Buffer):
void handleMessagePacket (void* Buffer)
{
void* localBuffer = (void*) malloc(INET_BUFFER_SIZE);
memcpy(localBuffer, Buffer, INET_BUFFER_SIZE); <--SEGFAULT
(...)
}
GDB -- Partial output:
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () (.....) <--Tells me it doesn't have source files, not relevant to the problem.
Basically, the problem happens when I copy one memory block from a void pointer to a local buffer. Both were malloc() heap memory of same type: void.
Any suggestions or comments are welcome!
Here is what is happening: when you pass the address of Buffer to recv, received data is placed in the space allocated to the address of your buffer, which is on the stack. When you subsequently call handleMessagePacket, the address that you pass is no longer valid: it has been written over by recv!
Removing the ampersand from the call of recv should fix this problem:
if ((packetSize = recv(listenSocket, Buffer, INET_BUFFER_SIZE, 0)) > 0)
// ^^ No ampersand
In general, situations like this are best diagnosed with a memory profiler, such as valgrind. The tool would promptly tell you that there is an invalid write into stack area for the Buffer write, and that the subsequent dereference of received data as a pointer (the issue that causes SIGSEGV now) is an invalid read.

copy_from_user gives null pointer

I'm attempting to write an Open RG kernel module that, at given intervals, sends a message up to the user space. To this end, I need the kernel to hold a pointer to a static buffer in the user space where this message will be stored. I'm having trouble sending the pointer to the kernel.
The user space function call is something like this (simplified, obviously):
typedef struct {
char msg[MAX_BOOT_MSG];
} msg_t;
static msg_t common_mem;
void user_space_func() {
openrg_module_ctrl(KOS_CDT_TEST, TEST_IOCTL_SET_COMMON_MEM, &common_mem.msg);
}
The kernel space usage is like this:
static void* msg_write;
static int do_ioctl(kos_chardev_t *context, unsigned int cmd,
unsigned long data) {
switch (cmd)
{
case TEST_IOCTL_SET_COMMON_MEM:
received_ioctl = 1;
int ret = copy_from_user(&msg_write, (void *)data, sizeof(char*));
printk("setting common mem to %p, received %d\n", msg_write, ret);
return 0;
}
default:
return -1;
}
The output is setting common mem to 0000000000000000, received 0. I see that common_mem.msg isn't NULL. Any idea what I'm doing wrong?
data is the address of the buffer, so by reading from that address, you are copying the contents of the buffer.
Please note that memory in user space can be moved or swapped out, so this address is valid only for the duration of the system call; you must not store the address for later usage.
Better allocate some memory in your driver, and allow the application to access it with mmap.

LightWeight IP: Buffer not freeing

I'm using an TCP/IP stack called lwip. I have implemented a function below to send data packets, inspired from a similar callback function that receives data packets.
Each time a packet is received, I create a buffer using the pbuf_alloc function. Then, I send the packet using udp_sendto. Finally, I free the buffer using pbuf_free. (See the code below.)
For some reason, pbuf_free is not freeing the buffer. (I get a buffer overflow after n packets, where n is the pool size.) The lwip wiki warns that:
The network driver may also not assume that the pbuf memory is
actually freed when it calls pbuf_free.
How can I force pbuf_free to free my buffer? How is the buffer overflow avoided?
(My implementation below.)
static err_t IAP_tftp_send_data_packet(struct udp_pcb *upcb, struct ip_addr *to, int to_port, int block)
{
err_t err;
struct pbuf *pkt_buf;
char packet[TFTP_DATA_PKT_LEN_MAX];
int bytesRead;
int bytesToSend;
/* Specify that we are sending data. */
IAP_tftp_set_opcode(packet, TFTP_DATA);
/* Specify the block number that we are sending. */
IAP_tftp_set_block(packet, block);
bytesRead = IAP_tftp_set_data(packet, block);
if(bytesRead != 0) {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - (512 - bytesRead + 1);
} else {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - 512;
}
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);
if (!pkt_buf)
{
print("(TFTP) Buffer overflow!\r\n");
}
/* Copy the file data onto pkt_buf. */
memcpy(pkt_buf->payload, packet, bytesToSend);
err = udp_sendto(upcb, pkt_buf, to, to_port);
/* free the buffer pbuf */
printf("%d\n\r", pbuf_free(pkt_buf));
return err;
}
What version of lwIP are you using?
Depending on different versions the answers vary a lot.
The memp_malloc() allocation function called inside the pbuf_alloc() has failed or the pbufs chaining has failed.So, it returns NULL.
pbuf_alloc() will also return NULL, if the passed arguments also contains NULL.(due to NULL arguments check).
In newer versions, could you show what value the MEMP_OVERFLOW_CHECK macro contains? The lwIP shows a diferent behavior when the macro value >= 2.
And another cause might be if you are using multi-threading, the locking mechanisms inside the pbuf_alloc() fail, might cause it to return NULL.
Some versions require that you call pbuf_init(), before calling pbuf_alloc().
You can try this:
pkt_buf = NULL;//Use NULL, just incase the NULL is not 0 as per your compiler.
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_REF);
if(pkt_buf == NULL)
{
printf("pbuf_alloc failed.\n");
}
else
{
/* Do something with the allocated pbufs and free it. */
}
PBUF_REF will allocate no buffer memory for pbuf. The pbuf should be used in a single thread only and if the pbuf gets queued, then pbuf_take should be called to copy the buffer.
You can also try PBUF_RAM which will allocate buffer in RAM.
For more informtaion, you can also browse the source files of the version of lwIP, that you are using.
The easiest solution seems to be to make the buffer static, i.e. re-use the same buffer for each call:
static struct pbuf *pkt_buf = NULL;
if( pkt_buf == NULL )
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);
if( pkt_buf == NULL )
{
print("(TFTP) Buffer overflow!\r\n");
}
If your scenario involves unloading/reloading the driver, it will leak memory. To fix that, make the buffer static outside the IAP_tftp_send_data_packet() function, and call pbuf_free() when the driver unloads (assuming lwip tells you).
Just a passing thought, possibly completely nonsensical. In this code:
if(bytesRead != 0) {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - (512 - bytesRead + 1);
} else {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - 512;
}
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);
...is it possible for bytesRead to assume the value 513 - TFTP_DATA_PKT_LEN_MAX ?
If it happened, wouldn't the request to allocate zero bytes fail? (this could be tested by printing the value of bytesToSend upon buffer overflow, and checking if it is nonzero).
struct pbuf does not represent a continuous region of memory. It is rather a chain of memory locations. Thus this will not work in general case:
memcpy(pkt_buf->payload, packet, bytesToSend);
You need to scatter-copy your data. The memcpy() from the code snippet may overflow the payload buffer and cause all kinds of side effects including inability to free the pbuf chain cleanly.

Resources