堆栈寄存器检查

堆栈寄存器检查

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 mmapmap sigaltstack了解更多示例

相关内容