我试图弄清楚当我们编写这两个代码时 Linux 内核(x86_64,v6.9)内部发生的事情的顺序:
// Null-dereference + writing to page zero
*(char *)0 = 0;
// Null-dereference + only reading from page zero
char c = *(char *)0;
我尝试用 Ftrace 对其进行分析,这就是我得到的结果:
handle_mm_fault <-- do_user_addr_fault
sanitize_fault_flags <-- handle_mm_fault
arch_vma_access_permitted <-- handle_mm_fault
bad_area_nosemaphore <-- do_user_addr_fault
__bad_area_nosemaphore <-- do_user_addr_fault
force_sig_fault <-- __bad_area_nosemaphore
因此,根据我的理解,我们导致了页面错误,并且以某种方式
arch_vma_access_permitted()
或sanitize_fault_flags()
决定返回VM_FAULT_SIGSEGV
,并且__bad_area_nosemaphore()
使用它向带有SIGSEGV
的进程发送force_sig_fault()
。我的问题是,第 0 页的权限是什么?它是否首先被映射?如果没有,那么我认为vma_is_foreign()
应该涵盖这种情况并导致分段错误。我还在 load_elf_binary()
中发现了一些有趣的东西,用于模拟以前 Linux 版本的 ABI 行为:
if (current->personality & MMAP_PAGE_ZERO) {
/* Why this, you ask??? Well SVr4 maps page 0 as read-only,
and some applications "depend" upon this behavior.
Since we do not have the power to recompile these, we
emulate the SVr4 behavior. Sigh. */
error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
}
其中最令人困惑的部分是
PROT_EXEC
。 为什么我们需要将指令存储在第 0 页内? 如果它也有 PROT_READ
,那么当 current->personality
有 MMAP_PAGE_ZERO
时,从第 0 页读取应该不会导致分段错误,对吗?找不到 SVr4 规范,所以我不确定详细信息。我也不确定这个 personality
何时适用于任务,但我们可以得出结论,在某些情况下,页面零 GETS 被映射(当然,如果 mmap()
为零,我们可以使用 mmap_min_addr
重新映射它,但我是现在谈论默认行为,而不是重新映射)。我找不到任何其他映射零页的 vm_mmap()
或 do_mmap()
。
零页的权限是什么?它一开始就被映射了吗?
事实并非如此。除了您显示的这段奇怪的代码,或其他异常(例如用户空间在
vm.mmap_min_addr = 0
时明确请求映射它),那么零页面永远不会自动映射。
这与任何其他页面相同,虚拟地址为零的事实并没有真正使它变得特别。当故障发生时,您将准确地结束于here,因为该进程没有对应的映射(
vma
)。
不知道为什么你要提到
vma_is_foreign()
,但如果你一开始就没有vma
,那么就不能真正调用它。 __bad_area_nosemaphore()
将负责 SIGSEGV
传递,调用 force_sig_fault()
,然后调用另一系列函数来传递信号。
其中最令人困惑的部分是PROT_EXEC。为什么我们需要将指令存储在第 0 页内?
旧软件期望读取意味着执行行为,因为这就是硬件过去的工作方式。事实上,对于较旧的 x86 32 位 CPU,甚至对于在 x86_64 上运行的某些 32 位软件,情况仍然如此。 看看这个评论例如。
如果它也有 PROT_READ,那么当 current->personality 有 MMAP_PAGE_ZERO 时,从第 0 页读取不应该导致分段错误,对吗?
对。
我也不确定这种性格何时适用于某项任务
主要取决于底层架构。特定于 Arch 的代码选择要在新执行人员上切换哪些个性位。
SET_PERSONALITY
和 SET_PERSONALITY2
宏由每个拱门定义,您可以在 here 和 here 看到不同的定义。