Segmentation fault on returned FFI buffer in release mode

I have a shared library, written in C, that receives a char **buffer used to return some string.
When calling it from rust using a debug build, everything works. If rust is compiled in release mode, the pointer is null.

I have the C library, compiled with gcc -o libtest.so -shared -Wall -fpic lib.c

#include <stdio.h>
static char *buffer = "this is a string";

void get_buffer(const char **out) {
    printf("Pointer: 0x%x\n", (size_t) out);
    *out = buffer;
}

And the rust program:

use std::ffi::CStr;

#[link(name = "test")]
unsafe extern "C" {
    fn get_buffer(out: &*mut std::ffi::c_char);
}

fn main() {
    let buffer: *mut std::ffi::c_char = std::ptr::null_mut();
    unsafe { get_buffer(&buffer); }
    
    println!("buffer: {}", buffer.addr());
    let cstr = unsafe { CStr::from_ptr(buffer) };
    let str = cstr.to_str().unwrap();
    println!("{}", str);
}

Running it gives these results:

root@628a88480eca:/app# LD_LIBRARY_PATH=. cargo run
Pointer: 0x278ff5d0
buffer: 139989080453120
this is a string

root@628a88480eca:/app# LD_LIBRARY_PATH=. cargo run --release
Pointer: 0x8a5d8120
buffer: 0
Segmentation fault

What is going on?

1 Like

This signature doesn't match the one on the C side, as you inverted the mutability of the two pointer types. A correct one would be &mut *const std::ffi::c_char.

Due to this error you're currently telling the compiler that the C side won't mutate the data pointed by the reference, but this is exactly what the C side is doing, thus resulting in UB.

4 Likes

Thanks! Time to go and report issues upstream :slight_smile: