TheDan64 / inkwell

It's a New Kind of Wrapper for Exposing LLVM (Safely)
https://thedan64.github.io/inkwell/
Apache License 2.0
2.32k stars 224 forks source link

`test_module::test_garbage_ir_fails_create_module_from_ir` triggers assertion in llvm 10 #198

Open nlewycky opened 4 years ago

nlewycky commented 4 years ago

To Reproduce Build LLVM 10 with Asserts enabled and run inkwell tests against it.

$ cargo test --features=llvm10-0 test_module::test_garbage_ir_fails_create_module_from_ir
test test_module::test_garbage_ir_fails_create_module_from_ir ... all-0b042a69785d4445: ../lib/Support/MemoryBuffer.cpp:47: void llvm::MemoryBuffer::init(const char*, const char*, bool): Assertion `(!RequiresNullTerminator || BufEnd[0] == 0) && "Buffer is not null terminated!"' failed.

Describe the Bug The assertion occurs inside the call to LLVMParseIRInContext. The function checks whether this is bitcode or not, and if not it goes down a path parsing plain assembly text which in turn calls MemoryBuffer::getMemBuffer() on a MemoryBufferRef referring to the original memory buffer we created. That call assumes the buffer is null terminated.

Expected Behavior We get an Err() back from Context::create_module_from_ir.

LLVM Version (please complete the following information):

Additional Context I started this expecting to find a bug in inkwell, but I think in this case LLVM isn't living up to its documented API guarantees. This may be a bug that needs to be fixed in LLVM. It should return an error instead of assert-failing on invalid input.

Possibly though, the test isn't testing what it meant to test. If you replace "garbage ir data" with "BC\xC0\xDEage ir data" then the test passes because it doesn't go through the .ll text file parsing path.

seanyoung commented 3 years ago

Stack trace:

#0  0x00007ffff7a8f9d5 in raise () from /lib64/libc.so.6
#1  0x00007ffff7a788a4 in abort () from /lib64/libc.so.6
#2  0x00007ffff7a78789 in __assert_fail_base.cold () from /lib64/libc.so.6
#3  0x00007ffff7a88026 in __assert_fail () from /lib64/libc.so.6
#4  0x0000555556e015d2 in llvm::MemoryBuffer::init(char const*, char const*, bool) ()
#5  0x0000555556e01662 in llvm::MemoryBuffer::getMemBuffer(llvm::StringRef, llvm::StringRef, bool) ()
#6  0x0000555556e016b2 in llvm::MemoryBuffer::getMemBuffer(llvm::MemoryBufferRef, bool) ()
#7  0x0000555555e12888 in llvm::parseAssemblyInto(llvm::MemoryBufferRef, llvm::Module*, llvm::ModuleSummaryIndex*, llvm::SMDiagnostic&, llvm::SlotMapping*, bool, llvm::StringRef) ()
#8  0x0000555555e12ecd in llvm::parseAssembly(llvm::MemoryBufferRef, llvm::SMDiagnostic&, llvm::LLVMContext&, llvm::SlotMapping*, bool, llvm::StringRef) ()
#9  0x0000555555e0fce4 in llvm::parseIR(llvm::MemoryBufferRef, llvm::SMDiagnostic&, llvm::LLVMContext&, bool, llvm::StringRef) ()
#10 0x0000555555e102d5 in LLVMParseIRInContext ()
#11 0x0000555555937766 in inkwell::context::Context::create_module_from_ir (
    self=0x7ffff7a4c2c8, memory_buffer=...) at src/context.rs:182
#12 0x000055555586de7c in all::test_module::test_garbage_ir_fails_create_module_from_ir () at tests/all/test_module.rs:134
#13 0x00005555558b788a in all::test_module::test_garbage_ir_fails_create_module_from_ir::{{closure}} () at tests/all/test_module.rs:128
#14 0x00005555558b252e in core::ops::function::FnOnce::call_once<closure-0,()>

So, LLVMParseIRInContext() needs the MemoryBuffer to have a 0 terminator, which is a little awkward in inkwell.

I can fix the test with:


#[test]
fn test_garbage_ir_fails_create_module_from_ir() {
    let context = Context::create();
    let memory_buffer =
        MemoryBuffer::create_from_memory_range(&b"garbage ir data\0"[0..15], "my_ir");

    assert_eq!(memory_buffer.get_size(), 15);
    assert_eq!(memory_buffer.as_slice(), b"garbage ir data");
    assert!(context.create_module_from_ir(memory_buffer).is_err());
}