Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segmentation Fault when printing FunctionValue which lives longer than Module #343

Open
ytoml opened this issue Aug 21, 2022 · 9 comments
Open
Labels
Milestone

Comments

@ytoml
Copy link
Contributor

ytoml commented Aug 21, 2022

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.

@ytoml
Copy link
Contributor Author

ytoml commented Aug 21, 2022

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?).

@TheDan64 TheDan64 added the bug label Aug 21, 2022
@TheDan64
Copy link
Owner

Good catch, the fn value lifetime should probably be tied to the module not the context

@TheDan64 TheDan64 added this to the 0.1.0 milestone Aug 21, 2022
@TheDan64
Copy link
Owner

I can't seem to reproduce the segfault in LLVM10, I wonder if something changed in LLVM14? 🤔

@TheDan64
Copy link
Owner

Prints out:

"declare double @test_fn()\n"
"declare double <badref>() addrspace(0)\n"

@ytoml
Copy link
Contributor Author

ytoml commented Aug 24, 2022

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...

@TheDan64
Copy link
Owner

I suspect that GlobalValues might have this same issue. Would you be able to confirm for me?

@ytoml
Copy link
Contributor Author

ytoml commented Aug 27, 2022

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}");

@TheDan64
Copy link
Owner

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)

@TheDan64
Copy link
Owner

See comment here as well: #347 (comment) will probably hold off until 0.2.0

@TheDan64 TheDan64 modified the milestones: 0.1.0, 0.2.0 Sep 26, 2022
@TheDan64 TheDan64 modified the milestones: 0.5.0, 0.6.0 Jul 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants