Центр Диагностики & База Системных Ошибок

Решения для Windows Server, Active Directory, 1С, СУБД, Linux, Cisco, MikroTik и IP-телефонии.

⚠️ Важная информация Все материалы, инструкции, команды и скрипты предоставлены исключительно в ознакомительных целях. Их применение может повлиять на работу операционной системы, баз данных и сетевого оборудования. Перед выполнением действий обязательно создайте резервную копию. При отсутствии необходимой квалификации обратитесь к ИТ-специалистам.
BPF: program rejected by verifier (infinite loop / memory access) Linux / DevOps

BPF program rejected by verifier — Ошибки BPF верификатора в Linux

Обновлено: 18.08.2026  ·  Официальная база знаний

Архитектура ошибки и симптомы сбоя

Ошибка «BPF: program rejected by verifier (infinite loop / memory access / R1 invalid mem access)» генерируется встроенным в ядро статическим анализатором безопасности eBPF Verifier при выполнении системного вызова bpf(BPF_PROG_LOAD, ...). Верификатор выполняет доказательство безопасности (Directed Acyclic Graph analysis) и блокирует загрузку программы, если обнаруживает риск бесконечного цикла, выход за границы указателя (Out-of-Bounds Memory Access), разыменование непроверенного NULL-указателя или превышение лимита сложности инструкций (1M instructions).

Диагностическая таблица параметров сбоя

ПараметрЗначениеИнженерный смысл сбоя
eBPF VerifierStatic Code AnalyzerЯдерный верификатор безопасности: гарантирует, что программа никогда не подвесит и не уронит ядро.
R0..R10 RegistersVirtual Register StateОтслеживание типов и диапазонов значений (min/max bounds) регистров процессора eBPF.
Back-edge / LoopInfinite Loop DetectionОбнаружение циклов без доказанного условия завершения за конечное число шагов.

Пошаговое дерево решений и сценарии траблшутинга

Сценарий 1: Просмотр полного лога верификатора (Verifier Log Buffer)

# Запуск программы с выводом отладочного лога верификатора:
# В libbpf задайте размер буфера логов или запустите через bpftrace -v:
sudo bpftrace -v script.bt

# Пример ошибки: 'R2 min value is negative, either use unsigned or 'var &= const''

Сценарий 2: Добавление явных проверок границ памяти (Bounds Checking)

Верификатор требует обязательной проверки смещений перед обращением к массиву или сетевому пакету:

// ОШИБКА: Прямой доступ к памяти пакета
// void *data = (void *)(long)ctx->data;
// char val = *((char *)data + offset);

// ИСПРАВЛЕНИЕ: Явная проверка границ перед чтением
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;

if ((void *)((char *)data + offset + 1) > data_end) {
    return XDP_DROP; // Проверка пройдена, верификатор спокоен
}
char val = *((char *)data + offset);

Сценарий 3: Исправление циклов через #pragma unroll или ограниченные циклы (Bounded Loops)

Начиная с Linux 5.3 ядро поддерживает циклы, но требует доказуемого конечного счетчика итераций:

// Принудительное разворачивание цикла компилятором:
#pragma unroll
for (int i = 0; i < 16; i++) {
    // Тело цикла
}

// Ограничение переменного цикла для верификатора:
#pragma clang loop unroll(disable)
for (int i = 0; i < 100; i++) {
    if (i >= dynamic_count) break; // Явный break
}

Сценарий 4: Безопасное чтение памяти ядра через bpf_probe_read_kernel

// Безопасное чтение произвольной структуры ядра без падения верификатора:
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
int pid;
bpf_probe_read_kernel(&pid, sizeof(pid), &task->tgid);
Собственные eBPF/XDP микросервисы отклоняются верификатором ядра?
ITSTM проводит аудит eBPF кода, исправление состояний регистров для верификатора и оптимизацию сетевых XDP-драйверов.
Практический опыт инженера: Всегда используйте хелпер bpf_core_read() из фреймворка BPF CO-RE (libbpf) для доступа к полям структур ядра. Это гарантирует автоматическую генерацию безопасного кода чтения, удовлетворяющего верификатор на любых версиях ядра.

Частые вопросы (FAQ)

Почему eBPF верификатор ругается на переменную, хотя в C-коде все корректно?

Компилятор Clang может оптимизировать код так, что связь между проверкой условия (if) и чтением памяти теряется в ассемблерных инструкциях BPF. Для верификатора регистр теряет статус проверенного диапазона (bounds), что приводит к отклонению программы.

Каков максимальный лимит инструкций, проверяемых BPF верификатором?

В современных ядрах Linux верификатор анализирует максимум 1 000 000 состояний инструкций (ранее лимит был 4096). Если программа слишком сложна и разветвлена, верификатор прекратит анализ с ошибкой 'program too complex'.

Что означает ошибка 'R1 invalid mem access 'inv''?

Она означает, что в регистре R1 находится невалидное скалярное значение (invalid pointer), которое программа пытается разыменовать как указатель памяти.

Как функция bpf_tail_call помогает обойти лимиты сложности верификатора?

Механизм хвоcтовых вызовов (Tail Calls) позволяет разбить монолитную сложную BPF программу на серию небольших независимых программ, каждая из которых проходит верификацию отдельно.