Docs  /  Guides

Fuzzing and Robustness Testing

Rerius processes untrusted binary files from potentially hostile sources. This guide explains how to fuzz the parser and analysis engine.

Repository: https://github.com/ECLS-Studio/rerius


Why Fuzz Rerius?#

Rerius is often used to analyze malware and obfuscated binaries. A malicious analyst might feed crafted binaries to crash the tool or trigger unexpected behavior. The parser handles arbitrary byte sequences as ELF/PE structures: integer overflows in size calculations or OOB reads are the primary risk class.

As of v1.0.0, Rerius applies a three-layer fault isolation model (include/core/dax_guard.h). Each of 32 analysis passes runs independently; a structural error in one pass causes a recovery notice on stderr rather than a crash. Fuzzing is still essential to: - Find inputs that bypass the isolation layer and cause actual crashes - Find inputs that trigger the fault path incorrectly on valid binaries - Measure which passes are most fragile against adversarial data


Quick Robustness Check#

Before fuzzing, verify Rerius handles obviously malformed input. With v1.0.0+, passes that encounter corrupt data print [!] pass '...' recovered from fault on stderr and continue: no crash, no hang.

# Empty file
touch /tmp/empty && ./rerius /tmp/empty

# Random bytes (not a valid ELF/PE)
dd if=/dev/urandom bs=4096 count=1 of=/tmp/random.bin 2>/dev/null
./rerius -X /tmp/random.bin < /dev/null   # -X also enables interactive mode; close stdin so it exits instead of waiting at the shell prompt

# Truncated ELF header
echo -n $'\x7fELF' > /tmp/bad_elf
./rerius /tmp/bad_elf

# All zeros
dd if=/dev/zero bs=4096 count=1 of=/tmp/zeros.bin 2>/dev/null
./rerius /tmp/zeros.bin

# ELF magic + garbage section headers
python3 -c "
import struct
# ELF magic + class=64 + LE + version + OS=Linux
hdr  = b'\x7fELF\x02\x01\x01\x00' + b'\x00'*8
# e_type=DYN, e_machine=x86_64, e_version=1
hdr += struct.pack('<HHI', 3, 62, 1)
# Garbage for everything else
hdr += b'\xff'*(64-len(hdr))
open('/tmp/elf_garbage.bin','wb').write(hdr)
"
./rerius -X /tmp/elf_garbage.bin < /dev/null

All of these should print error messages and exit cleanly: no segfault, no hang. Any pass that encounters a structural problem should print [!] pass '...' recovered from fault and continue. A segfault on any of these inputs is a bug: please report it.


AFL++ Fuzzing#

Important: setup.sh autodetects its own compiler and ignores an exported CC/CFLAGS_EXTRA: CC=afl-clang-fast make and make CFLAGS_EXTRA=... will silently build a normal, non-instrumented binary rather than failing loudly. Compile directly against the source list instead of going through setup.sh for fuzzing builds.

# Install AFL++
git clone https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus && make -j$(nproc) && sudo make install

# Build Rerius directly with AFL++ instrumentation (bypasses setup.sh)
cd /path/to/Rerius
afl-clang-fast -std=c99 -D_GNU_SOURCE -O1 -g \
    -fsanitize=address,undefined \
    -I./include -I./include/core -I./include/formats -I./include/arch -I./include/ui \
    src/cli/main.c src/cli/interactive.c \
    src/core/loader.c src/core/config.c src/core/plugin.c src/core/daxc.c src/core/hardening.c \
    src/formats/macho.c \
    src/arch/disasm.c src/arch/x86_decode.c src/arch/arm64_decode.c src/arch/riscv_decode.c \
    src/analysis/analysis.c src/analysis/cfg.c src/analysis/callgraph.c src/analysis/loops.c \
    src/analysis/symexec.c src/analysis/decomp.c src/analysis/correct.c src/analysis/dsa.c src/analysis/entropy.c \
    src/emu/emulate.c \
    src/util/sha256.c src/util/unicode.c src/util/demangle.c src/util/symbols.c \
    -lm -o rerius-afl

# Create seed corpus
mkdir -p fuzz/corpus fuzz/findings
cp /bin/ls       fuzz/corpus/ls_elf
cp /usr/bin/file fuzz/corpus/file_elf
# Add a small PE if available:
# cp /path/to/small.exe fuzz/corpus/small_pe

# Run AFL++
afl-fuzz -i fuzz/corpus -o fuzz/findings \
    -t 5000 -m 512 \
    -- ./rerius-afl -x -n @@

libFuzzer Integration#

Build a dedicated fuzz target:

