Conversion between a Rust str and ffi::CString and back again partially corrupts the string

前端 未结 1 597
孤独总比滥情好
孤独总比滥情好 2020-12-12 03:25
#![allow(non_camel_case_types)]

use libc::{c_uchar, size_t};
use std::str::FromStr;
use std::ffi::{CString, NulError};
use std::slice;

#[repr(C)]
pub struct c_str_         


        
相关标签:
1条回答
  • 2020-12-12 03:39

    You've missed one crucial step.

    fn to_c_str(&self) -> Result<c_str_t, NulError> {
        let result = match CString::new(&self[..]) {
            Ok(result) => result,
            Err(e) => {
                return Err(e);
            }
        };
        Ok(c_str_t { data: result.as_ptr() as *const u8, len: self.len() })
    }
    

    allocates a new CString structure, and takes a pointer to its data, but that data will still be freed once the to_c_str function runs to completion. This means that later code can overwrite the string contents in-memory. In your example case, it just happens to be that only the first character is overwritten.

    I'd recommend reading over the documentation of .as_ptr() as it tries to cover some of this.

    You could manually std::mem::forget, e.g.

    fn to_c_str(&self) -> Result<c_str_t, NulError> {
        let result = match CString::new(&self[..]) {
            Ok(result) => result,
            Err(e) => {
                return Err(e);
            }
        };
        let s = c_str_t { data: result.as_ptr() as *const u8, len: self.len() };
        std::mem::forget(result);
    
        Ok(s)
    }
    

    but the best approach would be to use .into_raw() to take ownership and return the pointer on its own.

    fn to_c_str(&self) -> Result<c_str_t, NulError> {
        let result = match CString::new(&self[..]) {
            Ok(result) => result,
            Err(e) => {
                return Err(e);
            }
        };
        Ok(c_str_t { data: result.into_raw() as *const u8, len: self.len() })
    }
    
    0 讨论(0)
提交回复
热议问题