# # Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) # Copyright (c) 2019 Paul Dreik # Copyright (c) 2021 Dmitry Arkhipov (grisumbras@gmail.com) # # Distributed under the Boost Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # # Official repository: https://github.com/boostorg/json # import common ; import link ; import os ; import path ; import property ; import sequence ; import testing ; # set the maximum size of the input, to avoid # big inputs which blow up the corpus size .MAXLEN = [ os.environ MAXLEN ] ; .MAXLEN ?= -max_len=4000 ; # set a timelimit (you may want to adjust this if you run locally) .MAXTIME = [ os.environ MAXTIME ] ; .MAXTIME ?= -max_total_time=30 ; # If doing fuzzing locally (not in CI), adjust this to utilize more # of your cpu. #JOBS="-jobs=32" .JOBS = [ os.environ JOBS ] ; # make sure ubsan stops in case anything is found .UBSAN_OPTIONS = [ common.variable-setting-command UBSAN_OPTIONS : halt_on_error=1 ] ; local corpus.tar = [ glob-ex . : corpus.tar ] ; local test-corpus ; if $(corpus.tar) { # if an old corpus exists, use it make old-corpus : $(corpus.tar) : @untar-corpus : oldcorpus ; explicit old-corpus ; } else { test-corpus = [ glob-tree-ex ../test : *.json ] ; } local old-runs = [ SORT [ glob-tree-ex old_crashes : * ] ] ; make old_crashes : : @mkdir : . ; explicit old_crashes ; local variants = basic_parser parse parser direct_parse ; for local variant in basic_parser parse parser direct_parse { local $(variant)-runs ; local fuzzer = fuzzer_$(variant) ; exe $(fuzzer) : fuzz_$(variant).cpp /boost/json//json_sources : requirements clang @fuzzer-props ; # make sure the old crashes pass without problems if $(old-runs) { run $(fuzzer) : target-name $(variant)-run-crashes : input-files $(old-runs) ; explicit $(variant)-run-crashes ; $(variant)-runs += $(variant)-run-crashes ; } local old-corpus-deps ; if $(corpus.tar) { old-corpus-deps = old-corpus ; } else { # make an initial corpus from the test data already in the repo for file in $(test-corpus) { local copied = $(variant)/$(file:D=) ; make $(copied) : $(file) : common.copy : oldcorpus ; explicit $(copied) ; old-corpus-deps += $(copied) ; } } make oldcorpus/$(variant) : $(old-corpus-deps) : @mkdir : . ; explicit oldcorpus/$(variant) ; # run the fuzzer for a short while make out/$(variant) : $(fuzzer) oldcorpus/$(variant) old_crashes : @run-fuzzer : . $(.MAXTIME) $(.MAXLEN) $(.JOBS) ; $(variant)-runs += out/$(variant) ; # minimize the corpus make cmin/$(variant) : $(fuzzer) out/$(variant) oldcorpus/$(variant) old_crashes : @run-fuzzer : . -merge=1 $(.MAXLEN) ; $(variant)-runs += cmin/$(variant) ; alias $(variant)-run : $($(variant)-runs) ; explicit $($(variant)-runs) ; } alias run : $(variants)-run ; explicit run $(variants)-run ; rule mkdir ( target : source * : props * ) { local dir = [ path.make [ on $(target) return $(LOCATE) ] ] ; dir = [ path.join $(dir) $(target:G=) ] ; common.MkDir $(dir) ; } rule fuzzer-props ( props * ) { local toolset = [ property.select toolset : $(props) ] ; if clang = $(toolset:G=) { return on speed on norecover -fsanitize=fuzzer -fsanitize=fuzzer # explicitly set BOOST_JSON_STACK_BUFFER_SIZE small so interesting # code paths are taken also for small inputs # (see https://github.com/boostorg/json/issues/333) BOOST_JSON_STACK_BUFFER_SIZE=64 ; } else { return no ; } } rule run-fuzzer ( target : sources * : props * ) { local flags = [ property.select flags : $(props) ] ; FLAGS on $(target) = $(flags:G=) ; LOG on $(target) = [ path.native [ path.join $(target) _log ] ] ; local dir = [ path.make [ on $(target) return $(LOCATE) ] ] ; dir = $(dir)/$(target:G=) ; common.MkDir $(dir) ; DEPENDS $(target) : $(dir) ; LOG on $(target) = [ path.native [ path.join $(dir) _log ] ] ; } .SET_STATUS = [ modules.peek testing : .SET_STATUS ] ; .STATUS = [ modules.peek testing : .STATUS ] ; .RUN_OUTPUT_NL = [ modules.peek testing : .RUN_OUTPUT_NL ] ; .STATUS_NOT_0 = [ modules.peek testing : .STATUS_NOT_0 ] ; .CATENATE = [ modules.peek testing : .CATENATE ] ; .ENDIF = [ modules.peek testing : .ENDIF ] ; .NULL_OUT = [ modules.peek common : NULL_OUT ] ; .RM = [ common.rm-command ] ; actions run-fuzzer { $(.UBSAN_OPTIONS) $(>[1]) $(<) $(>[2-]) $(FLAGS) > "$(LOG)" 2>&1 $(.SET_STATUS) if $(.STATUS_NOT_0) echo ====== BEGIN OUTPUT ====== $(.CATENATE) "$(LOG)" echo ====== END OUTPUT ====== $(RM) $(LOG) $(.NULL_OUT) exit 1 $(.ENDIF) } .TOUCH_FILE = [ common.file-touch-command ] ; actions untar-corpus { tar xf $(>) -C $(<:D) $(.TOUCH_FILE) $(<) }