Skip to content

Commit 2cd3089

Browse files
clearly describe how to perform fuzzing, and make a fuzz/ workspace
- add publish = false to fuzz subcrates - move fuzzing to a subdirectory in order to share a workspace - break out separate shards for specific named features, but only for the top-level workspace
1 parent 72cce40 commit 2cd3089

File tree

1,964 files changed

+270
-118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,964 files changed

+270
-118
lines changed

.github/workflows/ci.yaml

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,12 @@ jobs:
2222
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
2323
strategy:
2424
matrix:
25+
workspace: ['Cargo.toml', 'fuzz/Cargo.toml']
2526
os: [ubuntu-latest, macOS-latest, windows-latest]
2627
rustalias: [stable, nightly, msrv]
2728
feature_flag:
2829
- "--all-features"
2930
- "--no-default-features"
30-
- "--no-default-features --features deflate-flate2-zlib-rs"
31-
- "--no-default-features --features deflate-zopfli"
3231
- ""
3332
include:
3433
- rustalias: stable
@@ -37,24 +36,34 @@ jobs:
3736
rust: '1.83'
3837
- rustalias: nightly
3938
rust: nightly
39+
# Break out a separate test shard for specific dependencies on their own.
40+
- feature_flag: "--no-default-features --features deflate-flate2-zlib-rs"
41+
workspace: 'Cargo.toml'
42+
- feature_flag: "--no-default-features --features deflate-zopfli"
43+
workspace: 'Cargo.toml'
4044
name: 'Build and test ${{ matrix.feature_flag }}: ${{ matrix.os }}, ${{ matrix.rustalias }}'
4145
runs-on: ${{ matrix.os }}
4246
steps:
4347
- uses: actions/checkout@v5
4448
- run: rustup toolchain add ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
4549

46-
- run: cargo check --all ${{ matrix.feature_flag }} --bins --examples
47-
- run: cargo test --all ${{ matrix.feature_flag }}
50+
- run: cargo check --manifest-path ${{ github.workspace }}/${{ matrix.workspace }} --all ${{ matrix.feature_flag }} --bins --examples
51+
- run: cargo test --manifest-path ${{ github.workspace }}/${{ matrix.workspace }} --all ${{ matrix.feature_flag }}
4852
miri:
4953
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
5054
strategy:
5155
matrix:
56+
workspace: ['Cargo.toml', 'fuzz/Cargo.toml']
5257
feature_flag:
5358
- "--all-features"
5459
- "--no-default-features"
55-
- "--no-default-features --features deflate-flate2-zlib-rs"
56-
- "--no-default-features --features deflate-zopfli"
5760
- ""
61+
include:
62+
# Break out a separate test shard for specific dependencies on their own.
63+
- feature_flag: "--no-default-features --features deflate-flate2-zlib-rs"
64+
workspace: 'Cargo.toml'
65+
- feature_flag: "--no-default-features --features deflate-zopfli"
66+
workspace: 'Cargo.toml'
5867
name: 'Miri ${{ matrix.feature_flag }}'
5968
runs-on: ubuntu-latest
6069
steps:
@@ -64,18 +73,21 @@ jobs:
6473
- run: rustup toolchain add --force-non-host stable-s390x-unknown-linux-gnu
6574
- run: rustup target add s390x-unknown-linux-gnu --toolchain stable-s390x-unknown-linux-gnu
6675
- run: rustup component add --toolchain nightly-x86_64-unknown-linux-gnu miri
67-
- run: cargo +nightly miri test --target s390x-unknown-linux-gnu --all ${{ matrix.feature_flag }} --bins --examples
76+
- run: cargo +nightly miri test --manifest-path ${{ github.workspace }}/${{ matrix.workspace }} --target s390x-unknown-linux-gnu --all ${{ matrix.feature_flag }} --bins --examples
6877
cargo_semver:
6978
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
7079
strategy:
7180
matrix:
81+
# Only do semver checks on the released library.
82+
workspace: ['Cargo.toml']
7283
feature_group: ["all-features", "default-features", "only-explicit-features"]
7384
name: 'Semver checks: ${{ matrix.feature_group }}'
7485
runs-on: ubuntu-latest
7586
steps:
7687
- uses: actions/checkout@v5
7788
- uses: obi1kenobi/cargo-semver-checks-action@v2
7889
with:
90+
manifest-path: ${{ github.workspace }}/${{ matrix.workspace }}
7991
feature-group: ${{ matrix.feature_group }}
8092
cargo_fmt:
8193
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
@@ -86,38 +98,41 @@ jobs:
8698
- run: rustup toolchain add nightly && rustup default nightly && rustup component add rustfmt
8799
- name: fmt
88100
run: cargo fmt --all -- --check
89-
- name: fmt fuzz_read
90-
run: cargo fmt --manifest-path fuzz_read/Cargo.toml -- --check
91-
- name: fmt fuzz_write
92-
run: cargo fmt --manifest-path fuzz_write/Cargo.toml -- --check
101+
- name: fmt fuzz
102+
run: cargo fmt --all --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml -- --check
93103

