
Эксплуатация уязвимости CVE-2021-3679 и документация по её исправлению
Это proof-of-concept программа, предназначенная для воспроизведения и доказательства логической ошибки в rb_per_cpu_empty. Программы, попадающие в эту ошибку, зависают в бесконечном цикле в пространстве ядра внутри tracing_read_pipe, и их невозможно приостановить или завершить ни одним сигналом UNIX (включая SIGKILL).
Несмотря на внешне схожее поведение (бесконечный цикл и полная загрузка CPU) с ошибкой вычисления seq_buf_used, исправленной в linux 4.5, эта программа доказывает существование другого бага, имеющего совершенно иную причину и, как предполагается, присутствующего в ядрах с версии 3.10 по 5.14-rc1 (см. также результат выполнения POC).
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: эта proof-of-concept программа может подвесить вашу Linux-систему, потреблять много энергии и не может быть завершена сигналом UNIX при срабатывании. Используйте на свой страх и риск.
Перед выполнением сценария proof-of-concept должны быть установлены следующие пакеты или команды.
gcc
realpath
nm
Также требуются Bash и команды обработки строк, такие как awk и grep; скорее всего, они уже включены в ваш дистрибутив.
Хотя это bash-сценарий, часть кода, задающая uprobe, зависит от платформы (в uprobe нельзя использовать $argN); поддерживаются i386, x86_64, arm и aarch64. Вы можете свободно добавить поддержку своей платформы.
Для запуска proof-of-concept просто выполните следующую команду с правами root:
./rbdetonate

Если ошибка сработает, будет запущен процесс dd, потребляющий целое ядро CPU, и его невозможно будет убить лишь сигналом UNIX, например SIGKILL.
Процесс bash сценария rbdetonate можно убить, но отправка SIGINT может как завершить его, так и нет, в зависимости от реализации bash. Более новые версии bash можно убить по SIGINT только пока запущен порождённый процесс dd.

Сценарий rbdetonate может завершиться и вывести Nothing buggy has been detected, если ему не удастся создать процесс dd после нескольких попыток. Однако это может занять некоторое время, поскольку мы повторяем попытки 8192 раза.
Комментарий: теоретически эта ошибка существует во всех версиях с функцией трассировки пользовательского пространства (>=3.10), но в более старых ядрах uprobe во многих случаях работает некорректно. Будем признательны, если вы поможете нам исправить это поведение.
О: Просто выполните с правами root команду echo > /sys/kernel/debug/tracing/instances/rbdetonate/trace — это должно вывести программу из бесконечного цикла.
О: Нет. Инструменты сборки здесь используются только для сборки программы rbwrite, которая по задуманным нами шагам даёт детерминированный результат, просто записывая данные в страницы кольцевого буфера на CPU#0.
Тривиально для этого proof-of-concept можно также использовать скомпилированную версию кода программы rbwrite, а адрес точки трассировки можно получить уже после этого.
Что касается использования трассировки Linux: если вам не мешает шум, создаваемый приложениями, добавление kprobe-точек трассировки к системным вызовам и функциям ядра тоже будет порождать события. Но для данного proof-of-concept это может внести недетерминизм, поэтому такой подход нежелателен.
| Версия | Воспроизведено | Скриншот |
|---|
| 5.14.0-rc2-00479-g86020194bc7e (исправленная версия) | N | 5.14.0-rc2-00479-g86020194bc7e.png |
| 5.14.0-rc2-00478-g2734d6c1b1a0 (5.14.0-rc2) | Y | 5.14.0-rc2-00478-g2734d6c1b1a0.png |
| 5.4.0-77-generic | Y | 5.4.0-77-generic.png |
| 4.4.0-142-generic | Y | 4.4.0-142-generic.png |