seanmonstar/spmc

Segmentation fault when using spmc:::channel

ZiCog opened this issue · 10 comments

ZiCog commented

After a few days studying Rust I have come to a road block with segmentation faults and other errors when running a simple program using an spmc channel.

use std::thread;
use std::fs::File;
use std::io::{BufReader,BufRead};

pub fn main() {
    let (tx, rx) = spmc::channel();

    let mut handles = Vec::new();
    for n in 0..3 {
        let rx = rx.clone();
        handles.push(thread::spawn(move || {
            let mut word_count = 0;
            loop {
                let received = rx.recv();
                match received {
                    Ok(_word) => {
                        word_count = word_count + 1;
                    },
                    Err(_e) => {
                        println!("Reader {}: {}, ", n, word_count);
                        break;
                    }
                }
            }
        }));
    }

    handles.push(thread::spawn(move || {
        let file = File::open("/usr/share/dict/british-english-insane").unwrap();
        for line in BufReader::new(file).lines() {
            let word = line.unwrap();
            tx.send(word).unwrap();
        }
    }));

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Done.");
}

This code should simply read all the lines of a Debian dictionary file and forward them to four threads. The expected result is like:

$ ./target/debug/threads-rust
Reader 2: 221485,
Reader 1: 217272,
Reader 0: 215518,
Done.

But mostly I get seg faults and other errors like so:

$ RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
    Running `target/debug/threads-rust`
munmap_chunk(): invalid pointer
Aborted (core dumped)
$ RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
    Running `target/debug/threads-rust`
Segmentation fault (core dumped)
$ RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
    Running `target/debug/threads-rust`
Reader 0: 654276,
Done.
$ RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
    Running `target/debug/threads-rust`
munmap_chunk(): invalid pointer
Aborted (core dumped)
$ RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
    Running `target/debug/threads-rust`
free(): invalid size
Aborted (core dumped)
$ RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
    Running `target/debug/threads-rust`
double free or corruption (out)
Aborted (core dumped)

I get similar errors on a Raspberry Pi 3.

$ uname -a
Linux monster 4.4.0-17134-Microsoft #706-Microsoft Mon Apr 01 18:13:00 PST 2019 x86_64 GNU/Linux
$ cargo --version
cargo 1.36.0 (c4fcfb725 2019-05-15)
$ rustc --version --verbose
rustc 1.36.0 (a53f9df32 2019-07-03)
binary: rustc
commit-hash: a53f9df32fbb0b5f4382caaad8f1a46f36ea887c
commit-date: 2019-07-03
host: x86_64-unknown-linux-gnu
release: 1.36.0
LLVM version: 8.0    $ cargo  build
Compiling threads-rust v0.1.0 (/mnt/c/Users/zicog/conveqs/threads-rust)
    Finished dev [unoptimized + debuginfo] target(s) in 1.72s

Thanks for the report, I've managed to find it using loom, and will publish the fix ASAP.

Published 0.2.3 with the fix!

ZiCog commented

Unfortunately the code included in my report above still does not work reliably:

$ cargo clean
$ cargo build
    Updating crates.io index
Downloaded spmc v0.2.3
Compiling spmc v0.2.3
Compiling threads-rust v0.1.0 (/home/zicog/threads-rust-bug)
    Finished dev [unoptimized + debuginfo] target(s) in 23.17s
zicog@jhctech1:~/threads-rust-bug$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
    Running `target/debug/threads-rust`
munmap_chunk(): invalid pointer
Aborted
zicog@jhctech1:~/threads-rust-bug$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
    Running `target/debug/threads-rust`
Reader 0: 210836,
Reader 2: 218020,
Reader 1: 225417,
Done.
zicog@jhctech1:~/threads-rust-bug$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
    Running `target/debug/threads-rust`
Reader 1: 226301,
Reader 0: 208803,
Reader 2: 219172,
Done.
zicog@jhctech1:~/threads-rust-bug$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
    Running `target/debug/threads-rust`
Segmentation fault
zicog@jhctech1:~/threads-rust-bug$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
    Running `target/debug/threads-rust`
free(): invalid pointer
Aborted
zicog@jhctech1:~/threads-rust-bug$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
    Running `target/debug/threads-rust`
Reader 2: 217901,
Reader 0: 220238,
Reader 1: 216135,
Done.
zicog@jhctech1:~/threads-rust-bug$ uname -a
Linux jhctech1 4.19.0-5-amd64 #1 SMP Debian 4.19.37-5 (2019-06-19) x86_64 GNU/Linux
zicog@jhctech1:~/threads-rust-bug$ rustc --version
rustc 1.38.0-nightly (00ee1b47f 2019-08-11)

Oh boy!

ZiCog commented

Oddly enough when I run it using the jemallocator I cannot get it to fail. I just put this at the top of the file:

#[cfg(unix)]
extern crate jemallocator;
#[cfg(unix)] 
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

So it turns out this library is totally broken, I've found several bugs more looking into this... It's less surprising when I check the history and see this is one of the first libraries I wrote in Rust 😭

ZiCog commented

That's a shame, it's such a nice idea.

I have no idea if it's salvageable, being a rank beginner at Rust myself.

I've fixed a few more bugs, but in the process had the break the API, since fn send(&self) was unsound, and is now fn send(&mut self). So, I've published 0.3.0.

ZiCog commented

OK!

Changing to use "let (mut tx, rx) = spmc::channel();" gets v0.3.0 running on Debian x86 and Pi, and the Linux Subsystem for Windows.

Great stuff.

This triggers memory corruption in safe code, which could be exploitable. Please file a security advisory at https://github.com/RustSec/advisory-db. This will let users of this crate check if they're using a vulnerable version and upgrade.