You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在不可休眠的LSM BPF钩子中获取完整文件路径?

如何在不可休眠的LSM BPF钩子中获取完整文件路径?

我之前做eBPF文件审计工具时,正好踩过这个坑!在file_readfile_write这类不可休眠的LSM BPF钩子上下文里,直接调用bpf_d_path()肯定行不通——这个辅助函数可能会触发休眠操作,而不可休眠钩子的执行环境明确禁止休眠,强行调用要么报错要么被内核拦截。下面是我亲测有效的几个解决思路:

方案一:提前在可休眠钩子中缓存路径

这是我最常用的方案,核心思路是把路径的获取逻辑“挪”到允许休眠的钩子中完成

  • 利用file_open这个可休眠的LSM钩子,在这里安全调用bpf_d_path()拿到文件的绝对路径。
  • 把路径和对应的struct file指针(或者inode的唯一标识符,比如inode->i_ino加上挂载点ID)关联起来,存入一个BPF_LRU_HASH类型的map中(用LRU是为了自动清理不常用的条目,避免内存溢出)。
  • 等不可休眠钩子(比如file_write)触发时,直接通过当前的struct file指针去map里查询缓存好的路径即可。
  • 额外注意:要在file_close钩子(如果允许休眠)里清理对应缓存,或者依赖LRU map的过期机制,防止内存泄漏;另外硬链接的情况要特殊处理,同一个inode可能对应多个路径,缓存时可以考虑记录所有关联路径,或者只记录第一次打开的路径。

方案二:手动实现无休眠的路径拼接

如果不想依赖缓存,也可以尝试手动拼接路径,但这个方法实现起来更复杂:

  • struct file中拿到struct dentrystruct vfsmount,然后手动遍历dentry的父节点链,一步步拼接路径。
  • 全程要确保所有操作都不会触发休眠:不能调用任何可能休眠的内核辅助函数,遍历dentry时要检查是否到达根目录或者挂载点根,避免循环。
  • 这种方法可能需要提前缓存挂载点的全局路径(比如在mount相关的BPF钩子中缓存挂载点信息到map),才能把相对于挂载点的路径拼接成完整的系统绝对路径。另外要注意BPF程序的栈空间限制,路径拼接时尽量用BPF堆或者map来临时存储,避免栈溢出。

方案三:延迟到用户态处理

如果内核态的限制实在太多,也可以把“脏活”扔到用户态:

  • 在不可休眠钩子中,只收集必要的标识信息,比如struct file的指针、进程PID、FD编号等,然后通过BPF ring buffer或者perf buffer把这些信息发送到用户态程序。
  • 在用户态程序中,再通过这些信息获取完整路径:比如利用/proc/<pid>/fd/<fd>符号链接来读取文件的真实路径,或者调用用户态的路径解析函数来处理。
  • 这个方法的好处是完全避开内核态的休眠限制,但缺点是有一定的延迟,而且要处理文件已经被关闭的情况(此时/proc里的FD可能已经失效)。

内容来源于stack exchange

火山引擎 最新活动