Segfault inside Vec::index_mut call
I came across this while trying to implement input/output for brainfuck.
I looked at the crash for a bit in rr, it seems that something is calling index_mut without setting registers properly.
rsi = 0x0, which causes a null deref
In short:
Program received signal SIGSEGV, Segmentation fault.
0x00005586bde7eacc in alloc::vec::{{impl}}::index_mut<u8> (self=0x7fff4ff77878, index=...) at /shared/dev/rust/rust-patch/src/liballoc/vec.rs:1627
1627 fn index_mut(&mut self, index: ops::Range<usize>) -> &mut [T] {
(rr) print *self
$1 = alloc::vec::Vec<u8> {buf: alloc::raw_vec::RawVec<u8, alloc::heap::Heap> {ptr: core::ptr::Unique<u8> {pointer: core::nonzero::NonZero<*const u8> (0x7f0f8621e000 "\000"), _marker: core::marker::PhantomData<u8>}, cap: 256, a: alloc::heap::Heap}, len: 256}
(rr) bt
#0 0x00005586bde7eacc in alloc::vec::{{impl}}::index_mut<u8> (self=0x7fff4ff77878, index=...) at /shared/dev/rust/rust-patch/src/liballoc/vec.rs:1627
#1 0x00007f0f87570de8 in ?? ()
#2 0x00005586be19f250 in panic_loc.C ()
#3 0x00005586bdf4bcc1 in str.j ()
#4 0x000000000001459c in ?? ()
#5 0x00005586be19f250 in panic_loc.C ()
#6 0x00000000000005b0 in ?? ()
#7 0x00007f0f86229040 in ?? ()
#8 0x0000000000000004 in ?? ()
#9 0x0000000000000004 in ?? ()
#10 0x00007f0f8622a200 in ?? ()
#11 0x0000000000000096 in ?? ()
#12 0x0000000000000096 in ?? ()
#13 0x0000000000000000 in ?? ()
So this is definitely crashing on indexing the mem variable.
Here's code that triggers this crash: [Note: I use boxed traits, because the jit!() macro doesn't accept generic functions]
#![feature(plugin, custom_attribute)]
#![plugin(holyjit_plugin)]
#![feature(unboxed_closures)]
#[macro_use] extern crate holyjit_lib as hj;
use std::io;
use std::io::{Read, Write, Cursor};
jit!{ fn eval(jc: hj::JitContext, program: String, input: Box<Read>, output: Box<Write>) -> Result<(), ()> = eval_impl in jc; }
fn eval_impl(_jc: hj::JitContext, program: String, mut input: Box<Read>, mut output: Box<Write>) -> Result<(), ()> {
let prog = program.as_bytes();
let mut pc : usize = 0;
let mut ptr : usize = 0;
let mut mem : Vec<u8> = Vec::with_capacity(256);
mem.resize(256, 0);
loop {
if pc >= prog.len() {
return Ok(());
}
match *prog.get(pc).unwrap() {
b'>' => {
ptr += 1;
if ptr >= mem.len() {
mem.push(0);
}
}
b'<' => { ptr = ptr.saturating_sub(1); }
b'-' => { mem[ptr] = mem[ptr].wrapping_sub(1); }
b'+' => { mem[ptr] = mem[ptr].wrapping_add(1); }
b'.' => { output.write(&mem[ptr .. ptr + 1]).unwrap(); }
b',' => { input.read_exact(&mut mem[ptr .. ptr + 1]).unwrap(); }
b'[' => {
if mem[ptr] == 0 {
let mut iter = (pc + 1, 0);
loop {
iter = match (iter, prog[iter.0]) {
((p, 0), b']') => {
pc = p + 1;
break;
},
((p, d), b'[') => (p + 1, d + 1),
((p, d), b']') => (p + 1, d - 1),
((p, d), _) => (p + 1, d)
}
}
continue; // skip pc increment
}
}
b']' => {
let mut iter = (pc - 1, 0);
loop {
iter = match (iter, prog[iter.0]) {
((p, 0), b'[') => {
pc = p;
break;
},
((p, d), b'[') => (p - 1, d + 1),
((p, d), b']') => (p - 1, d - 1),
((p, d), _) => (p - 1, d)
}
}
continue; // skip pc increment
}
_ => { panic!("Unknown Symbol"); }
}
pc += 1;
}
}
fn main() {
let jc : hj::JitContext = Default::default();
let res = eval(jc, ",[.,]".into(), Box::new(Cursor::new(b"Hello, world!")), Box::new(io::stderr()));
// let res = eval(jc, "-[>-[>-[>-<-]<-]<-]<-]".into());
res.unwrap();
}
Thanks a lot for this great report :)
Here's code that triggers this crash: [Note: I use boxed traits, because the jit!() macro doesn't accept generic functions]
Yes, this is a problem which is due to the fact that we would have to re-implement the elision for the jit! macro, as it stores the signature of the function in a structure.
I looked at the crash for a bit in rr, it seems that something is calling index_mut without setting registers properly.
At the moment the assembly output is quite similar to the -O0 of LLVM, except for the fact that HolyJit does not yet use the ModRm addressing modes. You should be (almost) able to compare the assembly produced by HolyJit with the result of:
(rr) disas brainfuck::eval_impl
Looking at the generated code, it seems that the problem is that instead of giving the Range argument by reference, we give it by value to the index_mut function:
0x7f2fef121dc6 mov (%rsi),%rdi
0x7f2fef121dc9 movabs $0xfffffffffffffd68,%rsi
0x7f2fef121dd3 add %rbp,%rsi
0x7f2fef121dd6 mov (%rsi),%rax
0x7f2fef121dd9 mov 0x8(%rsi),%rcx
0x7f2fef121ddd mov %rax,%rsi
0x7f2fef121de0 mov %rdx,%rax
0x7f2fef121de3 mov %rcx,%rdx
0x7f2fef121de6 callq *%rax
The problem is likely located here: https://github.com/nbp/holyjit/blob/e4ed3be729ae91c5aa8ce93ca0c648afb642feb9/plugin/src/trans.rs#L1083-L1093