Skip to content

Illegal instruction with Rust 1.65.0 #691

@wks

Description

@wks

When mmtk-core is compiled with the most recent 1.65.0 toolchain, it generates a ud2 (undefined instruction) instruction where it usually executes normally. It only happens when doing release build (cargo build --release).

A disassembly snippet obtained from objdump -d libmmtk_ruby.so (It can be obtained from libmmtk_openjdk.so as well):

000000000002e140 <_ZN4mmtk6policy16largeobjectspace26LargeObjectSpace$LT$VM$GT$3new17h321b65ec1e29a73aE>:
   2e140:       55                      push   %rbp
   2e141:       41 57                   push   %r15
...
   2e264:       0f 28 44 24 30          movaps 0x30(%rsp),%xmm0
   2e269:       0f 11 84 24 90 00 00    movups %xmm0,0x90(%rsp)
   2e270:       00 
   2e271:       48 8d bc 24 b0 00 00    lea    0xb0(%rsp),%rdi
   2e278:       00 
   2e279:       48 8d 74 24 50          lea    0x50(%rsp),%rsi
   2e27e:       4c 89 e2                mov    %r12,%rdx
   2e281:       4c 89 f9                mov    %r15,%rcx
   2e284:       4d 89 f0                mov    %r14,%r8
   2e287:       e8 e4 2c 08 00          call   b0f70 <_ZN4mmtk6policy5space21CommonSpace$LT$VM$GT$3new17hd1ba7f2c88be724fE>
   2e28c:       0f 0b                   ud2   ;  !!!!!!!! HERE !!!!!!!!
   2e28e:       48 89 c3                mov    %rax,%rbx
   2e291:       48 8b bc 24 b0 00 00    mov    0xb0(%rsp),%rdi
   2e298:       00 
   2e299:       48 8b b4 24 b8 00 00    mov    0xb8(%rsp),%rsi
   2e2a0:       00 
   2e2a1:       e8 0a 7c 0c 00          call   f5eb0 <_ZN4core3ptr100drop_in_place$LT$alloc..vec..ExtendElement$LT$alloc..vec..Vec$LT$regex_syntax..ast..Span$GT$$GT$$GT$17hc319d5ea0dc
262a2E>

The ud2 instruction appears in the LargeObjectSpace<VM>::new function. It is usually placed after call sites that never returns normally (such as unwinding or panic).

I added some eprintln! to see where exactly it went wrong, due to inlining. As a result, I isolated the problem to a MaybeUninit::uninit()::assume_init() call.

impl<VM: VMBinding> FreeListPageResource<VM> {
    pub fn new_contiguous(start: Address, bytes: usize, vm_map: &'static VMMap) -> Self {
        eprintln!("new_contiguous {}", 1);
        let pages = conversions::bytes_to_pages(bytes);
        eprintln!("new_contiguous {}", 2);
        // We use MaybeUninit::uninit().assume_init(), which is nul, for a Box value, which cannot be null.
        // FIXME: We should try either remove this kind of circular dependency or use MaybeUninit<T> instead of Box<T>
        #[allow(invalid_value)]
        #[allow(clippy::uninit_assumed_init)]
        let common_flpr = unsafe {
            eprintln!("new_contiguous {} {}", 2, 1);
            let free_list = MaybeUninit::uninit().assume_init();     // !!!!!!!!!!!! HERE!!!!!!!!!!!!!!!
            eprintln!("new_contiguous {} {} {}", 2, 1, 1);
            let mut common_flpr = Box::new(CommonFreeListPageResource {
                free_list,
                start,
            });
            eprintln!("new_contiguous {} {}", 2, 2);
            ::std::ptr::write(
                &mut common_flpr.free_list,
                vm_map.create_parent_freelist(&common_flpr, pages, PAGES_IN_REGION as _),
            );
            eprintln!("new_contiguous {} {}", 2, 3);
            common_flpr
        };

According to the documentation of MaybeUninit::assume_init,

It is up to the caller to guarantee that the MaybeUninit<T> really is in an initialized state. Calling this when the content is not yet fully initialized causes immediate undefined behavior.

In this context, the type of the variable free_list is Box<RawMemoryFreeList>. Because Box contains a pointer/reference internally, calling assume_init on an MaybeUninit::uninit() value immediately results in undefined behaviour. And the 1.65.0 compiler chose to generate a ud2 instruction which crashes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions