我的 Rust 单元测试出现 'Invalid utf-8 sequence of 1 bytes from index 1' 和 'munmup_chunk(): invalid pointer' 错误

'Invalid utf-8 sequence of 1 bytes from index 1' and 'munmup_chunk(): invalid pointer' error in my Rust unit-tests

只有 lib.rs 个文件:

use libc::{self, O_RDONLY, strerror, open, close, __errno_location, read};
use std::ffi::{CString, CStr};
use std::{fmt, process};

// File descriptor
struct Fd {
    fd: i32,
}

impl Fd {
    fn new(fd: i32) -> Fd {
        Fd { fd }
    }

    fn get(&self) -> i32 {
        self.fd
    }
}

impl Drop for Fd {
    fn drop(&mut self) {
        unsafe {
            println!("closing fd - {}", self.fd);
            match close(self.fd) {
                -1 => Err( LacError::new().unwrap_or_else(|e| {
                        eprintln!("{}", e);
                        process::exit(1); 
                    }) ).unwrap_or_else(|e| {
                        eprintln!("{}", e);
                        process::exit(1);}),
                _ => (),
            }
        }
    }
}

// Linux API call Error
#[derive(Debug)]
struct LacError(String);

impl LacError {
    fn new() -> Result<LacError, Box<dyn std::error::Error>> {
        unsafe {
            Ok( LacError( CString::from_raw(strerror(*__errno_location()))
                .into_string()?) )
        }
    }
}

impl fmt::Display for LacError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl std::error::Error for LacError {}

// lac (Linux API call) functions
fn lac_open(path: &str) -> Result<Fd, Box<dyn std::error::Error>> {
    unsafe {
        let path_holder = CString::new(path)?;
        let path_ptr = path_holder.as_ptr();

        match open(path_ptr, O_RDONLY) {
            -1 => Err(Box::new(LacError::new()?)),
            fd => Ok(Fd::new(fd)) 
        }
    }
}

fn lac_read(fd: &Fd,
            buf: &mut String,
            count: usize) -> Result<isize, Box<dyn std::error::Error>>  {
    let buf_holder = CString::new("")?;
    let buf_ptr = buf_holder.as_ptr();

    unsafe {
        match read(fd.get(), buf_ptr as *mut libc::c_void, count) {
            0 => Ok(0),
            -1 => Err(Box::new(LacError::new()?)),
            num_of_red_bytes => {
                buf.push_str(CStr::from_ptr(buf_ptr).to_str()?);
                Ok(num_of_red_bytes)
            },
        }
    }
}    
 
// TESTS
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn whether_correctly_open() {
        let path = "test_file.txt";
        assert_ne!(match lac_open(path) {Ok(fd) => fd.get(), Err(_) => -1},
        -1);
    }

    #[test]
    #[should_panic]
    fn whether_correctly_panic() {
        let path = "testfile.txt";// For first issue "testfile.txt", for second "test_file.txt"
        match lac_open(path) {
            Ok(_) => (),
            Err(e) => panic!("{}", e),
        }
    }

    #[test]
    fn whether_correctly_read() {
        let path = "test_file.txt"; 
        let mut buf = String::from("");

        let fd = lac_open(path)
            .unwrap_or_else(|e| {panic!("{}", e);});
        let count = lac_read(&fd, &mut buf, 1)
            .unwrap_or_else(|e| {panic!("{}", e);});
        println!("{} {}", buf, count);
        assert_eq!(buf, "s"); 
    }
}

起初当我 运行 'cargo test -- -show-output' 第一个测试成功通过但第二个(第三个测试省略了一段时间)不仅失败了(

running 3 tests
test tests::whether_correctly_open ... ok
munmap_chunk(): invalid pointer
error: test failed, to rerun pass '--lib'

Caused by:
process didn't exit successfully: `/home/Michail/rPrgs/usl/target/debug/deps/usl-1be62c27ff5543fb --show-output` (signal: 6, SIGABRT: process abort signal)

) OS 向该进程发送信号可能是因为 LacError::new()-method:

中的操作
impl LacError {
    fn new() -> Result<LacError, Box<dyn std::error::Error>> {
        unsafe {
            Ok( LacError( CString::from_raw(strerror(*__errno_location()))
                .into_string()?) )
        }
    }
}

而且我完全不知道我在哪里犯了错误。

