[ru] Блокирование последовательного двойного освобождения ядерной памяти
Эта небольшая статья про то, как я улучшил SLUB-аллокатор в ядре Linux.
7 августа я выступил на хакерском фестивале SHA2017 (Still Hacking Anyway). Слайды и запись выступления:
В своем докладе я рассказал о уязвимости CVE-2017-2636
в ядре Linux и технике ее эксплуатации для локального повышения привилегий. Техника эксплуатации двойного освобождения памяти (double free
) состоит в том, что сначала нужно превратить его в ошибку типа "использование после освобождения" (use-after-free
). Обычно это достигается с помощью выделения блока памяти того же размера между двойным освобождением (отражено на схеме). Данная техника называется heap spraying
.
Однако в случае CVE-2017-2636
освобождаются сразу 13 элементов, причем двойное освобождение происходит одним из первых. Поэтому вышеописанная техника оказывается непригодной. Но все-таки мне удалось привести это состояние системы к ошибке использования после освобождения. Я воспользовался наивным поведением SLUB
, основного аллокатора ядра Linux.
Оказывается, SLUB-аллокатор
не препятствует последовательному двойному освобождению одного и того же участка памяти. В отличие от него аллокатор libc
выполняет проверку под названием fasttop
, сравнительно дешевую в отношении производительности. Идея проста: сообщить об ошибке в случае совпадения адреса освобождаемого элемента с адресом последнего элемента в списке свободных. Это позволит обнаруживать хотя бы некоторые ошибки двойного освобождения памяти.
Я добавил аналогичную проверку в функцию set_freepointer()
SLUB-аллокатора
и отправил патч в список рассылки ядра Linux (LKML). Патч вызвал оживленное обсуждение.
В этой доработке мейнтейнерам SLUB
не понравилось то, что:
- данная проверка выполняется на каждом добавлении элемента в список свободных (какие-то накладные расходы все же есть);
- данная проверка дублирует отладочный функционал
slub_debug
; - в случае двойного освобождения памяти происходит паника ядра (предложили опускать повторное добавление элемента в список свободных).
Я привел такие аргументы:
slub_debug
, действительно, предотвращает двойное освобождение памяти, однако по умолчанию не используется дистрибутивами Linux;- когда аллокатор зафиксировал двойное освобождение памяти, серьезная ошибка где-то в ядре Linux уже произошла. Поэтому не стоило бы доверять процессу, в рамках которого это случилось (он может быть эксплойтом).
В итоге при содействии Кейса Кука (Kees Cook) была достигнута договоренность включить предлагаемую проверку в опцию ядра CONFIG_SLAB_FREELIST_HARDENED
. В скором времени патч должен попасть в основную ветку, Linux Kernel mainline.
Обновление
В конечном итоге данная проверка попала в ядро v4.14
как часть функционала CONFIG_SLAB_FREELIST_HARDENED
. Больше подробностей доступно в обзоре от Кейса Кука.