{"id":"RUSTSEC-2026-0411","aliases":[],"status":"published","title":"Out-of-bounds read in `Log2Cache::nlog2n()`, `nlog2n_difference()` and `plog2p()`","description":"`Log2Cache::new(n)` fills its three tables with entries for `0..=n`, but the\nthree accessor methods index those tables with `get_unchecked` on a value the\ncaller supplies. Nothing bounds the argument and nothing documents a bound, so\nany value above the `n` the cache was built with reads past the end of the\nvector. `CountType` is `u32`, so the read can land up to `u32::MAX` elements\naway, and `plog2p()` returns the value it read, disclosing whatever lies there.\n\nFor example, `Log2Cache::new(4)` builds tables holding 5 entries (indices\n0,1,2,3,4). Then `cache.nlog2n(60000)` reads index 60000, far outside the\nallocation. Under AddressSanitizer this faults with `SEGV`; Miri reports it as\nundefined behavior at `src/log2cache.rs:42`.\n\n`nlog2n_difference()` is off by one further: in the affected versions `new(n)`\npushes `n` entries into that table rather than `n + 1`, so even\n`nlog2n_difference(n)` is out of bounds.\n\nThis can be triggered through safe public APIs — `Log2Cache::new()` and any of\n`nlog2n()`, `nlog2n_difference()` and `plog2p()` — with no `unsafe` required\nfrom the caller.","facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"published":"2026-10-22T12:00:00Z","modified":"2026-10-26T14:00:05Z","fixes":[{"ecosystem":"crates.io","package":"dahl-salso","fixed_in":["0.7.0"],"sources":["osv"]}],"affected":[{"source":"osv","ecosystem":"crates.io","package":"dahl-salso","ranges":[{"type":"SEMVER","events":[{"introduced":"0.0.0-0"},{"fixed":"0.7.0"}]}]}],"references":[{"url":"https://crates.io/crates/dahl-salso","tags":["package"]},{"url":"https://rustsec.org/advisories/RUSTSEC-2026-0411.html","tags":["advisory"]},{"url":"https://github.com/dbdahl/rust-dahl-salso/issues/2","tags":["report"]}],"sources":{"osv":[{"id":"RUSTSEC-2026-0411","title":"Out-of-bounds read in `Log2Cache::nlog2n()`, `nlog2n_difference()` and `plog2p()`","description":"`Log2Cache::new(n)` fills its three tables with entries for `0..=n`, but the\nthree accessor methods index those tables with `get_unchecked` on a value the\ncaller supplies. Nothing bounds the argument and nothing documents a bound, so\nany value above the `n` the cache was built with reads past the end of the\nvector. `CountType` is `u32`, so the read can land up to `u32::MAX` elements\naway, and `plog2p()` returns the value it read, disclosing whatever lies there.\n\nFor example, `Log2Cache::new(4)` builds tables holding 5 entries (indices\n0,1,2,3,4). Then `cache.nlog2n(60000)` reads index 60000, far outside the\nallocation. Under AddressSanitizer this faults with `SEGV`; Miri reports it as\nundefined behavior at `src/log2cache.rs:42`.\n\n`nlog2n_difference()` is off by one further: in the affected versions `new(n)`\npushes `n` entries into that table rather than `n + 1`, so even\n`nlog2n_difference(n)` is out of bounds.\n\nThis can be triggered through safe public APIs — `Log2Cache::new()` and any of\n`nlog2n()`, `nlog2n_difference()` and `plog2p()` — with no `unsafe` required\nfrom the caller.","status":"published","published":"2026-10-22T12:00:00Z","modified":"2026-10-26T14:00:05.091904359Z","updated_at":"2026-11-02T15:53:40.430679Z"}]}}
