Skip to content

test: Improve AFL++ fuzzing infrastructure#6618

Merged
vyavdoshenko merged 16 commits intomainfrom
bobik/fuzz_update
Feb 13, 2026
Merged

test: Improve AFL++ fuzzing infrastructure#6618
vyavdoshenko merged 16 commits intomainfrom
bobik/fuzz_update

Conversation

@vyavdoshenko
Copy link
Contributor

Expand seed corpus, add RESP-aware custom mutator, and improve fuzzing tooling.

Changes:
Custom mutator (fuzz/resp_mutator.py):

  • AFL++ Python mutator that parses RESP and mutates at the command/argument level instead of random bytes
  • Knows 150+ Dragonfly commands with correct arities
  • Mutations: replace/insert/remove commands, mutate arguments, wrap in MULTI/EXEC, swap order
  • Runs alongside AFL++ built-in mutations (havoc integration at 50%)

Seed corpus (fuzz/seeds/resp/):

  • Expanded from 17 to 79 seeds covering all command families (string, list, hash, set, zset, stream, JSON, search, bloom, geo, HLL, bitops, scripting, ACL, pub/sub, transactions, server ops)

Fuzzing scripts (fuzz/run_fuzzer.sh):

  • Default to 1 proactor thread for stable coverage
  • Sync AFL_PERSISTENT_RECORD with afl_loop_limit (default 10000) for full crash replay
  • Auto-configure system (core_pattern, CPU governor)
  • 512KB AFL bitmap (AFL_MAP_SIZE) to reduce hash collisions
  • Load custom mutator automatically

Dictionary (fuzz/dict/resp.dict):

  • Added 200+ tokens: missing commands, subcommands, arguments, malformed RESP patterns, and inline commands
  • Removed duplicate entries and oversized tokens (AFL++ warns on >33B)

Crash tooling:

  • replay_crash.py — replays RECORD files against a running instance
  • package_crash.sh — packages crash + RECORD files into a self-contained archive

Minor (src/server/dfly_main.cc):

  • Reduce fuzz client socket timeout from 2s to 200ms
  • Use MSG_NOSIGNAL to avoid SIGPIPE

Closes: #5713
Closes: #5710
Closes: #5708

@vyavdoshenko vyavdoshenko self-assigned this Feb 13, 2026
Copilot AI review requested due to automatic review settings February 13, 2026 15:32
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR significantly improves Dragonfly's AFL++ fuzzing infrastructure to make it more effective at finding bugs and easier to reproduce crashes. The changes implement a RESP-aware custom mutator, expand the seed corpus from 17 to 79 files covering all command families, improve crash replay capabilities, and optimize AFL++ configuration for better coverage stability and crash reproducibility.

Changes:

  • Added custom RESP mutator that operates at command/argument level instead of random bytes
  • Expanded seed corpus to 79 files covering all Redis command families (string, list, hash, set, zset, stream, JSON, search, bloom, geo, HLL, bitops, scripting, ACL, pub/sub, transactions, server ops)
  • Improved AFL++ configuration with persistent record for full crash replay, optimized bitmap size, and better system configuration
  • Added crash replay tooling (replay_crash.py, package_crash.sh) for reproducing and sharing crashes
  • Reduced fuzz client socket timeout from 2s to 200ms and added MSG_NOSIGNAL to prevent SIGPIPE
  • Enhanced dictionary with 200+ tokens while removing duplicates and oversized entries
  • Updated documentation with comprehensive fuzzing instructions

Reviewed changes

