这是一个关于进程和linux内核之间交互的问题。这是在正式证明的背景下,因此设置可能看起来有点奇怪。假设我有一个程序,以通常的方式从 ELF 文件加载,它不能以“未经批准”的方式写入自己的代码段或数据段,并且它也不能发出未经批准的系统调用,但它可以写入内存中的任何其他位置(也就是说,允许向任何位置发出写入)。这安全吗?如果程序访问未映射的页面也没关系,因为这会导致分段错误,从而导致程序明显错误退出。 (关心的是程序是否可以不是退出并做未经批准的事情。)
除了段错误之外,如果写入内存中的随机位置,还可能产生哪些影响?例如,进程可见页面中是否有任何 MMIO 或秘密替代系统调用/内核通信机制?
我希望真实的陈述如下:如果内存地址 [xy](程序的代码和数据)包含值 V 并且向 [xy] 之外的位置发出写入,则进程退出并带有错误代码,或者 [xy] 处的内存仍然包含 V(并且所有其他进程不变量仍然保持:进程仍处于用户模式,内核尚未创建任何 MMIO 映射等)。
- 如果页映射包含 [xy] 外部的镜像,则 [xy] 外部的写入可能会成功并更改 [xy] 中的数据。但我不认为内核通常会生成这些类型的页面,除非你要求它这样做。
- 虚拟内存中的某处有一个映射的 vDSO 页面,但写入它应该没问题,因为我不需要这些函数。
我希望能够写入内存的主要原因是为了简化堆栈处理。大多数编译器不产生堆栈边界检查,并且 Linux 使用保护页不完美地防止堆栈溢出。我相信可以通过在堆栈上分配一个非常大的数组并访问其中的精心选择的索引来绕过保护。如果它访问应用程序的数据页,程序肯定会崩溃,所以这至少需要保护,但我不知道还有什么需要保护。堆分配( mmap
ped 页面和sbrk
)距离堆栈有多远?
似乎应该可以使用像这样的“堆栈粉碎”技术来获得符合标准的 C 程序来违反抽象语义(使用大型常量大小数组或alloca
在不通知内核的情况下分配大量内存,然后使用malloc
获取大数组内的内存,因为内核不知道它已经分配,然后写入数组可以更改malloc
'd 区域的内容)。