Skip to content

Commit 6c4110d

Browse files
Fixes (google#236)
* Add reproduce command. * Remove unneeded run and just_run command with duplicate code as run_fuzzer. * Fix docs for reproduction.
1 parent 3c359b5 commit 6c4110d

File tree

8 files changed

+77
-87
lines changed

8 files changed

+77
-87
lines changed

docs/reproducing.md

+10-5
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ This file contains the bytes that were fed to the [Fuzz Target](http://libfuzzer
1414
If you have already [integrated](ideal_integration.md) the fuzz target with your build and test system,
1515
all you do is run:
1616
<pre>
17-
./fuzz_target_binary <b><i>$testcase_file_absolute_path</i></b>
17+
./fuzz_target_binary <testcase_path>
1818
</pre>
19+
20+
If this is a timeout bug, add the <b><i>-timeout=25</i></b> argument.
21+
If this is a OOM bug, add the <b><i>-rss_limit_mb=2048</i></b> argument.
22+
Read more on how timeouts and OOMs are handed [here](faq.md#how-do-you-handle-timeouts-and-ooms).
23+
1924
Depending on the nature of the bug, the fuzz target binary needs to be built with the appropriate [sanitizer](https://github.com/google/sanitizers)
2025
(e.g. if this is a buffer overflow, with [AddressSanitizer](http://clang.llvm.org/docs/AddressSanitizer.html)).
2126

@@ -26,7 +31,7 @@ to replicate the exact build steps used by OSS-Fuzz and then feed the reproducer
2631
- *Reproduce using latest OSS-Fuzz build:*
2732

2833
<pre>
29-
docker run --rm -ti -v <b><i>$testcase_file_absolute_path</i></b>:/testcase ossfuzz/<b><i>$project</i></b> reproduce <b><i>$fuzzer</i></b>
34+
python infra/helper.py reproduce $PROJECT_NAME <fuzzer_name> <testcase_path>
3035
</pre>
3136

3237
It builds the fuzzer from the most recent successful OSS-Fuzz build (usually last night's sources)
@@ -35,13 +40,13 @@ docker run --rm -ti -v <b><i>$testcase_file_absolute_path</i></b>:/testcase ossf
3540
E.g. for [libxml2](../projects/libxml2) project with fuzzer named `libxml2_xml_read_memory_fuzzer`, it will be:
3641

3742
<pre>
38-
docker run --rm -ti -v <b><i>~/Downloads/testcase</i></b>:/testcase ossfuzz/<b><i>libxml2</i></b> reproduce <b><i>libxml2_xml_read_memory_fuzzer</i></b>
43+
python infra/helper.py reproduce libxml2 libxml2_xml_read_memory_fuzzer ~/Downloads/testcase
3944
</pre>
4045
- *Reproduce using local source checkout:*
4146

4247
<pre>
43-
docker run --rm -ti -v <b><i>$local_source_checkout_dir</i></b>:/src/<b><i>$project</i></b> \
44-
-v <b><i>$testcase_file_absolute_path</i></b>:/testcase ossfuzz/<b><i>$project</i></b> reproduce <b><i>$fuzzer</i></b>
48+
python infra/helper.py build_fuzzers $PROJECT_NAME <source_path>
49+
python infra/helper.py reproduce $PROJECT_NAME <fuzzer_name> <testcase_path>
4550
</pre>
4651

4752
This is essentially the previous command that additionally mounts local sources into the running container.

infra/base-images/base-builder/Dockerfile

+2-4
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ ENV LIB_FUZZING_ENGINE="/usr/lib/libFuzzingEngine.a"
3636
# TODO: remove after tpm2 catchup.
3737
ENV FUZZER_LDFLAGS ""
3838

39-
COPY coverage_report compile compile_libfuzzer srcmap reproduce run just_run \
40-
/usr/local/bin/
39+
COPY compile compile_libfuzzer coverage_report srcmap /usr/local/bin/
4140

4241
WORKDIR $SRC
4342
CMD ["compile"]
@@ -46,5 +45,4 @@ ADD http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz $SRC/
4645
RUN mkdir afl && \
4746
cd afl && \
4847
tar -xzv --strip-components=1 -f $SRC/afl-latest.tgz && \
49-
rm -rf $SRC/afl-latest.tgz && \
50-
ls -lR /src
48+
rm -rf $SRC/afl-latest.tgz

infra/base-images/base-builder/just_run

-42
This file was deleted.

infra/base-images/base-builder/run

-19
This file was deleted.

infra/base-images/base-runner/Dockerfile

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
FROM ossfuzz/base-image
1818
MAINTAINER mike.aizatsky@gmail.com
1919
RUN apt-get install -y zip file
20-
COPY llvm-symbolizer test_all test_report run_fuzzer /usr/local/bin/
20+
COPY llvm-symbolizer reproduce run_fuzzer test_all test_report \
21+
/usr/local/bin/
2122

2223
# Default environment options for various sanitizers.
2324
# Note that these match the settings used in ClusterFuzz and
@@ -26,4 +27,4 @@ COPY llvm-symbolizer test_all test_report run_fuzzer /usr/local/bin/
2627
ENV ASAN_OPTIONS="alloc_dealloc_mismatch=0:allocator_may_return_null=1:allocator_release_to_os=1:check_malloc_usable_size=0:detect_container_overflow=1:detect_odr_violation=0:detect_leaks=1:detect_stack_use_after_return=1:fast_unwind_on_fatal=0:handle_abort=1:handle_segv=1:handle_sigill=1:max_uar_stack_size_log=16:print_scariness=1:quarantine_size_mb=10:strict_memcmp=1:strict_string_check=1:strip_path_prefix=/workspace/:symbolize=1:use_sigaltstack=1"
2728
ENV MSAN_OPTIONS="print_stats=1:strip_path_prefix=/workspace/:symbolize=1"
2829
ENV UBSAN_OPTIONS="halt_on_error=1:print_stacktrace=1:print_summary=1:strip_path_prefix=/workspace/:symbolize=1"
29-
30+
ENV FUZZER_ARGS="-rss_limit_mb=2048 -timeout=25"

infra/base-images/base-builder/reproduce infra/base-images/base-runner/reproduce

+1-3
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@ if [ ! -f $TESTCASE ]; then
2424
exit 1
2525
fi
2626

27-
28-
compile
2927
export PATH=/out:$PATH
3028
cd /out
3129

32-
$FUZZER $@ $TESTCASE
30+
$FUZZER $FUZZER_ARGS $@ $TESTCASE
3331

infra/base-images/base-runner/run_fuzzer

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ cd $OUT
2222

2323
FUZZER=$1
2424
shift
25-
CMD_LINE="$FUZZER $@"
25+
CMD_LINE="$FUZZER $FUZZER_ARGS $@"
2626

2727
OPTIONS_FILE="${FUZZER}.options"
2828
if [ -f $OPTIONS_FILE ]; then

infra/helper.py

+60-11
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
OSSFUZZ_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
3333
BUILD_DIR = os.path.join(OSSFUZZ_DIR, 'build')
3434

35-
3635
GLOBAL_ARGS = None
3736

3837
def main():
@@ -46,7 +45,7 @@ def main():
4645
help='do not specify --pull while building an image')
4746
parser.add_argument(
4847
'command',
49-
help='One of: generate, build_image, build_fuzzers, run_fuzzer, coverage, shell',
48+
help='One of: generate, build_image, build_fuzzers, run_fuzzer, coverage, reproduce, shell',
5049
nargs=argparse.REMAINDER)
5150
global GLOBAL_ARGS
5251
GLOBAL_ARGS = args = parser.parse_args()
@@ -65,6 +64,8 @@ def main():
6564
return run_fuzzer(args.command[1:])
6665
elif args.command[0] == 'coverage':
6766
return coverage(args.command[1:])
67+
elif args.command[0] == 'reproduce':
68+
return reproduce(args.command[1:])
6869
elif args.command[0] == 'shell':
6970
return shell(args.command[1:])
7071
else:
@@ -100,6 +101,11 @@ def _check_fuzzer_exists(project_name, fuzzer_name):
100101
return True
101102

102103

104+
def _get_absolute_path(path):
105+
"""Returns absolute path with user expansion."""
106+
return os.path.abspath(os.path.expanduser(path))
107+
108+
103109
def _get_command_string(command):
104110
"""Returns a shell escaped command string."""
105111
return ' '.join(pipes.quote(part) for part in command)
@@ -120,7 +126,7 @@ def _build_image(image_name):
120126
build_args = []
121127
if not GLOBAL_ARGS.nopull:
122128
build_args += ['--pull']
123-
build_args += ['-t', 'ossfuzz/' + image_name, dockerfile_dir ]
129+
build_args += ['-t', 'ossfuzz/%s' % image_name, dockerfile_dir ]
124130

125131
command = [ 'docker', 'build' ] + build_args
126132
print('Running:', _get_command_string(command))
@@ -151,6 +157,8 @@ def build_fuzzers(build_args):
151157
parser = argparse.ArgumentParser('helper.py build_fuzzers')
152158
parser.add_argument('-e', action='append', help="set environment variable")
153159
parser.add_argument('project_name')
160+
parser.add_argument('source_path', help='path of local source',
161+
nargs='?')
154162
args = parser.parse_args(build_args)
155163
project_name = args.project_name
156164

@@ -161,12 +169,20 @@ def build_fuzzers(build_args):
161169
if args.e:
162170
env += args.e
163171

164-
command = (['docker', 'run', '--rm', '-i', '--cap-add', 'SYS_PTRACE'] +
165-
sum([['-e', v] for v in env], []) +
166-
['-v', '%s:/out' % os.path.join(BUILD_DIR, 'out', project_name),
167-
'-v', '%s:/work' % os.path.join(BUILD_DIR, 'work', project_name),
168-
'-t', 'ossfuzz/' + project_name
169-
])
172+
command = (
173+
['docker', 'run', '--rm', '-i', '--cap-add', 'SYS_PTRACE'] +
174+
sum([['-e', v] for v in env], [])
175+
)
176+
if args.source_path:
177+
command += [
178+
'-v',
179+
'%s:/src/%s' % (_get_absolute_path(args.source_path), args.project_name)
180+
]
181+
command += [
182+
'-v', '%s:/out' % os.path.join(BUILD_DIR, 'out', project_name),
183+
'-v', '%s:/work' % os.path.join(BUILD_DIR, 'work', project_name),
184+
'-t', 'ossfuzz/%s' % project_name
185+
]
170186

171187
print('Running:', _get_command_string(command))
172188

@@ -209,6 +225,7 @@ def run_fuzzer(run_args):
209225
pipe = subprocess.Popen(command)
210226
pipe.communicate()
211227

228+
212229
def coverage(run_args):
213230
"""Runs a fuzzer in the container."""
214231
parser = argparse.ArgumentParser('helper.py coverage')
@@ -236,7 +253,7 @@ def coverage(run_args):
236253
'-v', '%s:/out' % os.path.join(BUILD_DIR, 'out', args.project_name),
237254
'-v', '%s:/cov' % temp_dir,
238255
'-w', '/cov',
239-
'-e', 'ASAN_OPTIONS=coverage=1,detect_leaks=0',
256+
'-e', 'ASAN_OPTIONS=coverage=1',
240257
'-t', 'ossfuzz/base-runner',
241258
'/out/%s' % args.fuzzer_name,
242259
'-max_total_time=%s' % args.run_time
@@ -264,6 +281,38 @@ def coverage(run_args):
264281
pipe.communicate()
265282

266283

284+
def reproduce(run_args):
285+
"""Reproduces a testcase in the container."""
286+
parser = argparse.ArgumentParser('helper.py reproduce')
287+
parser.add_argument('project_name', help='name of the project')
288+
parser.add_argument('fuzzer_name', help='name of the fuzzer')
289+
parser.add_argument('testcase_path', help='path of local testcase')
290+
291+
args = parser.parse_args(run_args)
292+
293+
if not _check_project_exists(args.project_name):
294+
return 1
295+
296+
if not _check_fuzzer_exists(args.project_name, args.fuzzer_name):
297+
return 1
298+
299+
if not _build_image('base-runner'):
300+
return 1
301+
302+
command = [
303+
'docker', 'run', '--rm', '-i', '--cap-add', 'SYS_PTRACE',
304+
'-v', '%s:/out' % os.path.join(BUILD_DIR, 'out', args.project_name),
305+
'-v', '%s:/testcase' % _get_absolute_path(args.testcase_path),
306+
'-t', 'ossfuzz/base-runner',
307+
'reproduce',
308+
'/out/%s' % args.fuzzer_name,
309+
]
310+
311+
print('Running:', _get_command_string(command))
312+
pipe = subprocess.Popen(command)
313+
pipe.communicate()
314+
315+
267316
def generate(generate_args):
268317
"""Generate empty project files."""
269318
parser = argparse.ArgumentParser('helper.py generate')
@@ -311,7 +360,7 @@ def shell(shell_args):
311360
'docker', 'run', '--rm', '-i', '--cap-add', 'SYS_PTRACE',
312361
'-v', '%s:/out' % os.path.join(BUILD_DIR, 'out', args.project_name),
313362
'-v', '%s:/work' % os.path.join(BUILD_DIR, 'work', args.project_name),
314-
'-t', 'ossfuzz/' + args.project_name,
363+
'-t', 'ossfuzz/%s' % args.project_name,
315364
'/bin/bash'
316365
]
317366
print('Running:', _get_command_string(command))

0 commit comments

Comments
 (0)