94104
check_minimal_versions:
95105
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
106+
strategy:
107+
matrix:
108+
# Only check minimal versions for the released library.
109+
workspace: ['Cargo.toml']
96110
runs-on: ubuntu-latest
97111

98112
steps:
99113
- uses: actions/checkout@v5
100114
- run: rustup toolchain add nightly && rustup default nightly
101115

102116
- name: resolve minimal versions
103-
run: cargo -Z minimal-versions update
117+
run: cargo -Z minimal-versions update --manifest-path ${{ github.workspace }}/${{ matrix.workspace }}
104118
- name: check
105-
run: cargo check --all-features
119+
run: cargo check --all-features --manifest-path ${{ github.workspace }}/${{ matrix.workspace }}
106120
- name: test
107-
run: cargo test --all-features
121+
run: cargo test --all-features --manifest-path ${{ github.workspace }}/${{ matrix.workspace }}
108122

109123
style_and_docs:
110124
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
111125
strategy:
112126
matrix:
127+
workspace: ['Cargo.toml', 'fuzz/Cargo.toml']
113128
feature_flag: ["--all-features", "--no-default-features", ""]
114129
runs-on: ubuntu-latest
115130
steps:
116131
- uses: actions/checkout@v5
117132
- run: rustup toolchain add nightly && rustup default nightly && rustup component add clippy
118133

119-
- run: cargo clippy --all-targets ${{ matrix.feature_flag }} -- -D warnings
120-
- run: cargo doc --no-deps ${{ matrix.feature_flag }}
134+
- run: cargo clippy --workspace ${{ matrix.feature_flag }} --manifest-path ${{ github.workspace }}/${{ matrix.workspace }} -- -D warnings
135+
- run: cargo doc --no-deps --workspace ${{ matrix.feature_flag }} --manifest-path ${{ github.workspace }}/${{ matrix.workspace }}
121136

