当CPU处于用户态时,CPU不能执行特权指令,也不能访问内核空间内存。
并且当CPU处于内核模式时,CPU可以执行所有指令并可以访问所有内存。
现在在 Linux 中,用户模式程序可以访问所有内存(使用/dev/mem
),并且可以执行两个特权指令IN
和OUT
(iopl()
我认为使用)。
所以Linux中的用户模式程序可以做内核模式下可以做的大多数事情(我认为是大多数事情)。
允许用户模式程序拥有所有这些功能是否违背了拥有 CPU 模式的目的?
答案1
所以Linux中的用户模式程序可以做内核模式下可以做的大多数事情(我认为是大多数事情)。
好吧,并非所有用户模式程序都可以,只有那些具有适当权限的程序才可以。这是由内核决定的。
/dev/mem
受通常的文件系统访问权限和CAP_SYS_RAWIO
功能的保护。iopl()
并且ioperm()
也通过相同的能力受到限制。
/dev/mem
也可以完全从内核中编译出来(CONFIG_DEVMEM
)。
允许用户模式程序拥有所有这些功能是否违背了拥有 CPU 模式的目的?
也许。这取决于您希望特权用户空间进程能够执行的操作。用户空间进程如果有权访问/dev/sda
(或同等),也可能会破坏整个硬盘驱动器,即使这违背了文件系统驱动程序处理存储访问的目的。
(还有一个事实是,它是iopl()
通过利用 i386 上的 CPU 特权模式来工作的,所以不能说它违背了它们的目的。)
答案2
modprobe
只是以与通过将新代码加载到内核中“破坏”安全性相同的方式。
由于各种原因,有时让半特权代码(例如 X 服务器内的图形驱动程序)在用户空间而不是内核线程中运行更有意义。
- 能够
kill
更容易地实现它,除非它锁定了硬件。 - 让它从文件系统中的文件中请求分页其代码/数据。 (内核内存不可分页)
- 为其提供自己的虚拟地址空间,其中 X 服务器中存在错误可能只是让 X 服务器崩溃,而不需要关闭内核。
它对安全性没有多大作用,但在可靠性和软件架构方面有很大的优势。
将图形驱动程序烘焙到内核中可能会减少 X 客户端和 X 服务器之间的上下文切换,就像只有一个用户->内核->用户,而不必将数据获取到另一个使用空间进程中,但 X 服务器历史上太大且故障太多希望它们完全进入内核。
是的,具有这些权限的恶意代码可以如果需要的话可以接管内核,使用/dev/mem
修改内核代码。
或者,例如在 x86 上,在进行系统调用以将其 IO 特权级别设置为 Ring 0cli
后,运行指令以禁用该内核上的中断。iopl
但即使 x86 iopl
“仅”允许访问某些指令:输入/输出(以及字符串版本输入/输出)和 cli/sti。它不允许您使用rdmsr
或wrmsr
读取或写入“特定于模型的寄存器”(例如,IA32_LSTAR
它设置 x86-64 指令的内核入口点地址syscall
),或用于lidt
替换中断描述符表(这将让您完全采取在现有内核的机器上,至少在该内核上。)
您甚至无法读取控制寄存器(例如 CR3,它保存顶级页目录的物理地址,攻击进程可能会发现它可以作为偏移量来/dev/mem
修改其自己的页表,作为mmap
ing more 的替代方案/dev/mem
。 )
invd
(使所有缓存无效没有回写!! (用例= 配置 RAM 之前的早期 BIOS))是另一个有趣的,它始终需要完整的 CPL 0(当前特权级别),而不仅仅是 IOPL。甚至wbinvd
具有特权,因为它非常慢(并且不可中断),并且必须刷新全部跨所有核心的缓存。 (看有没有办法刷新与程序相关的整个CPU缓存?和WBINVD指令用法)
导致跳转到以代码形式运行数据的错误地址的错误因此无法在用户空间 X 服务器中意外执行任何这些指令。
当前权限级别(在保护模式和长模式下)cs
是(代码段选择器)的低2位。 mov eax, cs
/and eax, 3
在任何模式下都可以读取权限级别。
要写入权限级别,您可以执行jmp far
或call far
来设置CS:RIP
(但是目标段的 GDT/LDT 条目可以根据旧的权限级别对其进行限制,这就是为什么用户空间无法执行此操作来提升自身)。或者您可以使用int
或syscall
在内核入口点切换到环 0。