Как указано на странице Википедии, на которую вы ссылались:
SMAP включается, когда подкачка памяти активна и установлен бит SMAP в регистре управления CR4. SMAP можно временно отключить для явного доступа к памяти, установив флаг EFLAGS.A C (проверка выравнивания). Инструкции stac
(Установить флаг A C) и clac
(Очистить флаг C) можно использовать для простой установки или сброса флага.
Ядро Linux делает именно это, чтобы временно отключить SMAP: он использует stac
для установки EFLAGS.A C перед копированием данных, а затем использует clac
для очистки EFLAGS.A C после завершения.
A C флаг существует с 486 как проверка выравнивания для загрузки / хранения в пользовательском пространстве ; SMAP перегружает значение этого флага. stac
/ clac
являются новыми с SMAP и по некоторым причинам разрешены только в режиме ядра; они ошибаются в пространстве пользователя. И на процессорах без SMAP, а также в режиме ядра.
Теоретически это довольно просто, но на практике кодовая база ядра Linux представляет собой джунгли функций, макросов, встроенных шаблонов сборки и т. Д. c. Чтобы точно узнать, как это сделать, мы можем посмотреть исходный код, начиная с copy_from_user()
:
Когда вызывается copy_from_user()
, он делает быстрая проверка, чтобы увидеть, допустим ли диапазон памяти, затем вызывает _copy_from_user()
...
..., который делает еще пару проверок, а затем вызывает raw_copy_from_user()
...
... который перед выполнением фактической копии вызывает __uaccess_begin_nospec()
...
... это просто макрос, который расширяется до stac(); barrier_nospec()
.
С фокусировкой на stac()
, которая является простой встроенной функцией, мы имеем:
alternative("", __ASM_STAC, X86_FEATURE_SMAP);
alternative()
- довольно сложный макрос для выбора альтернатив для инструкции во время загрузки ядра, основанный на поддержке процессора. Вы можете проверить исходный файл, в котором он определен, для получения дополнительной информации. В этом случае он используется, чтобы решить, нужно ли ядру использовать инструкцию stac
или нет, основываясь на поддержке ЦП (старые ЦП x86 не имеют SMAP, и поэтому не имеют инструкции: на этих ЦП это просто становится неактивным).
Глядя на макрос __ASM_STAC
, мы видим:
#define __ASM_STAC ".byte 0x0f,0x01,0xcb"
Какой код операции stac
собран в байтах. Это определяется с помощью директивы .byte
вместо mnemoni c, потому что, опять же, это необходимо для компиляции даже на старых инструментальных цепочках, где версия binutils не знает об этих инструкциях.
После загрузки инструкция cpuid
используется для проверки X86_FEATURE_SMAP
(бит 20 ebx
, когда cpuid
выполняется с eax=7, ecx=0
для получения расширенных возможностей ), и это сообщает ядру, SMAP доступен (переписать машинный код, чтобы инструкция стала stac
) или нет (оставив без операции).
После того, как все это безумие закончилось (что на самом деле все сводится к одна команда ), выполняется фактическое копирование из пользовательской памяти, а затем макрос __uaccess_end()
используется для повторного включения SMAP. Этот макрос использует alternative()
так же, как тот, который мы только что видели, и в итоге выполняет clac
(или nop
).