You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

内核模块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 ...。


问题根源
  1. 中断状态变量类型不匹配
    spin_lock_irqsave和spin_unlock_irqrestore的第二个参数要求是unsigned long类型,用于保存64位的中断状态(aarch64架构下unsigned long为8字节)。但代码中定义static int irq_disabled = 0;,int仅为4字节,调用这两个函数时会向int变量写入8字节数据,直接导致内存越界,破坏内核内存结构,最终触发Bad address错误。

  2. 缓冲区溢出风险
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 06:05:08