验证 /proc

Lobsters Hottest 工具

摘要

作者提出了一种验证 /proc 是否为真正的 proc 文件系统的方法,解决了 inotify 在检测挂载点方面的局限性,从而实现可靠的文件系统监控。

<p><a href="https://lobste.rs/s/qyr73e/verifying_proc">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/06/08 03:16

# 验证 /proc | arya dradjica 来源:https://bal-e.org/blog/2026/verifying-proc/ 我认为很明显我是个完美主义者。有时这种性格会把我带入 Linux 系统 API 的领域——我曾经写过 300 行 C 程序,用来替代状态栏里常见的“每 60 秒运行一次 `date`”循环。我的目标是尽可能使用所有能找到的酷炫系统调用,做到最高效,并且在**精确**时间更新日期。这非常有趣!在尝试构建文件系统监视器时,我同样钻进了类似的兔子洞,结果酷到值得单独写一篇博文。 我想构建一个“完美”的文件系统监视器。我定义的“完美”是:可以围绕监视器构建一个文件系统缓存,使得打开并读取某个 `path` 与从缓存中加载该 `path` 完全等价。因此,监视器必须捕获所有可能影响 `path` 的变化——创建与删除、重命名、符号链接更改等等。如果你之前研究过文件系统监视,你可能知道 `inotify(7)`,它看起来像是完成这项任务的“完美”工具。 不幸的是,`inotify` 缺少一样东西:关于挂载的通知。如果你用 `inotify` 监视路径 `/foo/bar`,那么在 `/foo` 处挂载新的文件系统将无法被检测到。那么如何检测文件系统挂载呢?经过一些研究,我了解到 `/proc/self/mounts` 列出了当前进程可见的挂载信息,并且它是可轮询的。这看起来完美!但是……我是否应该假设 `/proc` 总是正常工作的?如果它没有被挂载怎么办? `/proc` 是 Linux 系统 API 的重要组成部分——它超越了系统调用,提供了更多信息和控制能力。例如,`*at()` 系列系统调用允许你使用文件描述符遍历文件系统,这在应对竞争条件时更可靠,且可能涉及安全性。`linkat(2)` 的手册页建议使用 `/proc/self/fd/$fd` 将匿名文件(参见 `openat(2)` 配合 `O_TMPFILE`)链接到文件系统中(`AT_EMPTY_PATH` 也存在但需要某些权限)。显然,`glibc` 有时会通过读取 `/proc` 来填充系统调用。我们应该有一种清晰可靠的方式来访问 `/proc`……但据我所知,每个人都只是假设它存在。我们能做得更好吗? 我花了几个小时,但我想出了一个简单且安全的机制来验证 `/proc`: 1. 为 `/` 设置一个 `inotify` 监视。这让我们能够检测到对 `/proc` 的修改(排除挂载操作)。根据变化的性质,我们可以重新启动验证(最多 N 次)或放弃。 2. 打开 `/proc`,并使用生成的文件描述符进行所有后续操作。如果之后 `/proc` 被重新挂载,该文件描述符将继续引用挂载前的内容。我们将验证挂载前的内容是否指向一个真正的 `proc` 文件系统,并利用它来检查此类挂载。 3. 使用 `statfs(2)` 并验证 `f_type` 是否为 `PROC_SUPER_MAGIC`(`0x9fa0`)。至此,我们知道 `/proc` 是一个 `proc` 文件系统,但这还不够!一个真正的 `proc` 文件系统的绑定挂载可能导致 `/proc` 的根目录位于某个子目录(即 `/proc` 指向 `/real_proc/foo/bar`)。 4. 检查 `/proc` 的 inode 编号(例如使用 `statx(2)`)。`/proc` 根目录的 inode 编号应为 1;我认为这不会改变。我花了很长时间在这一步;起初我考虑通过创建一个临时文件描述符并在 `/proc/self/fd` 中查找来验证它。然后我看了 `/proc/self` 的 inode 编号(它相对可靠;至少在最近的 kernel 版本中,它是 `PROC_DYNAMIC_START`,即 `0xF0000000`,因为它是第一个动态分配的 inode 编号)。然后我意识到 `/proc` 本身也会有一个可验证的 inode 编号。我喜欢我最终的解! 5. 打开 `/proc/$PID`,验证它没有被挂载,然后使用生成的文件描述符进行所有后续操作。 6. 为 `/proc/$PID/mounts` 设置一个 `poll(2)` 监视。理想情况下,我们会检查 `/mountinfo`,但手册页只记录了前者支持 `poll(2)`。如果检测到变化,重新启动下一步。 7. 打开 `/proc/$PID/mountinfo`,并利用它了解系统上所有挂载点。检查 `/proc`**仍然**被正确挂载。得益于 `poll(2)` 监视,这是一个完美的实时视图! 我对此非常满意!我认为这个验证足够简单,任何关心底层细节的 Linux 应用程序都应该使用它。我很兴奋能编写自己的文件系统监视器,但如果标准 `inotify` [crate](https://crates.io/crates/inotify) 能支持挂载通知就更好了。我可能会考虑用这种机制在那里贡献挂载通知功能。 在我研究这个的时候,我在 [Libera.Chat](https://libera.chat/) 的 `#linux` 频道上与人们交流。有人问我我的使用场景和威胁模型是什么。我的答案很简单:我只是喜欢有可靠的方式来做事。因为“可能没问题”就把潜在问题挥之不去并不合我意。我喜欢花时间彻底理解我遇到的每一个问题;这一路我学到了很多,最重要的是,我乐在其中。 下次再见,直到下一个愚蠢的兔子洞 :)

相似文章

FreeBSD 中的原生 inotify

Lobsters Hottest

本文介绍了在 FreeBSD 中实现原生 inotify 支持的过程,通过将 inotify 移入内核,解决了现有用户空间库在处理文件监控时的问题,从而提升了性能和正确性。

内联函数的调试信息

Hacker News Top

Alan Maguire 在 Linux 存储、文件系统、内存管理和 BPF 峰会上主持了一场会议,提议对 BTF 进行扩展,以存储有关内联函数的信息,从而通过 kprobes 实现对这类函数的内核跟踪。

你怎能不为UNIX域套接字而心动?

Lobsters Hottest

一项技术调查,针对一个使用UNIX域套接字进行TTY仿真的iOS虚拟机项目中的崩溃问题,重点关注SSH连接初始化期间的inode号不匹配。

@jedisct1: epoll UAF

X AI KOLs Timeline

对 Linux 内核 epoll 子系统中的一个释放后使用(UAF)漏洞的详细分析,该漏洞通过切换到 RCU 修复,以及作者在现代设备上尝试利用该漏洞失败的经过。