Matrix::new() drops uninitialized memory
Description
Matrix::new()
internally calls Matrix::fill_with()
which uses *ptr = value
pattern to initialize the buffer.
This pattern assumes that there is an initialized struct at the address and drops it, which results in dropping of uninitialized struct.
Demonstration
- Crate: alg_ds
- Version: 0.3.1
- OS: Ubuntu 18.04.5 LTS
- Rust: rustc 1.45.2 (d3fb005a3 2020-07-31)
- Cargo flags: --release
#![forbid(unsafe_code)]
use alg_ds::ds::matrix::Matrix;
use std::sync::atomic::{AtomicUsize, Ordering};
static creation_cnt: AtomicUsize = AtomicUsize::new(0);
static drop_cnt: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone)]
struct DropDetector(u32);
impl Default for DropDetector {
fn default() -> Self {
creation_cnt.fetch_add(1, Ordering::Relaxed);
DropDetector(12345)
}
}
impl Drop for DropDetector {
fn drop(&mut self) {
drop_cnt.fetch_add(1, Ordering::Relaxed);
println!("Dropping {}", self.0);
}
}
fn main() {
// Please check along with the code snippets above.
{
// `*ptr = value` acts by dropping existing contents at `ptr`.
// `Matrix::fill_with()` uses this pattern which result in dropping
// uninitialized, unallocated struct.
//
// Note that the creation of a mutable reference to uninitialized memory
// region is already UB by itself.
// `ptr::write` and `MaybeUninit` should be used for the initialization.
let _ = Matrix::<DropDetector>::new(1, 1);
}
{
// (Bonus) Integer overflow in `layout()` allows to create a huge matrix.
// Fortunately, every access to the internal buffer are bound-checked,
// so this doesn't lead to obvious UB by itself.
let mat = Matrix::<usize>::new(15326306685794188004, 0x123456789);
println!(
"rows: {}, cols: {}, number of elements: {}",
mat.rows(),
mat.cols(),
mat.elements_number()
);
}
assert_eq!(
creation_cnt.load(Ordering::Relaxed),
drop_cnt.load(Ordering::Relaxed)
);
}
Output:
Dropping 0
Dropping 12345
rows: 0, cols: 4886718345, number of elements: 4
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `2`', src/main.rs:72:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Return Code: 101