Welcome! Log In Create A New Profile

Advanced

Why is fread() reading more than the supplied argument by making a read() system call?

Posted by tsn_2434 
I'm learning to write Linux Kernel Modules. I have written a character device which reads a string from user program and sends back that string to the user program when requested.

I the user program I'm using fread() and fwrite() functions to do this. The fwrite() function is working fine, but when I try to use fread() to read a string, for e.g. 10 bytes long string, I can see when I run the user program using strace, it is making a read() system call and trying to read 4096 bytes from the charater device. At the end, I displayed only 10 bytes on stdout. I think the fread() is reading more memory than the supplied input but is displaying the result correctly. I read about a concept of read ahead that fread() uses, but I don't understand it completely. This I believe is a security risk as well, because the buffer in the character device module is of 256 bytes and when I use fread() to read 10 bytes, it makes a read() syscall to read 4096 bytes and it successfully executes. Even though, I'm displayed only 10 bytes on my screen, the user program might have copied 4096 bytes of data from kernel space memory.

Another question is that, when I try to use read() or fgets() functions in my user program, the character device faults and crashes. When I run my user program using strace, I see that in the read() system call, it contains wierd values for the file descriptor and address of the buffer.

Additionally, in the user program, when I use fwrite() to write string for e.g of 11 bytes, it gets written successfully. But when I try to read the same string, supply a different value for the length of the string, say 13 bytes, the user program makes 2 read() system calls.

Here is code for the linux kernel module:

