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");
+}