首页 > 解决方案 > 有没有办法在 fsanitize 中断/崩溃时捕获子进程标准输出?

问题描述

我们有一个程序来测试其他程序。它只是比较某些输入上的输出是否正确。出于几个原因,我想从 Valgrind 切换到 fsanitize。

问题是我们的 Rust 程序无法使用 fsanitize 捕获具有某些错误的子进程的标准输出,但是使用 valgrind 时可以捕获所有输出。

那将是子进程。

let mut run_cmd = Command::new(format!("./{}", &self.meta.projdata.project_name))
    //assuming makefile_path = project path
    .current_dir(
        &self
            .meta
            .projdata
            .makefile_path
            .as_ref()
            .unwrap_or(&String::from("./")),
    )
    .args([&self.argv].iter().filter(|s| !s.is_empty()))
    .stdin(Stdio::piped())
    .stdout(Stdio::piped())
    .stderr(Stdio::piped())
    .envs(envs)
    .spawn()
    .expect("could not spawn process");

如果它在一定时间后仍然运行,它将被杀死。

fn command_timeout(
    cmd: Child,
    timeout: i32,
    number: i32,
) -> Result<(String, i32), ExecuteError> {
    let mut cmd = cmd;

    let mut output = String::new();

    match cmd
        .wait_timeout(Duration::from_secs(timeout as u64))
        .unwrap()
    {
        Some(expr) => {
            let mut tmp: Vec<u8> = Vec::new();

            cmd.stdout
                .as_mut()
                .unwrap()
                .read_to_end(&mut tmp)
                .expect("could not read stdout");

            output = format!("{}{}", output, String::from_utf8_lossy(&tmp));
            let retvar = expr.code().unwrap_or(-1);
            return Ok((output, retvar));
        }
        None => {
            println!("killing {} beacause of timeout", number);
            cmd.kill().expect("Upps, can't kill this one");

            return Err(ExecuteError::Timeout);
        }
    }
}

这个有问题的测试用例的输出是:

./a2 
output1 from c
AddressSanitizer:DEADLYSIGNAL
=================================================================
==11907==ERROR: AddressSanitizer: stack-overflow on address 0x7ffef3111660 (pc 0x565288af853a bp 0x7ffef310c8f0 sp 0x7ffef310c820 T0)
    #0 0x565288af853a in main (/home/k/esp/students/assignment2espep_693/a2+0xfc53a)
    #1 0x7fd97bd0c151 in __libc_start_main (/usr/lib/libc.so.6+0x28151)
    #2 0x565288a1c1cd in _start (/home/k/esp/students/assignment2espep_693/a2+0x201cd)

SUMMARY: AddressSanitizer: stack-overflow (/home/k/esp/students/assignment2espep_693/a2+0xfc53a) in main
==11907==ABORTING

重现问题的示例 C 代码:

#include <stdio.h>
int main()
{

    char array[1];

    puts("output1 from c");
    array[20000] = 'c';
    printf("output2 from c: %llu\n", (long long unsigned) (array[20000]) ); //this is on purpose
    return 0;
}

如果我现在通过 Rust 使用相同的输入文件,则 output / tmp 变量将为空。使用 fsanitize 时有什么方法可以捕获输出吗?我知道 fsanitize 打印到 stderr 并且这目前被忽略但我不明白为什么 stdout 上的子输出也没有被捕获。

C程序是用clang -Wall -Wextra -pedantic -std=c17 -fsanitize=address -fsanitize-recover=address

它开始于ASAN_OPTIONS=halt_on_error=0 ./testrunner

标签: ruststdoutchild-processaddress-sanitizer

解决方案


推荐阅读