rust-lang / lldb

No longer used, use https://github.com/rust-lang/llvm-project instead
Other
14 stars 6 forks source link

Segfault @ RustASTContext::FindEnumVariant #16

Closed cynecx closed 6 years ago

cynecx commented 6 years ago

I am currently on:

rustc 1.30.0-nightly (3edb355b7 2018-08-03)
llvm: 40868d437198d20a353ce4f4fb114b4d33efe5aa (upstream master)
clang: 074cccc152df061007c8500515698b57a5af7103 (upstream master)
lldb: 3dbe998969d457c5cef245f61b48bdaed0f5c059 (rust-release-70)

Testcase (taken from https://github.com/rust-lang-nursery/lldb/issues/14):

// a.rs
use std::option::Option;

enum Enum {A, B}
enum Union { A(usize), B(usize) }
enum Mixed { A(usize), B }
enum Field { A { field: usize }, B { field: usize } }

fn main() {
    let enum_a = Enum::A;
    let enum_b = Enum::B;
    let enum_some_a = Option::Some(Enum::A);
    let enum_some_b = Option::Some(Enum::B);
    let enum_none: Option<Enum> = Option::None;

    let union_a = Union::A(42);
    let union_b = Union::B(84);
    let union_some_a = Option::Some(Union::A(42));
    let union_some_b = Option::Some(Union::B(84));
    let union_none: Option<Union> = Option::None;

    let mixed_a = Mixed::A(42);
    let mixed_b = Mixed::B;
    let mixed_some_a = Option::Some(Mixed::A(42));
    let mixed_some_b = Option::Some(Mixed::B);
    let mixed_none: Option<Mixed> = Option::None;

    let field_a = Field::A { field: 42 };
    let field_b = Field::B { field: 84 };
    let field_some_a = Option::Some(Field::A { field: 42 });
    let field_some_b = Option::Some(Field::B { field: 84 });
    let field_none: Option<Field> = Option::None;

    let number = 42;
    let number_some = Option::Some(42);
    let number_none: Option<i32> = Option::None;

    println!("yay");
}

Calling fr v at the println! triggers the segfault.

Backtrace:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fe0b6e53314 in lldb_private::CompilerType::CompilerType (
    this=0x7ffc86409940, rhs=...)
    at /home/cynecx/dev/llvm/tools/lldb/include/lldb/Symbol/CompilerType.h:47
47        : m_type(rhs.m_type), m_type_system(rhs.m_type_system) {}
[Current thread is 1 (Thread 0x7fe0a6c315c0 (LWP 19653))]
(gdb) bt
#0  0x00007fe0b6e53314 in lldb_private::CompilerType::CompilerType (
    this=0x7ffc86409940, rhs=...)
    at /home/cynecx/dev/llvm/tools/lldb/include/lldb/Symbol/CompilerType.h:47
#1  0x00007fe0b72988cf in lldb_private::RustEnum::FindEnumVariant (
    this=0x55abb3d49680, discriminant=2)
    at /home/cynecx/dev/llvm/tools/lldb/source/Symbol/RustASTContext.cpp:629
#2  0x00007fe0b7295469 in lldb_private::RustASTContext::FindEnumVariant (
    this=0x55abb3f8a190, type=..., discriminant=2)
    at /home/cynecx/dev/llvm/tools/lldb/source/Symbol/RustASTContext.cpp:2007
#3  0x00007fe0b773b26f in lldb_private::RustLanguageRuntime::GetDynamicTypeAndAddress (this=0x55abb412ac30, in_value=..., 
    use_dynamic=lldb::eDynamicDontRunTarget, class_type_or_name=..., 
    dynamic_address=..., 
    value_type=@0x7ffc86409a30: lldb_private::Value::eValueTypeScalar)
    at /home/cynecx/dev/llvm/tools/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp:100
#4  0x00007fe0b70e73cb in lldb_private::ValueObjectDynamicValue::UpdateValue (
    this=0x55abb41766b0)
    at /home/cynecx/dev/llvm/tools/lldb/source/Core/ValueObjectDynamicValue.cpp:156
#5  0x00007fe0b70ccc9e in lldb_private::ValueObject::UpdateValueIfNeeded (
    this=0x55abb41766b0, update_format=false)
    at /home/cynecx/dev/llvm/tools/lldb/source/Core/ValueObject.cpp:200

It seems that idx = -1 (m_default) in RustEnum::FindEnumVariant triggers the segfault because this is basically an out-of-bounds access through FieldAt.

(gdb) p discriminant
$15 = 2
(gdb) p m_discriminants
$16 = std::unordered_map with 2 elements = {[1] = 1, [0] = 0}

Not sure what's the cause here as I am also not familiar with the code (seems that a discriminant record is missing?).

tromey commented 6 years ago

Thanks for the report. I'm looking into it.

tromey commented 6 years ago

Smaller failing test:

use std::option::Option;
enum Union { A(usize), B(usize) }
fn main() {
    let union_none: Option<Union> = Option::None;
    println!("yay");
}
tromey commented 6 years ago

In this example, I believe the Option shares a tag slot with Union, and in particular, None is given a discriminant value of 2. However, the DWARF does not indicate this anywhere; this is one of the debuginfo regressions from the enum optimizations that went in a while ago. See https://github.com/rust-lang/rust/issues/32920 for details.

One thing I don't currently understand is why RustLanguageRuntime::GetDynamicTypeAndAddress gets Union as the type of the value it is inspecting. I would have expected Option here. But maybe something is going wrong earlier.

tromey commented 6 years ago

There's still a bug here even with the fixed debuginfo:

(lldb) fr v
(core::option::Option<tt::Union>) union_none = {}

I think it should do something more like what gdb does:

(gdb) p union_none
$1 = core::option::Option<tt::Union>::None

I'll file a new bug.