C string from FFI getting strange symbols

I'm having problem with my wrapper of a C library
The following code:

let val = conf.value_get(sub_key).unwrap();
println!("value:{}", val);

Gives me this strange output value:�
If I add println!(); before this code i get the expected result value:10.
value_get() is a wrapper for a C function that returns a C string. It look like that.

pub fn value_get(&self, Id(key): Id) -> Result<&str, Error> {
    let mut size = 0;
    if !unsafe { clingo_configuration_value_get_size(&self.0, key, &mut size) } {
        Err(ClingoError::new(
            "Call to clingo_configuration_value_get_size() failed.",
        ))?
    }
    let mut value = Vec::<i8>::with_capacity(size);
    let value_ptr = value.as_mut_ptr();
    if !unsafe { clingo_configuration_value_get(&self.0, key, value_ptr, size) } {
        Err(ClingoError::new(
            "Call to clingo_configuration_value_get() failed.",
        ))?
    }
    let cstr = unsafe { CStr::from_ptr(value_ptr) };
    Ok(cstr.to_str()?)
}

Does anyone know what is going wrong?

The value vec is dropped at the end of value_get, so you're reading freed memory. You probably want to read data into a Vec<u8>, and then return a CString::new off that vec.

1 Like

Thanks i rewrote the function like this.

pub fn value_get(&self, Id(key): Id) -> Result<String, Error> {
    let mut size = 0;
    if !unsafe { clingo_configuration_value_get_size(&self.0, key, &mut size) } {
        Err(ClingoError::new(
            "Call to clingo_configuration_value_get_size() failed.",
        ))?
    }
    let mut value = Vec::<i8>::with_capacity(size);
    let value_ptr = value.as_mut_ptr();
    if !unsafe { clingo_configuration_value_get(&self.0, key, value_ptr, size) } {
        Err(ClingoError::new(
            "Call to clingo_configuration_value_get() failed.",
        ))?
    }
    let value_ref =
        unsafe { std::slice::from_raw_parts(value_ptr as *const u8, size) };
    Ok(String::from_utf8(value_ref.to_owned())?)
}

I got a few questions.
Is it clear that value is not yet dropped before the end of the function?
from_raw_parts() creates a second reference to value ... might this cause a double free problem?
I think to_owned() makes a copy and from_utf8() makes another copy ... maybe this can be done better?

Vec has (indirectly) an explicit Drop impl, which is guaranteed to only run at the end of the scope where the vec was defined (end of value_get() in this case).

The more straightforward code would be:

...
let mut value = Vec::<u8>::with_capacity(size);
if !unsafe { clingo_configuration_value_get(&self.0, key, value.as_mut_ptr() as *mut c_char, size) } {
     Err(ClingoError::new(
         "Call to clingo_configuration_value_get() failed.",
     ))?
}
Ok(String::from_utf8(value)?)

With this i get an only an empty string. I guess because the Vec is not filled via Vec methods. I probably should use vec![0;size] instead of with_capacity().

Oh sorry, I left out a value.set_len(size) call. But yeah, you can just zero-fill on construction instead (this is safer and likely won't make any perf difference here).

3 Likes