Пример исправления crash'а, взятого с syzbot и отправка патча в upstream ядра
Для обеспечения стабильности и безопасности ядра Linux необходимо регулярно выявлять и устранять потенциальные уязвимости. Syzkaller — это фреймворк, который помогает в этом процессе, автоматически генерируя случайные тесты с использованием метода фаззинга для выявления скрытых ошибок. Когда такие тесты приводят к сбою системы, это называется crash. Эти сбои затем автоматически отслеживаются и анализируются системой syzbot , которая уведомляет разработчиков о найденных проблемах, способствуя улучшению качества кода ядра.
Например, рассмотрим WARNING in mark_buffer_dirty :
1) Имеются репродюссеры на языке Си (C repro ) и на языке сизколлера, нам удобен Си - копируем исх код и компилируем программу:
$ gcc repro.c -pthread -o repro
2) Делаем пробный запуск с разными правами на интересующем ядре. В данном случае ошибка воспроизводится при запуске от рута. (Еще можно экспериментировать с capability для оценки уровня критичности ошибки.)
3) Согласно отчетам (ссылки report), к предупреждению приводит выполнение след. строки:
WARN_ON_ONCE(!buffer_uptodate(bh));
функции void mark_buffer_dirty(struct buffer_head *bh)
Сам вызов этой функции из модуля файловой системы bfs:
new = sb_getblk(sb, to);
memcpy(new->b_data, bh->b_data, bh->b_size);
mark_buffer_dirty(new);
Здесь же рядом в коде при запуске репродюссера обнаружено еще одно падение, связанное с попыткой разыменовать указатель struct buffer_head *new, содержащий нулевое значение после выполнения функции sb_getblk().
Для поиска мест падения в исходном коде, можно воспользоваться скриптом decode_stacktrace.sh из дерева ядра, который переводит строки с hex смещением
bfs_get_block+0x320/0xdf0 [bfs]
bfs_write_begin+0xd0/0xd0 [bfs]
в удобочитаемые
bfs_get_block (fs/bfs/file.c:125) bfs
bfs_write_begin (fs/bfs/file.c:66) bfs
Сам запуск скрипта, например, на ядре 6.1.105:
$ cat log.txt | ./scripts/decode_stacktrace.sh /usr/lib/debug/lib/modules/6.1.105-un-def-alt1/vmlinux /usr/src/debug/kernel-image-un-def-6.1.105-alt1/kernel-source-6.1 /usr/lib/debug/lib/modules/6.1.105-un-def-alt1/kernel/
Здесь мы передаем стектрейс (log.txt) на вход скрипту и указываем пути для vmlinux, каталогов исходного кода и модулей, которые поставляются пакетами с суффиксом -debuginfo.
4) Исправляем. Если есть сложности - смотрим на реализацию похожего функционала других подсистем.
В данном случае, добавим проверку на NULL и корректно выйдем из функции (1-й патч ):
new = sb_getblk(sb, to);
+ if (!new) {
+ brelse(bh);
+ return -EIO;
+ }
memcpy(new->b_data, bh->b_data, bh->b_size);
Касательно самой ошибки, пометим буфер (2-й патч ):
+ set_buffer_uptodate(new);
mark_buffer_dirty(new);
5) Генерим патчи (2 шт):
$ git format-patch -2
6) Проверяем оформление патчей скриптом checkpatch.pl :
$ ./scripts/checkpatch.pl 000*
7) Получаем список мейнтейнеров скриптом get_maintainer.pl :
$ ./scripts/get_maintainer.pl 000*
8) Настраиваем почту и отправляем, например, эти патчи отправлялись как 3-я версия командой:
$ git send-email --from=kovalev@altlinux.org --to=linux-fsdevel@vger.kernel.org,linux-kernel@vger.kernel.org,aivazian.tigran@gmail.com,stable@vger.kernel.org --cc=lvc-patches@linuxtesting.org,dutyrok@altlinux.org,kovalev@altlinux.org --compose ./000*
--compose здесь как раз для добавления сопроводительного письма в окно ввода:
Subject: [PATCH v3 0/2] bfs: fix null-ptr-deref and possible warning in bfs_move_block() func
https://syzkaller.appspot.com/bug?extid=d98fd19acd08b36ff422
[PATCH v3 1/2] bfs: prevent null pointer dereference in bfs_move_block()
v3: Changed the error handling
[PATCH v3 2/2] bfs: ensure buffer is marked uptodate before marking it dirty
v3: Replaced the buffer up-to-date check with an error exit by forcefully
setting the buffer as up-to-date before call mark_buffer_dirty()
Обычно при отправке патчей утилита send-email добавляет в копию всех адресатов, которых найдет в коммит сообщении. Если это не нужно, можно указать дополнительно опцию --suppress-cc=all