Skip to content

Commit 7bcb76d

Browse files
committed
Add my fuzzing work to what was started by catenacyber
1 parent 09265da commit 7bcb76d

14 files changed

+717
-11
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,9 @@ netdissect.dir/
3838
tcpdump.dir/
3939
tcpdump.sln
4040
.vs/
41+
fuzz/corpus/
42+
fuzz/bgp_print_fuzzer
43+
fuzz/ether_print_fuzzer
44+
fuzz/ip6_print_fuzzer
45+
fuzz/ip_print_fuzzer
46+
fuzz/fuzz_pcap

fuzz/CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
11
add_executable(fuzz_pcap onefile.c fuzz_pcap.c)
22
target_link_libraries(fuzz_pcap netdissect ${TCPDUMP_LINK_LIBRARIES})
3+
add_executable(bgp_print_fuzzer fuzzmain.c common.c bgp_print_fuzzer.c)
4+
add_executable(ip_print_fuzzer fuzzmain.c common.c ip_print_fuzzer.c)
5+
add_executable(ip6_print_fuzzer fuzzmain.c common.c ip6_print_fuzzer.c)
6+
add_executable(ether_print_fuzzer fuzzmain.c common.c ether_print_fuzzer.c)
7+
target_link_libraries(bgp_print_fuzzer netdissect ${TCPDUMP_LINK_LIBRARIES})
8+
target_link_libraries(ip_print_fuzzer netdissect ${TCPDUMP_LINK_LIBRARIES})
9+
target_link_libraries(ip6_print_fuzzer netdissect ${TCPDUMP_LINK_LIBRARIES})
10+
target_link_libraries(ether_print_fuzzer netdissect ${TCPDUMP_LINK_LIBRARIES})

