diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2ea4a7fa9..146a97269 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -41,6 +41,63 @@ jobs: $(find ./target/debug -type f -perm -100 | grep gimli | head -n 1) \ > /dev/null + build_fuzz_targets: + name: Build fuzz targets + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install rustup + run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal + - name: Install nightly rust + run: | + rustup install nightly + rustup default nightly + - name: Install `cargo fuzz` + run: cargo install cargo-fuzz --vers "^0.7.4" + - run: cargo fuzz build -Oa + - uses: actions/upload-artifact@v2 + with: + name: fuzz-targets + path: fuzz/target/x86_64-unknown-linux-gnu/release/debug_* + - uses: actions/upload-artifact@v2 + with: + name: fuzz-targets + path: fuzz/target/x86_64-unknown-linux-gnu/release/eh_* + + run_fuzz_targets: + strategy: + matrix: + fuzz_target: ["debug_abbrev", "debug_aranges", "debug_info", "debug_line", "eh_frame", "eh_frame_hdr"] + name: "Run `${{matrix.fuzz_target}}` fuzz target" + needs: build_fuzz_targets + runs-on: ubuntu-latest + steps: + - name: Clone the fuzz corpora + uses: actions/checkout@v2 + with: + repository: gimli-rs/gimli-libfuzzer-corpora + path: corpora + - name: Download fuzz targets + uses: actions/download-artifact@v1 + with: + name: fuzz-targets + # Note: -max_total_time=300 == 300 seconds == 5 minutes. + - name: "Run `${{matrix.fuzz_target}}` fuzz target" + run: | + mkdir ${{matrix.fuzz_target}}_artifacts + chmod +x ./fuzz-targets/${{matrix.fuzz_target}} + ./fuzz-targets/${{matrix.fuzz_target}} ./corpora/${{matrix.fuzz_target}} \ + -max_total_time=300 \ + -artifact_prefix=./${{matrix.fuzz_target}}_artifacts/ + # If fuzzing finds a new crash/panic/etc, upload the input artifacts so we + # can debug them. + - name: Upload fuzz artifacts + if: failure() + uses: actions/upload-artifact@v2 + with: + name: ${{matrix.fuzz_target}}_artifacts + path: ./${{matrix.fuzz_target}}_artifacts + features: runs-on: ubuntu-latest steps: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d30c77b2e..4f9e574ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ out to us in a GitHub issue, or ping `fitzgen` in `#rust` on `irc.mozilla.org`. * [Testing `gimli`](#testing) * [Test Coverage](#coverage) * [Using `test-assembler`](#test-assembler) + * [Fuzzing](#fuzzing) * [Benchmarking](#benchmarking) * [Style](#style) @@ -81,6 +82,27 @@ construct binary test data. It makes building complex test cases readable. [Here is an example usage in `gimli`](https://github.com/gimli-rs/gimli/blob/156451f3fe6eeb2fa62b84b362c33fcb176e1171/src/loc.rs#L263) +### Fuzzing + +First, install `cargo fuzz`: + +``` +$ cargo install cargo-fuzz +``` + +Optionally, [set up the corpora for our fuzz targets by following these +instructions](https://github.com/gimli-rs/gimli-libfuzzer-corpora/blob/master/README.md#using-these-corpora). + +Finally, run a fuzz target! In this case, we are running the `eh_frame` fuzz +target: + +``` +$ cargo fuzz run eh_frame +``` + +The fuzz target definitions live in `fuzz/fuzz_targets/*`. You can add new ones +via `cargo fuzz add `. + ## Benchmarking The benchmarks require nightly `rustc`, so use `rustup`: diff --git a/fixtures/self/eh_frame_hdr b/fixtures/self/eh_frame_hdr new file mode 100644 index 000000000..a590ba213 Binary files /dev/null and b/fixtures/self/eh_frame_hdr differ diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 000000000..572e03bdf --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 000000000..b9f3ca29a --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,44 @@ + +[package] +name = "gimli-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.3" + +[dependencies.gimli] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "debug_info" +path = "fuzz_targets/debug_info.rs" + +[[bin]] +name = "debug_abbrev" +path = "fuzz_targets/debug_abbrev.rs" + +[[bin]] +name = "debug_line" +path = "fuzz_targets/debug_line.rs" + +[[bin]] +name = "eh_frame" +path = "fuzz_targets/eh_frame.rs" + +[[bin]] +name = "debug_aranges" +path = "fuzz_targets/debug_aranges.rs" + +[[bin]] +name = "eh_frame_hdr" +path = "fuzz_targets/eh_frame_hdr.rs" diff --git a/fuzz/fuzz_targets/debug_abbrev.rs b/fuzz/fuzz_targets/debug_abbrev.rs new file mode 100755 index 000000000..26484a2f2 --- /dev/null +++ b/fuzz/fuzz_targets/debug_abbrev.rs @@ -0,0 +1,16 @@ +#![no_main] + +use gimli::{read::DebugAbbrev, DebugAbbrevOffset, LittleEndian}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|debug_abbrev: &[u8]| { + let len = debug_abbrev.len(); + let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian); + + let offset = DebugAbbrevOffset(0); + if let Ok(abbreviations) = debug_abbrev.abbreviations(offset) { + for i in 1..len { + let _ = abbreviations.get(i as u64); + } + } +}); diff --git a/fuzz/fuzz_targets/debug_aranges.rs b/fuzz/fuzz_targets/debug_aranges.rs new file mode 100755 index 000000000..a7484af79 --- /dev/null +++ b/fuzz/fuzz_targets/debug_aranges.rs @@ -0,0 +1,12 @@ +#![no_main] + +use gimli::{read::DebugAranges, LittleEndian}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|debug_aranges: &[u8]| { + let debug_aranges = DebugAranges::new(&debug_aranges, LittleEndian); + let mut items = debug_aranges.items(); + while let Ok(Some(_entry)) = items.next() { + continue; + } +}); diff --git a/fuzz/fuzz_targets/debug_info.rs b/fuzz/fuzz_targets/debug_info.rs new file mode 100644 index 000000000..8cad4483b --- /dev/null +++ b/fuzz/fuzz_targets/debug_info.rs @@ -0,0 +1,26 @@ +#![no_main] + +use gimli::{ + read::{DebugAbbrev, DebugInfo}, + LittleEndian, +}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|sections: (Vec, Vec)| { + let (debug_abbrev, debug_info) = sections; + let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian); + let debug_info = DebugInfo::new(&debug_info, LittleEndian); + + let mut units = debug_info.units(); + while let Ok(Some(unit)) = units.next() { + if let Ok(abbrevs) = unit.abbreviations(&debug_abbrev) { + let mut cursor = unit.entries(&abbrevs); + while let Ok(Some((_delta, entry))) = cursor.next_dfs() { + let mut attrs = entry.attrs(); + while let Ok(Some(_attr)) = attrs.next() { + continue; + } + } + } + } +}); diff --git a/fuzz/fuzz_targets/debug_line.rs b/fuzz/fuzz_targets/debug_line.rs new file mode 100755 index 000000000..c15cb45b9 --- /dev/null +++ b/fuzz/fuzz_targets/debug_line.rs @@ -0,0 +1,17 @@ +#![no_main] + +use gimli::{read::DebugLine, DebugLineOffset, LittleEndian}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|debug_line: &[u8]| { + let debug_line = DebugLine::new(&debug_line, LittleEndian); + + let offset = DebugLineOffset(0); + let address_size = 8; + if let Ok(program) = debug_line.program(offset, address_size, None, None) { + let mut rows = program.rows(); + while let Ok(Some(row)) = rows.next_row() { + let _ = row; + } + } +}); diff --git a/fuzz/fuzz_targets/eh_frame.rs b/fuzz/fuzz_targets/eh_frame.rs new file mode 100755 index 000000000..ab44bd0c1 --- /dev/null +++ b/fuzz/fuzz_targets/eh_frame.rs @@ -0,0 +1,34 @@ +#![no_main] + +use gimli::{ + read::{BaseAddresses, CieOrFde, EhFrame, UninitializedUnwindContext, UnwindSection}, + LittleEndian, +}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|eh_frame: &[u8]| { + let eh_frame = EhFrame::new(&eh_frame, LittleEndian); + + let mut ctx = UninitializedUnwindContext::new(); + let bases = BaseAddresses::default() + .set_eh_frame(0) + .set_eh_frame_hdr(0) + .set_text(0) + .set_got(0); + + let mut entries = eh_frame.entries(&bases); + while let Ok(Some(entry)) = entries.next() { + match entry { + CieOrFde::Cie(_) => continue, + CieOrFde::Fde(partial) => { + if let Ok(fde) = partial.parse(EhFrame::cie_from_offset) { + if let Ok(mut table) = fde.rows(&eh_frame, &bases, &mut ctx) { + while let Ok(Some(_row)) = table.next_row() { + continue; + } + } + } + } + }; + } +}); diff --git a/fuzz/fuzz_targets/eh_frame_hdr.rs b/fuzz/fuzz_targets/eh_frame_hdr.rs new file mode 100755 index 000000000..beaf2fa31 --- /dev/null +++ b/fuzz/fuzz_targets/eh_frame_hdr.rs @@ -0,0 +1,15 @@ +#![no_main] + +use gimli::{read::EhFrameHdr, BaseAddresses, LittleEndian}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|eh_frame_hdr: &[u8]| { + let eh_frame_hdr = EhFrameHdr::new(eh_frame_hdr, LittleEndian); + let bases = BaseAddresses::default() + .set_eh_frame(0) + .set_eh_frame_hdr(0) + .set_text(0) + .set_got(0); + let address_size = 8; + let _ = eh_frame_hdr.parse(&bases, address_size); +}); diff --git a/tests/parse_self.rs b/tests/parse_self.rs old mode 100644 new mode 100755 index 694511b41..524998368 --- a/tests/parse_self.rs +++ b/tests/parse_self.rs @@ -404,3 +404,24 @@ fn test_parse_self_eh_frame() { } } } + +#[test] +fn test_parse_self_eh_frame_hdr() { + use gimli::{BaseAddresses, EhFrameHdr}; + + let eh_frame_hdr = read_section("eh_frame_hdr"); + let eh_frame_hdr = EhFrameHdr::new(&eh_frame_hdr, LittleEndian); + + let bases = BaseAddresses::default() + .set_eh_frame(0) + .set_eh_frame_hdr(0) + .set_text(0) + .set_got(0); + + // `.eh_frame_hdr` was generated on a 64 bit machine. + let address_size = 8; + + let _parsed_header = eh_frame_hdr + .parse(&bases, address_size) + .expect("we can parse the `.eh_frame_hdr` section OK"); +}