Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019)

Материал из 0x1.tv

(перенаправлено с «20190827F»)
Докладчик
Дмитрий Левин.jpg
Дмитрий Левин

Рассказ о том, как исправляются ошибки в архитектуре ядра linux, на примере одного изъяна в архитектуре ядра linux на x86-64, который просуществовал с 2001 по 2019 год и был исправлен в linux 5.3.

Видео

on youtube

Презентация

Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019).pdf

Thesis

Введение

Архитектура x86-64 в ядре linux практически с самого начала поддерживала исполнение как традиционных инструкций x86, так и традиционных системных вызовов x86 с помощью прерывания int 0x80 таким образом, что прерывания int 0x80 могут вызывать как 64-битные, так и 32-битные процессы.

При этом ядро linux не предоставляло пользовательского интерфейса, позволяющего достоверно установить, какой именно системный вызов сделан, 64-битный или 32-битный, поэтому отладчики и трассировщики традиционно угадывали битность системного вызова по битности процесса.

Традиционный подход

Поддержка x86-64 в strace появилась 17 лет назад – в сентябре 2002 года, в ней битность системного вызова угадывалась по содержимому регистра CS, т.е. по битности процесса.

Первое известное мне упоминание о проблеме этого подхода датируется 2008 годом, в нём приводится следующий пример:

#include <stdio.h>
#include <unistd.h>
int main()
{
    printf("--------------------\n");
    __asm__("movl $2, %eax; int $0x80");
    printf("[i am %d]\n", getpid());
    return 0;
}

Обычный запуск этой программы на архитектуре x86-64 предсказуемо выводит

   --------------------
   [i am 29280]
   [i am 29281]

Однако под strace происходит примерно следующее:

   write(1, "--------------------\n", 21--------------------
   )  = 21
   open(NULL, O_RDONLY|O_CREAT|O_TRUNC|O_DIRECT|O_NOATIME|O_PATH|0x800020, 0154300) = 10873
   getpid()                                = 10872
   [i am 10873]
   write(1, "[i am 10872]\n", 13[i am 10872]
   )          = 13
   exit_group(0)                           = ?

Альтернативный подход

Одновременно с поддержкой x86-64 в strace был добавлен закомментированный код, определявший битность системного вызова путём анализа инструкции, приведшей к системному вызову. Такой подход требует чтения памяти по адресу, на который указывает регистр RIP. Однако чтение памяти процесса для выяснения битности системного вызова – это не только дополнительный замедляющий системный вызов для отладчика, но и race condition, поскольку содержимое памяти, прочитанное отладчиком, может отличаться от фактически выполненного системного вызова.

Обсуждение

Отсутствие в ядре linux пользовательского интерфейса, позволяющего достоверно установить, какой именно системный вызов сделан, было осознано как проблема достаточно давно. Например, в начале 2012 года была живая дискуссия на эту тему, в ней поучаствовали, в частности, Linus Torvalds, Andi Kleen, Andrew Lutomirski, Chris Evans, Denys Vlasenko, H. Peter Anvin, Indan Zupancic, Jamie Lokier, Oleg Nesterov, Pedro Alves, Roland McGrath, Will Drewry. Было высказано много идей разной степени экстравагантности, однако дальше дальше дискуссии дело не пошло.

Решение

Для того, чтобы перейти от общих рассуждений к решению, нужны были активно заинтересованные люди. В данном случае, к сожалению, их не было до до ноября 2018 года – именно тогда Эльвира Хабирова предложила к обсуждению первую редакцию решения(patch).

Обсуждение привело к тому, что вторая редакция содержала уже 16 патчей, 15 из которых расширяло и упорядочивало внутренний API ядра на многочисленных архитектурах. В результате последующих итераций обсуждений патчсет вырос и стал затрагивать разные подсистемы и все архитектуры настолько, что было принято решение разделить его и апстримить по частям через разные деревья, поскольку никто не хотел брать патчсет целиком.

Результат

В конечном итоге цель была достигнута. Всего для реализации интерфейса PTRACE_GET_SYSCALL_INFO в ядре linux понадобилось 29 патчей, часть из них прошла 11 итераций обсуждения, а со дня отправки первой редакции патча 7 ноября 2018 года до завершения приёма последнего патча 17 июля 2019 года прошло немногим менее 9 месяцев.

Поддержка интерфейса PTRACE_GET_SYSCALL_INFO реализована в strace, начиная с версии 4.26, выпущенной в декабре 2018 года. strace переключается на использование этого интерфейса автоматически, если ядро linux его поддерживает.

Linux Kernel — история одного изъяна (Дмитрий Левин, OSSDEVCONF-2019)!.jpg

Примечания и ссылки

В самом начале видео первого доклада (http://0x1.tv/20190827F), по красным меткам это 03:22:58, очень сильное падение резкости изображения, если с этим можно что-то сделать, будет здорово.

Plays:444   Comments:2