fuzz/README.md

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# The oss-fuzz infrastructure #
2+
3+
# How we use it #
4+
5+
For fuzzing purposes, we use _vsprintf_ to format the output to a
6+
static buffer. This is because during the
7+
fuzzing process, we sometimes get *EINTR* if writing to stdout.
8+
However, in order to replicate a crash, it can help to see what the
9+
printer was doing before it crashed, so if you set the environment
10+
variable *TCPDUMP_PRINT* to any value before running the replicator,
11+
the printer will print to stdout. If you set the environment
12+
variable *REPLICATE_TRUNCATE* to any value before running the
13+
replicator, the code will try the given packet and all prefixes
14+
(to try to find additional related truncated-packet problems).
15+
16+
# Reproducing a crash using an example #
17+
18+
The binaries that we build in this directory can take a data file
19+
as generated by the fuzzing infrastructure and run it through the
20+
same function. If you don't understand the output from the
21+
sanitizer that oss-fuzz provides, you can run again using valgrind:
22+
23+
~/src/tcpdump/fuzzing @us157.sjc> TCPDUMP_PRINT=1 valgrind ./ether_print_fuzzer ../../oss-fuzz/build/out/tcpdump/crash-20aa211fc54fda2cca155539d1d5189990e6bd4e
24+
==2963== Memcheck, a memory error detector
25+
==2963== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
26+
==2963== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
27+
==2963== Command: ./ether_print_fuzzer ../../oss-fuzz/build/out/tcpdump/crash-20aa211fc54fda2cca155539d1d5189990e6bd4e
28+
==2963==
29+
StandaloneFuzzTargetMain: running 1 inputs
30+
Running: ../../oss-fuzz/build/out/tcpdump/crash-20aa211fc54fda2cca155539d1d5189990e6bd4e
31+
==2963== Invalid read of size 1
32+
==2963== at 0x454E26: rpl_dio_printopt (print-icmp6.c:665)
33+
==2963== by 0x456C7F: rpl_dio_print (print-icmp6.c:714)
34+
==2963== by 0x456C7F: rpl_print (print-icmp6.c:839)
35+
==2963== by 0x456C7F: icmp6_print (print-icmp6.c:1148)
36+
==2963== by 0x4103EB: ip6_print (print-ip6.c:351)
37+
==2963== by 0x40C599: ethertype_print (print-ether.c:370)
38+
==2963== by 0x40CCCD: ether_print (print-ether.c:237)
39+
==2963== by 0x403AAF: LLVMFuzzerTestOneInput (in /home/fenner/src/tcpdump/fuzzing/ether_print_fuzzer)
40+
==2963== by 0x403D75: main (in /home/fenner/src/tcpdump/fuzzing/ether_print_fuzzer)
41+
==2963== Address 0x5d24b1c is 0 bytes after a block of size 156 alloc'd
42+
==2963== at 0x4C2AC36: malloc (vg_replace_malloc.c:299)
43+
==2963== by 0x403D12: main (in /home/fenner/src/tcpdump/fuzzing/ether_print_fuzzer)
44+
==2963==
45+
Done: ../../oss-fuzz/build/out/tcpdump/crash-20aa211fc54fda2cca155539d1d5189990e6bd4e: (156 bytes)
46+
IP6 truncated-ip6 - 4962 bytes missing!(class 0x10, hlim 58, next-header ICMPv6 (58) payload length: 14906) 793a:3a3a:3a3a:28ab:ab00:: > ce:dada:dada:dada:dada:dada:dada:9b9b: ICMP6, RPL, (CLR)DODAG Information Object [dagid:4ff:ffff:ffff:fffe:a08:d5:dada:8e61,seq:5,instance:255,rank:65344,mop:mop4,prf:4] opt:subopt:113 len:2 opt:destprefix len:7 opt:subopt:52 len:54 opt:subopt:255 len:4 opt:subopt:7 len:2 opt:pad0 opt:pad0==2963==
47+
48+
and, of course, when the bug is fixed you can validate it using the
49+
same sequence.
50+
51+
# Reproducing a crash inside the oss-fuzz infrastructure #
52+
53+
~/src/oss-fuzz @us157.sjc> python infra/helper.py reproduce -e TCPDUMP_PRINT=1 tcpdump ether_print_fuzzer build/out/tcpdump/crash-20aa211fc54fda2cca155539d1d5189990e6bd4e
54+
55+
# Turning the example into a pcap #
56+
57+
Of course, the tcpdump regression tests use pcap files as input.
58+
When the oss-fuzz framework creates a replication example, it is
59+
the raw data that the specific fuzzer accepts (see below). The
60+
script `corpus/corpus2pcap` converts an example into a pcap, using
61+
a heuristic as to what type of packet it is. (E.g., if it starts
62+
with 16 0xff's, then it's a BGP packet; if it starts with 0x45 then
63+
it is an IPv4 packet). If it guesses wrong, there is a `--type`
64+
argument to tell it what the type of the input is.
65+
66+
# When fixing a bug found by this infrastructure #
67+
68+
Remember to assign credit to "OSS-Fuzz"
69+
70+
# ip\_print\_fuzzer #
71+
72+
## Input ##
73+
74+
The ip\_print\_fuzzer takes an IPv4 packet as input.
75+
76+
## Corpus ##
77+
78+
The `pcap2corpus` run extracts all IPv4 packets from tests/\*.pcap
79+
into the ip\_print\_fuzzer\_seed\_corpus.zip
80+
81+
# ip6\_print\_fuzzer #
82+
83+
## Input ##
84+
85+
The ip6\_print\_fuzzer takes an IPv6 packet as input.
86+
87+
## Corpus ##
88+
89+
The `pcap2corpus` run extracts all IPv6 packets from tests/\*.pcap
90+
into the ip6\_print\_fuzzer\_seed\_corpus.zip
91+
92+
# bgp\_print\_fuzzer #
93+
94+
## Input ##
95+
96+
The bgp\_print\_fuzzer takes a BGP message, starting with marker,
97+
as input.
98+
99+
## Corpus ##
100+
101+
The `pcap2corpus` run extracts all BGP TCP payloads from tests/\*.pcap
102+
into the bgp\_print\_fuzzer\_seed\_corpus.zip
103+
104+
TODO: It would be nice to also extract individual messages split at the
105+
marker, to allow TCP payloads with multiple BGP messages to be parsed
106+
as individual BGP messages.
107+
108+
TODO: write a tool that takes MRT dump in and outputs every BGP message
109+
to an individual file
110+
111+
# ether\_print\_fuzzer #
112+
113+
## Input ##
114+
115+
The ether\_print\_fuzzer takes an Ethernet packet, starting with
116+
14-byte header, as input.
117+
118+
## Corpus ##
119+
120+
The `pcap2corpus` run extracts all Ethernet packets that do not match
121+
a more-specific type above into the ether\_print\_fuzzer\_seed\_corpus.zip
122+

