Skip to content

Removing a value from HashMap while read is ongoing results in use after free #176

@melvinzhang

Description

@melvinzhang

While using HashMap with multiple threads, we found that the value within read is inconsistent if the value is removed from another thread.

Below is a concrete example:

use std::time::Duration;
use std::thread;
use std::sync::Arc;
use scc::HashMap;

fn main() {
    println!("vec use after free");

    let map = HashMap::<String, Vec<u8>>::new();

    map.insert("first".into(), vec![123]).unwrap();

    let map = Arc::new(map);
    let map_clone = map.clone();
    std::thread::spawn(move || {
        println!("enter read");
        map_clone.read("first".into(), |_key, value| {
            let first_item = value.get(0);
            for _ in 0..3 {
                println!("value: {first_item:?} {}", value.len());
                std::thread::sleep(Duration::from_secs(1));
            }
        });
        println!("exit read");
    });

    thread::sleep(Duration::from_millis(100));

    loop {
        println!("will update");
        map.remove("first".into());
        println!("updated");
        std::thread::sleep(Duration::from_millis(500));
    }
}

The output when built and run on Linux is

vec use after free
enter read
value: Some(123) 1
will update
updated
will update
updated
value: Some(162) 1
will update
updated
will update
updated
value: Some(162) 1
will update
updated
will update
updated

We expected the ongoing read to block the call to remove.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions