In OCaml before 4.14.5, when the key of an ephemeron is a closure with Infix_tag (a function of a mutually recursive group that is not the first function), the mark phase does not keep the ephemeron's data alive, although the key is alive. The clean phase does treat the key as alive, so it leaves the data field filled. The sweep phase then frees the data block. After a major collection the ephemeron's data field still points to the freed block.
This looks like the part of #7810 that PR #9742 did not cover. PR #9742 fixed the clean phase and the minor collector. It did not change mark_ephe_aux.
Please note that the CVSS score high is under the conditions:
[@@@alert "-old_ephemeron_api"]
let infix n = let rec f () = n and g () = f () in g
let build key n =
let e = Ephemeron.K1.create () in
let w = Weak.create 1 in
let d = ref n in
Ephemeron.K1.set_key e key;
Ephemeron.K1.set_data e d;
Weak.set w 0 (Some d);
(e, w)
let run key =
let (e, w) = build key 1 in
Gc.full_major ();
Printf.printf "tag=%d weak=%b check_key=%b check_data=%b\n"
(Obj.tag (Obj.repr key)) (Weak.check w 0)
(Ephemeron.K1.check_key e) (Ephemeron.K1.check_data e);
ignore (Sys.opaque_identity key)
let () =
if Sys.argv.(1) = "infix" then run (infix 1) else run (ref 1)
Obj is used only to print the tag of the key. The program does not read the
data after the collection.
{
"cwe": [
"CWE-416"
],
"human_link": "https://github.com/ocaml/security-advisories/tree/main/advisories/2026/OSEC-2026-30.md",
"osv": "https://github.com/ocaml/security-advisories/tree/generated-osv/2026/OSEC-2026-30.json"
}