Skip to content

Commit

Permalink
fix: panic when head-skipping to a single-byte key
Browse files Browse the repository at this point in the history
- This was detected by fuzzing! Searching for `{` or `[`
with descendant would cause a panic
if the input started with the sequence
`{"` or `["`, respectively.

Ref: #281
  • Loading branch information
V0ldek committed Sep 21, 2023
1 parent 5c1bf7d commit abe4e61
Show file tree
Hide file tree
Showing 12 changed files with 1,932 additions and 779 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test-codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
value: rsonpath-test-documents
artifact-path:
description: Path to which the artifact should be extracted.
value: crates/rsonpath-lib/tests/documents
value: crates/rsonpath-test/tests/documents

env:
CARGO_TERM_COLOR: always
Expand Down Expand Up @@ -57,5 +57,5 @@ jobs:
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
with:
name: rsonpath-test-documents
path: crates/rsonpath-lib/tests/documents
path: crates/rsonpath-test/tests/documents
retention-days: 1
14 changes: 5 additions & 9 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -233,19 +233,15 @@ commit msg:

# === HOOKS ===

tmpdiff := if os() == "windows" {
`New-TemporaryFile`
} else {
`mktemp -t pre-commit-hook-diff-XXXXXXXX.$$`
}

[private]
hook-pre-commit:
hook-pre-commit:
#!/bin/sh
tmpdiff=$(mktemp -t pre-commit-hook-diff-XXXXXXXX.$$)
just assert-benchmarks-committed
git diff --full-index --binary > {{tmpdiff}}
git diff --full-index --binary > $tmpdiff
git stash -q --keep-index
(just verify-fmt && just verify-check); \
git apply --whitespace=nowarn < {{tmpdiff}} && git stash drop -q; rm {{tmpdiff}}
git apply --whitespace=nowarn < $tmpdiff}} && git stash drop -q; rm $tmpdiff

[private]
@hook-post-checkout: checkout-benchmarks
Expand Down
2 changes: 1 addition & 1 deletion crates/rsonpath-benchmarks
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub(crate) fn find_in_mask<I: Input>(
let mut result = (previous_block | (first << 1)) & second;
while result != 0 {
let idx = result.trailing_zeros() as usize;
if input.is_member_match(offset + idx - 2, offset + idx + label_size - 3, label) {
if offset + idx > 1 && input.is_member_match(offset + idx - 2, offset + idx + label_size - 3, label) {
return Some(offset + idx - 2);
}
result &= !(1 << idx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub(crate) fn find_in_mask<I: Input>(
while result != 0 {
let idx = result.trailing_zeros() as usize;
debug!("{offset} + {idx} - 2 to {offset} + {idx} + {label_size} - 3");
if input.is_member_match(offset + idx - 2, offset + idx + label_size - 3, label) {
if offset + idx > 1 && input.is_member_match(offset + idx - 2, offset + idx + label_size - 3, label) {
return Some(offset + idx - 2);
}
result &= !(1 << idx);
Expand Down
26 changes: 26 additions & 0 deletions crates/rsonpath-lib/tests/documents/toml/head_skip_for_curly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Define the JSON input for all query test cases.
[input]
# Short description of the input structure.
description = "Object with an empty key."
# Set to true only if your specific test input is fully compressed (no extraneous whitespace).
is_compressed = false

# Inline JSON document.
[input.source]
json_string = '{"":null}'

# Define queries to test on the input.
[[queries]]
# Valid JSONPath query string.
query = '$..["{"]'
# Short descritpion of the query semantics.
description = "descendant search for a key equal to the curly brace"

[queries.results]
# Number of expected matches.
count = 0
# Byte locations of spans of all matches, in order.
spans = []
# Stringified values of all matches, verbatim as in the input,
# in the same order as above.
nodes = []
26 changes: 26 additions & 0 deletions crates/rsonpath-lib/tests/documents/toml/head_skip_for_square.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Define the JSON input for all query test cases.
[input]
# Short description of the input structure.
description = "List with an empty string."
# Set to true only if your specific test input is fully compressed (no extraneous whitespace).
is_compressed = false

# Inline JSON document.
[input.source]
json_string = '[""]'

# Define queries to test on the input.
[[queries]]
# Valid JSONPath query string.
query = '$..["["]'
# Short descritpion of the query semantics.
description = "descendant search for a key equal to the square brace"

[queries.results]
# Number of expected matches.
count = 0
# Byte locations of spans of all matches, in order.
spans = []
# Stringified values of all matches, verbatim as in the input,
# in the same order as above.
nodes = []
2,610 changes: 1,851 additions & 759 deletions crates/rsonpath-lib/tests/end_to_end.rs

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion crates/rsonpath-test-codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ pub(crate) fn generate_test_fns(files: &mut Files) -> Result<impl IntoIterator<I
);
let full_description = format!(
r#"on document {} running the query {} ({}) with Input impl {} and result mode {}"#,
discovered_doc.name, query.query, query.description, input_type, result_type
escape_format(&discovered_doc.name),
escape_format(&query.query),
escape_format(&query.description),
escape_format(&input_type),
escape_format(&result_type)
);
let body = generate_body(
&full_description,
Expand Down Expand Up @@ -336,3 +340,11 @@ impl Display for EngineTypeToTest {
)
}
}

fn escape_format<D>(val: &D) -> impl Display
where
D: Display,
{
let s = val.to_string();
s.replace('{', "{{").replace('}', "}}")
}
2 changes: 1 addition & 1 deletion crates/rsonpath-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ publish = false
[build-dependencies]
eyre = "0.6.8"
md5 = "0.7.0"
rsonpath-test-codegen = { version = "0.8.0", path = "../rsonpath-test-codegen" }
rsonpath-test-codegen = { version = "0.8.1", path = "../rsonpath-test-codegen" }
5 changes: 3 additions & 2 deletions crates/rsonpath-test/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Just codegen

This crate is almost useless.
This crate should be used only for the declarative TOML tests.

It has no code in it except for the build script. The build script generates test cases for `rsonpath-lib`
It has no code in it except for the build script and the generated script.
The build script generates test cases for `rsonpath-lib` based on TOML files in `tests/documents`
using `rsonpath-test-codegen`. This is needed for the following reasons:

1. `rsonpath-test-codegen` cannot also have a `build.rs` script to generate the tests, since it would need to build-depend on itself;
Expand Down
4 changes: 2 additions & 2 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit abe4e61

Please sign in to comment.