The kernel module does the below tasks:
obj-m += simplechar.o
EXTRA_CFLAGS += -g
all:
make -C /root/kernel/linux-5.15.186 M=$(PWD) modules
clean:
make -C /root/kernel/linux-5.15.186 M=$(PWD) clean$ make$ mkdir /mnt/rootfs
$ mount /root/kernel/buildroot-2025.05/output/images/rootfs.ext2 /mnt/rootfs/
$ cp simplechar.ko /mnt/rootfs/opt/$ qemu-system-x86_64 -boot order=c -m 2048M \
-kernel /root/kernel/linux-5.15.186/arch/x86/boot/bzImage \
-hda /root/kernel/buildroot-2025.05/output/images/rootfs.ext2 \
-append "root=/dev/sda rw console=ttyS0,115200 nokaslr" \
-nographic$ insmod /opt/simplechar.ko
[ 28.060330] simplechar: loading out-of-tree module taints kernel.
[ 28.075624] simplechar: registered major 248$ mknod /dev/simplechar c 248 0
$ chmod 666 /dev/simplechar#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "simplechar"
#define MESSAGE "Hello from kernel module!\n"
static int major;
static int msg_len = sizeof(MESSAGE) - 1;
static int dev_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "simplechar: opened\n");
return 0;
}
static ssize_t dev_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
if (len < msg_len)
return -EINVAL; // user buffer too small
if (copy_to_user(buf, MESSAGE, msg_len))
return -EFAULT;
return msg_len;
}
static int dev_release(struct inode *inode, struct file *file) {
printk(KERN_INFO "simplechar: closed\n");
return 0;
}
static struct file_operations fops = {
.open = dev_open,
.read = dev_read,
.release = dev_release,
};
static int __init simplechar_init(void) {
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0)
return major;
printk(KERN_INFO "simplechar: registered major %d\n", major);
return 0;
}
static void __exit simplechar_exit(void) {
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_INFO "simplechar: unregistered\n");
}
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple char device module");
module_init(simplechar_init);
module_exit(simplechar_exit);$ gcc -static -o demo demo.c$ cp demo /mnt/rootfs/opt/$ qemu-system-x86_64 -boot order=c -m 2048M \
-kernel /root/kernel/linux-5.15.186/arch/x86/boot/bzImage \
-hda /root/kernel/buildroot-2025.05/output/images/rootfs.ext2 \
-append "root=/dev/sda rw console=ttyS0,115200 nokaslr" \
-nographic$ insmod /opt/simplechar.ko
$ mknod /dev/simplechar c 248 0
$ chmod 666 /dev/simplechar$ /opt/demo
[ 232.892346] simplechar: opened
Read from kernel module: Hello from kernel module!
[ 232.895639] simplechar: closedThe user-space program does the below tasks:
File: demo.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define DEVICE "/dev/simplechar"
int main() {
int fd = open(DEVICE, O_RDONLY);
if (fd < 0) {
perror("Failed to open device");
return 1;
}
char buf[128];
ssize_t ret = read(fd, buf, sizeof(buf) - 1);
if (ret < 0) {
perror("Failed to read");
close(fd);
return 1;
}
buf[ret] = '\0';
printf("Read from kernel module: %s", buf);
close(fd);
return 0;
} $ qemu-system-x86_64 -boot order=c -m 2048M \
-kernel /root/kernel/linux-5.15.186/arch/x86/boot/bzImage \
-hda /root/kernel/buildroot-2025.05/output/images/rootfs.ext2 \
-append "root=/dev/sda rw console=ttyS0,115200 nokaslr" \
-nographic \
-s -S$ gdb /root/kernel/linux-5.15.186/vmlinux
(gdb) target remote :1234(gdb) lx-symbols
loading vmlinux
scanning for modules in /root/kernel/module
loading @0xffffffffc0000000: /root/kernel/module/simplechar.ko(gdb) break dev_read
Breakpoint 1 at 0xffffffffc0000000: file /root/kernel/module/simplechar.c, line 17.
(gdb) continue$ insmod /opt/simplechar.ko
[ 111.235440] simplechar: loading out-of-tree module taints kernel.
[ 111.250356] simplechar: registered major 248
$ mknod /dev/simplechar c 248 0
$ chmod 666 /dev/simplechar$ /opt/demoBreakpoint 1, dev_read (file=0xffff8880049d5800, buf=0x7ffeb62dcda0 "\340WL", len=127,
offset=0xffffc900001d7f08) at /root/kernel/module/simplechar.c:17
17 if (len < msg_len)