Skip to content

Commit 4778e62

Browse files
committed
Add libFuzzer-based broker fuzzing infrastructure and fix security issues identified through automated review and fuzz testing.
1 parent a7841b6 commit 4778e62

16 files changed

Lines changed: 848 additions & 56 deletions

File tree

.github/workflows/Disabled/cifuzz.yml

Lines changed: 0 additions & 27 deletions
This file was deleted.

.github/workflows/fuzz.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Fuzz Testing
2+
3+
on:
4+
schedule:
5+
- cron: '0 4 * * 1' # Weekly Monday 4am UTC
6+
workflow_dispatch: # Manual trigger
7+
pull_request:
8+
branches: [ '*' ]
9+
10+
jobs:
11+
fuzz:
12+
runs-on: ubuntu-latest
13+
timeout-minutes: 30
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
include:
18+
# Full fuzz run (weekly/manual) - 10 minutes
19+
- name: fuzz-full
20+
fuzz_time: 600
21+
smoke_only: false
22+
# Quick smoke test (PR) - 60 seconds
23+
- name: fuzz-smoke
24+
fuzz_time: 60
25+
smoke_only: true
26+
27+
steps:
28+
- name: Checkout wolfMQTT
29+
uses: actions/checkout@v4
30+
31+
- name: ASLR workaround
32+
run: sudo sysctl vm.mmap_rnd_bits=28
33+
34+
- name: Run fuzzer
35+
if: ${{ !matrix.smoke_only || github.event_name == 'pull_request' }}
36+
run: ./scripts/fuzz.sh ${{ matrix.fuzz_time }}
37+
38+
- name: Upload crash artifacts
39+
if: failure()
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: fuzz-crashes-${{ matrix.name }}
43+
path: |
44+
crash-*
45+
oom-*
46+
timeout-*
47+
retention-days: 30
48+
if-no-files-found: ignore

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,6 @@ examples/websocket/websocket_client
125125
!/IDE/Espressif/**/config.h
126126

127127
src/mqtt_broker
128+
129+
tests/fuzz/broker_fuzz
130+
tests/fuzz/corpus/

Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ ACLOCAL_AMFLAGS= -I m4
3434
include src/include.am
3535
include wolfmqtt/include.am
3636
include examples/include.am
37+
include tests/include.am
3738
include scripts/include.am
3839
include IDE/include.am
3940
include cmake/include.am
@@ -56,6 +57,8 @@ test: check
5657

5758
DISTCLEANFILES+= wolfmqtt-config
5859

60+
clean-local:
61+
-rm -rf tests/fuzz/corpus
5962

6063
maintainer-clean-local:
6164
-rm Makefile.in
@@ -118,3 +121,4 @@ dist-hook:
118121
$(MKDIR_P) $(distdir)/examples/multithread
119122
$(MKDIR_P) $(distdir)/examples/pub-sub
120123
$(MKDIR_P) $(distdir)/examples/websocket
124+
$(MKDIR_P) $(distdir)/tests/fuzz

configure.ac

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ then
158158
test "$enable_v5" = "" && enable_v5=yes
159159
test "$enable_discb" = "" && enable_discb=yes
160160
test "$enable_mt" = "" && enable_mt=yes
161-
test "$enable_" = "" && enable_=yes
161+
test "$enable_broker" = "" && enable_broker=yes
162162
fi
163163

164164
# TLS Support with wolfSSL
@@ -491,6 +491,14 @@ AM_CONDITIONAL([BUILD_MULTITHREAD], [test "x$ENABLED_MULTITHREAD" = "xyes"])
491491
AM_CONDITIONAL([BUILD_WEBSOCKET], [test "x$ENABLED_WEBSOCKET" = "xyes"])
492492
AM_CONDITIONAL([BUILD_BROKER], [test "x$ENABLED_BROKER" = "xyes"])
493493

494+
# Fuzz target
495+
AC_ARG_ENABLE([fuzz],
496+
[AS_HELP_STRING([--enable-fuzz],[Enable libFuzzer targets (default: disabled)])],
497+
[ ENABLED_FUZZ=$enableval ],
498+
[ ENABLED_FUZZ=no ]
499+
)
500+
AM_CONDITIONAL([BUILD_FUZZ], [test "x$ENABLED_FUZZ" = "xyes"])
501+
494502

495503

496504
# HARDEN FLAGS
@@ -619,3 +627,4 @@ echo " * Multi-thread: $ENABLED_MULTITHREAD"
619627
echo " * Stress: $ENABLED_STRESS"
620628
echo " * WebSocket: $ENABLED_WEBSOCKET"
621629
echo " * Broker: $ENABLED_BROKER"
630+
echo " * Fuzz: $ENABLED_FUZZ"

scripts/broker.test

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ wait_for_file() {
7070
local file="$1"
7171
local timeout_sec="${2:-5}"
7272
local waited=0
73-
while [ ! -f "$file" ] && [ $waited -lt $timeout_sec ]; do
73+
local max_iters=$((timeout_sec * 10))
74+
while [ ! -f "$file" ] && [ $waited -lt $max_iters ]; do
7475
sleep 0.1
75-
waited=$(echo "$waited + 0.1" | bc)
76+
waited=$((waited + 1))
7677
done
7778
[ -f "$file" ]
7879
}

scripts/fuzz.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
# fuzz.sh - Build and run the wolfMQTT broker fuzzer
3+
#
4+
# Usage: ./scripts/fuzz.sh [seconds]
5+
# seconds: fuzz duration (default: 60)
6+
#
7+
# Requires: clang with libFuzzer support
8+
9+
set -e
10+
11+
FUZZ_TIME=${1:-60}
12+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
13+
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
14+
15+
cd "$ROOT_DIR"
16+
17+
# Check for clang
18+
if ! command -v clang >/dev/null 2>&1; then
19+
echo "Error: clang is required for fuzzing (libFuzzer)" >&2
20+
exit 1
21+
fi
22+
23+
# Generate configure if needed
24+
if [ ! -f ./configure ]; then
25+
./autogen.sh
26+
fi
27+
28+
# Configure with fuzzer and address sanitizer
29+
CC=clang ./configure --enable-broker --enable-v5 --enable-fuzz \
30+
--disable-tls --disable-examples \
31+
CFLAGS="-fsanitize=fuzzer-no-link,address -fno-omit-frame-pointer -g -O1" \
32+
LDFLAGS="-fsanitize=address"
33+
34+
make -j$(nproc)
35+
36+
# Generate seed corpus
37+
python3 tests/fuzz/gen_corpus.py
38+
39+
# Run fuzzer
40+
echo "Fuzzing for ${FUZZ_TIME} seconds..."
41+
export ASAN_OPTIONS="detect_leaks=1:abort_on_error=1:symbolize=1"
42+
43+
timeout "$FUZZ_TIME" \
44+
./tests/fuzz/broker_fuzz \
45+
tests/fuzz/corpus/ \
46+
-dict=tests/fuzz/mqtt.dict \
47+
-max_len=4096 \
48+
-timeout=10 \
49+
-rss_limit_mb=2048 \
50+
-print_final_stats=1 \
51+
|| FUZZ_RC=$?
52+
53+
# timeout returns 124 on normal expiry, fuzzer returns 0 on no crash
54+
if [ "${FUZZ_RC:-0}" -eq 124 ] || [ "${FUZZ_RC:-0}" -eq 0 ]; then
55+
echo "Fuzzer completed without crashes"
56+
else
57+
echo "Fuzzer found crashes (exit code $FUZZ_RC)"
58+
ls -la crash-* 2>/dev/null || true
59+
exit 1
60+
fi

scripts/include.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ endif # BUILD_EXAMPLES
2727
if BUILD_BROKER
2828
dist_noinst_SCRIPTS += scripts/broker.test
2929
endif # BUILD_BROKER
30+
31+
EXTRA_DIST += scripts/fuzz.sh

src/include.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ if BUILD_WEBSOCKET
3232
src_mqtt_broker_LDADD += -lwebsockets
3333
endif
3434
endif
35+

0 commit comments

Comments
 (0)