fuzz/bgp_print_fuzzer.c

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2019
3+
* Arista Networks, Inc.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions
7+
* are met:
8+
*
9+
* 1. Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* 2. Redistributions in binary form must reproduce the above copyright
12+
* notice, this list of conditions and the following disclaimer in
13+
* the documentation and/or other materials provided with the
14+
* distribution.
15+
* 3. The names of the authors may not be used to endorse or promote
16+
* products derived from this software without specific prior
17+
* written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
20+
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22+
*/
23+
24+
#include "common.h"
25+
26+
extern netdissect_options *ndo;
27+
28+
int bgp_print_fuzz(const uint8_t *data, size_t size) {
29+
int fakelen = 10000;
30+
31+
ndo->ndo_snapend = data + size;
32+
if (fakelen < size) {
33+
fakelen = size;
34+
}
35+
bgp_print(ndo, data, fakelen);
36+
return 0;
37+
}
38+
39+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
40+
fuzz_common(bgp_print_fuzz, data, size);
41+
return 0;
42+
}

fuzz/build.sh

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/bin/sh
2+
#
3+
# Use $OUT as a signal that we're being run inside the oss-fuzz
4+
# build_fuzzers target. See
5+
# https://github.com/google/oss-fuzz/blob/master/docs/new_project_guide.md
6+
# for the assumptions about the enviroment.
7+
if [ -z "$OUT" ]
8+
then
9+
echo "This script is only used when building the fuzz targets"
10+
echo "inside the oss-fuzz image."
11+
exit 1
12+
fi
13+
14+
# First, the pcap fuzzer.
15+
$CC $CFLAGS -I.. -I. -c ../fuzz/fuzz_pcap.c -o fuzz_pcap.o
16+
$CXX $CXXFLAGS fuzz_pcap.o -o $OUT/fuzz_pcap libnetdissect.a \
17+
$SRC/libpcap/build/libpcap.a $LIB_FUZZING_ENGINE
18+
19+
zip -r fuzz_pcap_seed_corpus.zip ../tests/*.pcap
20+
cp fuzz_pcap_seed_corpus.zip $OUT/
21+
22+
# Then, the individual per-printer fuzzers
23+
$CC $CFLAGS -I.. -I. -c ../fuzz/common.c -o common.o
24+
25+
for p in ip ip6 ether bgp
26+
do
27+
$CC $CFLAGS -I.. -I. -c ../fuzz/${p}_print_fuzzer.c -o ${p}_print_fuzzer.o
28+
$CXX $CXXFLAGS ${p}_print_fuzzer.o common.o -o $OUT/${p}_print_fuzzer \
29+
libnetdissect.a $SRC/libpcap/build/libpcap.a $LIB_FUZZING_ENGINE
30+
done
31+
32+
# Build the per-printer corpus from the tests
33+
cd $WORK
34+
mkdir corpus
35+
cd corpus
36+
$SRC/tcpdump/fuzz/corpus/pcap2corpus $SRC/tcpdump/tests/*.pcap
37+
for d in *
38+
do
39+
zip -r $OUT/${d}_print_fuzzer_seed_corpus.zip $d/*
40+
done
41+
42+
# Copy options
43+
cp $SRC/tcpdump/fuzz/*.options $OUT/

fuzz/common.c

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) 2019
3+
* Arista Networks, Inc.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions
7+
* are met:
8+
*
9+
* 1. Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* 2. Redistributions in binary form must reproduce the above copyright
12+
* notice, this list of conditions and the following disclaimer in
13+
* the documentation and/or other materials provided with the
14+
* distribution.
15+
* 3. The names of the authors may not be used to endorse or promote
16+
* products derived from this software without specific prior
17+
* written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
20+
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22+
*/
23+
24+
#include <stdio.h>
25+
#include <stdlib.h>
26+
#include <string.h>
27+
#include "common.h"
28+
#include "netdissect-alloc.h"
29+
30+
netdissect_options *ndo;
31+
32+
/*
33+
* When fuzzing, we end up sometimes getting EINTR from printf,
34+
* so instead, we sprintf (to make sure that we format the args)
35+
* and then discard that string.
36+
*/
37+
static int
38+
ndo_mysprintf(netdissect_options *ndo, const char *fmt, ...)
39+
{
40+
va_list args;
41+
int ret;
42+
char buf[1000];
43+
44+
va_start(args, fmt);
45+
ret = vsnprintf(buf, sizeof(buf), fmt, args);
46+
va_end(args);
47+
48+
return (ret);
49+
}
50+
51+
int LLVMFuzzerInitialize(int *argc, char ***argv) {
52+
ndo = calloc(1, sizeof(netdissect_options));
53+
ndo_set_function_pointers(ndo);
54+
if (!getenv("TCPDUMP_PRINT")) {
55+
/* If we're replicating, we do want to print so it's easier to
56+
* see where the failure is, but if we're fuzzing, we want to
57+
* just use sprintf to avoid the EINTR */
58+
ndo->ndo_printf = ndo_mysprintf;
59+
}
60+
ndo->program_name = "fuzztcpdump";
61+
ndo->ndo_nflag = 1; // don't try to look up addresses
62+
ndo->ndo_vflag = 2; // decode as much as we can
63+
return 0;
64+
}
65+
66+
void
67+
fuzz_common(int (*fuzzfunc)(const uint8_t *, size_t), const uint8_t *data, size_t size) {
68+
/*
69+
* longjmp-based truncation: if the infrastructure detects
70+
* truncation, it longjmps to here.
71+
*/
72+
if (setjmp(ndo->ndo_truncated) == 0) {
73+
fuzzfunc(data, size);
74+
} else {
75+
ND_PRINT(" [|%s]", ndo->ndo_protocol);
76+
return;
77+
}
78+
if (getenv("REPLICATE_TRUNCATE")) {
79+
size_t i;
80+
uint8_t *data2;
81+
printf("original size %u\n", (unsigned)size);
82+
for (i = size - 1; i > 1; i--) {
83+
printf("trying again with size %u\n", (unsigned)i);
84+
data2 = malloc(i);
85+
memcpy(data2, data, i);
86+
fuzzfunc(data2, i);
87+
free(data2);
88+
}
89+
}
90+
/*
91+
* If the printer allocated any memory, free it.
92+
*/
93+
nd_free_all(ndo);
94+
}

fuzz/common.h

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2019
3+
* Arista Networks, Inc.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions
7+
* are met:
8+
*
9+
* 1. Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* 2. Redistributions in binary form must reproduce the above copyright
12+
* notice, this list of conditions and the following disclaimer in
13+
* the documentation and/or other materials provided with the
14+
* distribution.
15+
* 3. The names of the authors may not be used to endorse or promote
16+
* products derived from this software without specific prior
17+
* written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
20+
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22+
*/
23+
24+
#ifndef FUZZ_COMMON_H
25+
#define FUZZ_COMMON_H
26+
27+
#include "config.h"
28+
#include "netdissect-stdinc.h"
29+
#include "netdissect.h"
30+
#include "print.h"
31+
32+
void
33+
fuzz_common(int (*fuzzfunc)(const uint8_t *, size_t), const uint8_t *, size_t);
34+
35+
36+
#endif

0 commit comments

Comments
 (0)