gimli-rs / gimli

A library for reading and writing the DWARF debugging format

Home Page:https://docs.rs/gimli/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Segmentation fault in `ArrayVec::clear`

Tiwalun opened this issue · comments

After updating to version 0.26.0 of gimli, I get a segmentation fault exception when trying to evaluate a gimli::Expression. The code worked fine with version 0.25.0.

This is the call stack when the segmentation fault occurs:

drop_in_place<gimli::read::op::Location<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>, usize>> (@core::ptr::drop_in_place$LT$gimli..read..op..Location$LT$gimli..read..endian_reader..EndianReader$LT$gimli..endianity..LittleEndian$C$alloc..rc..Rc$LT$$u5b$u8$u5d$$GT$$GT$$C$usize$GT$$GT$::hc69bb22e585a93be:9)
drop_in_place<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>, usize>> (@core::ptr::drop_in_place$LT$gimli..read..op..Piece$LT$gimli..read..endian_reader..EndianReader$LT$gimli..endianity..LittleEndian$C$alloc..rc..Rc$LT$$u5b$u8$u5d$$GT$$GT$$C$usize$GT$$GT$::h0101c99477a7dd32:9)
drop_in_place<[gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>, usize>]> (@core::ptr::drop_in_place$LT$$u5b$gimli..read..op..Piece$LT$gimli..read..endian_reader..EndianReader$LT$gimli..endianity..LittleEndian$C$alloc..rc..Rc$LT$$u5b$u8$u5d$$GT$$GT$$C$usize$GT$$u5d$$GT$::haa51ac4966b5e74f:31)
clear<alloc::vec::Vec<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>, usize>, alloc::alloc::Global>> (/Users/tiwalun/.cargo/registry/src/github.com-1ecc6299db9ec823/gimli-0.26.0/src/read/util.rs:124)
drop<alloc::vec::Vec<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>, usize>, alloc::alloc::Global>> (/Users/tiwalun/.cargo/registry/src/github.com-1ecc6299db9ec823/gimli-0.26.0/src/read/util.rs:187)
drop_in_place<gimli::read::util::ArrayVec<alloc::vec::Vec<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>, usize>, alloc::alloc::Global>>> (@core::ptr::drop_in_place$LT$gimli..read..util..ArrayVec$LT$alloc..vec..Vec$LT$gimli..read..op..Piece$LT$gimli..read..endian_reader..EndianReader$LT$gimli..endianity..LittleEndian$C$alloc..rc..Rc$LT$$u5b$u8$u5d$$GT$$GT$$C$usize$GT$$GT$$GT$$GT$::h6bc732b473a5f0de:10)
into_vec<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>, usize>> (/Users/tiwalun/.cargo/registry/src/github.com-1ecc6299db9ec823/gimli-0.26.0/src/read/util.rs:182)
result<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian, alloc::rc::Rc<[u8]>>> (/Users/tiwalun/.cargo/registry/src/github.com-1ecc6299db9ec823/gimli-0.26.0/src/read/op.rs:1135)
expr_to_piece (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:1166)
extract_location (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:1995)
process_tree_node_attributes (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:1180)
process_tree (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:1418)
process_tree (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:1561)
get_function_variables (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:1605)
get_stackframe_info (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:719)
next (/Users/tiwalun/code/probe-rs/probe-rs/src/debug/mod.rs:445)
{closure#9} (/Users/tiwalun/code/probe-rs/cli/src/debugger.rs:267)
call_once<probe_rs_cli::debugger::{impl#0}::new::{closure#9}, (&mut probe_rs_cli::debugger::CliData, &[&str])> (@core::ops::function::FnOnce::call_once::he117dd2f4822a97a:17)
execute_command (/Users/tiwalun/code/probe-rs/cli/src/debugger.rs:399)
handle_line (/Users/tiwalun/code/probe-rs/cli/src/debugger.rs:382)

The relevant code in our application is found here.

rustc version: 1.56.0
OS: macOS Monterey (12.0.1)

Gimli uses unsafe code in two places:

Your application uses unsafe code too. Could you run something like valgrind to see where the actual UB occurs? It may be that unsafe code in your application causes something to get corrupt, which could then show up as a crash inside unrelated code.

The unsafe code in probe-rs is only used when the --ftdi feature is activated, which was not used when the segmentation fault occured. This should not have caused the segmentation fault.

The call stack above was from LLDB, valgrind is unfortunately not available on macOS. I will try to reproduce it on Linux.

I managed to fetch a valgrind trace:

==56526== Invalid read of size 2
==56526==    at 0xBB052D: core::ptr::drop_in_place<gimli::read::op::Location<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>> (mod.rs:188)
==56526==    by 0xBB001E: core::ptr::drop_in_place<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>> (mod.rs:188)
==56526==    by 0xBB097E: core::ptr::drop_in_place<[gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>]> (mod.rs:188)
==56526==    by 0xC0D664: gimli::read::util::ArrayVec<A>::clear (util.rs:124)
==56526==    by 0xBBA9DA: <gimli::read::util::ArrayVec<A> as core::ops::drop::Drop>::drop (util.rs:187)
==56526==    by 0xBB1C56: core::ptr::drop_in_place<gimli::read::util::ArrayVec<alloc::vec::Vec<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>>>> (mod.rs:188)
==56526==    by 0xC0E54D: gimli::read::util::ArrayVec<alloc::vec::Vec<T>>::into_vec (util.rs:182)
==56526==    by 0xB3EA98: gimli::read::op::Evaluation<R>::result (op.rs:1135)
==56526==    by 0xB8B3C9: probe_rs::debug::UnitInfo::expr_to_piece (mod.rs:1166)
==56526==    by 0xB9758A: probe_rs::debug::UnitInfo::extract_location (mod.rs:1995)
==56526==    by 0xB8C5BD: probe_rs::debug::UnitInfo::process_tree_node_attributes (mod.rs:1180)
==56526==    by 0xB911BD: probe_rs::debug::UnitInfo::process_tree (mod.rs:1418)
==56526==  Address 0x28 is not stack'd, malloc'd or (recently) free'd
==56526== 
==56526== 
==56526== Process terminating with default action of signal 11 (SIGSEGV)
==56526==  Access not within mapped region at address 0x28
==56526==    at 0xBB052D: core::ptr::drop_in_place<gimli::read::op::Location<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>> (mod.rs:188)
==56526==    by 0xBB001E: core::ptr::drop_in_place<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>> (mod.rs:188)
==56526==    by 0xBB097E: core::ptr::drop_in_place<[gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>]> (mod.rs:188)
==56526==    by 0xC0D664: gimli::read::util::ArrayVec<A>::clear (util.rs:124)
==56526==    by 0xBBA9DA: <gimli::read::util::ArrayVec<A> as core::ops::drop::Drop>::drop (util.rs:187)
==56526==    by 0xBB1C56: core::ptr::drop_in_place<gimli::read::util::ArrayVec<alloc::vec::Vec<gimli::read::op::Piece<gimli::read::endian_reader::EndianReader<gimli::endianity::LittleEndian,alloc::rc::Rc<[u8]>>,usize>>>> (mod.rs:188)
==56526==    by 0xC0E54D: gimli::read::util::ArrayVec<alloc::vec::Vec<T>>::into_vec (util.rs:182)
==56526==    by 0xB3EA98: gimli::read::op::Evaluation<R>::result (op.rs:1135)
==56526==    by 0xB8B3C9: probe_rs::debug::UnitInfo::expr_to_piece (mod.rs:1166)
==56526==    by 0xB9758A: probe_rs::debug::UnitInfo::extract_location (mod.rs:1995)
==56526==    by 0xB8C5BD: probe_rs::debug::UnitInfo::process_tree_node_attributes (mod.rs:1180)
==56526==    by 0xB911BD: probe_rs::debug::UnitInfo::process_tree (mod.rs:1418)
==56526==  If you believe this happened as a result of a stack
==56526==  overflow in your program's main thread (unlikely but
==56526==  possible), you can try to increase the size of the
==56526==  main thread stack using the --main-stacksize= flag.
==56526==  The main thread stack size used in this run was 8388608.
==56526== 
==56526== HEAP SUMMARY:
==56526==     in use at exit: 1,673,523 bytes in 26,908 blocks
==56526==   total heap usage: 80,683 allocs, 53,775 frees, 20,160,543 bytes allocated
==56526== 
==56526== LEAK SUMMARY:
==56526==    definitely lost: 0 bytes in 0 blocks
==56526==    indirectly lost: 0 bytes in 0 blocks
==56526==      possibly lost: 8,311 bytes in 61 blocks
==56526==    still reachable: 1,665,212 bytes in 26,847 blocks
==56526==         suppressed: 0 bytes in 0 blocks
==56526== Rerun with --leak-check=full to see details of leaked memory
==56526== 
==56526== For lists of detected and suppressed errors, rerun with: -s
==56526== ERROR SUMMARY: 4 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)

I hope it helps a little.

I think the issue is that at

unsafe { ptr::drop_in_place(ptr) };
all elements in the storage are dropped, but the storage is never told that the elements are no longer existing, so doing .clear() twice will drop some elements twice.

The storage doesn't need to be told because it is MaybeUninit.

I think the problem is that into_vec replaces the storage without updating the length, and so when clear derefs it gets an invalid slice.

cc @nbdd0121

Ah indeed! I think I probably didn't notice the issue when testing because I am using a Reader that is Copy.

As a possible additional problem, I'm unsure if we're allowed to convert a Box to a Vec in into_vec. The documentation says it is highly unlikely to be correct if the allocation wasn't a String/Vec.

As a possible additional problem, I'm unsure if we're allowed to convert a Box to a Vec in into_vec. The documentation says it is highly unlikely to be correct if the allocation wasn't a String/Vec.

I think it should be fine because Vec has a layout guarantee.

Thanks for the report. Published a fix in 0.26.1. I'll also look into removing some of this unsafe code in future.

Thanks for fixing this so quickly! ❤️