https://marc.info/?l=openbsd-tech&m=152035796722258&w=2
OpenBSD 引入了堆栈寄存器检查
“它是由内核机会主义地强制执行的。”-> 这意味着它是可选的?
“当系统调用发生时,我们检查堆栈指针寄存器是否指向这样的页面。如果没有,程序就会被终止。”- 有人可以向非程序员更详细地解释一下堆栈寄存器吗?如果它是可选的,为什么它会杀死所有没有它的程序?
答案1
堆栈指针寄存器是一个硬件寄存器。它指向用于堆栈的区域中的内存位置*。在对堆栈上的数据进行寻址时使用堆栈指针寄存器,通常在内存访问之前或之后递增或递减指针的值。该检查在系统调用发生时执行,检查寄存器是否指向有效的堆栈地址。 “机会主义”并不意味着“可选”。
[*]:在某些架构上,有效地址也可以是堆栈页最后地址之后的地址。
答案2
每个线程都有堆栈。堆栈是线程用来存储局部变量、函数返回地址等的特殊内存区域。当CPU执行线程时,它的寄存器(SP)必须指向堆栈的内存地址。
在大多数情况下,内核分配线程堆栈,但有时堆栈是由应用程序创建的:应用程序可能会为信号处理程序创建自定义堆栈(当内核向进程发送信号时执行的代码),或者它们可能会这样做来实现线程库。
为此,应用程序向内核请求内存区域,然后将其标记为堆栈,以便内核知道该区域是堆栈。但由于开发人员的错误,应用程序可能会要求内核使用一些未注册的内存用于堆栈。在这种情况下,可能会发生一些不好的事情:内核可能会覆盖有用的数据,因为它认为它写入堆栈,但它不是堆栈!黑客可能会用它来破坏程序。
在 OpenBSD 中,当您保留内存(例如使用 mmap)时,您必须明确说明:
晴天场景:
- 应用程序:内核,我可以有一些内存用作堆栈吗?
- 内核:当然,这是你的内存
- 应用程序:好的,请使用它作为信号堆栈
- 内核:ok(配置内部结构,使CPU SP寄存器在处理信号时指向该内存)
雨天情景:
- 应用程序:内核,我可以有一些内存吗?
- 内核:当然,这是你的内存
- 应用程序:好的,请使用它作为信号堆栈
- 内核:但是这个内存不是用于堆栈的!你想愚弄我!
- (内核杀死应用程序)
以下是此类标志的更多示例:
PROT_EXEC:要求内核保留内存来存储代码(CPU 可以执行的东西) PROT_WRITE:要求内核保留内存来可写数据
因此,您无法执行数据或写入代码(这称为 W^X,OpenBSD 人为此感到自豪)
您可以阅读man mmap
和map sigaltstack
了解更多示例