Отправка прерываний пользовательского режима на x86 - PullRequest
2 голосов
/ 14 января 2020

В Linux x86, могу ли я отправлять прерывания (например, вызванные таймером или другим механизмом), которые будут обрабатываться кодом, работающим в пользовательском режиме?

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

1 Ответ

4 голосов
/ 14 января 2020

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

Существует множество больших препятствий для запуска обработчика прерываний в кольце 3 или из виртуального адреса пространства пользователя, который отображается только одним указанным c процессом. (Даже если вы закрепите эту память так, чтобы ее нельзя было выгружать, она все равно будет отображаться только в том случае, если для CR3 заданы таблицы страниц этого процесса. X86 использует виртуальные адреса в IDT (таблица дескрипторов прерываний), и страница должна отображаться когда срабатывает прерывание (или, иначе, вы получаете ошибку страницы, что, на самом деле, вы не хотите, чтобы это происходило полностью асинхронно). Это не проблема для обычных обработчиков прерываний ядра; он всегда сохраняет код ядра, сопоставленный с одним и тем же виртуальным адрес во всех таблицах страниц пользовательского пространства.)

API ядра, который позволял бы регистрировать указатель функции пользовательского пространства в качестве обработчика прерываний кольца 0, передавал бы ключи королевству этому процессу пользовательского пространства, буквально работая с привилегии ядра, так что это в значительной степени неразумно.


Технически возможно для x86 иметь обработчик прерываний, который выполняется в кольце 3, но если прерывание сработало в то время как в кольце 0, iret произойдет сбой вместо того, чтобы вернуться к коду ядра, который был прерван .

Обработчик прерываний должен быть написан специально для возврата с iret и для сохранения всех регистров. например, __attribute__((interrupt_handler)) https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html. И любой другой процесс в том же ядре будет зависеть от этого процесса; любые ошибки в этом (такие как разрушение некоторого архитектурного состояния или загрязнение регистров SSE / AVX) могут повлиять на другие процессы. (Если вы можете выяснить, как получить код в одном процессе для запуска в то время как CR3 может быть установлен для другого процесса ...)

Избежание тупиковых ситуаций также будет большой проблемой; в ядре существует множество ограничений на то, что вы можете сделать в собственном обработчике прерываний («верхняя половина»), потому что он может выполняться асинхронно между любыми другими инструкциями (если вы не отключаете прерывания на этом ядре).


Я не думаю, что Linux действительно правдоподобно позволить вам сделать это; даже если вы каким-то образом решите все (очень сложные) проблемы и даже заставите обработчик работать в кольце 3, ядру все равно придется доверять ему, чтобы не наступать на архитектурное состояние любого другого процесса.

Существует прецедент для таких вещей, как получение X-серверами привилегий для выполнения инструкций in / out (через iopl) и / или доступа к /dev/mem (что теоретически позволило бы ему красть информацию из других процессов). Но это будет еще хуже и даст вам легкий доступ к снимкам состояния регистра из других процессов.

...