122137
fuzz_read:
123138
runs-on: ubuntu-latest
@@ -136,16 +151,16 @@ jobs:
136151
- name: cargo afl system-config
137152
run: cargo afl system-config
138153
- name: clippy
139-
run: cargo afl clippy --all-features --manifest-path ${{ github.workspace }}/fuzz_read/Cargo.toml -- -D warnings
154+
run: cargo afl clippy --all-features -p fuzz_read --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml -- -D warnings
140155
- name: compile fuzz
141-
run: cargo afl build --all-features --manifest-path ${{ github.workspace }}/fuzz_read/Cargo.toml
156+
run: cargo afl build --all-features -p fuzz_read --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml
142157
- name: run fuzz
143158
timeout-minutes: 130
144-
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz_read/in -o out -V 7200 -- ${{ github.workspace }}/fuzz_read/target/debug/fuzz_read
159+
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz/read/in -o out -V 7200 -- ${{ github.workspace }}/fuzz/target/debug/fuzz_read
145160
- name: Minimize corpus
146-
run: cargo afl cmin -i out/default/queue -o out_cmin -- ${{ github.workspace }}/fuzz_read/target/debug/fuzz_read
161+
run: cargo afl cmin -i out/default/queue -o out_cmin -- ${{ github.workspace }}/fuzz/target/debug/fuzz_read
147162
- name: Report coverage
148-
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz_read/target/debug/fuzz_read
163+
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz/target/debug/fuzz_read
149164
- run: sudo apt install rename
150165
if: always()
151166
- name: Rename files
@@ -188,14 +203,14 @@ jobs:
188203
- name: cargo afl system-config
189204
run: cargo afl system-config
190205
- name: clippy
191-
run: cargo afl clippy --no-default-features --manifest-path ${{ github.workspace }}/fuzz_read/Cargo.toml -- -D warnings
206+
run: cargo afl clippy --no-default-features -p fuzz_read --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml -- -D warnings
192207
- name: compile fuzz
193-
run: cargo afl build --manifest-path ${{ github.workspace }}/fuzz_read/Cargo.toml
208+
run: cargo afl build -p fuzz_read --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml
194209
- name: run fuzz
195210
timeout-minutes: 130
196-
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz_read/in -o out -V 7200 -- ${{ github.workspace }}/fuzz_read/target/debug/fuzz_read
211+
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz/read/in -o out -V 7200 -- ${{ github.workspace }}/fuzz/target/debug/fuzz_read
197212
- name: Report coverage
198-
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz_read/target/debug/fuzz_read
213+
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz/target/debug/fuzz_read
199214
- run: sudo apt install rename
200215
if: always()
201216
- name: Rename files
@@ -233,16 +248,16 @@ jobs:
233248
- name: cargo afl system-config
234249
run: cargo afl system-config
235250
- name: clippy
236-
run: cargo afl clippy --all-features --manifest-path ${{ github.workspace }}/fuzz_write/Cargo.toml -- -D warnings
251+
run: cargo afl clippy --all-features -p fuzz_write --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml -- -D warnings
237252
- name: compile fuzz
238-
run: cargo afl build --all-features --manifest-path ${{ github.workspace }}/fuzz_write/Cargo.toml
253+
run: cargo afl build --all-features -p fuzz_write --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml
239254
- name: run fuzz
240255
timeout-minutes: 130
241-
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz_write/in -o out -V 7200 -x ${{ github.workspace }}/fuzz_write/fuzz.dict -- ${{ github.workspace }}/fuzz_write/target/debug/fuzz_write
256+
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz/write/in -o out -V 7200 -x ${{ github.workspace }}/fuzz/write/fuzz.dict -- ${{ github.workspace }}/fuzz/target/debug/fuzz_write
242257
- name: Minimize corpus
243-
run: cargo afl cmin -i out/default/queue -o out_cmin -- ${{ github.workspace }}/fuzz_write/target/debug/fuzz_write
258+
run: cargo afl cmin -i out/default/queue -o out_cmin -- ${{ github.workspace }}/fuzz/target/debug/fuzz_write
244259
- name: Report coverage
245-
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz_write/target/debug/fuzz_write
260+
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz/target/debug/fuzz_write
246261
- run: sudo apt install rename
247262
if: always()
248263
- name: Rename files
@@ -285,14 +300,14 @@ jobs:
285300
- name: cargo afl system-config
286301
run: cargo afl system-config
287302
- name: clippy
288-
run: cargo afl clippy --no-default-features --manifest-path ${{ github.workspace }}/fuzz_write/Cargo.toml -- -D warnings
303+
run: cargo afl clippy --no-default-features -p fuzz_write --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml -- -D warnings
289304
- name: compile fuzz
290-
run: cargo afl build --all-features --manifest-path ${{ github.workspace }}/fuzz_write/Cargo.toml
305+
run: cargo afl build --all-features -p fuzz_write --manifest-path ${{ github.workspace }}/fuzz/Cargo.toml
291306
- name: run fuzz
292307
timeout-minutes: 130
293-
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz_write/in -o out -V 7200 -x ${{ github.workspace }}/fuzz_write/fuzz.dict -- ${{ github.workspace }}/fuzz_write/target/debug/fuzz_write
308+
run: cargo afl fuzz -i ${{ github.workspace }}/fuzz/write/in -o out -V 7200 -x ${{ github.workspace }}/fuzz/write/fuzz.dict -- ${{ github.workspace }}/fuzz/target/debug/fuzz_write
294309
- name: Report coverage
295-
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz_write/target/debug/fuzz_write
310+
run: cargo afl showmap -C -i out -o map -- ${{ github.workspace }}/fuzz/target/debug/fuzz_write
296311
- run: sudo apt install rename
297312
if: always()
298313
- name: Rename files