第二次,当我将“testfile.txt”与“test_file.txt”交换并使第二次测试真的失败时,我 运行 'cargo test -- --show-output' 和(我相信`原因'cargo test' 运行 默认情况下在几个线程中)接收

running 3 tests
test tests::whether_correctly_open ... ok
test tests::whether_correctly_panic ... FAILED
test tests::whether_correctly_read ... ok

successes:

---- tests::whether_correctly_open stdout ----
closing fd - 3

---- tests::whether_correctly_read stdout ----
s 1
closing fd - 3


successes:
    tests::whether_correctly_open
    tests::whether_correctly_read

failures:

---- tests::whether_correctly_panic stdout ----
closing fd - 3
note: test did not panic as expected

failures:
    tests::whether_correctly_panic

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

如果几次运行

test tests::whether_correctly_open ... ok
test tests::whether_correctly_panic ... FAILED
test tests::whether_correctly_read ... FAILED

successes:

---- tests::whether_correctly_open stdout ----
closing fd - 3


successes:
    tests::whether_correctly_open

failures:

---- tests::whether_correctly_panic stdout ----
closing fd - 3
note: test did not panic as expected
---- tests::whether_correctly_read stdout ----
thread 'tests::whether_correctly_read' panicked at 'invalid utf-8 sequence of 1 bytes from index 2', src/lib.rs:119:34
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
closing fd - 3


failures:
    tests::whether_correctly_panic
    tests::whether_correctly_read

test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

也就是说,随着时间的推移,当我从上面重复 运行 命令时,我会收到不同的结果。我认为与该代码有关的 utf-8 问题

fn lac_read(fd: &Fd,
            buf: &mut String,
            count: usize) -> Result<isize, Box<dyn std::error::Error>>  {
    let buf_holder = CString::new("")?;
    let buf_ptr = buf_holder.as_ptr();

    unsafe {
        match read(fd.get(), buf_ptr as *mut libc::c_void, count) {
            0 => Ok(0),
            -1 => Err(Box::new(LacError::new()?)),
            num_of_red_bytes => {
                buf.push_str(CStr::from_ptr(buf_ptr).to_str()?);//<-----------------------
                Ok(num_of_red_bytes)
            },
        }
    }
}    

此外,当我重复运行 'cargo test -- --show-output --test-threads=1' 第三次测试总是失败。

UPD:在测试之前,我通过

在“test_file.txt”中编写了常见的拉丁字符
echo sometext > test_file.txt

最终更新:第一期我决定只是从借用的 strerror 字符串创建新的拥有的字符串:

impl LacError {
    fn new() -> Result<LacError, Box<dyn std::error::Error>> {
        unsafe {
            Ok( LacError( String::from( CStr::from_ptr(strerror(*__errno_location())).to_str()? ) ) )
        }
    }
}

第二个最有趣,因为 CString 不是可增长的字符串,读取系统调用将字符插入空格。我决定编写 for 循环以在临时 'container' 中推送 'count' 个空间,这是可增长的拥有字符串:

fn lac_read(fd: &Fd,
            buf: &mut String,
            count: usize) -> Result<isize, Box<dyn std::error::Error>>  {
    let mut container = String::new();
    for _ in 0..count {
        container.push(' ');
    }
    let buf_holder = CString::new(container.as_str())?;
    let buf_ptr = buf_holder.into_raw();

    unsafe {
        match read(fd.get(), buf_ptr as *mut libc::c_void, count) {
            0 => Ok(0),
            -1 => Err(Box::new(LacError::new()?)),
            num_of_red_bytes => {
                buf.push_str(CString::from_raw(buf_ptr).to_str()?);
                Ok(num_of_red_bytes)
            },
        }
    }
}    

问题是 CString 会在超出范围时尝试取消分配其字符串。相反,你应该使用 CStr 不会这样做:

impl LacError {
    fn new() -> Result<LacError, Box<dyn std::error::Error>> {
        unsafe {
            Ok(LacError(
                CStr::from_ptr(strerror(*__errno_location())).to_string_lossy().to_string()
            ))
        }
    }
}

通常 creates/allocates/etc 负责 destruction/de-allocation/etc,除非文档另有明确说明。由于字符串是由 OS、.

分配的

下一个问题:为什么你的第三次测试失败了:

read() 调用将最多 count 个字节读入提供的缓冲区。但它不会 调整缓冲区大小。您的应用程序创建了一个大小为 1 的缓冲区(它是 1,因为 C 字符串以 0 终止):

let buf_holder = CString::new("")?;

问题是 C 字符串 必须 0 终止。因此,如果您读取的内容不是 0,CStr::from_ptr() 将尝试从缓冲区外的未初始化内存中读取,这是未定义的行为。您可以通过更改计数使其 100% 可重现:

let count = lac_read(&fd, &mut buf, 100).unwrap();

现在您的应用程序将尝试将 100 个字节读入该 1 字节缓冲区,破坏它之后的所有内容。有了它,我总是得到:

malloc(): corrupted top size

因此,为了解决这个问题,您必须确保您的缓冲区足够大以容纳数据(并且不要忘记尾随 0!)