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

如何将argv传递给Linux自定义内核模块中定义的系统调用

内核态自定义系统调用接收双参数的最佳实践

核心思路

内核处理用户态参数的核心是只拿必要信息,拒绝不可控的复杂结构,直接传递指针数组(比如argv)风险太高——用户态可以篡改指针指向,导致内核访问非法内存。下面是几种安全且高效的传参方案:

推荐方案

1. 独立传递字符串指针+长度

这是最直接的方式,用户态把两个参数的指针和各自长度传给系统调用,内核按以下流程处理:

  • 先检查两个指针是否为NULL,任一为空直接返回-EINVAL
  • 限制参数长度(比如设最大值为4096,避免恶意传递超大值),超过返回-E2BIG
  • 用copy_from_user分别把两个字符串拷贝到内核栈(长度小的话)或堆内存
  • 手动给拷贝后的字符串加\0结尾(防止用户态未传递终止符)
  • 最后做业务相关的有效性校验(比如字符集、格式等)

伪代码示例:

// 用户态调用
long sys_mycmd(const char *arg1, size_t len1, const char *arg2, size_t len2);

// 内核态实现
SYSCALL_DEFINE4(mycmd, const char __user *, arg1, size_t, len1, const char __user *, arg2, size_t, len2) {
    #define MAX_ARG_LEN 4096
    char buf1[MAX_ARG_LEN];
    char buf2[MAX_ARG_LEN];

    // 检查参数是否存在
    if (!arg1 || !arg2)
        return -EINVAL;
    // 检查长度是否合法
    if (len1 >= MAX_ARG_LEN || len2 >= MAX_ARG_LEN)
        return -E2BIG;
    // 拷贝用户态数据
    if (copy_from_user(buf1, arg1, len1) || copy_from_user(buf2, arg2, len2))
        return -EFAULT;
    // 确保字符串终止
    buf1[len1] = '\0';
    buf2[len2] = '\0';

    // 这里做参数有效性校验,比如检查是否包含非法字符等
    // ...

    return 0;
}

2. 用结构体封装参数(适合未来扩展)

如果以后可能增加参数个数,可以定义一个用户态和内核态共用的结构体,把参数计数、指针、长度都装进去:

// 共用头文件(用户态和内核态都包含)
struct mycmd_args {
    int argc;          // 参数个数,这里必须是2
    const char *arg1;
    size_t len1;
    const char *arg2;
    size_t len2;
};

内核处理步骤:

  • 先把整个结构体从用户态拷贝到内核
  • 检查argc是否为2,不是直接返回-EINVAL
  • 再依次检查每个字符串的指针和长度,拷贝并校验

这种方案扩展性强,后续加参数只需要修改结构体,不用调整系统调用的参数列表。

避坑提醒

  • 别直接传argv数组:用户态的argv是指针数组,内核无法验证每个指针的合法性,很容易被恶意利用导致内核崩溃或漏洞。
  • 别用\0拼接字符串:不仅冗余,还容易因为用户传递的字符串本身包含\0导致解析错误,可靠性差。

额外安全细节

  • 内核拷贝数据时,短字符串用栈(效率高),长字符串用kmalloc分配,用完记得释放。
  • 对拷贝后的字符串做严格校验:比如禁止控制字符、限制允许的字符集、检查格式是否符合业务需求,避免注入类风险。
  • 用户态提前检查argc:用户程序在调用系统调用前先判断argc == 3(因为argv[0]是程序名),确保有两个有效参数再调用,减少内核的无效处理。

内容的提问来源于stack exchange,提问作者G. Ianni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:33:22