cat > fuzz/fuzz_rerius.c << 'EOF'
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "dax.h"

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    if (size < 4) return 0;

    // Write to temp file (dax_load_binary takes a path)
    char tmpname[] = "/tmp/rerius_fuzz_XXXXXX";
    int fd = mkstemp(tmpname);
    if (fd < 0) return 0;
    write(fd, data, size);
    close(fd);

    dax_binary_t bin;
    memset(&bin, 0, sizeof(bin));

    if (dax_load_binary(tmpname, &bin) == 0) {
        dax_opts_t opts;
        memset(&opts, 0, sizeof(opts));
        opts.color = 0;

        // Exercise core analysis
        dax_sym_load(&bin);
        dax_xref_build(&bin);

        // Exercise CFG builder
        for (int i = 0; i < bin.nsections; i++) {
            dax_section_t *sec = &bin.sections[i];
            if (sec->type == SEC_TYPE_CODE &&
                sec->size > 0 &&
                sec->offset + sec->size <= bin.size) {
                dax_cfg_build(&bin,
                    bin.data + sec->offset,
                    (size_t)sec->size,
                    sec->vaddr, -1);
            }
        }

        // Exercise unicode scanner
        dax_scan_unicode(&bin);

        // Exercise entropy
        dax_entropy_scan(&bin, &opts, fopen("/dev/null","w"));

        // Exercise IVF
        dax_ivf_scan(&bin, &opts, fopen("/dev/null","w"));

        dax_free_binary(&bin);
    }

    unlink(tmpname);
    return 0;
}
EOF

# Compile with libFuzzer
clang -fsanitize=fuzzer,address,undefined \
    -std=c99 -D_GNU_SOURCE \
    -I./include -I./include/core -I./include/formats -I./include/arch -I./include/ui \
    fuzz/fuzz_rerius.c \
    src/core/loader.c src/core/config.c src/core/plugin.c src/core/daxc.c src/core/hardening.c \
    src/formats/macho.c \
    src/arch/disasm.c src/arch/x86_decode.c src/arch/arm64_decode.c src/arch/riscv_decode.c \
    src/analysis/analysis.c src/analysis/cfg.c src/analysis/callgraph.c src/analysis/loops.c \
    src/analysis/symexec.c src/analysis/decomp.c src/analysis/correct.c src/analysis/dsa.c src/analysis/entropy.c \
    src/emu/emulate.c \
    src/util/sha256.c src/util/unicode.c src/util/demangle.c src/util/symbols.c \
    -lm \
    -o fuzz/fuzz_rerius

# Run libFuzzer
./fuzz/fuzz_rerius fuzz/corpus/ \
    -max_total_time=3600 \
    -max_len=65536 \
    -jobs=4 -workers=4

AddressSanitizer Build#

For catching memory errors during normal testing, compile directly rather than through setup.sh (which ignores CFLAGS_EXTRA):

clang -std=c99 -D_GNU_SOURCE -O1 -g -fsanitize=address,undefined \
    -I./include -I./include/core -I./include/formats -I./include/arch -I./include/ui \
    src/cli/main.c src/cli/interactive.c \
    src/core/loader.c src/core/config.c src/core/plugin.c src/core/daxc.c src/core/hardening.c \
    src/formats/macho.c \
    src/arch/disasm.c src/arch/x86_decode.c src/arch/arm64_decode.c src/arch/riscv_decode.c \
    src/analysis/analysis.c src/analysis/cfg.c src/analysis/callgraph.c src/analysis/loops.c \
    src/analysis/symexec.c src/analysis/decomp.c src/analysis/correct.c src/analysis/dsa.c src/analysis/entropy.c \
    src/emu/emulate.c \
    src/util/sha256.c src/util/unicode.c src/util/demangle.c src/util/symbols.c \
    -lm -o rerius-asan

./rerius-asan -X ./binary < /dev/null   # run normally - ASan/UBSan report any issues on exit; stdin closed since -X also enables the interactive shell

What to Fuzz#

Priority targets based on attack surface:

  1. dax_load_binary() - parses ELF/PE headers; integer overflows in e_phoff, sh_offset, sh_size
  2. dax_cfg_build() - decodes arbitrary byte sequences as instructions
  3. dax_scan_unicode() - scans non-code sections byte by byte
  4. dax_ivf_scan() - linear scan of code sections
  5. dax_rda_section() - BFS with queue; queue overflow potential

Reporting Crashes#

If fuzzing finds a crash, follow SECURITY.md for private reporting. Include: - The crashing input (minimized with afl-tmin or llvm-reduce) - The ASAN/UBSAN output - The command that triggered it

Edit this page on GitHub Source: docs/FUZZING.md · Rerius v1.0.0
On this page
Fuzzing and Robustness Testing Why Fuzz Rerius? Quick Robustness Check AFL++ Fuzzing libFuzzer Integration AddressSanitizer Build What to Fuzz Reporting Crashes
ESC
↑↓ navigate openesc close