【问题标题】:Assembly page fault handler cannot be called due to invalid stack pointer由于无效的堆栈指针,无法调用程序集页面错误处理程序
【发布时间】:2020-06-26 11:17:40
【问题描述】:

当我的页面错误处理程序中断被调用(它应该挂起系统)时,在调用它之前有一些变量被推入堆栈。我启用了虚拟内存,当我设置一个无效的堆栈指针 (esp) 并且 int14 处理程序被调用时,它会立即导致另一个页面错误等等。我应该如何解决这种情况?

我的 int14 代码:

isr14:
    ; interrupt handler for isr14
    jmp $
    iretd

导致它崩溃的代码:

mov esp, 0x1000 ; 0x1000 is not mapped in the VM directory
push dword 'A'
jmp $

我的 IDT 表部分:

irq14:
    dw isr14
    dw 0x0008
    db 0x00
    db 10101110b
    dw 0x0000

irq15:
........

【问题讨论】:

  • 我对此不是很熟悉,但我记得解决方案是有一个单独的安全堆栈,在处理页面错误时中断描述符切换到。
  • 我明白了,但是在调用中断之前我应该​​如何切换到该堆栈?
  • 我对细节有点模糊,但我记得你可以在中断描述符的某个地方对其进行编码。让我检查一下文档。
  • 在 TSS 中定义要切换到的堆栈。然后设置一个带有非零字段的中断门,该字段引用保存在 TSS 中的堆栈段/指针。这应该可以解决问题。
  • @fuz:我似乎记得 Michael Petch 曾对此发表过回答。也许只是因为 TSS 是允许内核处理内核堆栈上的中断的机制,而不是让用户空间崩溃或拥有机器。例如Creating a proper Task State Segment (TSS) structure with and without an IO Bitmap?/During an x86 software interrupt, when exactly is a context switch made?wiki.osdev.org/Getting_to_Ring_3 看起来很相关。

标签: assembly x86 kernel virtual-machine interrupt


【解决方案1】:

我应该如何解决这种情况?

我会通过避免使用来解决这种情况 - 首先不要让内核有一个狡猾的堆栈指针(不要让内核堆栈被发送到交换空间,不要使用页面错误来表示“自动-不断增长的内核堆栈”等)。请注意,如果用户空间发生页面错误(CPL=3),CPU 将自动切换到内核堆栈,因此用户空间是否有不可靠的堆栈指针无关紧要。

替代方案是:

  • 当内核代码 (CPL=0) 导致页面错误时强制内核堆栈切换。这可以使用硬件任务切换(保护模式)或页面错误异常处理程序的 IST 机制(长模式)来完成。这将是恢复的最佳选择(例如,更容易找出问题所在,修复它,然后返回)。

  • 当内核代码 (CPL=0) 导致双重错误时强制内核堆栈切换。这可以使用硬件任务切换(保护模式)或双故障异常处理程序的 IST 机制(长模式)来完成。这将是性能的最佳选择(不会增加正常页面错误的开销)。

注意 1:请注意,硬件任务切换/任务门和 IST 都不是可重入的。对于硬件任务切换,如果在处理第一个页面错误时发生第二个页面错误,您将获得一般保护错误(因为“页面错误任务”很忙);对于 IST,如果在处理第一个页面错误时发生第二个页面错误,则第二个页面错误将丢弃/覆盖第一个页面错误的堆栈并使其无法恢复。理论上,您可以通过尽快切换到不同的任务或不同的堆栈来缓解这些问题,但这很复杂/混乱,并且可能会导致更多问题。

注意 2:使用硬件任务切换或 IST,您可能最终会遇到避免和双重故障的组合;双重故障处理程序执行“冻结系统并转储信息/恐慌”作为灾难性内核故障的通用后备(应该避免但没有)。

注3:如果你想支持“自动增长的内核栈”;您可以改用“堆栈探测器” - 基本上,只需在将内存用于堆栈之前从“未来堆栈”执行虚拟读取/s(在函数结尾),以便在仍有足够的内核堆栈留给页面时发生页面错误故障处理程序。

【讨论】:

  • 谢谢!我读到有一个 TSS(我还没有设置,我在没有它的情况下进行任务切换)会使处理器在中断发生时切换到内核堆栈。我会努力做到的。
  • 或者我必须有一个 TSS 吗?如果我在进入错误进程之前只切换环就足够了吗?
  • 请注意,IST 机制也可用于 16 位和 32 位保护模式。它甚至可能比任务门更容易使用。
  • 我应该如何设置我的 TSS?我创建了一个条目,但是如何将其添加到 GDT 然后加载它以便 CPU 在切换环时知道 esp0?
  • @DrCarnivore:用于保护模式;如果您使用其他特权级别(例如 CPL=3),您至少需要一个 TSS 来处理特权级别更改(因为这是 CPU 将在更改为“更多特权”特权级别时加载“SS:ESP”的地方 - 例如从 CPL =3 到 CPL=0,因为中断或系统调用)。通常情况下,每个 CPU 都会有多个硬件任务 - 一个主要硬件任务(使用软件任务切换在正常任务之间切换),然后是更多不进行软件任务切换的专用硬件任务(对于双重故障​​、NMI 和/或机器检查异常)。
猜你喜欢
  • 2019-04-20
  • 2014-11-28
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-15
相关资源
最近更新 更多