Proc Filesystem Interaction
The /proc filesystem is a virtual filesystem in Linux that provides an interface to kernel data structures. While primarily used for process information (hence the name), it is also commonly used by kernel modules to provide simple interfaces without managing major/minor numbers or device nodes manually.
The Driver Code
This driver is functionally similar to the character device example but registers itself under /proc using proc_create.
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
MODULE_LICENSE("GPL");
static int device_open(struct inode *inode, struct file *filp) {
printk(KERN_ALERT "Device opened.");
return 0;
}
static int device_release(struct inode *inode, struct file *filp) {
printk(KERN_ALERT "Device closed.");
return 0;
}
static ssize_t device_read(struct file *filp, char *buffer, size_t length,
loff_t *offset) {
char *msg = "Hello kernel-pwn!\n";
// copy_to_user returns the number of bytes that *failed* to copy.
// We want to return the number of bytes successfully copied.
return strlen(msg) - copy_to_user(buffer, msg, strlen(msg));
}
static ssize_t device_write(struct file *filp, const char *buf, size_t len,
loff_t *off) {
printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
return -EINVAL;
}
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
struct proc_dir_entry *proc_entry = NULL;
int init_module(void) {
// Create /proc/kernel-pwn-char with rw-rw-rw- permissions
proc_entry = proc_create("kernel-pwn-char", 0666, NULL, &fops);
printk(KERN_ALERT "/proc/kernel-pwn-char created!");
return 0;
}
void cleanup_module(void) {
if (proc_entry)
proc_remove(proc_entry);
printk(KERN_ALERT "/proc/kernel-pwn-char removed!");
}Lifecycle Stages
Initialization (
init_module):- When the module is loaded,
init_moduleis executed. - It uses
proc_createto register a new entry in the/procfilesystem. - It assigns the
file_operationsstructure (fops) to this entry, linking thereadandwritesystem calls to the driver’s functions.
- When the module is loaded,
Operational Phase (Event Handling): These functions are callbacks invoked by the kernel when users interact with
/proc/kernel-pwn-char.- Open (
device_open): Triggered when the file is opened. - Read (
device_read): Triggered when reading from the file. It copies data to user space. - Write (
device_write): Triggered when writing to the file. Returns-EINVALin this example. - Release (
device_release): Triggered when the file is closed.
- Open (
Cleanup (
cleanup_module):- When the module is unloaded,
cleanup_moduleruns. - It calls
proc_removeto delete the entry from the/procfilesystem, ensuring no stale entries remain.
- When the module is unloaded,
Key Differences from Character Devices
- No Major/Minor Numbers: You don’t need
register_chrdev.proc_createhandles the VFS registration directly. - Automatic Node Creation: The file
/proc/kernel-pwn-charappears automatically when the module is loaded. You do not need to runmknod. - Permissions: The second argument to
proc_create(0666) sets the file permissions (read/write for everyone).
Interaction Example (Bash)
Since it’s just a file in /proc, you can interact with it using standard tools like cat and echo.
# 1. Load the module
insmod proc_mod.ko
dmesg | tail -n 1
# Output: /proc/kernel-pwn-char created!
# 2. Check if the file exists
ls -l /proc/kernel-pwn-char
# Output: -rw-rw-rw- 1 root root 0 Jan 1 00:00 /proc/kernel-pwn-char
# 3. Read from the file
cat /proc/kernel-pwn-char
# Output: Hello kernel-pwn!
# 4. Write to the file (fails as expected)
echo "test" > /proc/kernel-pwn-char
# Output: write error: Invalid argumentInteraction with C
The C interaction is identical to any other file interaction. You simply open the path /proc/kernel-pwn-char.
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define PROC_FILE "/proc/kernel-pwn-char"
int main() {
int fd;
char buffer[1024];
// 1. Open the proc file
printf("Opening %s...\n", PROC_FILE);
fd = open(PROC_FILE, O_RDWR);
if (fd < 0) {
perror("Failed to open proc file");
return 1;
}
printf("Opened successfully.\n");
// 2. Read from the file
printf("Reading...\n");
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read < 0) {
perror("Failed to read");
} else {
buffer[bytes_read] = '\0';
printf("Read %zd bytes: %s\n", bytes_read, buffer);
}
// 3. Write to the file
printf("Writing...\n");
const char *msg = "Testing write";
ssize_t bytes_written = write(fd, msg, strlen(msg));
if (bytes_written < 0) {
perror("Failed to write (expected)");
}
close(fd);
return 0;
}