- BrainTools - https://www.braintools.ru -
В вашем ядре сейчас есть баги, которые не найдут в течение многих лет. Я знаю это, потому что проанализировала 125 183 из них, каждая в 20-летней git-истории ядра Linux помечена прослеживаемым тегом Fixes:.
Средний баг ядра существует 2,1 года. А некоторые подсистемы намного хуже: драйверы CAN‑шины [шины сети контроллеров] — в среднем 4,2 года, работа с сетью памяти [1] из‑за подсчёта ссылок в netfilter, существовавшей 19 лет.
Я создала инструмент, который выявляет 92% исторических багов в отложенном тестовом наборе данных о времени коммита. Вот что я узнала.
Краткий обзор основных выводов
|
Цифры |
Описание |
|---|---|
|
125 183 |
Пары «исправления багов» с прослеживаемыми тегами |
|
123 696 |
Валидные записи после фильтрации (0 < время существования < 20,7 лет) |
|
2,1 года |
Среднее время до обнаружения бага |
|
20,7 лет |
Самый долгоживущий баг (переполнение буфера ethtool) |
|
0% → 69% |
Баги, найденные за 1 год (c 2010 по 2022 гг.) |
|
92,2% |
Полнота VulnBERT на отложенном тестовом наборе 2024 года |
|
1,2% |
Доля ложноположительных результатов (против 48% у чистой CodeBERT) |
Я начала с анализа последних 10 000 коммитов с тегом Fixes: в ядре Linux. После фильтрации невалидных ссылок (коммитов, указывающих на хеши за пределами репозитория, некорректных тегов или коммитов слияния) у меня осталось 9 876 валидных записей об уязвимостях. Для анализа времени существования я исключила 27 исправлений, сделанных в тот же день. Это баги, возникшие и исправленные в течение нескольких часов. Осталось 9 849 багов со значительным временем существования.
Результаты поразили меня:
|
Метрика |
Значение |
|---|---|
|
Проанализировано багов |
9 876 |
|
Среднее время существования |
2,8 года |
|
Медианное время существования |
1,0 года |
|
Максимум |
20,7 лет |
Почти 20% багов скрывались 5 и более лет. Особенно плохо выглядела сетевая подсистема: в среднем по 5,1 года В netfilter я обнаружила утечку из-за счетчика ссылок, которая находилась в ядре 19 лет.
Первоначальные результаты: половина ошибок обнаруживается в течение года, а 20% скрываются более пяти лет.
Но что-то меня беспокоило: мой набор данных содержал исправления только 2025 года. Видела ли я полную картину, или только верхушку айсберга?
Я переписала свой майнер так, чтобы он находил каждый тег Fixes: с тех пор, как в 2005 году Linux перешел на Git. Шесть часов спустя у меня были 125 183 записи об уязвимостях, что в 12 раз больше, чем в первоначальном наборе.
Цифры существенно изменились:
|
Метрика |
Только 2025 |
Полная история (2005–2025 гг.) |
|---|---|---|
|
Проанализировано багов |
9 876 |
125 183 |
|
Среднее время существования |
2,8 года |
2,1 года |
|
Медианное время существования |
1,0 года |
0,7 года |
|
Более 5 лет |
19,4% |
13,5% |
|
10 и более лет |
6,6% |
4,2% |
Полная история: 57% ошибок обнаружено в течение года. Длинный хвост меньше, чем кажется на первый взгляд.
Откуда такая разница? Мой первоначальный набор данных только за 2025 год был предвзят. Исправления в 2025 году содержат:
Новые баги, появившиеся и устранённые недавно.
Древние баги, наконец обнаруженные спустя много лет.
Древние баги искажали средний показатель вверх. Если включить полную историю всех багов, которые были обнаружены и исправлены за год, среднее время существования упадёт с 2,8 до 2,1 года.
Самый поразительный вывод из полного набора данных: баги, появившиеся в последние годы, исправляются гораздо быстрее.
|
Год появления |
Баги |
Среднее время существования |
Процентов найдено за год |
|---|---|---|---|
|
2010 |
1 033 |
9,9 лет |
0% |
|
2014 |
3 991 |
3,9 года |
31% |
|
2018 |
11 334 |
1,7 года |
54% |
|
2022 |
11 090 |
0,8 года |
69% |
На поиск багов, появившихся в 2010 году, ушло почти 10 лет. А появившиеся в 2024 году баги обнаруживаются за 5 месяцев. На первый взгляд выглядит как улучшение в 20 раз!
Но вот загвоздка: эти данные ограничены в правой части [временной шкалы]. Баги, появившиеся в 2022 году, не могут существовать 10 лет, ведь сейчас только 2026 год. В 2030 году мы можем обнаружить больше багов 2022 года, что повысит среднее значение.
Более справедливое сравнение — «Процентов найдено за год» и улучшение: с 0% 2010 г. до 69% 2022 г. Это реальный прогресс, вероятно, обусловленный:
Syzkaller, выпущенным в 2015-м.
Санитайзерами KASAN, KMSAN, KCSAN.
Лучшим статическим анализом.
Большим числом ревьюеров.
Но есть отставание. Когда я смотрю только на баги, исправленные в 2024–2025 годах:
60% обнаружены за последние 2 года — это новые, быстро обнаруженные баги.
18% появились 5–10 лет назад.
6,5% появились более 10 лет назад.
Мы одновременно быстрее выявляем новые баги и медленно работаем над ~5400 древними багами, которые скрывались более 5 лет.
В ядре есть соглашение: когда коммит исправляет баг, он содержит тег Fixes:, указывающий на коммит, где баг возник.
commit de788b2e6227
Author: Florian Westphal <fw@strlen.de>
Date: Fri Aug 1 17:25:08 2025 +0200
netfilter: ctnetlink: fix refcount leak on table dump
Fixes: d205dc40798d ("netfilter: ctnetlink: ...")
Я написала майнер, который:
Запускает git log --grep="Fixes:", чтобы найти все коммиты-исправления.
Извлекает хеш упомянутого коммита из тега Fixes:.
Извлекает даты из обоих коммитов.
Классифицирует подсистему по путям к файлам (более 70 выражений).
Обнаруживает тип бага по ключевым словам в сообщении коммита.
Рассчитывает время существования бага.
fixes_pattern = r'Fixes:s*([0-9a-f]{12,40})'
match = re.search(fixes_pattern, commit_message)
if match:
introducing_hash = match.group(1)
lifetime_days = (fixing_date - introducing_date).days
Подробности о наборе данных:
|
Параметр |
Значение |
|---|---|
|
Версия ядра |
v6.19-rc3 |
|
Дата сбора данных |
6 января 2026 г. |
|
Исправления, собранные с тех пор |
16 апреля 2005 г. (эпоха git) |
|
Всего записей |
125 183 |
|
Уникальные коммиты исправлений |
119 449 |
|
Уникальные авторы, которые ввели баг |
9 159 |
|
С идентификатором CVE |
158 |
|
Для стабильного бекпорта |
27 875 (22%) |
Примечание о покрытии. В ядре ~448 000 коммитов, где в той или иной форме есть слово fix, но только у ~124 000 (28%) правильно проставлен тег Fixes:. Мой набор данных отражает хорошо документированные баги, то есть баги, у которых мейнтейнеры выявили основную причину.
В некоторых подсистемах есть баги, которые сохраняются гораздо дольше, чем в других:
|
Подсистема |
Количество багов |
Среднее время существования |
|---|---|---|
|
Драйверы/CAN |
446 |
4,2 года |
|
Сеть/SCTP |
279 |
4,0 года |
|
Сеть/IPV4 |
1,661 |
3,6 года |
|
USB |
2 505 |
3,5 года |
|
TTY |
1 033 |
3,5 года |
|
netfilter |
1 181 |
2,9 года |
|
Сеть |
6 079 |
2,9 года |
|
Память |
2 459 |
1,8 года |
|
GPU |
5 212 |
1,4 года |
|
BPF |
959 |
1,1 года |
Баги CAN-шины и SCTP сохраняются дольше всего. Баги BPF и GPU обнаруживаются быстрее всех.
Драйверы CAN-шины и сети SCTP имеют баги, которые сохраняются дольше всего, вероятно, потому, что оба — нишевые протоколы с меньшим охватом тестирования. Баги графического процессора (особенно Intel i915) и BPF обнаруживаются быстрее всех, вероятно, благодаря специальной инфраструктуре фаззинга.
Интересный вывод при сравнении истории только за 2025 год со всей историей:
|
Подсистема |
Среднее, только 2025 |
Среднее, полная история |
Разница |
|---|---|---|---|
|
Сеть |
5,2 года |
2,9 года |
-2,3 года |
|
Файловая система |
3,8 года |
2,6 года |
-1,2 года |
|
Драйверы/Сеть |
3,3 года |
2,2 года |
-1,1 года |
|
GPU |
1,4 года |
1,4 года |
0 лет |
По данным только за 2025 год сеть выглядела ужасно (5,2 года!), но на самом деле она ближе к среднему значению за всю историю (2,9 года). Исправления 2025 года направлены на устранение накопившихся древних сетевых багов. В любом случае данные по GPU выглядят одинаково, и эти баги выявляются стабильно быстро.
Сложнее всего обнаружить состояние гонки. На обнаружение в среднем уходит 5,1 года:
|
Тип бага |
Количество |
Среднее время существования |
Медианное время существования |
|---|---|---|---|
|
Состояние гонки |
1 188 |
5,1 года |
2,6 года |
|
Целочисленное переполнение |
298 |
3,9 года |
2,2 года |
|
UAF [использование после освобождения] |
2 963 |
3,2 года |
1,4 года |
|
Утечка памяти |
2 846 |
3,1 года |
1,4 года |
|
Переполнение буфера |
399 |
3,1 года |
1,5 года |
|
Подсчёт ссылок |
2 209 |
2,8 года |
1,3 года |
|
Разыменовывание нулевого указателя |
4 931 |
2,2 года |
0,7 года |
|
Взаимоблокировка |
1 683 |
2,2 года |
0,8 года |
Почему состояния гонки так долго скрываются? Они недетерминированы и срабатывают только при определённых условиях во времени, которые могут возникать один раз на миллион выполнений. Даже такие санитайзеры, как KCSAN, могут отмечать только гонки, за которыми наблюдают.
30% багов исправляются их автором. Их исправляет человек, допустивший баг. Полагаю, владение кодом имеет значение.
Меньше фаззинга. Syzkaller превосходно справляется с фаззингом системных вызовов, но затрудняется с протоколами, где отслеживается состояние. Фаззинг netfilter требует создания валидных последовательностей пакетов, которые проходят через специфичные состояния [в системе] отслеживания соединений.
Сложнее спровоцировать. Многие сетевые баги требуют:
Определённой последовательности пакетов.
Состояния гонки между конкурентными потоками.
Нехватки памяти во время табличных операций.
Особых топологий NUMA.
Старый код с меньшим числом наблюдателей. Основная сетевая инфраструктура, например nf_conntrack, написана в середине 2000-х. Она работает, поэтому никто не переписывает. Но «стабильность» означает, что меньше разработчиков активно проверяют код.
Один из самых старых сетевых багов в моём наборе данных обнаружена в августе 2006 года и исправлена в августе 2025 года:
// ctnetlink_dump_table() - забагованный путь выполнения
if (res < 0) {
nf_conntrack_get(&ct->ct_general); // инкремент счётчика ссылок
cb->args[1] = (unsigned long)ct;
break;
}
Ирония. Коммит d205dc40798d сам по себе был исправлением: «[NETFILTER]: ctnetlink: исправление взаимоблокировки при дампе таблицы». Патрик МакХарди исправлял тупик, удалив вызов _put(). При этом он допустил утечку из-за подсчёта ссылок, которая останется на 19 лет.
Баг: код не проверяет условие ct == last. Если текущая запись такая же, как уже сохранённая, то её счётчик ссылок увеличивается дважды, но только один раз уменьшается. Память никогда не освободится от объекта:
// Что должно проверяться:
if (res < 0) {
if (ct != last) // <-- эта проверка упускалась 19 лет
nf_conntrack_get(&ct->ct_general);
cb->args[1] = (unsigned long)ct;
break;
}
Последствия. Утечки памяти накапливаются. В конце концов nf_conntrack_cleanup_net_list() вечно ждёт, пока счётчик ссылок достигнет нуля. Удаление сетевого пространства имён зависает. Если вы используете контейнеры, это блокирует их очистку на неопределённый срок.
Почему баг существовал 19 лет? Вам приходилось из-за нехватки памяти запускать conntrack_resize.sh в цикле в течение ~20 минут? В коммите исправления сказано: «Это можно воспроизвести, запустив самотестирование conntrack_resize.sh в цикле. У меня это занимает ~20 минут на вытесняющем ядре». Никто не проводил конкретно эту последовательность испытаний в течение двух десятилетий.
Вот закономерность, которую я продолжаю наблюдать: кто-то замечает неопределённое поведение [3], отправляет исправление, но оно не закрывает дыру полностью.
Пример: проверка полей множества netfilter
|
Дата |
Коммит |
Что произошло |
|---|---|---|
|
Январь 2020 |
|
Стефано Бривио добавил поддержку множеств с несколькими полями диапазона. Чтобы указать длину поля, вводит |
|
Январь 2024 |
|
Пабло Нейра замечает, что код не проверяет, соответствует ли сумма длин полей общей длине ключа. Отправляет исправление. Сообщение коммита: «Я пока не спровоцировал падение из-за несовпадения длин полей и общей длины ключа для множества в |
|
Январь 2025 |
|
Исследователь безопасности находит обходной путь. Исправление 2024 года было неполным — всё ещё оставались пути кода, где могло быть несовпадение. Настоящее исправление отправлено. |
Исправление 2024 года было признанием того, что что-то было не так, но Пабло не смог найти сбой, поэтому оно было консервативным. Год спустя кто-то нашел место падения.
Этот паттерн предполагает возможность обнаружить коммиты, где говорится нечто вроде this is undefined behavior [это неопределённое поведение] или I couldn’t trigger this but… [Я не смог спровоцировать его, но…]. Эти фразы — флаги. Автор знает, что что-то не так, но не полностью описал баги. Такие баги заслуживают особой скрупулёзности.
Глядя на баги, существующие более 10 лет, я вижу общие закономерности:
1. Баги подсчёта ссылок
kref_get(&obj->ref);
// ... путь с ошибкой возвращает [управление] без вызова kref_put()
Они не роняют систему сразу. Память утекает медленно. В долго работающей системе вы можете не заметить, пока несколько месяцев спустя убийца OOM [Out Of Memory — недостаточно памяти] не начнёт стрелять.
2. Отсутствие проверок на NULL после разыменования
struct foo *f = get_foo();
f->bar = 1; // сначала происходит разыменовывание
if (!f) return -EINVAL; // проверка идёт позже
Компилятор может оптимизировать проверку NULL, поскольку вы уже разыменовали. Баги сохраняются, потому что на практике указатель редко имеет значение NULL.
3. Целочисленное переполнение при вычислении размера
size_t total = n_elements * element_size; // может переполниться
buf = kmalloc(total, GFP_KERNEL);
memcpy(buf, src, n_elements * element_size); // копирует больше, чем выделяет
Если n_elements поступает из пользовательского пространства, злоумышленник может вызвать выделение небольшого буфера, за которым следует большая копия.
4. Состояния гонки в конечных автоматах
spin_lock(&lock);
if (state == READY) {
spin_unlock(&lock);
// здесь окно: другой поток может изменить состояние
do_operation(); // предполагает, что состояние всё ещё READY
}
Для состояния гонки требуется точный момент. Баги могут проявляться редкими сбоями, воспроизвести которые не сможет никто.
Каждый день, когда в ядре появляется баг, — это ещё один день, когда миллионы устройств становятся уязвимыми. Смартфоны Android, серверы, встраиваемые системы, облачная инфраструктура — всё это работающий код ядра с багами, которые не будут найдены в течение многих лет.
Я создала VulnBERT. Эта модель прогнозирует, приведёт ли коммит к уязвимости.
Эволюция [4] модели:
|
Модель |
Полнота |
FPR (доля ложноположительных срабатываний) |
F1 |
Примечания |
|---|---|---|---|---|
|
Случайный лес |
76,8% |
15,9% |
0,80 |
Только созданные вручную признаки |
|
CodeBERT (тонкая настройка) |
89,2% |
48,1% |
0,65 |
Высокая полнота, бесполезная FPR |
|
VulnBERT |
92,2% |
1,2% |
0,95 |
Лучшее от обоих подходов |
Проблема с чистой CodeBERT. Сначала я попробовала выполнить тонкую настройку CodeBERT напрямую. Результаты — 89% полноты, но 48% ложноположительных срабатываний (измерения проводились на том же тестовом наборе). Это бесполезно, отмечена половина всех коммитов.
Почему так плохо? CodeBERT изучает простое решение: «большая разница — опасно», «много указателей — рискованно». Эти корреляции существуют в обучающих данных, но не обобщаются. Модель сопоставляет поверхностным, а не настоящим паттернам багов.
Подход VulnBERT. Объединение распознавания нейронных паттернов с экспертизой в области знаний от человека.
┌───────────────────────────────────────────────────────────────────┐
│ ВХОД: Git Diff │
└───────────────────────────────┬───────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────────────┐
│ Энкодер нарезанного diff │ │ Извлечение созданных признаков │
│ (CodeBERT + внимание) │ │ (51 ручной признак) │
└─────────────┬─────────────┘ └─────────────────┬─────────────────┘
│ [768 признаков] │ [51 признак]
└───────────────┬───────────────────┘
▼
┌───────────────────────────────┐
│ Перекрёстное слияние │
│ "Когда код выглядит как X, │
│ признак Y становится весомее" │
└───────────────┬───────────────┘
▼
┌───────────────────────────────┐
│ Классификатор риска │
└───────────────────────────────┘
Основа производительности — эти нововведения:
1. Кодирование фрагментов для длинных различий. Ограничение CodeBERT в 512 токенов отсекает большинство различий ядра [в diff] (часто более 2000 токенов). Я разбиваю код на куски, кодирую каждый, а затем агрегирую при помощи обучаемого внимания [5]:
# Обучаемое внимание к кускам diff
chunk_attention = nn.Sequential(
nn.Linear(hidden_size, hidden_size // 4),
nn.Tanh(),
nn.Linear(hidden_size // 4, 1)
)
attention_weights = F.softmax(chunk_attention(chunk_embeddings), dim=1)
pooled = (attention_weights * chunk_embeddings).sum(dim=1)
Модель изучает, какие фрагменты важны, например, со spin_lock, но без spin_unlock; это не бойлерплейт.
2. Объединение признаков за счёт перекрёстного внимания. Нейронные сети не учитывают паттерны, специфичные для области знаний. Я извлекаю 51 признак, созданный вручную, с помощью регулярных выражений и AST-подобного анализа различий:
|
Категория |
Признаки |
|---|---|
|
Базовые (4) |
|
|
Память (3) |
|
|
Подсчёт ссылок (5) |
|
|
Блокировка (5) |
|
|
Указатели (4) |
|
|
Обработка ошибок (6) |
|
|
Семантика (13) |
|
|
Структурные (11) |
|
Ключевые признаки паттерна бага:
'unbalanced_refcount': 1, # kref_get без kref_put → утечка
'unbalanced_lock': 1, # spin_lock без spin_unlock → взаимоблокировка
'has_deref_no_null_check': 0,# *ptr без if(!ptr) → разыменование null
'has_alloc_no_free': 0, # kmalloc без kfree → утечка
Перекрёстное внимание изучает условные отношения. Когда CodeBERT видит паттерны блокировки и unbalanced_lock=1 — это высокий риск. Сигналов по отдельности недостаточно, значение имеет комбинация.
# Слияние признаков через перекрёстное внимание
feature_embedding = feature_projection(handcrafted_features) # 51 → 768
attended, _ = cross_attention(
query=code_embedding, # Какие паттерны есть в коде?
key=feature_embedding, # Что говорят созданные вручную признаки?
value=feature_embedding
)
fused = fusion_layer(torch.cat([code_embedding, attended], dim=-1))
3. Потеря фокуса в сложных паттернах. Обучающие данные несбалансированы там, где большинство коммитов безопасно. Стандартные обновления градиента кросс-энтропии на простых примерах. Очаговая потеря:
Standard loss when p=0.95 (easy): 0.05
Focal loss when p=0.95: 0.000125 (400x smaller)
Модель фокусируется на неоднозначных коммитах: «жёстких», важных пяти процентах.
Влияние каждого компонента (оценивается на основе экспериментов с абляцией):
|
Компонент |
Оценка F1 |
|---|---|
|
Базовый уровень CodeBERT |
~76% |
|
+ Очаговая потеря |
~80% |
|
+ Слияние признаков |
~88% |
|
+ Контрастное обучение [6] |
~91% |
|
Вся VulnBERT |
95,4% |
Влияние отдельных компонентов приблизительно; точную атрибуцию затрудняет взаимодействие между компонентами.
Ключевой вывод: ни нейронные сети, ни созданные вручную правила не достигают наилучших результатов сами по себе. Комбинация — да.
Результаты временной проверки (обучающий набор — ≤ 2023, тестовый — 2024):
|
Метрика |
Цель |
Результат |
|---|---|---|
|
Полнота |
90% |
92,2% ✓ |
|
FPR |
<10% |
1,2% ✓ |
|
Точность |
— |
98,7% |
|
F1 |
— |
95.4% |
|
AUC |
— |
98,4% |
Что означают эти показатели:
Полнота 92,2%. Из всех коммитов, вызывающих баги, обнаруживается 92,2%. Отсутствует 7,8% багов.
FPR 1,2%. Из всех безопасных коммитов некорректно отмечаются 1,2%. Ниже FPR = меньше ложных срабатываний.
Точность 98,7%. Из коммитов, которые отмечаются как рискованные, на самом деле таковыми являются 98,7%. Модель почти всегда права, когда поднимает тревогу.
F1 95,4%. Гармоническое среднее значение точности и полноты. Одно число, суммирующее общую производительность.
AUC 98,4%. Площадь под кривой ROC. Измеряет качество ранжирования — насколько хорошо модель отделяет баги от безопасных коммитов по всем пороговым значениям.
Модель правильно различает один и тот же баг на разных стадиях:
|
Коммит |
Описание |
Риск |
|---|---|---|
|
|
Исправление UAF в xe_vfio |
12,4% |
|
|
Введено UAF |
83,8% |
При анализе коммита, содержащего баг d205dc40798d:
- if (ct == last) {
- nf_conntrack_put(&last->ct_general); // удалено!
- }
+ if (ct == last) {
+ last = NULL;
continue;
}
if (ctnetlink_fill_info(...) < 0) {
nf_conntrack_get(&ct->ct_general); // ещё здесь
Извлечённые признаки:
|
Признак |
Значение |
Сигнал |
|---|---|---|
|
|
1 |
Есть |
|
|
0 |
Удалена |
|
|
1 |
Обнаруженные несоответствия |
|
|
1 |
Использует |
|
|
1 |
Использует |
Прогноз модели: 72% риска: HIGH.
Признак unbalanced_refcount активируется, поскольку put() удалена, но get() осталась. Классический паттерн утечки из-за подсчёта ссылок.
Ограничения набора данных:
Только баги с тегами Fixes: (~28% коммитов исправлений). Предвзятость отбора: хорошо документированные баги, как правило, более серьёзны.
Только ветка main: не содержит исправления только для стабильных веток и исправления вендоров.
Эвристическая классификация подсистем (регулярное выражение по файловым путям).
Обнаружение типа бага по соответствию ключевых слов в сообщениях коммита, многие баги относятся к типу неизвестных.
При расчёте времени существования учитываются даты авторов ошибки [7], а не даты коммитов, перебазирование может искажать временные метки.
Некоторые «баги» могут быть теоретическими (комментарии типа fix possible race [исправить возможную гонку] без подтверждённого триггера).
Ограничения модели:
92,2% полноты приходится на отложенный тестовый набор 2024 года, что не гарантия для будущих багов.
Нельзя обнаружить семантические баги (баги логики без синтаксического сигнала).
Межфункциональные «слепые зоны» (баг охватывает несколько файлов).
Смещение обучающих данных (паттерны изучаются по найденным багам, новые могут быть пропущены).
Ложные срабатывания из-за намеренного поведения [8] (инициализация/очистка в разных коммитах).
Протестировано только на коде ядра Linux, не может распространяться на другие кодовые базы.
Статистические ограничения:
Смещение времени существования багов при сравнении по годам (недавние баги пока не могут существовать долго).
Корреляция ≠ причинно‑следственная связь для различий подсистемы/типа бага.
Что это означает: VulnBERT — это инструмент сортировки, а не гарантия. Она обнаруживает 92% багов с узнаваемыми закономерностями. Остальные 8% и новые классы ошибок все ещё нуждаются в человеческом анализе и фаззинге.
Полнота 92,2% и FPR 1,2% готовы к работе в продакшене. Но есть ещё кое‑что, что нужно сделать:
Исследование на основе RL. Вместо статического сопоставления с образцом научите агента исследовать пути кода и самостоятельно находить баги. Текущая модель прогнозирует риск; агент RL cможет генерировать триггерные входные данные.
Интеграция Syzkaller. Сделать покрытие фаззера сигналом вознаграждения. Если модель отмечает коммит и Syzkaller обнаруживает сбой в данном пути кода, это сильный положительный сигнал.
Модели, специфичные для подсистемы. Сетевые баги имеют иные закономерности, чем баги драйверов. Модель, настроенная на netfilter, на коммитах netfilter может превзойти общую.
Цель не в том, чтобы заменить ревьюеров, а в том, чтобы указать на 10% коммитов, которые, скорее всего, будут проблематичны, чтобы люди могли сосредоточить внимание там, где это важно.
При извлечении набора данных применяется соглашение о тегах ядра Fixes:.
Вот основная логика [9]:
def extract_fixes_tag(commit_msg: str) -> Optional[str]:
"""Извлекает ID коммита из тега Fixes:"""
pattern = r'Fixes:s*([a-f0-9]{12,40})'
match = re.search(pattern, commit_msg, re.IGNORECASE)
return match.group(1) if match else None
# Майнит все теги Fixes: из истории git
git log --since="2005-04-16" --grep="Fixes:" --format="%H"
# Для каждого коммита исправления:
# - Извлекает хеш коммита, где баг введён
# - Берёт даты обоих коммитов
# - Вычисляет время существования
# - Классифицирует подсистему по путям к файлам
Полный код майнера и набор данных: github.com/quguanni/kernel-vuln-data [10].
125 183 бага проанализировано в 20-летней истории ядра Linux в git (123 696 с валидным временем существования).
Средний срок существования бага: 2,1 года (2,8 года по данным только за 2025 год из‑за систематического сдвига времени существования в недавних исправлениях).
0% → 69% багов, обнаруженных в течение 1 года (2010–2022 гг.). Это реальный прогресс за счёт лучших инструментов.
13,5% багов скрываются более 5 лет. Это самые опасные баги.
Дольше всего скрываются состояния гонки (в среднем 5,1 года).
VulnBERT обнаруживает 92,2% багов в отложенном тестовом наборе 2024 года с FPR всего 1,2% (98,4% AUC).
Набор данных: github.com/quguanni/kernel-vuln-data [10].
Если вы работаете над безопасностью ядра, обнаружением уязвимостей или машинным обучением для анализа кода, мне бы хотелось поговорить: jenny@pebblebed.com [11].
Автор: stranger777
Источник [12]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/25680
URLs in this post:
[1] памяти: http://www.braintools.ru/article/4140
[2] Image: https://sourcecraft.dev/
[3] поведение: http://www.braintools.ru/article/9372
[4] Эволюция: http://www.braintools.ru/article/7702
[5] внимания: http://www.braintools.ru/article/7595
[6] обучение: http://www.braintools.ru/article/5125
[7] ошибки: http://www.braintools.ru/article/4192
[8] поведения: http://www.braintools.ru/article/5593
[9] логика: http://www.braintools.ru/article/7640
[10] github.com/quguanni/kernel-vuln-data: https://github.com/quguanni/kernel-vuln-data
[11] jenny@pebblebed.com: mailto:jenny@pebblebed.com
[12] Источник: https://habr.com/ru/articles/996682/?utm_source=habrahabr&utm_medium=rss&utm_campaign=996682
Нажмите здесь для печати.