From Harald Welte:
[obnox/wireshark/wip.git] / tools / fuzz-test.sh
1 #!/bin/bash
2 #
3 # $Id$
4
5 # Fuzz-testing script for TShark
6 #
7 # This script uses Editcap to add random errors ("fuzz") to a set of
8 # capture files specified on the command line.  It runs TShark on
9 # each fuzzed file and checks for errors.  The files are processed
10 # repeatedly until an error is found.
11
12 # This needs to point to a 'date' that supports %s.
13 DATE=/bin/date
14 BASE_NAME=fuzz-`$DATE +%Y-%m-%d`-$$
15
16 # Directory containing binaries.  Default current directory.
17 BIN_DIR=.
18
19 # Temporary file directory and names.
20 # (had problems with this on cygwin, tried TMP_DIR=./ which worked)
21 TMP_DIR=/tmp
22 if [ "$OSTYPE" == "cygwin" ] ; then
23         TMP_DIR=`cygpath --windows "$TMP_DIR"`
24 fi
25 TMP_FILE=$BASE_NAME.pcap
26 ERR_FILE=$BASE_NAME.err
27
28 # Loop this many times (< 1 loops forever)
29 MAX_PASSES=0
30
31 # Perform a two pass analysis on the capture file?
32 TWO_PASS=
33
34 # Specific config profile ?
35 CONFIG_PROFILE=
36
37 # These may be set to your liking
38 # Stop the child process if it's running longer than x seconds
39 MAX_CPU_TIME=900
40 # Stop the child process if it's using more than y * 1024 bytes
41 MAX_VMEM=500000
42 # Stop the child process if its stack is larger than than z * 1024 bytes
43 # Windows XP:   2033
44 # Windows 7:    2034
45 # OS X 10.6:    8192
46 # Linux 2.6.24: 8192
47 # Solaris 10:   8192
48 MAX_STACK=2033
49 # Insert z times an error into the capture file (0.02 seems to be a good value to find errors)
50 ERR_PROB=0.02
51 # Trigger an abort if a dissector finds a bug.
52 # Uncomment to enable
53 # Note that if ABORT is enabled there will be no info
54 #  output to stderr about the DISSECTOR_BUG.
55 #  (There'll just be a core-dump).
56 ###export WIRESHARK_ABORT_ON_DISSECTOR_BUG="True"
57
58
59 # To do: add options for file names and limits
60 while getopts ":b:d:e:C:Pp:" OPTCHAR ; do
61     case $OPTCHAR in
62         b) BIN_DIR=$OPTARG ;;
63         C) CONFIG_PROFILE="-C $OPTARG " ;;
64         d) TMP_DIR=$OPTARG ;;
65         e) ERR_PROB=$OPTARG ;;
66         p) MAX_PASSES=$OPTARG ;;
67         P) TWO_PASS="-P " ;;
68     esac
69 done
70 shift $(($OPTIND - 1))
71
72 # Tweak the following to your liking.  Editcap must support "-E".
73 TSHARK="$BIN_DIR/tshark"
74 EDITCAP="$BIN_DIR/editcap"
75 CAPINFOS="$BIN_DIR/capinfos"
76
77 if [ "$BIN_DIR" = "." ]; then
78     export WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1
79 fi
80
81 # set some limits to the child processes, e.g. stop it if it's running longer then MAX_CPU_TIME seconds
82 # (ulimit is not supported well on cygwin and probably other platforms, e.g. cygwin shows some warnings)
83 ulimit -S -t $MAX_CPU_TIME -v $MAX_VMEM -s $MAX_STACK
84 ulimit -c unlimited
85
86 ### usually you won't have to change anything below this line ###
87
88 # TShark arguments (you won't have to change these)
89 # n Disable network object name resolution
90 # V Print a view of the details of the packet rather than a one-line summary of the packet
91 # x Cause TShark to print a hex and ASCII dump of the packet data after printing the summary or details
92 # r Read packet data from the following infile
93 TSHARK_ARGS="${CONFIG_PROFILE}${TWO_PASS}-nVxr"
94
95 NOTFOUND=0
96 for i in "$TSHARK" "$EDITCAP" "$CAPINFOS" "$DATE" "$TMP_DIR" ; do
97     if [ ! -x $i ]; then
98         echo "Couldn't find $i"
99         NOTFOUND=1
100     fi
101 done
102 if [ $NOTFOUND -eq 1 ]; then
103     exit 1
104 fi
105
106 # Make sure we have a valid test set
107 FOUND=0
108 for CF in "$@" ; do
109     if [ "$OSTYPE" == "cygwin" ] ; then
110         CF=`cygpath --windows "$CF"`
111     fi
112     "$CAPINFOS" "$CF" > /dev/null 2>&1 && FOUND=1
113     if [ $FOUND -eq 1 ] ; then break ; fi
114 done
115
116 if [ $FOUND -eq 0 ] ; then
117     cat <<FIN
118 Error: No valid capture files found.
119
120 Usage: `basename $0` [-b bin_dir] [-C config_profile] [-p passes] [-d work_dir] [-P ] [-e error probability] capture file 1 [capture file 2]...
121 FIN
122     exit 1
123 fi
124
125 DISSECTOR_PLUGINS=`$TSHARK -G plugins | grep dissector | wc -l`
126 # 10 is an arbritary value.
127 if [ $DISSECTOR_PLUGINS -lt 10 ] ; then
128     echo "Error: Found fewer plugins than expected."
129     exit 1
130 fi
131
132 HOWMANY="forever"
133 if [ $MAX_PASSES -gt 0 ]; then
134         HOWMANY="$MAX_PASSES passes"
135 fi
136 echo "Running $TSHARK with args: $TSHARK_ARGS ($HOWMANY)"
137 echo ""
138
139 # Clean up on <ctrl>C, etc
140 trap "rm -f $TMP_DIR/$TMP_FILE $TMP_DIR/$ERR_FILE; echo ""; exit 0" HUP INT TERM
141
142
143 ##############################################################################
144 ### Set up environment variables for fuzz testing                          ###
145 ##############################################################################
146 # Initialize (ep_ and se_) allocated memory to 0xBADDCAFE and freed memory
147 # to 0xDEADBEEF
148 export WIRESHARK_DEBUG_SCRUB_MEMORY=
149 # Use canaries in se_ allocations (off by default due to the memory usage)
150 export WIRESHARK_DEBUG_SE_USE_CANARY=
151 # Verify that ep_ and se_ allocated memory is not passed to certain routines
152 # which need the memory to be persistent.
153 export WIRESHARK_EP_VERIFY_POINTERS=
154 export WIRESHARK_SE_VERIFY_POINTERS=
155
156 # Turn on GLib memory debugging (since 2.13)
157 export G_SLICE=debug-blocks
158 # Cause glibc (Linux) to abort() if some memory errors are found
159 export MALLOC_CHECK_=3
160 # Cause FreeBSD (and other BSDs) to abort() on allocator warnings and
161 # initialize allocated memory (to 0xa5) and freed memory (to 0x5a).  see:
162 # http://www.freebsd.org/cgi/man.cgi?query=malloc&apropos=0&sektion=0&manpath=FreeBSD+8.2-RELEASE&format=html
163 export MALLOC_OPTIONS=AJ
164
165 # MacOS options; see http://developer.apple.com/library/mac/releasenotes/DeveloperTools/RN-MallocOptions/_index.html
166 # Initialize allocated memory to 0xAA and freed memory to 0x55
167 export MallocPreScribble=1
168 export MallocScribble=1
169 # Add guard pages before and after large allocations
170 export MallocGuardEdges=1
171 # Call abort() if heap corruption is detected.  Heap is checked every 1000
172 # allocations (may need to be tuned!)
173 export MallocCheckHeapStart=1000
174 export MallocCheckHeapEach=1000
175 export MallocCheckHeapAbort=1
176 # Call abort() if an illegal free() call is made
177 export MallocBadFreeAbort=1
178
179
180 # Iterate over our capture files.
181 PASS=0
182 while [ $PASS -lt $MAX_PASSES -o $MAX_PASSES -lt 1 ] ; do
183     PASS=`expr $PASS + 1`
184     echo "Starting pass $PASS:"
185     RUN=0
186
187     for CF in "$@" ; do
188         RUN=$(( $RUN + 1 ))
189         if [ $(( $RUN % 50 )) -eq 0 ] ; then
190             echo "    [Pass $PASS]"
191         fi
192         if [ "$OSTYPE" == "cygwin" ] ; then
193             CF=`cygpath --windows "$CF"`
194         fi
195         echo -n "    $CF: "
196
197         "$CAPINFOS" "$CF" > /dev/null 2> $TMP_DIR/$ERR_FILE
198         RETVAL=$?
199         if [ $RETVAL -eq 1 ] ; then
200             echo "Not a valid capture file"
201             rm -f $TMP_DIR/$ERR_FILE
202             continue
203         elif [ $RETVAL -ne 0 ] ; then
204             # Some other error
205             echo ""
206             echo " ERROR"
207             echo -e "Processing failed.  Capture info follows:\n"
208             echo "  Input file: $CF"
209             echo -e "stderr follows:\n"
210             cat $TMP_DIR/$ERR_FILE
211             exit 1
212         fi
213
214         DISSECTOR_BUG=0
215
216         "$EDITCAP" -E $ERR_PROB "$CF" $TMP_DIR/$TMP_FILE > /dev/null 2>&1
217         if [ $? -ne 0 ] ; then
218             "$EDITCAP" -E $ERR_PROB -T ether "$CF" $TMP_DIR/$TMP_FILE \
219             > /dev/null 2>&1
220             if [ $? -ne 0 ] ; then
221             echo "Invalid format for editcap"
222             continue
223             fi
224         fi
225
226         "$TSHARK" $TSHARK_ARGS $TMP_DIR/$TMP_FILE \
227             > /dev/null 2>> $TMP_DIR/$ERR_FILE
228         RETVAL=$?
229         # Uncomment the next two lines to enable dissector bug
230         # checking.
231         #grep -i "dissector bug" $TMP_DIR/$ERR_FILE \
232         #    > /dev/null 2>&1 && DISSECTOR_BUG=1
233         if [ $RETVAL -ne 0 -o $DISSECTOR_BUG -ne 0 ] ; then
234                 echo ""
235             echo " ERROR"
236             echo -e "Processing failed.  Capture info follows:\n"
237             echo "  Output file: $TMP_DIR/$TMP_FILE"
238             echo -e "stderr follows:\n"
239             cat $TMP_DIR/$ERR_FILE
240             exit 1
241         fi
242         echo " OK"
243             rm -f $TMP_DIR/$TMP_FILE $TMP_DIR/$ERR_FILE
244     done
245 done