1. Тема дня
Unit test проверяет конкретный сценарий. Property-based test- ing проверяет законы системы на множестве автоматически сгенерированных сценариев.
random / coverage-guided bytes
-> semantic decoder
-> MODEM / PHASE / CAN events
-> production FSM
-> invariants
-> PASS / FAILГлавная мысль: fuzzer не обязан знать правильный output для каждого сценария; ему достаточно знать свойства, которые никогда не должны нарушаться.
2. Зачем это нужно
Для EC25 fuzzer генерирует последовательности AT_OK, AT_ERROR, CEREG, TIMEOUT, POWER, MQTT_LOST и ищет stale-event bugs: поздний OK, старый timeout, timeout после generation change. Для phase FSM генерирует RED/GREEN/YELLOW edges, debounce timers, stale input, ADC fault и проверяет, что RED && GREEN не остаётся quality VALID. Для DMA/pool генерирует DMA_COMPLETE, CPU_ACQUIRE, CPU_RELEASE и ловит double release, stale handle, ownership corruption.
3. Теория
Типы properties
Safety:
RED && GREEN && quality == NORMAL never happensBounds:
retry_count <= MAX_RETRY
queue_count <= capacityMonotonic:
transaction_id never decreases
config_generation never decreasesConsistency:
mqtt == CONNECTED -> tcp == CONNECTED
state == ONLINE -> registration == REGISTEREDIdempotency:
duplicate CEREG=1 does not start PDP twiceStale-event:
AT_OK tx != current_tx does not finish current transactionSemantic decoder
FSM fuzzer лучше кормить не raw bytes, а нормализованными событиями. Например 4 bytes per event:
byte 0: event type
byte 1: argument
byte 2: transaction relation
byte 3: generation relationВажно генерировать интересные значения: current, current-1, current+1, 0, UINT32_MAX.
Разделяй parser fuzz и FSM fuzz
Parser fuzz получает arbitrary bytes. FSM fuzz получает semantic events.
Shrinking
Найденный crash из 87 событий нужно минимизировать до короткой последовательности. LibFuzzer умеет -minimize_crash=1, а semantic shrinker дополнительно может удалять события и упрощать аргументы.
4. Типичные ошибки
- Fuzzing всей прошивки вместо отдельного core.
- Нет invariants.
- Неверная property даёт ложные баги.
- Генерировать только happy-path events.
- Генерировать полностью случайные events и не доходить до глубоких состояний.
- Не использовать boundary values.
- Не генерировать stale transaction/generation IDs.
- Использовать real clock.
- Оставлять global state между LLVMFuzzerTestOneInput() calls.
- Огромный max_len с самого начала.
- Не ограничивать action count/internal transitions.
- Не использовать ASan/UBSan.
- Не минимизировать reproducer.
- После fix удалить crash input вместо regression corpus.
5. Практическое задание
Создай tests/fuzz/modem/modem_fuzz.c. События:
typedef enum {
FUZZ_EV_POWER = 0,
FUZZ_EV_AT_OK,
FUZZ_EV_AT_ERROR,
FUZZ_EV_CEREG,
FUZZ_EV_TIMER,
FUZZ_EV_MQTT_LOST,
FUZZ_EV_COUNT,
} fuzz_event_type_t;Сделай transaction generator:
static uint32_t fuzz_transaction_id(const modem_core_t *modem, uint8_t selector)
{
switch (selector % 5U) {
case 0: return modem->transaction_id;
case 1: return modem->transaction_id - 1U;
case 2: return modem->transaction_id + 1U;
case 3: return 0U;
default: return UINT32_MAX;
}
}Проверь invariants:
retry_count <= retry_limit
MODEM_OFF -> mqtt != CONNECTED
mqtt == CONNECTED -> modem == ONLINE
action_count <= ACTION_CAPACITY
stale AT_OK не завершает текущую transactionСборка:
clang -std=c17 -O1 -g -fno-omit-frame-pointer \
-fsanitize=fuzzer,address,undefined \
modem_core.c tests/fuzz/modem/modem_fuzz.c \
-Icomponents/modem_core/include \
-o build/modem_fuzzЗапуск:
build/modem_fuzz tests/fuzz/modem/corpus -max_len=256 -max_total_time=606. Что попробовать дальше
- Конвертировать .evlog из урока 54 в seed corpus.
- Добавить fuzz target для phase_fsm.
- Сделать differential fuzzing старой и новой реализации FSM.
- Генерировать HIL-сценарии из минимальных fuzz cases.
Задание
Сбой требует 87 semantic events. Как превратить его в regression input? Почему для конечного счётчика свойству «transaction_id никогда не уменьшается» нужно условие epoch/wrap?
Критерии самопроверки: Сохраните минимальный regression input и явно оговорите монотонность при wrap счётчика.
Показать ответ автора
Минимизируйте последовательность с сохранением сбоя, затем оставьте reproducer в regression corpus и проверяйте те же invariants. Конечный счётчик может перейти с UINT32_MAX на 0, поэтому безусловная числовая монотонность даст ложный bug; задайте epoch или ограниченный интервал без wrap, отдельно проверяя wrap handling.