Linux内核模块struct nameidata/open_flags无法识别问题求助
内核模块kprobe挂载path_openat时struct nameidata/open_flags未定义问题
问题背景
开发Linux内核模块,为path_openat函数挂载kprobe以实现文件/目录的open、unlink、rename等操作监控。处理函数首个参数struct nameidata时,虽已引入<linux/namei.h>,仍触发编译错误:invalid use of undefined type struct nameidata,struct open_flags也存在同样问题。手动自定义结构体定义会损害模块的可移植性,需找到合规解决办法。
前置处理代码
static int openat_pre_handler(struct kretprobe_instance *p, struct pt_regs *the_regs){ // path openat pre handler struct nameidata * nd; struct open_flags * op_flags; struct inode * inode; struct filename * pathname_struct; const char *pathname; int flags; // 模块处于关闭或记录关闭状态时,不执行后置处理 if(monitor->state == 0 || monitor->state == 1) goto end; atomic_inc((atomic_t*)&open_audit_counter); // x86-64 系统调用传参约定:%rdi, %rsi, %rdx, %r10, %r8 和 %r9 /*path_openat(struct nameidata *nd, const struct open_flags *op, unsigned flags)*/ // nameidata是第一个参数,包含inode和文件名信息 nd = (struct nameidata *) the_regs->di; inode = nd->inode; pathname_struct = nd->name; pathname = pathname_struct->name; // open_flag是第二个参数 op_flags = (struct open_flags *) the_regs->si; flags = op_flags->open_flag; // 检查文件是否以只写或读写模式打开 if (flags & O_WRONLY || flags & O_RDWR){ // 检查文件是否受保护 if (inode_in_protected_paths(inode->i_ino)){ // 写入日志记录访问拦截 printk("%s: Access on %s blocked correctly \n", MODNAME,pathname); //print_flag(flags); // 需要执行kretprobe后置处理:拦截该访问 return 0; } } end: // 不执行后置处理,访问合法 return 1; }
头文件引入列表
#include <linux/kernel.h> #include <linux/module.h> #include <linux/kprobes.h> #include <linux/fs.h> #include <linux/printk.h> #include <linux/spinlock.h> #include <linux/file.h> #include <linux/version.h> #include <linux/path.h> #include <linux/slab.h> #include <linux/fdtable.h> #include <linux/fs_struct.h> #include <linux/namei.h> #include <linux/dcache.h>
解决办法
1. 补充缺失的头文件
struct open_flags的定义位于<linux/open.h>中,当前头文件列表未引入该文件,添加:
#include <linux/open.h>
2. 适配内核版本的结构体可见性
struct nameidata在5.x及以上的新内核中,定义被标记为内部可见,直接引用会触发未定义错误。可通过两种方式处理:
- 启用编译宏:添加内核编译宏
-DCONFIG_FS_POSIX_ACL或对应版本的特定宏,使结构体定义对模块可见; - 偏移量访问:通过内核调试工具
pahole分析目标内核版本中struct nameidata成员的内存偏移量,再通过指针计算间接访问成员。示例:
// 假设通过pahole获取到nd->inode的偏移量为0x10 #define NAMEIDATA_INODE_OFFSET 0x10 struct inode *inode = *(struct inode **)((char *)nd + NAMEIDATA_INODE_OFFSET);
3. 替换为稳定探测点
path_openat属于内核内部函数,接口稳定性差。可转而探测用户态sys_openat系统调用,或内核中更稳定的do_sys_openat函数,这类函数的参数定义在公共头文件中更易访问。
4. 开启内核调试配置
确保内核编译时开启CONFIG_DEBUG_INFO、CONFIG_PROC_FS选项,这些配置会让内核结构体的完整定义对模块可见。
内容的提问来源于stack exchange,提问作者Luca Esposito
相关产品推荐
相关产品推荐

