OSEC-2026-21 json
Str partial-match stale capture registers underflow
published 2026-10-23 16 d ago modified 2026-11-06 2 d ago
- cvss
- —
- not scored
- cisa kev
- Not listed
- not in CISA's catalog
- epss
- —
- no score, EPSS covers CVEs only
- public exploits
- 0
- none known
Summary
ANT-2026-KKC4QS4S Str partial-match stale capture registers underflow
`Str.string_partial_match` records the beginning and end of a captured group. There was an implicit assumption that `beginning <= end`. However if it reached the end of the matched string but not the end of the regexp, it was possible for the beginning of the group to be matched at iteration N of the star operator, and the end of the group to be matched at iteration N-1, resulting in `beginning > end`. `Str.matched_group` is safe, it fails with an `Invalid_argument` on groups with `beginning > end`, but `Str.replace_matched` can crash, trying to memcpy `end - beginning` bytes.
The example in the ANT is `ab` partially matched against `\(\(a\)b\)+`. Group 2 has `beginning = 2` and `end = 1`.
## Fix
The fix is to record groups with `beginning > end` as not matched.
## Am I affected?
All of the following conditions are necessary: * you need to use `Str.string_partial_match` * if you don't use this function you are not affected * you need to use one of `Str.{replace_matched,global_replace,replace_first}` on the same regexp after a call to `Str.string_partial_match`. * If you use `Str.group_beginning` and `Str.group_end` and do your own string manipulation that is safe, because the String module would raise on out of bounds accesses. Similarly `Str.matched_group` is safe (uses `String` functions in its OCaml implementation) * the match replacement must reference a group > 0 * if the replacement string argument is fixed, and doesn't reference any groups, or references only group 0, then you are not affected * the referenced replacement group must be under a repetition operator in the original regexp * if there is no repetition operator in the regexp, or there are no groups under the repetition operator then you are not affected
For example `{|\(\(a\)b\)+|}` as the regular expression in a call to `Str.string_partial_match`, followed by a reference to `\2` in the argument of `Str.replace_matched` could be affected.
## Timeline
- 2026-08-24: vulnerability was found - 2026-08-27: forwarded report to security@ocaml.org - 2026-09-29: fix merged to trunk
Patch / fix
- opam ocaml 4.14.5, 5.5.2
Affected packages
| ecosystem | package | affected | fixed in |
|---|---|---|---|
| opam | ocaml | <4.14.5 >=5, <5.5.2 |
4.14.5 5.5.2 |
Sources
- OSV OSEC-2026-21
Fencer Vulns keeps one entry per vulnerability: the CVE id wins, then GHSA, and records that alias each other are merged, except that two CVEs always keep separate entries. The same data is available as JSON.