#include <linux/init.h>           
    #include <linux/module.h>         
    #include <linux/device.h>         
    #include <linux/kernel.h>         
    #include <linux/fs.h>    
    #include <linux/types.h>
    #include <linux/errno.h>     
    #include <linux/slab.h>     
    #include <linux/uaccess.h> 
    
    MODULE_LICENSE("GPL"winking smiley;            
    MODULE_AUTHOR("TSN_2434"winking smiley;    
    MODULE_DESCRIPTION("A simple Linux char driver Read/Write"winking smiley;  
    MODULE_VERSION("0.1"winking smiley;    
    
    static int device_open(struct inode *, struct file *);
    static int device_release(struct inode *, struct file *);
    static ssize_t device_read(struct file *, char *, size_t, loff_t *);
    static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
    
    #define SUCCESS 0
    #define DEVICE_NAME "chardev"
    #define CLASS_NAME "chardev_class"
    #define BUF_LEN 256
    
    static int Major;
    static int device_open_counter = 0;
    //static char msg[BUF_LEN] = {0};
    static short message_size;
    //static char *msg_Ptr;
    static struct class* chardevClass = NULL;
    static struct device* chardevDevice = NULL;
    
    char *tx_buffer;
    char *rx_buffer;
    
    int actual_rx_size = 0;
    
    static struct file_operations fops = {
    	.read = device_read,
    	.write = device_write,
    	.open = device_open,
    	.release = device_release
    };
    
    static int __init chardev_init(void){
    	
    	printk(KERN_INFO "\n**********chardev: Initializing the chardev LKM**********\n"winking smiley;
    	
    	Major = register_chrdev(0, DEVICE_NAME, &fops);
    	
    	if(Major<0){
    		printk(KERN_INFO "chardev: Registering char device failed with Major Number: %d.\n", Major);
    		return Major;
    	}
    	
    	printk(KERN_INFO "chardev: Registering char device successfule with Major Number %d.\n", Major);	
    	
    	chardevClass = class_create(THIS_MODULE, CLASS_NAME);
    	
    	if(IS_ERR(chardevClass)){
    		unregister_chrdev(Major, DEVICE_NAME);
    		printk(KERN_INFO "chardev: Failed to register device class\n"winking smiley;
    		return PTR_ERR(chardevClass);
    	}
    	
    	printk(KERN_INFO "chardev: Device class registered SUCCESSFUL.\n"winking smiley;
    	
    	chardevDevice = device_create(chardevClass, NULL, MKDEV(Major, 0), NULL, DEVICE_NAME);
    	if(IS_ERR(chardevDevice)){
    		class_destroy(chardevClass);
    		unregister_chrdev(Major, DEVICE_NAME);
    		printk(KERN_INFO "chardev: Failed to create the device.\n"winking smiley;
    		return PTR_ERR(chardevDevice);
    	}
    	
    	printk(KERN_INFO "chardev: Device class creation SUCCESSFUL.\n"winking smiley;
    	
    	tx_buffer = kmalloc(BUF_LEN, GFP_KERNEL);
    	rx_buffer = kmalloc(BUF_LEN, GFP_KERNEL);
    	
    	if (!tx_buffer && !rx_buffer){
    		printk(KERN_INFO "chardev: Memory allocation for tx and rx FAILED!\n"winking smiley;
    		return -ENOMEM;
    	}
    	
    	printk(KERN_INFO "chardev: Memory allocation for tx and rx SUCCESSFUL!\n"winking smiley;
    	
    	memset(tx_buffer, 0, BUF_LEN);
    	memset(rx_buffer, 0, BUF_LEN);
    	
    	return SUCCESS;
    }
    	
    static void __exit chardev_exit(void){
    	
    	device_destroy(chardevClass, MKDEV(Major, 0));
    	class_unregister(chardevClass);
    	class_destroy(chardevClass);
    	unregister_chrdev(Major, DEVICE_NAME);
    	
    	if(tx_buffer)
    		kfree(tx_buffer);
    	if(rx_buffer)
    		kfree(rx_buffer);
    	
    	printk(KERN_INFO "----------chardev: Goodbye from the LKM!----------\n\n"winking smiley;
    }
    
    static int device_open(struct inode *inodep, struct file *filep){
    	
    	device_open_counter++;
    	
    	printk(KERN_INFO "chardev: Device has been opened %d time(s).\n", device_open_counter);
    	
    	return SUCCESS;
    }
    
    static int device_release(struct inode *inodep, struct file *filep){
    	
    	printk(KERN_INFO "chardev: Device successfully closed\n"winking smiley;
        
        return SUCCESS;
    }
    
    static ssize_t device_read(struct file *filep, char *buffer, size_t length, loff_t *offset){
    
    	ssize_t bytes;
    	
    	if(actual_rx_size < length){
    		bytes = actual_rx_size;
    	}
    	else{
    		bytes = length;
    	}
    	
    	printk(KERN_INFO "chardev: User requesting (%d) bytes. tx_buffer has (%d) bytes\n", length, actual_rx_size);
    	
    	if(bytes > BUF_LEN){
    		printk(KERN_INFO "chardev: User trying to read more than the allocted memory.\n"winking smiley;
    		return -EFAULT;
    	}
    	
    	int retval = copy_to_user(buffer, rx_buffer, bytes);
    	
    	if(retval){
    		printk(KERN_INFO "chardev: copy_to_user FAILED!\n"winking smiley;
    		return -EFAULT;
    	}
    	else{
    		printk(KERN_INFO "chardev: copy_to_user SUCCESSFUL.\n"winking smiley;
    		actual_rx_size -= bytes;
    		return bytes; 
    	}
    }
    
    static ssize_t device_write(struct file *filep, const char *buffer, size_t length, loff_t *offset){
    	
    	memset(tx_buffer, 0, BUF_LEN);
    	memset(rx_buffer, 0, BUF_LEN);
    	
    	printk(KERN_INFO "chardev: Message received from User Space of Length: %lu bytes\n", length);
    	if(length > BUF_LEN){
    		printk(KERN_INFO "chardev: User attempting to write more than the allocated memory\n"winking smiley;
    		return -EFAULT;
    		}
    	int retval = copy_from_user(tx_buffer, buffer, length);
    	
    	printk(KERN_INFO "chardev: Message received from User Space: [%s]\n", tx_buffer);
    	
    	memcpy(rx_buffer, tx_buffer, length);
    	
    	printk(KERN_INFO "chardev: memcpy(tx_buffer, rx_buffer, length) SUCCESSFUL. rx_buffer contains: [%s]\n", rx_buffer);
    	
    	actual_rx_size = length;
    	
    	return length;
    
    }
    
    module_init(chardev_init);
    module_exit(chardev_exit);



Here is the code for my user program:

#include <unistd.h>     
    #include <fcntl.h>      
    #include <stdio.h>
    #include <stdlib.h>     
    #include <string.h>
    
    int main(int args, char *argv[])
    {
    int BUFFER_SIZE = 256;
    
    char internal_buf[BUFFER_SIZE];
    int to_read = 0;
    
    memset(internal_buf,0,BUFFER_SIZE);
    
    if (args < 3) {
        printf("2 Input arguments needed\nTo read 10 bytes: \"%s read 10\" \
        \nTo write string \"hello\": \"%s write hello\"\nExiting..\n", argv[0], argv[0]);
        return 1;
    }
    
    
    
    if (strcmp(argv[1],"write"winking smiley == 0) {
    
        printf("\n\ninput lenght:%d\n", strlen(argv[2]));
    
        if(strlen(argv[2]) >= BUFFER_SIZE) {
            printf("too long input string, max buffer [%d]\nExiting..\n", BUFFER_SIZE);
            return 2;
        }
    
        printf("\n\nBegin write operation\n\n"winking smiley;
        memcpy(internal_buf,argv[2], strlen(argv[2]));
    
        printf("Writing [%s]\n\n", internal_buf);
    
        FILE * filepointer;
        filepointer = fopen("/dev/chardev", "w+"winking smiley;
        fwrite(internal_buf, sizeof(char) , strlen(argv[2]), filepointer);
        fclose(filepointer);
    
    } else if (strcmp(argv[1],"read"winking smiley == 0) {
        printf("\n\nBegin read operation\n\n"winking smiley;
    
        to_read = atoi(argv[2]);
    	//printf("\nto_read:%d\n", to_read);
        FILE * filepointer;
        filepointer = fopen("/dev/chardev", "r"winking smiley;
        int retval = fread(internal_buf, sizeof(char), to_read, filepointer);
        fclose(filepointer);
    
        printf("Read %d bytes from driver. Driver replied: [%s]\n\n", retval, internal_buf);
    } else {
        printf("first argument has to be 'read' or 'write'\nExiting..\n"winking smiley;
        return 1;
    }
    
    
    return 0;
    }



Here are the screenshots:

[Running user program][1]


[Output of kern.log][2]


[Running user program using strace][3]


[1]: [i.stack.imgur.com]
[2]: [i.stack.imgur.com]
[3]: [i.stack.imgur.com]
Author:

Your Email:


Subject:


Spam prevention:
Please, solve the mathematical question and enter the answer in the input field below. This is for blocking bots that try to post this form automatically.
Question: how much is 17 plus 19?
Message: