Segmentation Fault when printing `FunctionValue` which lives longer than `Module`
Describe the Bug
When trying to print the content of FuncitonValue that originates in already dropped Module, it leads to segmentation fault.
To Reproduce
use inkwell::context::Context;
fn main() {
let ctx = Context::create();
// fn_value lives longer than module.
let fn_value =
{
let module = ctx.create_module("test");
let fn_type = ctx.f64_type().fn_type(&[], false);
let fn_value = module.add_function("test_fn", fn_type, None);
// This is OK, it prints "define double @test_fn()\n"
println!("{fn_value}");
fn_value
};
// Segmentation fault
println!("{fn_value}");
}
Expected Behavior
Expected to print "define double @test_fn()\n" again.
(or print nothing because the module already dropped? Actually I'm not confident because I'm LLVM newbie...)
LLVM Version (please complete the following information):
- LLVM Version: Homebrew LLVM 14.0.6
- Inkwell Branch Used: [e.g. llvm14-0]
Desktop (please complete the following information):
- OS: macOS 12.5 (with Apple M1, 2020 macbook pro)
Additional Context Nothing special.
I met this bug when following Kareidoscope tutorial.
In my first implementation, I wrapped compiling procedure like:
fn compile<'ctx>(
ctx: &'ctx Context,
mod_name: &str,
function: &Function,
) -> compiler::Result<FunctionValue<'ctx>> {
let builder = ctx.create_builder();
let module = ctx.create_module(mod_name);
let fpm = PassManager::create(&module);
fpm.add_instruction_combining_pass();
fpm.add_reassociate_pass();
fpm.add_gvn_pass();
fpm.add_cfg_simplification_pass();
fpm.initialize();
Compiler::default()
.builder(&builder)
.context(ctx)
.module(&module)
.function_pass_manager(&fpm)
.compile(function)
}
and the Module instance drops this after this function but FunctionValue lives.
Then, printing the returned FunctionValue led to segmentation fault.
With lldb, I found it triggers EXC_BAD_ACCESS(code=1, address=0x17), but I couldn't figure out further details.
It seems to touch freed memory address after dropping Module.
I feel like it would be better to force FunctionValue not to outlive its originating Module's lifetime, just make panicking when such usage detected, or confirm freeings are triggered after all related values are dropped (via Rc?).
Good catch, the fn value lifetime should probably be tied to the module not the context
I can't seem to reproduce the segfault in LLVM10, I wonder if something changed in LLVM14? 🤔
Prints out:
"declare double @test_fn()\n"
"declare double <badref>() addrspace(0)\n"
I see. It seems to be hard to find out what exactly occurs and I feel like it would be better to make such difference invisible from users by introducing additional lifetimes...
I suspect that GlobalValues might have this same issue. Would you be able to confirm for me?
Yes, exactly.
let ctx = Context::create();
// gvalue lives longer than module.
let gvalue =
{
let module = ctx.create_module("test");
let gvalue = module.add_global(ctx.i32_type(), Some(AddressSpace::Const), "global");
// This is OK, it prints ""@global = external addrspace(4) global i32"
println!("{gvalue}");
gvalue
};
// Segmentation fault
println!("{gvalue}");
Okay, even though I wasn't able to reproduce the issue in gdb, I think it was maybe just luck? Like maybe the memory was never overridden on my system. I think I might have repro'd in valgrind:
==27295== 1 errors in context 43 of 43:
==27295== Invalid read of size 1
==27295== at 0x119B865: llvm::Value::print(llvm::raw_ostream&, bool) const (in /home/dkolsoi/repos/inkwell/target/release/deps/all-99ce1af85ee5a8d2)
==27295== by 0x11E763C: LLVMPrintValueToString (in /home/dkolsoi/repos/inkwell/target/release/deps/all-99ce1af85ee5a8d2)
==27295== by 0x42E3B0: <inkwell::values::fn_value::FunctionValue as core::fmt::Display>::fmt (in /home/dkolsoi/repos/inkwell/target/release/deps/all-99ce1af85ee5a8d2)
See comment here as well: https://github.com/TheDan64/inkwell/issues/347#issuecomment-1249920735 will probably hold off until 0.2.0