“所有” Windows 版本的新1 位漏洞利用内核代码中处理滚动条的错误。这让我思考。为什么windows在内核而不是用户模式下处理滚动条?历史原因?有没有其他操作系统可以做到这一点?
1 回答
TL;DR:微软为了性能牺牲了安全性。
滚动条在 Windows 上有点特殊。大多数滚动条不是真正的窗口,而是作为“父”窗口上的装饰来实现的。这将我们引向一个更普遍的问题;为什么 Windows 在 Windows 上以内核模式实现?
让我们看看替代方案:
- 用户模式下的每个进程。
- 用户模式下的单个“主”进程。
在处理自己的窗口时,备选方案 1 有很大的优势;没有上下文切换/内核转换。问题当然是来自不同进程的窗口存在于同一个屏幕上,当用户切换到不同的窗口时,必须有人负责决定哪个窗口处于活动状态并协调更改。这个人必须是一个特殊的系统进程或内核,因为这个信息不能是每个进程的,它必须存储在全局的某个地方。这种双重信息设计将变得复杂,因为全局窗口管理器无法信任每个进程的信息。我敢肯定这个理论设计还有很多其他的缺点,但我不会在这里花更多的时间。
Windows NT 3 实现了替代方案 2 的变体。窗口管理器在 NT 4 中被移入内核模式,主要是出于性能原因:
...窗口管理器 (USER) 和图形设备接口 (GDI) 已从 Win32 子系统移至 Windows NT 执行程序。Win32 用户模式设备驱动程序,包括图形显示和打印机驱动程序,也已移至Executive。这些更改旨在简化图形处理、降低内存需求并提高性能。
...在同一份文件中,还有更多的技术细节和理由:
最初设计 Windows NT 时,Win32 环境子系统被设计为支持 MS-DOS、POSIX 和 OS/2 中的应用程序的环境子系统的对等体。但是,应用程序和其他子系统需要使用 Win32 子系统中的图形、窗口和消息传递功能。为了避免重复这些功能,Win32 子系统被用作所有子系统的图形功能服务器。
这种设计在 Windows NT 3.5 和 3.51 上运行良好,但它低估了图形调用的数量和频率。在单独的进程中拥有像消息传递和窗口控制这样的基本功能会从客户端/服务器消息传递、数据收集和管理多个线程产生大量内存开销。它还需要多个上下文切换,这会消耗 CPU 周期和内存。每秒的图形支持调用量降低了系统的性能。很明显,在 Windows NT 4.0 中重新设计这个方面可以回收这些浪费的系统资源并提高性能。
如今,其他子系统不再那么重要,但性能问题仍然存在。
如果我们看一个像IsWindowVisible这样的简单函数,那么当窗口管理器处于内核模式时不会有很多开销:该函数将在用户模式下执行几条指令,然后将 CPU 切换到 ring 0,整个操作在此执行 (验证传入的窗口句柄,如果有效,则检索可见属性)在内核模式下执行。然后它切换回用户模式,仅此而已。
如果窗口管理器存在于另一个进程中,那么您将至少使内核转换的数量增加一倍,并且您必须以某种方式将函数输入和输出传递给窗口管理器进程,并且您必须以某种方式使窗口管理器进程在您等待时执行为结果。NT 3 通过使用共享内存、LPC和称为配对线程的晦涩功能的组合来做到这一点。