bad_address()是 Linux 内核中一个用于安全探测内核地址是否可读的辅助函数。它的核心作用是:在不触发内核崩溃(Oops)的前提下,检查一个给定的内核地址是否有效可读。
核心机制:get_kernel_nofault
get_kernel_nofault()是一个特殊的宏/函数,它利用内核的异常表(exception table)机制来实现“安全访问”:
正常读取:它尝试从指定地址读取数据。
异常捕获:如果这次读取触发了缺页异常(地址无效),CPU 会跳转到异常表中预先注册的修复地址,而不是执行正常的 Oops 流程。
返回结果:读取成功返回
0,失败返回非零值。
因此,bad_address(p)的返回值语义非常明确:
返回
0:地址p是可读的,数据已成功读取到dummy变量中。返回非零:地址
p不可读(如未映射、权限不足),但内核安然无恙。
在dump_pagetable中的用途
这个函数通常出现在页表转储相关的代码中。在遍历页表层级(PGD → PUD → PMD → PTE)时,内核需要读取每一级页表项的值。然而,这些页表项本身可能指向无效的物理地址,或者由于页表损坏,对应的内核虚拟地址可能已经不可读。
如果直接解引用这些指针,一旦遇到坏地址,内核就会立即 Oops。而使用bad_address()作为“探雷器”,可以在尝试读取之前先确认地址的安全性,避免在诊断问题的过程中引入新的崩溃。
与probe_kernel_read的关系
bad_address()和probe_kernel_read()功能相似,但bad_address()更轻量。它只关心“能不能读”,而不关心“读到了什么”。在页表遍历这种对性能敏感的调试路径中,使用bad_address()可以快速过滤掉不安全的地址,然后再用probe_kernel_read()去实际读取数据,是一种常见的“两阶段”安全访问模式。