购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.4 栈溢出问题

与用户模式不同,内核模式栈位于直接映射的内存中。当一个进程调用一个可能在内部被深度嵌套的内核服务时,它有可能会超出当前的内存运行范围。最糟糕的是,内核会察觉不到这种情况。内核程序员通常会使用各种调试选项来跟踪栈使用情况并检测溢出,而这些方法都不便于在生产系统上防止栈溢出。这里也排除了通过使用保护页面的传统保护方式(因为它浪费了一个实际的内存页面)来避免内核栈溢出问题。

内核开发者倾向于遵循编码标准——尽量减少使用本地数据、避免递归、避免深度嵌套等,以减少栈被破坏的可能性。但是,实现功能丰富和深度分层的内核子系统可能会带来各种设计挑战和复杂性,尤其是对于文件系统、存储驱动程序和网络代码可以堆叠在多个层次中的存储子系统,这会导致深度嵌套的函数调用。

在相当长的时间里,Linux内核社区一直在思考如何预防这类栈破坏问题,为此,决定将内核栈的大小扩展到16KB(x86-64,自内核3.15开始)。内核栈的扩展可能会阻止一部分破坏,但代价是为每个进程内核栈占用许多直接映射的内核内存。但是,为了系统的可靠运行,内核期望在生产系统上出现栈破坏时能够优雅地处理它们。

随着4.9版本的发布,内核已经有了一个新的系统来建立虚拟映射的内核栈。由于当前正在使用虚拟地址来映射甚至直接映射页,内核栈实际上并不需要物理上连续的页。内核为虚拟映射的内存预留了一个单独的地址范围,并且在调用vmalloc()时分配此范围内的地址。这个内存范围称为vmalloc范围。当程序需要分配大量内存时使用这个范围,这些内存实际上是虚拟连续的,但物理上是分散的。使用这个方法,内核栈现在可以分配为单独的页,映射到vmalloc范围。虚拟映射还可以防止溢出,因为它可以使用页表项分配一个不可访问的保护页(而不浪费实际页)。保护页会提示内核在内存溢出时弹出oops消息,并杀死溢出进程。

具有保护页的虚拟映射内核栈目前仅适用于x86-64体系结构(对于其他体系结构的支持,看似也会跟进)。这可以通过选择HAVE_ARCH_VMAP_STACK或CONFIG_VMAP_ STACK的构建时选项来开启该功能。 skAA1KXR6jBd03rDQN6HXFUHZb9GXNVvHshoIjeeT8sfsxuLLqtm7qry7Fo3muy5

点击中间区域
呼出菜单
上一章
目录
下一章
×