Cargo.toml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ keywords = ["zip", "archive", "compression"]
1313
# Any change to rust-version must be reflected also in `README.md` and `.github/workflows/ci.yaml`.
1414
# The MSRV policy is documented in `README.md`.
1515
rust-version = "1.83.0"
16+
categories = ["compression", "filesystem", "parser-implementations"]
1617
description = """
1718
Library to support the reading and writing of zip files.
1819
"""
@@ -23,10 +24,8 @@ exclude = ["tests/**", "examples/**", ".github/**", "fuzz_read/**", "fuzz_write/
2324
all-features = true
2425
rustdoc-args = ["--cfg", "docsrs"]
2526

26-
[workspace.dependencies]
27-
time = { version = "0.3.37", default-features = false }
28-
2927
[dependencies]
28+
arbitrary = { version = "1.4", features = ["derive"], optional = true }
3029
aes = { version = "0.8", optional = true }
3130
bzip2 = { version = "0.6.0", optional = true }
3231
chrono = { version = "^0.4.27", optional = true }
@@ -42,7 +41,7 @@ nt-time = { version = "0.10.6", default-features = false, optional = true }
4241
ppmd-rust = { version = "1.2", optional = true }
4342
pbkdf2 = { version = "0.12", optional = true }
4443
sha1 = { version = "0.10", optional = true }
45-
time = { workspace = true, optional = true, features = [
44+
time = { version = "0.3.37", default-features = false, optional = true, features = [
4645
"std",
4746
] }
4847
zeroize = { version = "1.8", optional = true, features = ["zeroize_derive"] }
@@ -52,19 +51,17 @@ deflate64 = { version = "0.1.9", optional = true }
5251
lzma-rust2 = { version = "0.13", optional = true, default-features = false, features = ["std", "encoder", "optimization", "xz"] }
5352
bitstream-io = { version = "4.5.0", optional = true }
5453

55-
[target.'cfg(fuzzing)'.dependencies]
56-
arbitrary = { version = "1.4.1", features = ["derive"] }
57-
5854
[dev-dependencies]
5955
bencher = "0.1.5"
6056
getrandom = { version = "0.3.1", features = ["wasm_js", "std"] }
6157
walkdir = "2.5"
62-
time = { workspace = true, features = ["formatting", "macros"] }
58+
time = { version = "0.3", features = ["formatting", "macros"] }
6359
anyhow = "1.0.95"
6460
clap = { version = "=4.4.18", features = ["derive"] }
6561
tempfile = "3.15"
6662

6763
[features]
64+
arbitrary = ["dep:arbitrary"]
6865
aes-crypto = ["dep:aes", "dep:constant_time_eq", "hmac", "pbkdf2", "sha1", "getrandom", "zeroize"]
6966
chrono = ["dep:chrono"]
7067
_deflate-any = []

README.md

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
zip
2-
========
2+
===
33

44
[![Build Status](https://github.com/zip-rs/zip2/actions/workflows/ci.yaml/badge.svg)](https://github.com/Pr0methean/zip/actions?query=branch%3Amaster+workflow%3ACI)
55
[![Crates.io version](https://img.shields.io/crates/v/zip.svg)](https://crates.io/crates/zip)
66

77
[Documentation](https://docs.rs/zip/latest/zip/)
88

9-
Info
10-
----
11-
9+
# Info
1210

1311
A zip library for rust which supports reading and writing of simple ZIP files. Formerly hosted at
1412
https://github.com/zip-rs/zip2.
@@ -28,8 +26,7 @@ Currently unsupported zip extensions:
2826

2927
* Multi-disk
3028

31-
Features
32-
--------
29+
# Features
3330

3431
The features available are:
3532

@@ -53,17 +50,15 @@ The features available are:
5350

5451
By default `aes-crypto`, `bzip2`, `deflate`, `deflate64`, `lzma`, `ppmd`, `time`, `xz` and `zstd` are enabled.
5552

56-
MSRV
57-
----
53+
# MSRV
5854

5955
Our current Minimum Supported Rust Version is **1.83**. When adding features,
6056
we will follow these guidelines:
6157

6258
- We will always support a minor Rust version that has been stable for at least 6 months.
6359
- Any change to the MSRV will be accompanied with a **minor** version bump.
6460

65-
Examples
66-
--------
61+
# Examples
6762

6863
See the [examples directory](examples) for:
6964

@@ -74,10 +69,9 @@ See the [examples directory](examples) for:
7469
* How to read a zip from the standard input.
7570
* How to append a directory to an existing archive
7671

77-
Fuzzing
78-
-------
72+
# Fuzzing
7973

80-
Fuzzing support is through [cargo afl](https://rust-fuzz.github.io/book/afl.html). To install cargo afl:
74+
Fuzzing support is through [`cargo afl`](https://rust-fuzz.github.io/book/afl/tutorial.html). To install `cargo afl`:
8175

8276
```bash
8377
cargo install cargo-afl
@@ -86,13 +80,65 @@ cargo install cargo-afl
8680
To start fuzzing zip extraction:
8781

8882
```bash
89-
cargo +nightly afl build --all-features --manifest-path fuzz_read/Cargo.toml
90-
cargo +nightly afl run fuzz_read/target/debug/fuzz_read
83+
mkdir -vp fuzz-read-out
84+
cargo afl build --manifest-path=fuzz/Cargo.toml --all-features -p fuzz_read
85+
# Curated input corpus:
86+
cargo afl fuzz -i fuzz/read/in -o fuzz-read-out fuzz/target/debug/fuzz_read
87+
# Test data files:
88+
cargo afl fuzz -i tests/data -e zip -o fuzz-read-out fuzz/target/debug/fuzz_read
9189
```
9290

9391
To start fuzzing zip creation:
9492

9593
```bash
96-
cargo +nightly afl build --all-features --manifest-path fuzz_write/Cargo.toml
97-
cargo +nightly afl run fuzz_write/target/debug/fuzz_write
94+
mkdir -vp fuzz-write-out
95+
cargo afl build --manifest-path=fuzz/Cargo.toml --all-features -p fuzz_write
96+
# Curated input corpus and dictionary schema:
97+
cargo afl fuzz -x fuzz/write/fuzz.dict -i fuzz/write/in -o fuzz-write-out fuzz/target/debug/fuzz_write
98+
```
99+
100+
## Fuzzing stdio
101+
102+
The read and write fuzzers can also receive input over stdin for one-off validation. Note here that the fuzzers can be configured to build in support for DEFLATE, or not:
103+
```bash
104+
# Success, no output:
105+
cargo run --manifest-path=fuzz/Cargo.toml --quiet --all-features -p fuzz_read <tests/data/deflate64.zip
106+
# Error, without deflate64 support:
107+
cargo run --manifest-path=fuzz/Cargo.toml --quiet -p fuzz_read <tests/data/deflate64.zip
108+
109+
thread 'main' (537304) panicked at fuzz_read/src/main.rs:40:36:
110+
called `Result::unwrap()` on an `Err` value: UnsupportedArchive("Compression method not supported")
111+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
112+
```
113+
114+
The zip creation fuzzer will try to print out a description of the kind of input it translated the input bytes into:
115+
```bash
116+
# This is an empty input case:
117+
<fuzz/write/in/id-000000,time-0,execs-0,orig-0011743621118ab6c5278ffbb8fd14bddd8369ee.min \
118+
cargo run --manifest-path=fuzz/Cargo.toml --quiet --all-features -p fuzz_write
119+
# This input was translated into one or more test cases:
120+
<fuzz/write/in/id-000000,time-0,execs-0,orig-0011743621118ab6c5278ffbb8fd14bddd8369ee.min \
121+
cargo run --manifest-path=fuzz/Cargo.toml --quiet -p fuzz_write
122+
writer.start_file_from_path("", FileOptions { compression_method: Stored, compression_level: None, last_modified_time: DateTime::from_date_and_time(2048, 1, 1, 0, 0, 0)?, permissions: None, large_file: false, encrypt_with: None, extended_options: ExtendedFileOptions {extra_data: vec![].into(), central_extra_data: vec![].into()}, alignment: 0 })?;
123+
writer.write_all(&[])?;
124+
writer
125+
let _ = writer.finish_into_readable()?;
126+
```
127+
128+
The zip creation fuzzer uses [`arbitrary::Unstructured`](https://docs.rs/arbitrary/latest/arbitrary/struct.Unstructured.html) to convert bytes over stdin to random inputs, so it can be triggered with other sources of random input:
129+
```bash
130+
# Usually, the random input is translated into zero test cases:
131+
head -c50 /dev/random | cargo run --manifest-path=fuzz/Cargo.toml --quiet --all-features -p fuzz_write
132+
# Sometimes, one or more test cases are generated and successfully evaluated:
133+
head -c50 /dev/random | cargo run --manifest-path=fuzz/Cargo.toml --quiet --all-features -p fuzz_write
134+
writer.set_raw_comment([20, 202])?;
135+
let mut writer = ZipWriter::new_append(writer.finish()?)?;
136+
let sub_writer = {
137+
let mut initial_junk = Cursor::new(vec![106]);
138+
initial_junk.seek(SeekFrom::End(0))?;
139+
let mut writer = ZipWriter::new(initial_junk);
140+
writer
141+
};
142+
writer.merge_archive(sub_writer.finish_into_readable()?)?;
143+
let mut writer = ZipWriter::new_append(writer.finish()?)?;
98144
```

0 commit comments

Comments
 (0)