Skip to content

Commit ad6a73f

Browse files
author
bors-servo
authored
Auto merge of #1068 - fitzgen:predicate-script, r=pepyakin
Factor out a general purpose predicate script from the `csmith` driver This allows it to be used by both fuzzers and when using `creduce`. See each commit for details. r? @pepyakin
2 parents c744e68 + 349c1f9 commit ad6a73f

File tree

3 files changed

+330
-79
lines changed

3 files changed

+330
-79
lines changed

CONTRIBUTING.md

+69-35
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ out to us in a GitHub issue, or stop by
2525
- [Generating Graphviz Dot Files](#generating-graphviz-dot-files)
2626
- [Debug Logging](#debug-logging)
2727
- [Using `creduce` to Minimize Test Cases](#using-creduce-to-minimize-test-cases)
28+
- [Getting `creduce`](#getting-creduce)
2829
- [Isolating Your Test Case](#isolating-your-test-case)
2930
- [Writing a Predicate Script](#writing-a-predicate-script)
3031

@@ -337,11 +338,25 @@ $ RUST_LOG=bindgen cargo test
337338

338339
## Using `creduce` to Minimize Test Cases
339340

340-
If you are hacking on `bindgen` and find a test case that causes an unexpected
341-
panic, results in bad Rust bindings, or some other incorrectness in `bindgen`,
342-
then using `creduce` can help reduce the test case to a minimal one.
341+
If you find a test case that triggers an unexpected panic in `bindgen`, causes
342+
`bindgen` to emit bindings that won't compile, define structs with the wrong
343+
size/alignment, or results in any other kind of incorrectness, then using
344+
`creduce` can help reduce the test case to a minimal one that still exhibits
345+
that same bad behavior.
343346

344-
[Follow these instructions for building and/or installing `creduce`.](https://github.com/csmith-project/creduce/blob/master/INSTALL)
347+
***Reduced test cases are SUPER helpful when filing bug reports!***
348+
349+
### Getting `creduce`
350+
351+
Often, you can install `creduce` from your OS's package manager:
352+
353+
```
354+
$ sudo apt install creduce
355+
$ brew install creduce
356+
$ # Etc...
357+
```
358+
359+
[Otherwise, follow these instructions for building and/or installing `creduce`.](https://github.com/csmith-project/creduce/blob/master/INSTALL)
345360

346361
Running `creduce` requires two things:
347362

@@ -352,7 +367,7 @@ Running `creduce` requires two things:
352367

353368
With those two things in hand, running `creduce` looks like this:
354369

355-
$ creduce ./predicate.sh ./isolated_test_case.h
370+
$ creduce ./predicate.sh ./isolated-test-case.h
356371

357372
### Isolating Your Test Case
358373

@@ -369,12 +384,9 @@ standalone test case.
369384

370385
### Writing a Predicate Script
371386

372-
Writing a `predicate.sh` script for a `bindgen` test case is fairly
373-
straightforward. One potential gotcha is that `creduce` can and will attempt to
374-
reduce test cases into invalid C/C++ code. That might be useful for C/C++
375-
compilers, but we generally only care about valid C/C++ input headers.
376-
377-
Here is a skeleton predicate script:
387+
Writing a `predicate.sh` script for a `bindgen` test case is straightforward. We
388+
already have a general purpose predicate script that you can use, you just have
389+
to wrap and configure it.
378390

379391
```bash
380392
#!/usr/bin/env bash
@@ -384,39 +396,61 @@ Here is a skeleton predicate script:
384396
# * we access any undefined variable.
385397
set -eu
386398

387-
# Print out Rust backtraces on panic. Useful for minimizing a particular panic.
388-
export RUST_BACKTRACE=1
399+
# Invoke the general purpose predicate script that comes in the
400+
# `bindgen` repository.
401+
#
402+
# You'll need to replace `--whatever-flags` with things that are specific to the
403+
# incorrectness you're trying to pin down. See below for details.
404+
path/to/rust-bindgen/csmith-fuzzing/predicate.py \
405+
--whatever-flags \
406+
./isolated-test-case.h
407+
```
408+
409+
When hunting down a particular panic emanating from inside `bindgen`, you can
410+
invoke `predicate.py` like this:
389411

390-
# If the `libclang.so` you're using for `bindgen` isn't the system
391-
# `libclang.so`, let the linker find it.
392-
export LD_LIBRARY_PATH=~/path/to/your/directory/containing/libclang
412+
```bash
413+
path/to/rust-bindgen/csmith-fuzzing/predicate.py \
414+
--expect-bindgen-fail \
415+
--bindgen-grep "thread main panicked at '<insert panic message here>'" \
416+
./isolated-test-case.h
417+
```
393418

394-
# Make sure that the reduced test case is valid C/C++ by compiling it. If it
395-
# isn't valid C/C++, this command will exit with a nonzero exit code and cause
396-
# the whole script to do the same.
397-
clang[++ --std=c++14] -c ./pre_processed_header.hpp
419+
Alternatively, when hunting down a bad `#[derive(Eq)]` that is causing `rustc`
420+
to fail to compile `bindgen`'s emitted bindings, you can invoke `predicate.py`
421+
like this:
398422

399-
# Run `bindgen` and `grep` for the thing your hunting down! Make sure to include
400-
# `2>&1` to get at stderr if you're hunting down a panic.
401-
~/src/rust-bindgen/target/debug/bindgen \
402-
./pre_processed_header.hpp \
403-
[ <extra flags> ] \
404-
2>&1 \
405-
| grep "<pattern in generated bindings or a panic string or ...>"
423+
```bash
424+
path/to/rust-bindgen/csmith-fuzzing/predicate.py \
425+
--bindings-grep NameOfTheStructThatIsErroneouslyDerivingEq \
426+
--expect-compile-fail \
427+
--rustc-grep 'error[E0277]: the trait bound `f64: std::cmp::Eq` is not satisfied' \
428+
./isolated-test-case.h
406429
```
407430

408-
When hunting down a panic, I `grep`ed like this:
431+
Or, when minimizing a failing layout test in the compiled bindings, you can
432+
invoke `predicate.py` like this:
409433

410-
... | grep "thread main panicked at '<panic error message here>'"
434+
```bash
435+
path/to/rust-bindgen/csmith-fuzzing/predicate.py \
436+
--bindings-grep MyStruct \
437+
--expect-layout-tests-fail \
438+
--layout-tests-grep "thread 'bindgen_test_layout_MyStruct' panicked" \
439+
./isolated-test-case.h
440+
```
441+
442+
For details on all the flags that you can pass to `predicate.py`, run:
411443

412-
When hunting down bad codegen for a base member, I `grep`ed like this:
444+
```
445+
$ path/to/rust-bindgen/csmith-fuzzing/predicate.py --help
446+
```
413447

414-
... | grep "pub _base: MyInvalidBaseTypeThatShouldntBeHere"
448+
And you can always write your own, arbitrary predicate script if you prefer.
449+
(Although, maybe we should add extra functionality to `predicate.py` -- file an
450+
issue if you think so!)
415451

416-
That's pretty much it! I want to impress upon you that `creduce` is *really*
417-
helpful and has enabled me to reduce 30k lines of test case into 5 lines. And it
418-
works pretty quickly too. Super valuable tool to have in your belt when hacking
419-
on `bindgen`!
452+
`creduce` is *really* helpful and can cut hundreds of thousands of lines of test
453+
case down to 5 lines.
420454

421455
Happy bug hunting and test case reducing!
422456

csmith-fuzzing/driver.py

+13-44
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,12 @@ def run_logged(cmd):
2828
cat(stdout.name, title="stderr")
2929
return result
3030

31-
def run_bindgen(input, output):
32-
return run_logged([
33-
"bindgen",
34-
"--with-derive-partialeq",
35-
"--with-derive-eq",
36-
"--with-derive-partialord",
37-
"--with-derive-ord",
38-
"--with-derive-hash",
39-
"--with-derive-default",
40-
"-o", output.name,
41-
input.name,
42-
"--",
43-
"-I", os.path.abspath(os.path.dirname(sys.argv[0])),
44-
])
45-
46-
def run_rustc(output, test):
47-
return run_logged([
48-
"rustc",
49-
"--crate-type", "lib",
50-
"--test",
51-
output.name,
52-
"-o", test.name,
53-
])
31+
BINDGEN_ARGS = "--with-derive-partialeq \
32+
--with-derive-eq \
33+
--with-derive-partialord \
34+
--with-derive-ord \
35+
--with-derive-hash \
36+
--with-derive-default"
5437

5538
def main():
5639
print("Fuzzing `bindgen` with C-Smith...\n")
@@ -65,36 +48,22 @@ def main():
6548
if result.returncode != 0:
6649
exit(1)
6750

68-
output = NamedTemporaryFile(delete=False, prefix="output-", suffix=".rs")
69-
output.close()
70-
result = run_bindgen(input, output)
71-
if result.returncode != 0:
72-
cat(input.name)
73-
cat(output.name)
74-
exit(1)
75-
76-
test = NamedTemporaryFile(delete=False, prefix="test-")
77-
test.close()
78-
result = run_rustc(output, test)
79-
if result.returncode != 0:
80-
cat(input.name)
81-
cat(output.name)
82-
exit(1)
83-
84-
result = run_logged([test.name])
51+
result = run_logged([
52+
"./predicate.py",
53+
"--bindgen-args",
54+
"{} -- -I{}".format(BINDGEN_ARGS, os.path.abspath(os.path.dirname(sys.argv[0]))),
55+
input.name
56+
])
8557
if result.returncode != 0:
8658
cat(input.name)
87-
cat(output.name)
8859
exit(1)
8960

9061
os.remove(input.name)
91-
os.remove(output.name)
92-
os.remove(test.name)
93-
9462
iterations += 1
9563

9664
if __name__ == "__main__":
9765
try:
66+
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
9867
main()
9968
except KeyboardInterrupt:
10069
exit()

0 commit comments

Comments
 (0)