操作系统和汇编:什么阻止用户模式将选择器设置为任意值?

Cha*_* Li 3 x86 assembly operating-system gdt memory-segmentation

我知道操作系统通过使用分段和特权级别来限制对内核代码和数据的访问。然而,用户可以更改段寄存器的值,如果以下代码执行成功,我们似乎可以访问内核数据:

mov eax, 0x10 
mov es, ax   #point selector to the item 2 in GDT with RPL 0, which is the data segment
les bx, [0]
Run Code Online (Sandbox Code Playgroud)

所以我想知道阻止此代码成功执行的机制是什么?

Ros*_*dge 7

mov es, ax指令将导致一般保护 (#GP) 错误,因为当前特权级别 (CPL) 大于描述符的特权级别 (DPL),或者请求的特权级别 (RPL) 将被忽略,因为它在数值上不高于 DPL 。在您的示例中,由于它在用户模式下运行,因此 CPL 为 3。这意味着描述符的 DPL 也必须为 3,否则指令将出错。如果 DPL 为 3,则不会出现故障,但 RPL 会被有效忽略,因为它不能高于 DPL。

\n

(请注意,段特权级别检查仅在加载段寄存器时执行,因此它只是mov es, ax可能会因此而崩溃。)

\n

Intel 软件开发人员手册中 MOV 指令的文档解释了加载段寄存器时何时会导致 #GP 错误:

\n
\n
IF DS, ES, FS, or GS is loaded with non-NULL selector\nTHEN\n    IF segment selector index is outside descriptor table limits\n    OR segment is not a data or readable code segment\n    OR ((segment is a data or nonconforming code segment)\n            AND ((RPL > DPL) or (CPL > DPL)))\n        THEN #GP(selector); FI;\n    IF segment not marked present\n        THEN #NP(selector);\n    ELSE\n        SegmentRegister := segment selector;\n        SegmentRegister := segment descriptor; FI;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

所使用的最高 DPL 和 RPL 的行为记录在英特尔 SDM 第 3 卷“5.5 特权级别”中:

\n
\n
    \n
  • 请求的特权级别 (RPL) \xe2\x80\x94 [...] 即使请求访问段的程序或任务有足够的特权来访问该段,如果 RPL 没有足够的特权级别,访问也会被拒绝。也就是说,如果段选择器的 RPL 在数值上大于 CPL,则 RPL 会覆盖 CPL,反之亦然。[...]
  • \n
\n
\n

选择器的 RPL 字段只允许将有效特权级别增加到比 DPL 在数值上更高或更低的特权级别。如果将其设置为较低的数字级别,则不会产生任何效果。

\n

换句话说,如果选择器 0x10 引用内核模式数据段 (DPL = 0),那么您的代码将会崩溃。如果选择器 0x10 是用户模式数据段 (DPL = 3),则其处理方式与使用 0x13 (RPL = 3) 相同。

\n
\n

请注意,实际上这并不重要,因为所有现代操作系统都使用平面段模型,每个段都以 0 为基数,并且可以访问整个线性地址空间。用户模式代码实际上并不通过段检查来限制访问内核代码和数据,而是通过页保护来限制。这些仅使用 CPL 来确定是否应授予对管理员模式(内核)页面的访问权限。

\n