mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-07-29 19:21:57 -07:00
globset: add opt-in Arbitrary
trait implementations
This feature is mandatory when using `Glob` in fuzz testing. Closes #2720
This commit is contained in:
committed by
Andrew Gallant
parent
5548e538b1
commit
95979048c9
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@@ -230,3 +230,28 @@ jobs:
|
||||
env:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
run: cargo doc --no-deps --document-private-items --workspace
|
||||
|
||||
fuzz_testing:
|
||||
name: Compile Fuzz Test Targets
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install required packages (Ubuntu)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install g++ --yes
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install Fuzzer
|
||||
run: cargo install cargo-fuzz
|
||||
working-directory: fuzz
|
||||
|
||||
- name: Verify fuzz targets build
|
||||
run: cargo check
|
||||
working-directory: fuzz
|
||||
|
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -17,6 +17,15 @@ version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
@@ -85,6 +94,17 @@ version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
@@ -126,6 +146,7 @@ name = "globset"
|
||||
version = "0.4.16"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"arbitrary",
|
||||
"bstr",
|
||||
"glob",
|
||||
"log",
|
||||
|
@@ -20,6 +20,7 @@ exclude = [
|
||||
"/pkg/brew",
|
||||
"/benchsuite/",
|
||||
"/scripts/",
|
||||
"/crates/fuzz",
|
||||
]
|
||||
build = "build.rs"
|
||||
autotests = false
|
||||
|
@@ -11,4 +11,4 @@ if ! command -V sudo; then
|
||||
fi
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
zsh xz-utils liblz4-tool musl-tools brotli zstd
|
||||
zsh xz-utils liblz4-tool musl-tools brotli zstd g++
|
||||
|
@@ -21,6 +21,7 @@ bench = false
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "1.1.1"
|
||||
arbitrary = { version = "1.3.2", optional = true, features = ["derive"] }
|
||||
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
|
||||
log = { version = "0.4.20", optional = true }
|
||||
serde = { version = "1.0.188", optional = true }
|
||||
@@ -41,6 +42,7 @@ serde_json = "1.0.107"
|
||||
|
||||
[features]
|
||||
default = ["log"]
|
||||
arbitrary = ["dep:arbitrary"]
|
||||
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime
|
||||
# dispatch.
|
||||
simd-accel = []
|
||||
|
@@ -72,6 +72,7 @@ impl MatchStrategy {
|
||||
/// It cannot be used directly to match file paths, but it can be converted
|
||||
/// to a regular expression string or a matcher.
|
||||
#[derive(Clone, Debug, Eq)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
pub struct Glob {
|
||||
glob: String,
|
||||
re: String,
|
||||
@@ -194,6 +195,7 @@ pub struct GlobBuilder<'a> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
struct GlobOptions {
|
||||
/// Whether to match case insensitively.
|
||||
case_insensitive: bool,
|
||||
@@ -220,6 +222,7 @@ impl GlobOptions {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
struct Tokens(Vec<Token>);
|
||||
|
||||
impl std::ops::Deref for Tokens {
|
||||
@@ -236,6 +239,7 @@ impl std::ops::DerefMut for Tokens {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
enum Token {
|
||||
Literal(char),
|
||||
Any,
|
||||
|
@@ -94,6 +94,19 @@ Standard Unix-style glob syntax is supported:
|
||||
|
||||
A `GlobBuilder` can be used to prevent wildcards from matching path separators,
|
||||
or to enable case insensitive matching.
|
||||
|
||||
# Crate Features
|
||||
|
||||
This crate includes optional features that can be enabled if necessary.
|
||||
These features are not required but may be useful depending on the use case.
|
||||
|
||||
The following features are available:
|
||||
|
||||
* **arbitrary** -
|
||||
Enabling this feature introduces a public dependency on the
|
||||
[`arbitrary`](https://crates.io/crates/arbitrary)
|
||||
crate. Namely, it implements the `Arbitrary` trait from that crate for the
|
||||
[`Glob`] type. This feature is disabled by default.
|
||||
*/
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
4
fuzz/.gitignore
vendored
Normal file
4
fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
target
|
||||
corpus
|
||||
artifacts
|
||||
coverage
|
188
fuzz/Cargo.lock
generated
Normal file
188
fuzz/Cargo.lock
generated
Normal file
@@ -0,0 +1,188 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuzz"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"libfuzzer-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.16"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"arbitrary",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
25
fuzz/Cargo.toml
Normal file
25
fuzz/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "fuzz"
|
||||
version = "0.0.1"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
globset = { path = "../crates/globset", features = ["arbitrary"] }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_glob"
|
||||
path = "fuzz_targets/fuzz_glob.rs"
|
||||
test = false
|
||||
doc = false
|
52
fuzz/README.md
Normal file
52
fuzz/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Fuzz Testing
|
||||
|
||||
## Introduction
|
||||
|
||||
Fuzz testing produces pseudo-random / arbitrary data that is used to find
|
||||
stability issues within a code base. While Rust provides a strong type system,
|
||||
this does not guarantee that an object will convert properly from one struct
|
||||
to another. It is the responsibility of the developer to ensure that a struct
|
||||
is converted properly. Fuzz testing will generate input within the domain of
|
||||
each property. This arbitrary data can then be used to convert from ObjectA
|
||||
to ObjectB and then back. This type of testing will help catch bugs that the
|
||||
type system is not able to see.
|
||||
|
||||
## Installation
|
||||
|
||||
This crate relies on the `cargo-fuzz` component. To install this component,
|
||||
run the following from the `fuzz` directory:
|
||||
|
||||
```bash
|
||||
cargo install cargo-fuzz
|
||||
```
|
||||
|
||||
## Listing Targets
|
||||
|
||||
Once installed, fuzz targets can be listed by running the following command:
|
||||
|
||||
```bash
|
||||
cargo fuzz list
|
||||
```
|
||||
|
||||
This command will print out a list of all targets that can be tested.
|
||||
|
||||
## Running Fuzz Tests
|
||||
|
||||
To run a fuzz test, the target must be specified:
|
||||
|
||||
```bash
|
||||
cargo fuzz run <target>
|
||||
```
|
||||
|
||||
Note that the above will run the fuzz test indefinitely. Use the
|
||||
`-max_total_time=<num seconds>` flag to specify how many seconds the test
|
||||
should run for:
|
||||
|
||||
```bash
|
||||
cargo fuzz run <target> -- -max_total_time=5
|
||||
```
|
||||
|
||||
The above command will run the fuzz test for five seconds. If the test
|
||||
completes without error it will show how many tests were run successfully.
|
||||
The test will abort and return a non-zero error code if it is able to produce
|
||||
an error. The arbitrary input will be displayed in the event of a failure.
|
22
fuzz/fuzz_targets/fuzz_glob.rs
Normal file
22
fuzz/fuzz_targets/fuzz_glob.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
#![no_main]
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use globset::Glob;
|
||||
|
||||
libfuzzer_sys::fuzz_target!(|glob_str: &str| {
|
||||
let Ok(glob) = Glob::new(glob_str) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(glob2) = Glob::from_str(glob_str) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Verify that a `Glob` constructed with `new` is the same as a `Glob`` constructed
|
||||
// with `from_str`.
|
||||
assert_eq!(glob, glob2);
|
||||
|
||||
// Verify that `Glob::glob` produces the same string as the original.
|
||||
assert_eq!(glob.glob(), glob_str);
|
||||
});
|
Reference in New Issue
Block a user