0

I created a kernel module and I want to communicate using a /proc file between the module and a script in python. I am using the Ubuntu 22.04 kernel version v5.15. I tried to create the /proc file in my module below:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>         // kmalloc()
#include <linux/uaccess.h>      // copy_to/from_user()
#include <linux/proc_fs.h>

static struct proc_dir_entry *parent;
char etx_array[] = "hello how are you?";
int len = sizeof(etx_array) / sizeof(char);
static int open_proc(struct inode *inode, struct file *file)
{
  pr_info("proc file opend.....\t");
  return 0;
}

/*
 * This function will be called when we close the procfs file
 */
static int release_proc(struct inode *inode, struct file *file)
{
    pr_info("proc file released.....\n");
    return 0;
}
/*
 * This function will be called when we read the procfs file
 */
static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length, loff_t * offset)
{
    pr_info("proc file read.....\n");

    if (copy_to_user(buffer, etx_array, len))
        pr_err("Data Send : Err!\n");
 
    return length;;
}
/*
 * This function will be called when we write the procfs file
 */
static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t * off)
{
     pr_info("proc file wrote.....\n");

     if (copy_from_user(etx_array, buff, len))
        pr_err("Data Write : Err!\n");

     return len;
  } 

static struct proc_ops proc_fops = {
        .proc_open = open_proc,
        .proc_read = read_proc,
        .proc_write = write_proc,
        .proc_release = release_proc,
};

static int etx_driver_init(void)
{
   proc_create("etx_proc", 0666, parent, &proc_fops);
   return 0;
}

static void etx_driver_exit(void)
{
   proc_remove(parent);
}

module_init(etx_driver_init);
module_exit(etx_driver_exit);
 
MODULE_LICENSE("GPL");

And if I try to use /proc file by python3 in user space like this:

import os 

pf = open("/proc/etx_proc","r")
print(pf.read())

So I get this (running the python program): screenshot

0andriy
  • 4,183
  • 1
  • 24
  • 37

1 Answers1

1

The problem here is that pf.read() in Python reads the entire file. How does the kernel know when the content of your proc file is over? When you return 0 from your .proc_read function. Since you are always copying etx_array out and you are always returning length, this means that to any application that tries to read the file (and also to the kernel itself) it will look like the file has infinite length. Python will keep reading and reading until it eventually runs out of memory and gets killed by the kernel OOM killer (or until your system freezes).

Always returning the requested size (length) is wrong as it does not match the actual length of the data copied to userspace. You have to check the requested size, copy no more than that and also no more than your buffer size (len), then return a meaningful value. You can keep track of how much you read with the loff_t * pointer that gets passed to your functions.

The same reasoning goes for your .proc_write function: it doesn't make sense to always return len. Check the requested size, cap it as needed, then perform the operation and return a meaningful value.

Copying data to/from userspace is a lot trickier than you might think, and there are a lot of pitfalls, you need to check everything that could possibly go wrong, specially when dealing with sizes. Here's an example of how this would work:

// Side note: this is the correct "len" you want.
size_t len = sizeof(etx_array) / sizeof(char) - 1;

// ...

static ssize_t read_proc(struct file *filp, char __user *buf, size_t size, loff_t *off)
{
    loff_t offset = *off;
    size_t remaining;

    pr_info("proc file read\n");
    
    if (offset < 0)
        return -EINVAL;

    if (offset >= len || size == 0)
        return 0;

    if (size > len - offset)
        size = len - offset;

    remaining = copy_to_user(buf, etx_array + offset, size);
    if (remaining == size) {
        pr_err("copy_to_user failed\n");
        return -EFAULT;
    }

    size -= remaining;
    *off = offset + size;
    return size;
}

static ssize_t write_proc(struct file *filp, const char *buf, size_t size, loff_t *off)
{
    loff_t offset = *off;
    size_t remaining;
    
    pr_info("proc file write\n");

    if (offset < 0)
        return -EINVAL;

    if (offset >= len || size == 0)
        return 0;

    if (size > len - offset)
        size = len - offset;

    remaining = copy_from_user(etx_array + offset, buf, size);
    if (remaining == size) {
        pr_err("copy_from_user failed\n");
        return -EFAULT;
    }

    size -= remaining;
    *off = offset + size;
    return size;
}

Check out this other answer of mine talking about simple_read_from_buffer() / simple_write_to_buffer(), which are simpler kernel interfaces that accomplish exactly the same as the above code for you automatically and (most importantly) correctly.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • Thank you for you'r answer but i get the same result – Jonatan Haish May 10 '22 at 10:47
  • @JonatanHaish works perfectly fine for me using the code in my answer: [screenshot](https://i.stack.imgur.com/SlPAz.png). Not sure what you are doing wrong, but if this doesn't work there's some other problem somewhere else. Make sure you re-built the module correctly, removed the old one and inserted the new one. – Marco Bonelli May 10 '22 at 17:35
  • I think I have a problem with the opening of the proc file. I tried to upload the module and then just open a file using: "pf = open("/proc/etx_proc","r")" by python and i get the same result. And i dont know why – Jonatan Haish May 11 '22 at 07:42
  • @JonatanHaish whelp, can't really solve a problem that I can't see... sorry. – Marco Bonelli May 11 '22 at 16:46