'Linux kernel v5.15 - how to read and write from /proc file by python
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):

Solution 1:[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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Marco Bonelli |
