See how ocaml compares to other vendors in security performance
Insufficient sanitisation in the OCaml compiler versions 4.04.0 and 4.04.1 allows external code to be executed with raised privilege in binaries marked as setuid, by setting the CAMLCPLUGINS, CAMLNATIVECPLUGINS, or CAMLBYTECPLUGINS environment variable.
The camlbadeserialize function in byterun/bigarray.c in the standard library in OCaml 4.06.0 has an integer overflow which, in situations where marshalled data is accepted from an untrusted source, allows remote attackers to cause a denial of service (memory corruption) or possibly execute arbitrary code via a crafted object.
OCaml before 4.03.0 does not properly handle sign extensions, which allows remote attackers to conduct buffer overflow attacks or obtain sensitive information as demonstrated by a long string to the String.copy function.
In OCaml before 4.14.3 and 5.x before 5.4.1, a buffer over-read in Marshal deserialization (runtime/intern.c) enables remote code execution through a multi-phase attack chain. The vulnerability stems from missing bounds validation in the readblock() function, which performs unbounded memcpy() operations using attacker-controlled lengths from crafted Marshal data.
In OCaml opam before 2.5.1, a .install field containing a destination filepath can use ../ to reach a parent directory.
OCaml compiler allows attackers to have unspecified impact via unknown vectors, a similar issue to CVE-2017-9772 "but with much less impact."
The postgresql-ocaml bindings 1.5.4, 1.7.0, and 1.12.1 for PostgreSQL libpq do not properly support the PQescapeStringConn function, which might allow remote attackers to leverage escaping issues involving multibyte character encodings.
In OCaml before 4.14.3 and 5.x before 5.4.1, a buffer over-read in Marshal deserialization (runtime/intern.c) enables remote code execution through a multi-phase attack chain. The vulnerability stems from missing bounds validation in the readblock() function, which performs unbounded memcpy() operations using attacker-controlled lengths from crafted Marshal data.
In OCaml through 4.14.3, Bigarray.reshape allows an integer overflow, and resultant reading of arbitrary memory, when untrusted data is processed.
On 2/27/26 14:39, Florian Weimer wrote: Alan Coopersmith: https://sympa.inria.fr/sympa/arc/ocsf-ocaml-security-announcements/2026-02/msg00000.html announces: From: Hannes Mehnert <hannes () mehnert org> To: ocsf-ocaml-security-announcements () inria fr Subject: [ocsf-ocaml-security-announcements] OSEC-2026-01 in the OCaml runtime: Buffer Over-Read in OCaml Marshal Deserialization Date: Tue, 17 Feb 2026 15:16:54 +0100 Dear everyone, it is my pleasure to announce the first security announcement of this year, and the first on this mailing list. It should any moment now also appear at https://osv.dev/list?q=OSEC-2026-01 Human link: https://github.com/ocaml/security-advisories/tree/main/advisories/2026/OSEC-2026-01.md Surprised to read this. I think this comment from 2018 is still appropriate:
| Marshal should not used in contexts where an attacker can control the | data. I don't believe it is, at least in any project I'm aware of, and | if it were, it's unlikely that those project perform enough check on | the result of Marshal to make the use safe anyway.
<https://github.com/ocaml/ocaml/issues/7765#issuecomment-473076288>
The demarshaller does not have access to type information from the program, so it has the ability to construct an arbitrary object graph. That is indeed true. However, unlike in many other languages, this does not directly allow arbitrary code execution. The only third-party code that runs during unmarshalling is the C code responsible for unmarshalling custom blocks. One of the vulnerabilities was that this code did not have enough information to validate its inputs. The manual has a formatting glitch which hides the last sentence quoted below:
| Warning: marshaling is currently not type-safe. The type of marshaled | data is not transmitted along the value of the data, making it | impossible to check that the data read back possesses the type | expected by the context. In particular, the result type of the | Marshal.from functions is given as 'a, but this is misleading: the | returned OCaml value does not possess type 'a for all 'a; it has one, | unique type which cannot be determined at compile-time. The programmer | should explicitly give the expected type of the returned value, using | the following syntax: | | (Marshal.fromchannel chan : type) | | Anything can happen at run-time if the object in the file does not | belong to the given type.
<https://ocaml.org/manual/5.4/api/Marshal.html>
And the party supplying the serialized blob can chose whether the type matches. So the inherent insecurity of unmarshalling (“Anything can happen”) is actually documented, just not very clearly. My reading is that unmarshalling untrusted input should be safe: the runtime should either throw an exception or return a well-formed value. What is not safe is using the returned value without validating it first. One can use functions in the Obj module to do this.
I do question if there are any programs in the wild that accept untrusted marshalled data and do sufficient validation to be secure without this. Marshalled data is not guaranteed to be portable between versions of OCaml, which severely limits the usefulness of doing so. Also, the format is not documented. -- Sincerely, Demi Marie Obenour (she/her/hers)
Demi Marie Obenour: On 2/27/26 14:39, Florian Weimer wrote: Alan Coopersmith: https://sympa.inria.fr/sympa/arc/ocsf-ocaml-security-announcements/2026-02/msg00000.html announces: From: Hannes Mehnert <hannes () mehnert org> To: ocsf-ocaml-security-announcements () inria fr Subject: [ocsf-ocaml-security-announcements] OSEC-2026-01 in the OCaml runtime: Buffer Over-Read in OCaml Marshal Deserialization Date: Tue, 17 Feb 2026 15:16:54 +0100 Dear everyone, it is my pleasure to announce the first security announcement of this year, and the first on this mailing list. It should any moment now also appear at https://osv.dev/list?q=OSEC-2026-01 Human link: https://github.com/ocaml/security-advisories/tree/main/advisories/2026/OSEC-2026-01.md Surprised to read this. I think this comment from 2018 is still appropriate:
| Marshal should not used in contexts where an attacker can control the | data. I don't believe it is, at least in any project I'm aware of, and | if it were, it's unlikely that those project perform enough check on | the result of Marshal to make the use safe anyway.
<https://github.com/ocaml/ocaml/issues/7765#issuecomment-473076288>
The demarshaller does not have access to type information from the program, so it has the ability to construct an arbitrary object graph. That is indeed true. However, unlike in many other languages, this does not directly allow arbitrary code execution. Not really.
This code
type x = A of int | B of int | C of int | D of int | E of int let f x fA fB fC fD fE = match x with | A a -> fA a | B b -> fB b | C c -> fC c | D d -> fD d | E e -> fE e
gets compiled to:
0000000000000000 <camlBlah.f5>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 49 89 c0 mov %rax,%r8 7: 49 89 d1 mov %rdx,%r9 a: 4d 3b 3e cmp (%r14),%r15 d: 76 51 jbe 60 <camlBlah.f5+0x60> f: 49 0f b6 40 f8 movzbq -0x8(%r8),%rax 14: 48 8d 15 00 00 00 00 lea 0x0(%rip),%rdx # 1b <camlBlah.f5+0x1b> 17: RX8664PC32 .rodata-0x4 1b: 48 63 04 82 movslq (%rdx,%rax,4),%rax 1f: 48 01 c2 add %rax,%rdx 22: ff e2 jmp %rdx 24: 49 8b 00 mov (%r8),%rax 27: 48 8b 3b mov (%rbx),%rdi 2a: 5d pop %rbp 2b: ff e7 jmp %rdi 2d: 0f 1f 00 nopl (%rax) 30: 49 8b 00 mov (%r8),%rax 33: 48 8b 37 mov (%rdi),%rsi 36: 48 89 fb mov %rdi,%rbx 39: 5d pop %rbp 3a: ff e6 jmp %rsi 3c: 49 8b 00 mov (%r8),%rax 3f: 48 8b 3e mov (%rsi),%rdi 42: 48 89 f3 mov %rsi,%rbx 45: 5d pop %rbp 46: ff e7 jmp %rdi 48: 49 8b 00 mov (%r8),%rax 4b: 49 8b 39 mov (%r9),%rdi 4e: 4c 89 cb mov %r9,%rbx 51: 5d pop %rbp 52: ff e7 jmp %rdi 54: 49 8b 00 mov (%r8),%rax 57: 48 8b 39 mov (%rcx),%rdi 5a: 48 89 cb mov %rcx,%rbx 5d: 5d pop %rbp 5e: ff e7 jmp %rdi 60: e8 00 00 00 00 call 65 <camlBlah.f5+0x65> 61: RX8664PLT32 camlcallgc-0x4 65: eb a8 jmp f <camlBlah.f5+0xf> 67: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 6e: 00 00
Add offset 0x1b, there's the tag load, and this tag is used to index a jump table without a bounds check.
Admittedly, This does not give full control over program execution directly. One would have to search for a suitable gadget. There are likely better ways to exploit unsafe demarshalling, this is just the first approach I could think of.
Thanks, Florian
On 3/2/26 04:53, Florian Weimer wrote: Demi Marie Obenour: On 2/27/26 14:39, Florian Weimer wrote: Alan Coopersmith: https://sympa.inria.fr/sympa/arc/ocsf-ocaml-security-announcements/2026-02/msg00000.html announces: From: Hannes Mehnert <hannes () mehnert org> To: ocsf-ocaml-security-announcements () inria fr Subject: [ocsf-ocaml-security-announcements] OSEC-2026-01 in the OCaml runtime: Buffer Over-Read in OCaml Marshal Deserialization Date: Tue, 17 Feb 2026 15:16:54 +0100 Dear everyone, it is my pleasure to announce the first security announcement of this year, and the first on this mailing list. It should any moment now also appear at https://osv.dev/list?q=OSEC-2026-01 Human link: https://github.com/ocaml/security-advisories/tree/main/advisories/2026/OSEC-2026-01.md Surprised to read this. I think this comment from 2018 is still appropriate:
| Marshal should not used in contexts where an attacker can control the | data. I don't believe it is, at least in any project I'm aware of, and | if it were, it's unlikely that those project perform enough check on | the result of Marshal to make the use safe anyway.
<https://github.com/ocaml/ocaml/issues/7765#issuecomment-473076288>
The demarshaller does not have access to type information from the program, so it has the ability to construct an arbitrary object graph. That is indeed true. However, unlike in many other languages, this does not directly allow arbitrary code execution. Not really.
This code
type x = A of int | B of int | C of int | D of int | E of int let f x fA fB fC fD fE = match x with | A a -> fA a | B b -> fB b | C c -> fC c | D d -> fD d | E e -> fE e
gets compiled to:
0000000000000000 <camlBlah.f5>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 49 89 c0 mov %rax,%r8 7: 49 89 d1 mov %rdx,%r9 a: 4d 3b 3e cmp (%r14),%r15 d: 76 51 jbe 60 <camlBlah.f5+0x60> f: 49 0f b6 40 f8 movzbq -0x8(%r8),%rax 14: 48 8d 15 00 00 00 00 lea 0x0(%rip),%rdx # 1b <camlBlah.f5+0x1b> 17: RX8664PC32 .rodata-0x4 1b: 48 63 04 82 movslq (%rdx,%rax,4),%rax 1f: 48 01 c2 add %rax,%rdx 22: ff e2 jmp %rdx 24: 49 8b 00 mov (%r8),%rax 27: 48 8b 3b mov (%rbx),%rdi 2a: 5d pop %rbp 2b: ff e7 jmp %rdi 2d: 0f 1f 00 nopl (%rax) 30: 49 8b 00 mov (%r8),%rax 33: 48 8b 37 mov (%rdi),%rsi 36: 48 89 fb mov %rdi,%rbx 39: 5d pop %rbp 3a: ff e6 jmp %rsi 3c: 49 8b 00 mov (%r8),%rax 3f: 48 8b 3e mov (%rsi),%rdi 42: 48 89 f3 mov %rsi,%rbx 45: 5d pop %rbp 46: ff e7 jmp %rdi 48: 49 8b 00 mov (%r8),%rax 4b: 49 8b 39 mov (%r9),%rdi 4e: 4c 89 cb mov %r9,%rbx 51: 5d pop %rbp 52: ff e7 jmp %rdi 54: 49 8b 00 mov (%r8),%rax 57: 48 8b 39 mov (%rcx),%rdi 5a: 48 89 cb mov %rcx,%rbx 5d: 5d pop %rbp 5e: ff e7 jmp %rdi 60: e8 00 00 00 00 call 65 <camlBlah.f5+0x65> 61: RX8664PLT32 camlcallgc-0x4 65: eb a8 jmp f <camlBlah.f5+0xf> 67: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 6e: 00 00
Add offset 0x1b, there's the tag load, and this tag is used to index a jump table without a bounds check.
Admittedly, This does not give full control over program execution directly. One would have to search for a suitable gadget. There are likely better ways to exploit unsafe demarshalling, this is just the first approach I could think of.
Thanks, Florian One can use C code or the Obj module to validate the value before using it. In this case, one could use Obj.isint to check that this is a boxed value, Obj.tag to check that its tag is in the correct range, Obj.len to check that the length is correct, and Obj.field and Obj.isint to validate the constructor payloads. Only after validation is complete would one call Obj.obj to cast the validated value to the intended type.
It's also possible to unmarshal to an opaque type and provide safe functions for traversing the object graph. This is unlike Java, Ruby, or Python, where arbitrary code can be executed during the unmarshalling process itself.
I do agree that whether existing code does this is questionable, but it is definitely possible to do correctly. -- Sincerely, Demi Marie Obenour (she/her/hers)