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

使用kretprobe挂钩__x64_sys_execve获取argv时触发页错误致系统崩溃

kretprobe挂钩__x64_sys_execve时argv处理导致内核崩溃的修复方案

问题定位

你碰到的核心问题是:用kretprobe挂钩__x64_sys_execve的entry handler时,处理char** argv参数引发内核页错误(权限违规),卸载模块时进程被杀、系统崩溃;仅读取filename时模块运行完全正常。环境为VMware Ubuntu22.04,内核版本6.5.0-41-generic。

根本原因

char** argv是用户态的二级指针,直接操作会踩两个关键坑:

  • 需先读取用户态的指针数组本身,再逐个读取每个指针指向的字符串,不能直接用strncpy_from_user操作二级指针
  • 未验证用户态地址的合法性,导致内核访问了非法地址触发页错误;卸载时残留的非法地址引用进一步引发系统崩溃

修复思路与代码示例

1. 安全读取用户态二级指针的步骤

  • 先用copy_from_user读取argv数组中的单个字符串指针
  • 用access_ok验证指针的用户态合法性(新内核中strncpy_from_user内部会做校验,但显式验证更稳妥)
  • 设置参数个数上限(比如64个),避免无限循环或越界
  • 每一步操作都要检查返回值,失败立即终止处理

2. 修复后的代码片段

#include <linux/kprobes.h>
#include <linux/sched.h>
#include <linux/uaccess.h>

#define MAX_ARGC 64
#define MAX_STRLEN 256

static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    char __user *filename = (char __user *)regs->di;
    char __user __user **argv = (char __user __user **)regs->si;
    char filename_buf[MAX_STRLEN] = {0};
    char argv_buf[MAX_ARGC][MAX_STRLEN] = {0};
    int i = 0;

    // 读取filename(这部分你已验证正常)
    if (strncpy_from_user(filename_buf, filename, MAX_STRLEN-1) < 0) {
        pr_err("Failed to read execve filename\n");
        return 0;
    }
    pr_info("Execve called: %s\n", filename_buf);

    // 安全读取argv参数
    while (i < MAX_ARGC) {
        char __user *arg_ptr;
        // 先读取argv数组中的单个指针
        if (copy_from_user(&arg_ptr, &argv[i], sizeof(char __user *))) {
            pr_err("Failed to get argv[%d] pointer\n", i);
            break;
        }
        if (!arg_ptr) { // 遇到NULL,argv参数结束
            break;
        }
        // 验证地址是否为合法用户态地址
        if (!access_ok(arg_ptr, MAX_STRLEN)) {
            pr_err("argv[%d] points to invalid user address\n", i);
            break;
        }
        // 读取字符串内容
        if (strncpy_from_user(argv_buf[i], arg_ptr, MAX_STRLEN-1) < 0) {
            pr_err("Failed to read argv[%d] content\n", i);
            break;
        }
        pr_info("argv[%d]: %s\n", i, argv_buf[i]);
        i++;
    }

    return 0;
}

static struct kretprobe execve_kretprobe = {
    .kp.symbol_name = "__x64_sys_execve",
    .entry_handler = entry_handler,
};

static int __init execve_kretprobe_init(void)
{
    int ret = register_kretprobe(&execve_kretprobe);
    if (ret < 0) {
        pr_err("register_kretprobe failed: %d\n", ret);
        return ret;
    }
    pr_info("kretprobe registered for __x64_sys_execve\n");
    return 0;
}

static void __exit execve_kretprobe_exit(void)
{
    unregister_kretprobe(&execve_kretprobe);
    pr_info("kretprobe unregistered\n");
}

module_init(execve_kretprobe_init);
module_exit(execve_kretprobe_exit);
MODULE_LICENSE("GPL");

额外注意事项

  • 编译模块时必须确保内核头文件版本与运行内核(6.5.0-41-generic)完全匹配,否则可能出现隐性兼容性问题
  • 测试时先在简单场景验证(比如执行ls、echo等基础命令),避免在复杂进程(如桌面应用)上测试
  • 卸载模块前,尽量确保没有正在执行execve的进程,或等待所有kretprobe实例处理完成,避免残留引用引发崩溃
  • 内核页错误提示权限违规,本质是内核直接访问了未通过安全函数校验的用户态地址,必须严格使用copy_from_user、strncpy_from_user等内核提供的用户态访问函数

内容的提问来源于stack exchange,提问作者sena

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 11:23:09