内核模块sysfs写入失败:copy_from_user返回Bad address问题排查
问题描述
我正在编写一个用于模拟锁/解锁操作的测试内核模块,代码如下:
#define MODULE_NAME "lock_injection" #define COMMAND_BUF_SIZE 128 static DEFINE_MUTEX(my_mutex); static DEFINE_SPINLOCK(my_spinlock); static int irq_disabled = 0; static char command_buf[COMMAND_BUF_SIZE]; // Function to execute the commands static void execute_command(const char *command) { if (strcmp(command, "lock_mutex") == 0) { mutex_lock(&my_mutex); } else if (strcmp(command, "unlock_mutex") == 0) { mutex_unlock(&my_mutex); } else if (strcmp(command, "spinlock_irq") == 0) { spin_lock_irqsave(&my_spinlock, irq_disabled); } else if (strcmp(command, "spinlock") == 0) { spin_lock(&my_spinlock); } else if (strcmp(command, "spinunlock") == 0) { spin_unlock(&my_spinlock); } else if (strcmp(command, "spinunlock_irq") == 0) { spin_unlock_irqrestore(&my_spinlock, irq_disabled); } } // Sysfs attribute for command execution static ssize_t command_store(struct kobject *kobj, struct kobj_attribute *attr, const char __user *buf, size_t count) { if (count > COMMAND_BUF_SIZE) return -EINVAL; memset(command_buf, 0, COMMAND_BUF_SIZE); pr_info ("caling copy_from_user, count=%d\n",count); if (copy_from_user(command_buf, buf, count)) return -EFAULT; command_buf[count] = '\0'; pr_info("Command received : %s\n", command_buf); execute_command(command_buf); return count; } static struct kobj_attribute command_attr = __ATTR(command, 0664, NULL, command_store); static struct attribute *attrs[] = { &command_attr.attr, NULL, }; static struct attribute_group attr_group = { .attrs = attrs, }; static struct kobject *my_kobj; static int __init my_module_init(void) { int result; pr_info("Module loaded\n"); my_kobj = kobject_create_and_add(MODULE_NAME, kernel_kobj); if (!my_kobj) return -ENOMEM; pr_info("calling sysfs_create_group\n"); result = sysfs_create_group(my_kobj, &attr_group); if (result) kobject_put(my_kobj); return result; } static void __exit my_module_exit(void) { kobject_put(my_kobj); }
模块可成功加载,但向sysfs节点写入命令时失败,报错信息如下:
# insmod /locking.ko [ 30.260289] Module loaded [ 30.261227] calling sysfs_create_group # # cd /sys/kernel/lock_injection/ # echo lock_mutex > command [ 57.351275] caling copy_from_user, count=11 sh: write error: Bad address #
运行环境为QEMU aarch64平台上的原生6.5.0内核,启动参数为qemu-system-aarch64 -smp 4 -m 2G -machine virt -cpu cortex-a57 ...。
问题根源
中断状态变量类型不匹配
spin_lock_irqsave和spin_unlock_irqrestore的第二个参数要求是unsigned long类型,用于保存64位的中断状态(aarch64架构下unsigned long为8字节)。但代码中定义static int irq_disabled = 0;,int仅为4字节,调用这两个函数时会向int变量写入8字节数据,直接导致内存越界,破坏内核内存结构,最终触发Bad address错误。缓冲区溢出风险
command_buf[count] = '\0';一行中,当count等于COMMAND_BUF_SIZE时,会访问数组的越界位置(数组下标范围为0~COMMAND_BUF_SIZE-1),存在非法内存访问风险。
修复后的代码
#define MODULE_NAME "lock_injection" #define COMMAND_BUF_SIZE 128 static DEFINE_MUTEX(my_mutex); static DEFINE_SPINLOCK(my_spinlock); // 修正为unsigned long类型,匹配spinlock函数的参数要求 static unsigned long irq_disabled = 0; static char command_buf[COMMAND_BUF_SIZE]; // Function to execute the commands static void execute_command(const char *command) { if (strcmp(command, "lock_mutex") == 0) { mutex_lock(&my_mutex); } else if (strcmp(command, "unlock_mutex") == 0) { mutex_unlock(&my_mutex); } else if (strcmp(command, "spinlock_irq") == 0) { spin_lock_irqsave(&my_spinlock, irq_disabled); } else if (strcmp(command, "spinlock") == 0) { spin_lock(&my_spinlock); } else if (strcmp(command, "spinunlock") == 0) { spin_unlock(&my_spinlock); } else if (strcmp(command, "spinunlock_irq") == 0) { spin_unlock_irqrestore(&my_spinlock, irq_disabled); } } // Sysfs attribute for command execution static ssize_t command_store(struct kobject *kobj, struct kobj_attribute *attr, const char __user *buf, size_t count) { // 留一个字节给终止符,避免越界 if (count >= COMMAND_BUF_SIZE) return -EINVAL; memset(command_buf, 0, COMMAND_BUF_SIZE); pr_info("calling copy_from_user, count=%d\n", count); if (copy_from_user(command_buf, buf, count)) return -EFAULT; // 确保字符串终止,且不会越界 command_buf[count] = '\0'; pr_info("Command received : %s\n", command_buf); execute_command(command_buf); return count; } static struct kobj_attribute command_attr = __ATTR(command, 0664, NULL, command_store); static struct attribute *attrs[] = { &command_attr.attr, NULL, }; static struct attribute_group attr_group = { .attrs = attrs, }; static struct kobject *my_kobj; static int __init my_module_init(void) { int result; pr_info("Module loaded\n"); my_kobj = kobject_create_and_add(MODULE_NAME, kernel_kobj); if (!my_kobj) return -ENOMEM; pr_info("calling sysfs_create_group\n"); result = sysfs_create_group(my_kobj, &attr_group); if (result) kobject_put(my_kobj); return result; } static void __exit my_module_exit(void) { kobject_put(my_kobj); } module_init(my_module_init); module_exit(my_module_exit); MODULE_LICENSE("GPL");
额外说明
- 修复了
irq_disabled的类型,确保与spinlock函数的参数类型匹配,避免内存越界写入。 - 调整了
count的判断条件为count >= COMMAND_BUF_SIZE,确保留一个字节用于存储字符串终止符,消除缓冲区溢出风险。 - 补充了模块必需的
module_init、module_exit和MODULE_LICENSE声明,符合内核模块的规范要求。
内容的提问来源于stack exchange,提问作者InsaneCoder
相关产品推荐
相关产品推荐