Copilot reviewed 69 out of 69 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/server/dfly_main.cc Reduced socket timeout to 200ms, added MSG_NOSIGNAL flag
fuzz/resp_mutator.py AFL++ custom mutator for RESP protocol with 150+ commands
fuzz/run_fuzzer.sh AFL++ wrapper script with system configuration and environment setup
fuzz/replay_crash.py Tool to replay crashes from AFL_PERSISTENT_RECORD files
fuzz/package_crash.sh Script to package crashes for sharing with developers
fuzz/seeds/resp/* 79 seed files covering all command families
fuzz/dict/resp.dict Enhanced dictionary with duplicates removed
fuzz/FUZZING.md Comprehensive fuzzing documentation

"""

import random
import struct
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'struct' is not used.

Suggested change
import struct

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +40
except Exception:
pass

try:
s.recv(4096)
except Exception:
pass
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
except Exception:
pass
try:
s.recv(4096)
except Exception:
pass
except Exception as e:
sys.stderr.write(f"\033[0;31m[WARN]\033[0m Failed to send data: {e}\n")
try:
s.recv(4096)
except Exception as e:
sys.stderr.write(f"\033[0;31m[WARN]\033[0m Failed to receive response: {e}\n")

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +40
except Exception:
pass

try:
s.recv(4096)
except Exception:
pass
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
except Exception:
pass
try:
s.recv(4096)
except Exception:
pass
except OSError as exc:
# Ignore send errors: the fuzzer only needs best-effort delivery.
print(f"\033[0;33m[WARN]\033[0m Send failed: {exc}", file=sys.stderr)
try:
s.recv(4096)
except OSError as exc:
# Ignore receive errors: crash reproduction only depends on what was sent.
print(f"\033[0;33m[WARN]\033[0m Receive failed: {exc}", file=sys.stderr)

Copilot uses AI. Check for mistakes.
@augmentcode
Copy link

augmentcode bot commented Feb 13, 2026

🤖 Augment PR Summary

Summary: This PR expands Dragonfly’s AFL++ fuzzing infrastructure to improve coverage and make crashes easier to reproduce/share.

Changes:

  • Updates fuzz/run_fuzzer.sh to default to 1 proactor thread, add AFL_LOOP_LIMIT, sync AFL_PERSISTENT_RECORD, increase AFL bitmap size, and auto-load a Python RESP-aware custom mutator.
  • Adds a RESP protocol custom mutator (fuzz/resp_mutator.py) that mutates at command/argument granularity (including MULTI/EXEC wrapping) while keeping framing valid.
  • Greatly expands the RESP seed corpus under fuzz/seeds/resp/ to cover more command families.
  • Extends the AFL dictionary (fuzz/dict/resp.dict) with many additional commands/tokens and smaller RESP framing fragments.
  • Adds crash tooling: fuzz/replay_crash.py to replay RECORD sequences and fuzz/package_crash.sh to bundle crashes for sharing.
  • Updates fuzz/FUZZING.md to document the new workflow, env vars, mutator, and crash replay/packaging steps.
  • Adjusts the fuzz client in src/server/dfly_main.cc to use shorter socket timeouts and MSG_NOSIGNAL to avoid SIGPIPE during fuzzing.

Technical Notes: The focus is on higher-quality inputs (RESP-aware mutations), more stable coverage (1 thread + larger map), and reproducible stateful crashes via persistent RECORD replay.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 5 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

KEYS
$1
*
*3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seed appears to contain invalid RESP array counts (e.g., the SCAN request starts with *3 but includes 5 bulk strings), which likely makes the seed rejected by the RESP parser and reduces initial corpus usefulness.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

$3
val
*2
$2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bulk length for DEL is declared as $2 but the payload is DEL (3 bytes), making this seed invalid RESP and likely rejected before command execution.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

tk
$3
val
*1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WATCH command is encoded with *1 but includes both WATCH and the key (2 elements), so this request is invalid RESP and may never reach the transaction logic.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

(b"MULTI", 0, 0), (b"EXEC", 0, 0), (b"DISCARD", 0, 0),
(b"WATCH", 1, 3), (b"UNWATCH", 0, 0),
# Script
(b"EVAL", 2, 6), (b"EVALSHA", 2, 6), (b"EVALRO", 2, 6),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dragonfly registers the readonly scripting commands as EVAL_RO/EVALSHA_RO, but the mutator uses EVALRO and doesn’t include EVALSHA_RO; this likely generates unknown commands and reduces scripting coverage.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

if (connect(s, (struct sockaddr*)&a, sizeof(a)) == 0) {
send(s, data, len, 0);
// Just read once - don't wait for full response
send(s, data, len, MSG_NOSIGNAL);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MSG_NOSIGNAL is not universally available across platforms; if USE_AFL is ever built on non-Linux targets this may break compilation in the fuzz harness path.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

@vyavdoshenko vyavdoshenko merged commit 05ea2a3 into main Feb 13, 2026
16 checks passed
@vyavdoshenko vyavdoshenko deleted the bobik/fuzz_update branch February 13, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants