Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions crates/caja/RUSTSEC-0000-0000.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
```toml
[advisory]
id = "RUSTSEC-0000-0000"
package = "caja"
date = "2026-10-21"
url = "https://github.com/EmanuelGCC/Caja/issues/2"
informational = "unsound"
categories = ["memory-corruption"]
keywords = ["null-pointer", "unsound"]

[versions]
patched = [">= 0.3.2"]
```

# Null pointer passed to `dealloc` and `slice::from_raw_parts` for an empty `Caja`

`Caja::new_uninitialized(0)` returns a `Caja` whose data pointer is null, and
every safe constructor reaches it for size 0: `Caja::new(0, x)`,
`Caja::from(&[])` and `clone()` of an empty `Caja`. The rest of the type then
uses that null pointer as if it were an allocation. `Drop` passes it to
`dealloc` with `Layout::array::<T>(0)`, and `as_slice()`, `as_mut_slice()` and
the `Debug` implementation pass it to `slice::from_raw_parts`. Both are
undefined behavior.

For example, `Caja::<u8>::new(0, 0)` followed by a drop aborts in a debug build
with `NonNull::new_unchecked requires that the pointer is non-null`; Miri
reports it as undefined behavior at `src/lib.rs:130`. `Caja::<u8>::from(&[][..])`
followed by `as_slice()` aborts with the `slice::from_raw_parts` precondition
check at `src/lib.rs:107`.

This can be triggered through safe public APIs — `Caja::new()`, `Caja::from()`
or `clone()` with an empty input, followed by a drop or `as_slice()` — with no
`unsafe` required from the caller.
Loading