Linux 内核如何在 copy_from_user 中临时禁用 x86 SMAP?

Posted

技术标签:

【中文标题】Linux 内核如何在 copy_from_user 中临时禁用 x86 SMAP?【英文标题】:How does the Linux kernel temporarily disable x86 SMAP in copy_from_user? 【发布时间】:2020-08-09 22:50:31 【问题描述】:

我想知道Linux内核在执行copy_from_user()函数时如何禁用x86 SMAP。我试图在源代码中找到一些东西,但我失败了。

Supervisor Mode Access Prevention (SMAP) 是 x86 CPU 的一项安全功能,可防止内核访问意外的用户空间内存,有助于抵御各种攻击。

【问题讨论】:

如果您能告诉我相关代码,我将不胜感激 能否请您扩展“SMAP”的首字母缩写词? 管理模式访问保护 (SMAP) 是 Intel CPU 的一项安全功能,可防止内核访问意外的用户空间内存,进而帮助抵御各种攻击。 copy_from_user 函数将数据从用户空间复制到内核空间,但在 SMAP 保护下,在内核空间访问用户空间内存是非法的。所以当真正复制数据的时候,内核会暂时关闭SMAP保护,这样就可以从用户空间传输数据,当传输完成后,SMAP保护会再次开启。 更多信息可见en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention 【参考方案1】:

如您链接的***页面中所述:

当内存分页处于活动状态并且 CR4 控制寄存器中的 SMAP 位被设置时,SMAP 被启用。通过设置 EFLAGS.AC(对齐检查)标志,可以暂时禁用 SMAP 以进行显式内存访问。 stac(设置 AC 标志)和clac(清除 AC 标志)指令可用于轻松设置或清除标志。

Linux 内核正是这样做来临时禁用 SMAP:它在复制数据之前使用stac 设置 EFLAGS.AC,然后在完成后使用clac 清除 EFLAGS.AC。

The AC flag 自 486 以来一直以alignment check for user-space load/store 存在; SMAP 重载了该标志位的含义。 stac/clac 是 SMAP 的新功能,仅允许在内核模式下使用 (CPL=0);它们在用户空间出现故障(在没有 SMAP 的 CPU 上,也在内核模式下)。


理论上它很简单,但实际上 Linux 内核代码库是一个由函数、宏、内联汇编模板等组成的丛林。要确切了解这是如何完成的,我们可以查看源代码,从 @987654325 开始@:

    copy_from_user()被调用时,它会快速检查内存范围是否有效,然后调用_copy_from_user()...

    ...它会再做几次检查,然后调用raw_copy_from_user()...

    ...在进行实际复制之前,它调用__uaccess_begin_nospec()...

    ...这只是一个扩展为stac(); barrier_nospec()的宏。

    关注stac(),这是一个简单的内联函数,我们有:

     alternative("", __ASM_STAC, X86_FEATURE_SMAP);
    

alternative() 宏是一个非常复杂的宏,用于在内核启动时根据 CPU 支持选择指令的替代项。您可以检查定义它的源文件以获取更多信息。在这种情况下,它用于根据 CPU 支持来决定内核是否需要使用 stac 指令(旧的 x86 CPU 没有可用的 SMAP,因此没有指令:在那些 CPU 上,这只是变成无操作)。

看看我们看到的__ASM_STAC宏:

#define __ASM_STAC  ".byte 0x0f,0x01,0xcb"

这是组装的stac 操作码(以字节为单位)。这是使用.byte 指令而不是助记符定义的,因为即使在旧的工具链上也需要编译,而 binutils 的版本不知道这些指令。

在启动时,cpuid 指令用于检查X86_FEATURE_SMAPebx 的第 20 位,当cpuideax=7, ecx=0 一起执行以获得extended features),这告诉内核SMAP 是否可用(重写机器代码以使指令变为stac)或不可用(保持无操作)。

一旦完成所有这些疯狂(实际上都归结为一条指令),从用户内存执行实际复制,然后使用__uaccess_end() 宏重新-启用 SMAP。这个宏使用alternative()的方式和我们刚刚看到的一样,最终执行clac(或nop)。

【讨论】:

您删除了关于 stac / clac 的“出于某种原因”评论,仅在 CPL0 上可用。它确实值得评论,因为您可以在任何模式下使用pushf/popf 做同样的事情。如果我不得不猜测,那是为了性能,所以stac 可以在启用 SMAP 时切换权限检查,而不是对齐检查。 (AC的对齐检查效果只适用于用户空间,CPL3)。 IDK 如果它或 popf 序列化加载/存储;如果重命名 SMAP 状态,他们可能会这样做。

以上是关于Linux 内核如何在 copy_from_user 中临时禁用 x86 SMAP?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Linux 内核访问用户空间内存?

copy_from_user的疑问

当有copy_from_user时应用get_user

[转] copy_to_user和copy_from_user两个函数的分析

内核空间和应用空间的数据拷贝(copy_to_user & copy_from_user)

Linux用户与内核空间交互—debugfs