Skip to content
This repository has been archived by the owner on May 21, 2019. It is now read-only.

Segfault @ RustASTContext::FindEnumVariant #16

Closed
cynecx opened this issue Aug 5, 2018 · 4 comments
Closed

Segfault @ RustASTContext::FindEnumVariant #16

cynecx opened this issue Aug 5, 2018 · 4 comments
Assignees

Comments

@cynecx
Copy link

cynecx commented Aug 5, 2018

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 #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
Copy link
Collaborator

tromey commented Aug 7, 2018

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

@tromey tromey self-assigned this Aug 7, 2018
@tromey
Copy link
Collaborator

tromey commented Aug 7, 2018

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
Copy link
Collaborator

tromey commented Aug 7, 2018

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 rust-lang/rust#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 added a commit that referenced this issue Aug 7, 2018
Sometimes the DWARF can omit information about a discriminant, for
example when an Option shares a discriminant slot with an enum that it
wraps.  In this case, lldb could crash, because the discriminant was
not found and because there was no default variant.

No test case because this relies on a compiler bug that will soon be
fixed.

Fixes #16
@tromey tromey closed this as completed Aug 7, 2018
@tromey
Copy link
Collaborator

tromey commented Aug 9, 2018

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.

tromey added a commit that referenced this issue Aug 28, 2018
Sometimes the DWARF can omit information about a discriminant, for
example when an Option shares a discriminant slot with an enum that it
wraps.  In this case, lldb could crash, because the discriminant was
not found and because there was no default variant.

No test case because this relies on a compiler bug that will soon be
fixed.

Fixes #16
tromey added a commit that referenced this issue Nov 27, 2018
Sometimes the DWARF can omit information about a discriminant, for
example when an Option shares a discriminant slot with an enum that it
wraps.  In this case, lldb could crash, because the discriminant was
not found and because there was no default variant.

No test case because this relies on a compiler bug that will soon be
fixed.

Fixes #16
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants