Repository navigation
fix: linear backref must reject groups without a completed span (\1 OOM, #122) - #134
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #134 +/- ##
=========================================
- Coverage 85.4% 85.4% -0.1%
Complexity 1 1
=========================================
Files 163 163
Lines 48740 48759 +19
Branches 6966 6970 +4
=========================================
+ Hits 41635 41647 +12
- Misses 5098 5104 +6
- Partials 2007 2008 +1
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
…lete The unconditional guard pushed generated matches() methods past HotSpot's 325-byte FreqInlineSize cliff (323->329 bytes, ~25% throughput drop on (\\w)(\\w)\\1\\2). Track completed groups through the op sequence; guard only self-/forward-references and quantifier-body backrefs.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 049de688ec
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
What does this PR do?
Fixes #122. A backreference evaluated by the
LINEAR_BACKREFERENCEstrategy against a group that has no completed span (self- or forward-reference, e.g.(\1),(a\1)) computed a negative group length:groupEnds[group]was still the-1initializer, thepos + groupLen > lenbounds check passed,regionMatchesvacuously succeeded, andpos += groupLenmoved the match position backward. The matcher then produced bogus matches at every position andfindAllnever advanced — the reported ReDoS-like hang ending inOutOfMemoryError.The fix:
generateBackrefChecknow also fails when the group end is unset — a backref may only consult a completed group span, which is exactlyjava.util.regex'sPattern$BackRefbehavior.(\1)and friends now return no match like the JDK; the negative-span path is unreachable so the backward-position corruption cannot recur.Motivation
Issue #122 (
(\1)OOM), reported against 0.3.0, blocking PCRE-parity expectations. Also fixes a silent wrong-answer divergence ((a\1)returned true where the JDK returns false) found while reproducing the OOM.Related Issue(s)
Fixes #122. Related: #39 (self-referencing backreference support), #48.
Change Type
Checklist
./gradlew build) — full build +cleanTest test(codegen/runtime/processor) + integration tests greenBackrefSelfReferenceTest(JDK-differential: expected values computed fromjava.util.regexat runtime; covers self-refs, forward refs, quantified self-refs, nested, and valid-backref controls)doc/agents-fallback-and-limitations.mdself-reference sectionPerformance Impact
None on valid backref patterns — verified by JMH
BackreferenceBenchmarkA/B on workspace-jb (Temurin 21.0.12-tem): all 17 reggie entries within ±0.6% of baseline. The end-group guard is emitted only for self-/forward-referencing backrefs and quantifier-body backrefs; plain backward references compile to byte-identical code.Additional Notes
AI-assisted change.
Scope notes:
(\1)family routed toLINEAR_BACKREFERENCE— the issue's root cause is inLinearPatternBytecodeGenerator, not theRECURSIVE_DESCENTself-ref path.RECURSIVE_DESCENT(C-01/C-03 partial-open sentinel), a required self-reference ((\1+),(a\1+)) resolves to a zero-length match where the JDK (which resolves mid-iteration backrefs against the last completed span) returns no match. Optional self-references (\1?) behave equivalently to the JDK for match/no-match. Changing C-03 unilaterally would break the canonical^(a\1?){4}$acceptance cases on inputs like"aaaaaa". Documented indoc/agents-fallback-and-limitations.md.(\1)is also correctly rejected (JAVA_FALLBACKrefusal) when it routes through nullable-group guards — unchanged behavior.