From b1d2b7cfb807410871a3679ee36e412ed2451d72 Mon Sep 17 00:00:00 2001 From: Will Lillis Date: Thu, 1 May 2025 23:46:40 -0400 Subject: [PATCH] fix(query): correct `last_child_step_index` in cases where a new step wasn't created. This fixes an OOB access to `self.steps` when a last child anchor immediately follows a predicate. --- cli/src/tests/query_test.rs | 11 +++++++++++ lib/src/query.c | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index 313714d8..ec77c91c 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -5621,3 +5621,14 @@ const foo = [ assert_eq!(matches.len(), 1); assert_eq!(matches[0].1, captures); } + +#[test] +fn test_query_with_predicate_causing_oob_access() { + let language = get_language("rust"); + + let query = "(call_expression + function: (scoped_identifier + path: (scoped_identifier (identifier) @_regex (#any-of? @_regex \"Regex\" \"RegexBuilder\") .)) + (#set! injection.language \"regex\"))"; + Query::new(&language, query).unwrap(); +} diff --git a/lib/src/query.c b/lib/src/query.c index eb4e906c..02933926 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -2523,6 +2523,9 @@ static TSQueryError ts_query__parse_pattern( child_is_immediate, &child_capture_quantifiers ); + // In the event we only parsed a predicate, meaning no new steps were added, + // then subtract one so we're not indexing past the end of the array + if (step_index == self->steps.size) step_index--; if (e == PARENT_DONE) { if (stream->next == ')') { if (child_is_immediate) { @@ -2531,7 +2534,7 @@ static TSQueryError ts_query__parse_pattern( return TSQueryErrorSyntax; } // Mark this step *and* its alternatives as the last child of the parent. - QueryStep *last_child_step = &self->steps.contents[last_child_step_index]; + QueryStep *last_child_step = array_get(&self->steps, last_child_step_index); last_child_step->is_last_child = true; if ( last_child_step->alternative_index != NONE &&