I'm calling the LLVM API via Rust's FFI. LLVMPrintModuleToString
uses strdup to create a string. However, when I wrap the pointer in a CString
, I get an error when Rust drops it.
#![feature(cstr_memory)]
use std::ffi::CString;
extern crate llvm_sys as llvm;
fn main() {
let llvm_ir_cstring;
unsafe {
let module = llvm::core::LLVMModuleCreateWithName(b"nop\0".as_ptr() as *const _);
let llvm_ir_char_ptr = llvm::core::LLVMPrintModuleToString(module);
llvm::core::LLVMDisposeModule(module);
llvm_ir_cstring = CString::from_ptr(llvm_ir_char_ptr);
}
println!("llvm_ir_cstring: {:?}", llvm_ir_cstring);
}
Valgrind error:
==10413== Invalid read of size 4
==10413== at 0x4870586: pthread_mutex_lock (in /usr/lib/libpthread-2.21.so)
==10413== by 0x17F89B: je_arena_dalloc_small (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== by 0x178C30: je_sdallocx (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== by 0x10FA57: heap::imp::deallocate::h1fb92d59333c497bkja (heap.rs:268)
==10413== by 0x10F999: heap::deallocate::h5680e3fedc9e96320da (heap.rs:89)
==10413== by 0x10F929: heap::exchange_free::h452463f962f7ec23kfa (heap.rs:131)
==10413== by 0x10F8C5: Box$LT$$u5b$u8$u5d$$GT$::drop.1769::haf7017472635c7cf (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== by 0x10F836: std..ffi..c_str..CString::drop.1766::h04d2b3db8d468f0c (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== by 0x10F5FF: main::h04b7feb343e229ccgaa (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== by 0x16DBCA: rt::unwind::try::try_fn::h2403085009213705177 (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== by 0x16FF5A: rust_try_inner (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== by 0x16FF33: rust_try (in /home/wilfred/projects/bfc/target/debug/bfc)
==10413== Address 0x1d684 is not stack'd, malloc'd or (recently) free'd
Why is this? What's the correct way to handle a *char
from a C API?
According to the function's documentation:
Use LLVMDisposeMessage to free the string.
In the general case, if you call a function in a library that allocates memory, you should call another function in that library that frees the memory; this should generally be documented as part of the function's contract.
If the documentation for a function tells you to use free
, then you'll have a problem if your application is not linking with the free
corresponding to the library's malloc
(e.g. your application links with msvcr120, but the library links with msvcr100). This is the reason why good libraries provide a method to deallocate resources that it allocates for you.
The default memory allocator in Rust is not C's malloc
, but another allocator called jemalloc. CString
assumes the string was allocated with Rust's memory allocator, so when CString
's destructor runs, it runs jemalloc's free
(you can tell from the je_
-prefixed functions in your call stack), but it fails because the string wasn't allocated with jemalloc's malloc
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With