Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
build
build-fuzz
corpus-workdir
26 changes: 26 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
cmake_minimum_required(VERSION 3.20)

option(TEST_ENABLE "Enable tests" off)
option(FUZZ_ENABLE "Enable fuzz targets (requires clang with libFuzzer)" off)

if (${TEST_ENABLE})
project(easyframes)
Expand Down Expand Up @@ -95,6 +96,7 @@ add_executable(ef-tests
test/ef-test.cxx
test/ef-tests.cxx
test/test-ef-parse-bytes.cxx
test/test-padding.cxx
test/ifh-ignore.cxx
)

Expand All @@ -103,3 +105,27 @@ include(CTest)
add_test(ef-tests ./ef-tests)
add_test(parser-tests.rb ${CMAKE_CURRENT_SOURCE_DIR}/test/parser-tests.rb)
endif()

if (${FUZZ_ENABLE})
# libFuzzer's runtime is C++; clang may not find GCC's libstdc++.
# Ask GCC for the path as a fallback.
find_library(_STDCXX stdc++)
if (NOT _STDCXX)
execute_process(
COMMAND gcc --print-file-name=libstdc++.so
OUTPUT_VARIABLE _STDCXX_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
if (_STDCXX_PATH AND NOT _STDCXX_PATH STREQUAL "libstdc++.so")
get_filename_component(_STDCXX_DIR "${_STDCXX_PATH}" DIRECTORY)
endif()
endif()

foreach(target fuzz-parse-bytes fuzz-argc-frame fuzz-roundtrip)
add_executable(${target} fuzz/${target}.c)
target_link_libraries(${target} libef stdc++)
target_compile_options(${target} PRIVATE -fsanitize=fuzzer,address)
target_link_options(${target} PRIVATE -fsanitize=fuzzer,address)
if (_STDCXX_DIR)
target_link_directories(${target} PRIVATE ${_STDCXX_DIR})
endif()
endforeach()
endif()
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,42 @@ To learn more, have a look at the help message below.
$ sudo make install


# Fuzzing

Three [libFuzzer](https://llvm.org/docs/LibFuzzer.html) harnesses exercise
ef's parsing logic with AddressSanitizer enabled:

- **fuzz-parse-bytes**: feeds random strings to `parse_bytes()` (numeric,
hex, IPv4, IPv6, MAC address parsing)
- **fuzz-argc-frame**: splits input on null bytes into argv and calls
`argc_frame()` (all header and field parsers)
- **fuzz-roundtrip**: property-based test: parses a frame, serializes it,
and asserts that the result matches its own receive filter via
`bequal_mask()`. Uses the same input format and corpus as fuzz-argc-frame.

Build (requires clang):

$ cmake -S . -B build-fuzz -DFUZZ_ENABLE=ON -DCMAKE_C_COMPILER=clang
$ cmake --build build-fuzz -j$(nproc)

Run (from build-fuzz/):

$ cd build-fuzz
$ mkdir -p corpus-workdir
$ ./fuzz-parse-bytes corpus-workdir ../fuzz/corpus-parse-bytes -dict=../fuzz/ef.dict -max_total_time=1200 -jobs=12 -workers=12
$ ./fuzz-argc-frame corpus-workdir ../fuzz/corpus-argc-frame -dict=../fuzz/ef.dict -max_total_time=1200 -jobs=12 -workers=12
$ ./fuzz-roundtrip corpus-workdir ../fuzz/corpus-argc-frame -dict=../fuzz/ef.dict -max_total_time=1200 -jobs=12 -workers=12

When two corpus directories are given, libFuzzer reads seeds from both
but writes new discoveries only to the first. This keeps the checked-in
seed corpus (`fuzz/corpus-*`) clean. The `corpus-workdir` directory is
gitignored and can be deleted at any time.

The dictionary helps the fuzzer discover valid keywords. Adjust `-jobs`
and `-workers` to match available cores. Each job writes its own log to
`fuzz-<N>.log`.

If a crash is found, libFuzzer writes a `crash-<hash>` file that can be
reproduced with:

$ ./fuzz-parse-bytes crash-<hash>
Binary file added fuzz/corpus-argc-frame/data_ascii
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/data_hex
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/data_pattern_cnt
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/double_vlan
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_arp
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_basic
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_coap
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_hsr
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ign_all
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ign_fields
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ign_ipv6
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv4
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv4_icmp
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv4_icmp_fields
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv4_igmp
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv4_prp
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv4_tcp
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv4_udp
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv6_mld
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ipv6_udp
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_mrp_lnk
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_mrp_prop_nack
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_mrp_topo
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_mrp_tst
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_oam_ccm
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_oam_laps
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_oam_lb
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_oam_lt
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_oam_raps
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_opcua
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_profinet_rtc
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_announce
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_follow_up
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_peer_request
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_peer_resp_fu
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_peer_response
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_request
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_response
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_ptp_sync
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_rtag_ipv4
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_stag_ctag_ipv4
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/eth_sv
Binary file not shown.
Binary file added fuzz/corpus-argc-frame/payload_only
Binary file not shown.
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/bin_byte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0b10110011
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/bin_prefix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0b1111
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/decimal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
100
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/ethertype_oam
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0x8902
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/ethertype_ptp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0x88f7
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/hex_long
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xdeadbeef
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/hex_prefix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0x0800
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/int_simple
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/ipv4
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.2.3.4
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/ipv4_private
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
192.168.1.1
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/ipv6_linklocal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe80::1
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/ipv6_multicast
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ff02::1
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/ipv6_short
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::1
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/mac
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ff:ff:ff:ff:ff:ff
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/mac_oam
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
01:80:c2:00:00:30
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/octal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0o777
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/octal_prefix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0o777
1 change: 1 addition & 0 deletions fuzz/corpus-parse-bytes/zero
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
204 changes: 204 additions & 0 deletions fuzz/ef.dict
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# ef keywords helps libFuzzer discover valid parse paths

# Commands
"help"
"hex"
"ign"
"ignore"
"name"
"pcap"
"rate"
"rep"
"repeat"
"rx"
"tx"

# Headers Layer 2
"ctag"
"eth"
"htag"
"prp"
"rtag"
"stag"

# Headers Layer 3/4
"arp"
"icmp"
"igmp"
"igmpv3_group"
"ipv4"
"ipv6"
"mld"
"mldv2_group"
"tcp"
"udp"

# Headers OAM
"oam-ccm"
"oam-laps"
"oam-lb"
"oam-lt"
"oam-raps"

# Headers MRP
"mrp_lnk"
"mrp_prop_nack"
"mrp_topo"
"mrp_tst"

# Headers PTP
"ptp-announce"
"ptp-follow-up"
"ptp-peer-request"
"ptp-peer-response"
"ptp-peer-response-follow-up"
"ptp-request"
"ptp-response"
"ptp-sync"
"ptp-tlv-org"
"ptp-tlv-path"

# Headers Industrial / Application
"coap"
"coap-opt"
"coap-parms"
"opc-ua"
"profinet-rtc"
"sv"

# Headers Payload / Padding
"data"
"padding"

# Headers IFH (Injection/Extraction Frame Headers)
"ifh-jr2"
"ifh-oc1"
"ifh-sparx5"
"lp-jr2"
"lp-oc1"
"lp-sparx5"
"sp-jr2"
"sp-oc1"
"sp-sparx5"

# Field names Ethernet
"dmac"
"smac"
"et"

# Field names VLAN
"vid"
"pcp"
"dei"
"seqn"
"lanid"
"pathid"
"recv"
"suffix"
"size"

# Field names IPv4
"ver"
"ihl"
"dscp"
"ecn"
"id"
"flags"
"offset"
"ttl"
"proto"
"chksum"
"sip"
"dip"

# Field names IPv6
"flow"
"next"
"hlim"

# Field names TCP/UDP
"sport"
"dport"
"len"
"seqn"
"ack"
"doff"
"fin"
"syn"
"rst"
"psh"
"urg"
"win"
"urgp"

# Field names ARP
"htype"
"ptype"
"hlen"
"plen"
"oper"
"sha"
"spa"
"tha"
"tpa"

# Field names ICMP
"type"
"code"
"hd"

# Field names IGMP/MLD
"max_resp"
"ga"
"ng"
"ns"
"qrv"
"qqic"
"rec_type"
"auxlen"

# Field names PTP
"hdr-messageType"
"hdr-versionPTP"
"hdr-domainNumber"
"hdr-clockId"
"hdr-portNumber"
"hdr-sequenceId"
"hdr-correctionField"
"hdr-flagField"
"hdr-logMessageInterval"

# Field names OAM
"opcode"
"mel"

# Parse format keywords
"ascii"
"ascii0"
"pattern"
"zero"
"ones"
"cnt"

# Numeric formats
"0x"
"0b"
"0o"
"::"
"0x0800"
"0x86dd"
"0x8100"
"0x88a8"
"0x88f7"
"0x8902"
"0x88e1"
"ff:ff:ff:ff:ff:ff"
"01:80:c2:00:00:30"
"01:19:1b:00:00:00"
"1.2.3.4"
"::1"
"ff02::1"
"fe80::1"
"255"
"1000"
"0xdeadbeef"
Loading