Отслеживание одного шага в ядре из контекста процесса?

#linux #linux-kernel #kernel #ptrace

#linux #linux-ядро #ядро #ptrace

Вопрос:

Мне было интересно, что произойдет, если из ядра (в данном случае Linux) вы вызовете ptrace_request с помощью PTRACE_SINGLESTEP в контексте процесса (системный вызов, ошибка страницы и т. Д.). Будет ли это один шаг инструкции пространства пользователя или инструкции пространства ядра. Я понимаю, что ptrace может выполнять только одношаговые инструкции пользователя, поэтому мне любопытно, какое поведение это приведет.

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

Это происходит потому, что task_struct для процесса (если он выгружен) по-прежнему будет указывать на обработчик пространства ядра IIRC, поэтому будет ли один шаг с помощью ptrace обходить это и выполнять правильную инструкцию пользовательского пространства или просто не делать этого вообще?

Ответ №1:

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

Я рекомендую вам взглянуть на arch/x86/kernel/ptrace.c, чтобы понять, как на самом деле работает один шаг. Одношаговая инструкция фактически эмулируется ядром, IIRC для этого нет аппаратной поддержки.

Ответ №2:

Чтобы понять ответ на ваш вопрос, вам нужно разобраться в оборудовании Intel.

Сначала мы начнем с простейшей инструкции (потому что SINGLE_STEP переводит процессор Intel в одношаговый режим и возвращается к обработчику прерываний после выполнения одной структуры):

movl (eax), ebx

Следуя формату Intel, это означает, что значения внутри eax обрабатываются как указатель на память, получают доступ к памяти, получают 4-байтовые значения и копируют их в ebx.

Эта ОДНА инструкция по сборке — при выполнении в ядре и пользовательском контексте будет иметь ДРУГОЕ поведение / значение.

Если в ядре, таблица страниц ядра будет использоваться для доступа к памяти и копирования данных в ebx. В пользовательском процессе пользовательская таблица страниц будет использоваться (кстати, аппаратным обеспечением MMU) для чтения данных из памяти и копирования в ebx. Идентичные значения в eax, но с разными значениями в регистре CR3 (что означает различный контекст процесса), вызовут чтение разных частей памяти. Так что отслеживание программы пользовательского пространства в ядре действительно смешно. Потому что вам нужно выполнить переключение контекста (которое включало в себя весь набор операций хранения и восстановления регистров), выполняемый до и после выполнения инструкции пользователя, что означает 4 операции эффективно.

Как вы можете видеть, я не ссылался ни на один API функций ядра, как вы упомянули. На первом месте стоит общее концептуальное понимание.