Как ядро ​​Linux временно отключает SMAP x86 в copy_from_user? - PullRequest
0 голосов
/ 26 апреля 2020

Я хочу знать, как ядро ​​Linux отключает SMAP x86 при выполнении функции copy_from_user(). Я пытался найти что-то в исходном коде, но мне не удалось.

Предотвращение доступа в режиме супервизора (SMAP) - это функция безопасности процессоров x86, предотвращающая доступ ядра к непреднамеренной памяти пользовательского пространства. , который помогает отбиваться от различных подвигов.

1 Ответ

3 голосов
/ 29 апреля 2020

Как указано на странице Википедии, на которую вы ссылались:

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():

  1. Когда вызывается copy_from_user(), он делает быстрая проверка, чтобы увидеть, допустим ли диапазон памяти, затем вызывает _copy_from_user() ...

  2. ..., который делает еще пару проверок, а затем вызывает raw_copy_from_user() ...

  3. ... который перед выполнением фактической копии вызывает __uaccess_begin_nospec() ...

  4. ... это просто макрос, который расширяется до stac(); barrier_nospec().

  5. С фокусировкой на 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).

...