{"id":"OSEC-2026-21","summary":"Str partial-match stale capture registers underflow","details":"ANT-2026-KKC4QS4S Str partial-match stale capture registers underflow \n\n`Str.string_partial_match` records the beginning and end of a captured group.\nThere was an implicit assumption that `beginning \u003c= end`.\nHowever 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 \u003e end`.\n`Str.matched_group` is safe, it fails with an `Invalid_argument` on groups with `beginning \u003e end`, but `Str.replace_matched` can crash, trying to memcpy `end - beginning` bytes.\n\nThe example in the ANT is `ab` partially matched against `\\(\\(a\\)b\\)+`. Group 2 has `beginning = 2` and `end = 1`.\n\n## Fix\n\nThe fix is to record groups with `beginning \u003e end` as not matched.\n\n## Am I affected?\n\nAll of the following conditions are necessary:\n  * you need to use `Str.string_partial_match`\n    * if you don't use this function you are not affected\n  * 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`.\n    * 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)\n  * the match replacement must reference a group \u003e 0\n    * if the replacement string argument is fixed, and doesn't reference any groups, or references only group 0, then you are not affected\n  * the referenced replacement group must be under a repetition operator in the original regexp\n    * if there is no repetition operator in the regexp, or there are no groups under the repetition operator then you are not affected\n\nFor 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.\n\n## Timeline\n\n- 2026-08-24: vulnerability was found\n- 2026-08-27: forwarded report to security@ocaml.org\n- 2026-09-29: fix merged to trunk","modified":"2026-10-23T22:15:03.171385049Z","published":"2026-10-23T21:30:00Z","database_specific":{"osv":"https://github.com/ocaml/security-advisories/tree/generated-osv/2026/OSEC-2026-21.json","human_link":"https://github.com/ocaml/security-advisories/tree/main/advisories/2026/OSEC-2026-21.md","cwe":["CWE-122"]},"affected":[{"package":{"name":"ocaml","ecosystem":"opam","purl":"pkg:opam/ocaml"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.5.2"}]},{"type":"GIT","repo":"https://github.com/ocaml/ocaml","events":[{"introduced":"0"},{"fixed":"090e61ca5ab5700b0fe3153534365191753aa5b6"}]},{"type":"GIT","repo":"https://github.com/ocaml/ocaml","events":[{"introduced":"0"},{"fixed":"feccd4744ebe25b90208e569d03efd514b964dce"}]}],"versions":["3.07","3.07+1","3.07+2","3.08.0","3.08.1","3.08.2","3.08.3","3.08.4","3.09.0","3.09.1","3.09.2","3.09.3","3.10.0","3.10.1","3.10.2","3.11.0","3.11.1","3.11.2","3.12.0","3.12.1","4.00.0","4.00.1","4.01.0","4.02.0","4.02.1","4.02.2","4.02.3","4.02.4","4.03.0","4.03.1","4.04.0","4.04.1","4.04.2","4.04.3","4.05.0","4.05.1","4.06.0","4.06.1","4.06.2","4.07.0","4.07.1","4.07.2","4.08.0","4.08.1","4.08.2","4.09.0","4.09.1","4.09.2","4.10.0","4.10.1","4.10.2","4.10.3","4.11.0","4.11.1","4.11.2","4.11.3","4.12.0","4.12.1","4.12.2","4.13.0","4.13.1","4.13.2","4.14.0","4.14.1","4.14.2","4.14.3","4.14.4","4.14.5","5.0.0","5.0.1","5.1.0","5.1.1","5.1.2","5.2.0","5.2.1","5.2.2","5.3.0","5.3.1","5.4.0","5.4.1","5.4.2","5.5.0","5.5.1","5.5.0-rc1","5.5.0-beta1","5.5.0-alpha3","5.5.0-alpha2","5.5.0-alpha1","flambda_fork_point"],"ecosystem_specific":{"opam_constraint":"ocaml {\u003c \"5.5.2\"}","affected_bindings":["Str.string_partial_match","Str.replace_matched","Str.global_replace","Str.replace_first"]},"database_specific":{"source":"https://github.com/ocaml/security-advisories/blob/generated-osv/2026/OSEC-2026-21.json"}}],"schema_version":"1.9.2","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N/E:P/AU:Y/R:U"}],"credits":[{"name":"Anthropic","type":"REPORTER"},{"name":"Hannes Mehnert","type":"COORDINATOR"},{"name":"Edwin Török","type":"COORDINATOR"},{"name":"Nicolás Ojeda Bär","type":"REMEDIATION_REVIEWER"},{"name":"Edwin Török","type":"REMEDIATION_REVIEWER"},{"name":"Xavier Leroy","type":"REMEDIATION_DEVELOPER"}]}