Linux内核模块调用strstr检测指定路径打开文件夹时系统崩溃求助
问题排查与解决方案
1 崩溃原因说明
首先明确:Linux内核原生提供strstr()函数(定义在<linux/string.h>),本身可以在内核模块中正常使用,你的崩溃是代码其他逻辑错误导致的,核心问题如下:
kmalloc参数顺序写反
内核kmalloc的函数原型为void *kmalloc(size_t size, gfp_t flags),你当前传入的kmalloc(GFP_KERNEL,100*sizeof(char))完全颠倒了参数顺序,实际是请求分配数值等于GFP_KERNEL(通常为0xCC0)大小的内存,分配标志用错误的100,大概率返回NULL或者非法地址,后续d_path写入buf时直接访问非法内存触发崩溃。- 访问
cwd前未做错误指针校验d_path执行失败时返回的是ERR_PTR类型的错误指针(比如路径长度超过buf长度时返回ERR_PTR(-ENAMETOOLONG)),这类指针的值不是NULL,你的if(cwd)判断完全无效,把错误指针传给strstr就会触发非法地址访问。 - 遍历fdtable时越界访问
你直接用while(fdt->fd[i] != NULL)遍历fd数组,没有判断i < fdt->max_fds,很容易超出fd数组的实际长度访问野指针,触发崩溃。 - 遍历内核动态结构未加锁
for_each_process遍历进程列表、访问进程的files结构体和fdtable时,必须持有对应的锁(比如RCU读锁),否则遍历过程中数据结构被其他CPU修改/释放,就会触发野指针访问。 - 没有检查
buf分配结果
你没有判断kmalloc的返回值是否为NULL,分配失败时后续写入直接崩溃。
2 修复后的实现方案
2.1 基础修复代码
#include <linux/module.h> #include <linux/sched.h> #include <linux/fdtable.h> #include <linux/dcache.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/rcupdate.h> static int __init path_check_init(void) { struct task_struct *task_list; struct fdtable *fdt = NULL; struct path files_path; char *cwd; const char *myPath = "/home/anh/src/"; size_t myPath_len = strlen(myPath); char *buf; char *ret; // 修正kmalloc参数顺序,分配PAGE_SIZE大小的buf避免路径过长 buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) { printk(KERN_ERR "buf alloc failed\n"); return -ENOMEM; } // 遍历进程前加RCU读锁 rcu_read_lock(); for_each_process(task_list) { struct files_struct *files = get_files_struct(task_list); if (!files) continue; spin_lock(&files->file_lock); fdt = files_fdtable(files); // 遍历fd带上限判断避免越界 for (unsigned int i = 0; i < fdt->max_fds; i++) { if (!fdt->fd[i]) continue; files_path = fdt->fd[i]->f_path; cwd = d_path(&files_path, buf, PAGE_SIZE); // 校验cwd是合法指针再操作 if (!IS_ERR(cwd)) { // 你的需求是路径前缀匹配,用strncmp比strstr更高效,也不会匹配到路径中间的字段 if (strncmp(cwd, myPath, myPath_len) == 0) { printk(KERN_INFO "Open file with fd %d path: %s", i, cwd); } // 如果一定要用strstr也可以,确认引入<linux/string.h>即可 // ret = strstr(cwd, myPath); // if (ret) { // printk(KERN_INFO "Open file with fd %d path: %s", i, cwd); // } } } spin_unlock(&files->file_lock); put_files_struct(files); } rcu_read_unlock(); kfree(buf); return 0; } static void __exit path_check_exit(void) { printk(KERN_INFO "module exit\n"); } module_init(path_check_init); module_exit(path_check_exit); MODULE_LICENSE("GPL");
2.2 字符串包含校验的替代方案
如果确实需要判断字符串包含关系,除了直接用内核原生的strstr(),也可以根据场景选择更适配的函数:
- 前缀匹配:用
strncmp(),如上面示例代码,性能更高,不会出现误匹配 - 后缀匹配:用
strends()(部分内核版本支持)或者自行实现后向比较逻辑 - 任意位置匹配:直接用内核原生
strstr(),确保引入<linux/string.h>头文件即可
内容的提问来源于stack exchange,提问作者Anh Hoang
相关产品推荐
相关产品推荐

