Skip to content

Commit

Permalink
Fuzzer: Separate arguments used to make the fuzz wasm from the opts w…
Browse files Browse the repository at this point in the history
…e run on it (#6357)

Before FUZZ_OPTS was used both when doing --translate-to-fuzz/-ttf to generate the
wasm from the random bytes and also when later running optimizations to generate
a second wasm file for comparison. That is, we ended up doing this, if the opts were -O3:

wasm-opt random.input -ttf -o a.wasm -O3
wasm-opt a.wasm -O3 -o b.wasm

Now we have a pair a.wasm,b.wasm which we can test. However, we have run -O3
on both which is a little silly - the second -O3 might not actually have anything left
to do, which would mean we compare the same wasm to itself.

Worse, this is incorrect, as there are things we need to do only during the
generation phase, like --denan. We need that in order to generate a valid wasm to
test on, but it is "destructive" in itself: when removing NaNs (to avoid nondeterminism)
if replaces them with 0, which is different. As a result, running --denan when
generating the second wasm from the first could lead to different execution in them.
This was always a problem, but became more noticable recently now that DeNaN
modifies SIMD operations, as one optimization we do is to replace a memory.copy
with v128.load + v128.store, and --denan will make sure the loaded value has no
NaNs...

To fix this, separate the generation and optimization phase. Instead of

wasm-opt random.input -ttf -o a.wasm --denan -O3
wasm-opt a.wasm --denan -O3 -o b.wasm

(note how --denan -O3 appears twice), do this:

wasm-opt random.input -ttf -o a.wasm --denan
wasm-opt a.wasm -O3 -o b.wasm

(note how --denan appears in generation, and -O3 in optimization).
  • Loading branch information
kripken committed Feb 27, 2024
1 parent d5157e0 commit a1afe47
Showing 1 changed file with 16 additions and 8 deletions.
24 changes: 16 additions & 8 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,13 @@ def update_feature_opts(wasm):


def randomize_fuzz_settings():
# a list of the arguments to pass to wasm-opt -ttf when generating the wasm
global GEN_ARGS
GEN_ARGS = []

# a list of the optimizations to run on the wasm
global FUZZ_OPTS
FUZZ_OPTS = []

# a boolean whether NaN values are allowed, or we de-NaN them
global NANS
Expand All @@ -186,20 +191,19 @@ def randomize_fuzz_settings():
# a boolean whether we legalize the wasm for JS
global LEGALIZE

FUZZ_OPTS = []
if random.random() < 0.5:
NANS = True
else:
NANS = False
FUZZ_OPTS += ['--denan']
GEN_ARGS += ['--denan']
if random.random() < 0.5:
OOB = True
else:
OOB = False
FUZZ_OPTS += ['--no-fuzz-oob']
GEN_ARGS += ['--no-fuzz-oob']
if random.random() < 0.5:
LEGALIZE = True
FUZZ_OPTS += ['--legalize-and-prune-js-interface']
GEN_ARGS += ['--legalize-and-prune-js-interface']
else:
LEGALIZE = False

Expand All @@ -209,6 +213,10 @@ def randomize_fuzz_settings():
# https://github.com/WebAssembly/binaryen/pull/5665
# https://github.com/WebAssembly/binaryen/issues/5599
if '--disable-gc' not in FEATURE_OPTS:
GEN_ARGS += ['--dce']

# Add --dce not only when generating the original wasm but to the
# optimizations we use to create any other wasm file.
FUZZ_OPTS += ['--dce']

print('randomized settings (NaNs, OOB, legalize):', NANS, OOB, LEGALIZE)
Expand Down Expand Up @@ -1267,7 +1275,7 @@ def handle(self, wasm):
second_input = abspath('second_input.dat')
make_random_input(second_size, second_input)
second_wasm = abspath('second.wasm')
run([in_bin('wasm-opt'), second_input, '-ttf', '-o', second_wasm] + FUZZ_OPTS + FEATURE_OPTS)
run([in_bin('wasm-opt'), second_input, '-ttf', '-o', second_wasm] + GEN_ARGS + FEATURE_OPTS)

# sometimes also optimize the second module
if random.random() < 0.5:
Expand Down Expand Up @@ -1364,14 +1372,14 @@ def test_one(random_input, given_wasm):
# wasm had applied. that is, we need to preserve properties like not
# having nans through reduction.
try:
run([in_bin('wasm-opt'), given_wasm, '-o', abspath('a.wasm')] + FUZZ_OPTS + FEATURE_OPTS)
run([in_bin('wasm-opt'), given_wasm, '-o', abspath('a.wasm')] + GEN_ARGS + FEATURE_OPTS)
except Exception as e:
print("Internal error in fuzzer! Could not run given wasm")
raise e
else:
# emit the target features section so that reduction can work later,
# without needing to specify the features
generate_command = [in_bin('wasm-opt'), random_input, '-ttf', '-o', abspath('a.wasm')] + FUZZ_OPTS + FEATURE_OPTS
generate_command = [in_bin('wasm-opt'), random_input, '-ttf', '-o', abspath('a.wasm')] + GEN_ARGS + FEATURE_OPTS
if INITIAL_CONTENTS:
generate_command += ['--initial-fuzz=' + INITIAL_CONTENTS]
if PRINT_WATS:
Expand All @@ -1385,7 +1393,7 @@ def test_one(random_input, given_wasm):
print('pre wasm size:', wasm_size)
update_feature_opts('a.wasm')

# create a second wasm for handlers that want to look at pairs.
# create a second (optimized) wasm for handlers that want to look at pairs.
generate_command = [in_bin('wasm-opt'), abspath('a.wasm'), '-o', abspath('b.wasm')] + opts + FUZZ_OPTS + FEATURE_OPTS
if PRINT_WATS:
printed = run(generate_command + ['--print'])
Expand Down

0 comments on commit a1afe47

Please sign in to comment.