c - 将字符串从 Rust 传递到 C 会导致段错误或错误

标签 c rust ffi dpkg

我正在使用 rust-bindgen 在 Rust 中创建到 libdpkg 的绑定(bind)(主要是为了了解 Rust FFI)。

我在实现的一开始就遇到了问题。

我有以下 Rust 代码:

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

use std::convert::TryInto;
use std::ffi::CString;
use std::os::raw::c_char;

pub struct Dpkg {
    program_name: *mut c_char,
    root_directory: Option<*mut c_char>,
}

impl Dpkg {
    pub fn new(program_name: &str, root_directory: Option<&str>) -> Dpkg {
        let dpkg = Dpkg {
            program_name: CString::new(program_name).unwrap().into_raw(),
            root_directory: match root_directory {
                Some(dir) => Some(CString::new(dir).unwrap().into_raw()),
                None => None,
            },
        };

        unsafe {
            dpkg_set_progname(dpkg.program_name);
            push_error_context();

            if let Some(dir) = dpkg.root_directory {
                dpkg_db_set_dir(dir);
            }
            modstatdb_init();
            modstatdb_open(modstatdb_rw_msdbrw_available_readonly);
        }

        dpkg
    }
}

impl Drop for Dpkg {
    fn drop(&mut self) {
        unsafe {
            pkg_db_reset();
            modstatdb_done();
            modstatdb_shutdown();

            pop_error_context(ehflag_normaltidy.try_into().unwrap());

            let _ = CString::from_raw(self.program_name);
            if let Some(dir) = self.root_directory {
                let _ = CString::from_raw(dir);
            }

        }
    }
}

我有以下集成测试:

extern crate dpkg;

use dpkg::Dpkg;
use std::mem;

#[test]
fn test_new() {
    let dpkg = Dpkg::new("test", None);
    mem::drop(dpkg);
    let dpkg = Dpkg::new("test2", None);
    mem::drop(dpkg);
}

#[test]
fn test_new2() {
    let dpkg = Dpkg::new("test", None);
    mem::drop(dpkg);
}

在第一个测试中,无论我调用多少次 Dpkg::new() 一切都井井有条并且工作正常。

但是,如果我在 test_new2() 中执行 Dpkg::new(),结果要么是段错误,要么是以下错误:

test: unrecoverable fatal error, aborting:
 parsing file '/var/lib/dpkg/status' near line 10 package 'fonts-sil-abyssinica':
 'Replaces' field, invalid package name '�': must start with an alphanumeric character

虽然我不太确定为什么。 以下 C 程序按预期运行:

#include <stdio.h>
#define LIBDPKG_VOLATILE_API

#include <dpkg/dpkg-db.h>
#include <dpkg/dpkg.h>
#include <stdlib.h>

void setup_dpkg(const char *program_name, const char *root_directory) {
    dpkg_set_progname(program_name);
    push_error_context();

    if (root_directory != NULL) {
        dpkg_db_set_dir(root_directory);
    }
    modstatdb_init();
    modstatdb_open(msdbrw_available_readonly);
}

void destroy_dpkg() {
    pkg_db_reset();
    modstatdb_done();

    pop_error_context(ehflag_normaltidy);
}

int main() {
    for (int i =0; i < 100; i++) {
        char *program_name = (char*)malloc(32 * sizeof(char));
        strcpy(program_name, "test");
        char *dir = (char*)malloc(16 * sizeof(char));
        strcpy(dir, "/var/lib/dpkg");
        setup_dpkg(program_name, dir);
        printf("%d time!\n", i);
        destroy_dpkg();
        free(program_name);
        free(dir);
    }
    return 0;
}

输出是:

0 time!
1 time!
2 time!
3 time!
4 time!
5 time!
6 time!
7 time!
8 time!
9 time!
10 time!
11 time!
12 time!
13 time!
14 time!
15 time!
16 time!
17 time!
18 time!
19 time!
20 time!
21 time!
22 time!
23 time!
24 time!
25 time!
26 time!
27 time!
28 time!
29 time!
30 time!
31 time!
32 time!
33 time!
34 time!
35 time!
36 time!
37 time!
38 time!
39 time!
40 time!
41 time!
42 time!
43 time!
44 time!
45 time!
46 time!
47 time!
48 time!
49 time!
50 time!
51 time!
52 time!
53 time!
54 time!
55 time!
56 time!
57 time!
58 time!
59 time!
60 time!
61 time!
62 time!
63 time!
64 time!
65 time!
66 time!
67 time!
68 time!
69 time!
70 time!
71 time!
72 time!
73 time!
74 time!
75 time!
76 time!
77 time!
78 time!
79 time!
80 time!
81 time!
82 time!
83 time!
84 time!
85 time!
86 time!
87 time!
88 time!
89 time!
90 time!
91 time!
92 time!
93 time!
94 time!
95 time!
96 time!
97 time!
98 time!
99 time!

我是不是做错了什么(这很可能),还是 Rust 测试运行器导致了这些问题?

最佳答案

问题不在于我的代码,而在于 libdpkg 不是线程安全的。

从实现的角度来看这很好,但是 Rust 并行运行测试会导致段错误。

关于c - 将字符串从 Rust 传递到 C 会导致段错误或错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58153674/

相关文章:

ruby - 帮助 Ruby FFI

c - 打印 scanf 输入,可能来自被忽略的非字母之间

rust - 我们如何在 Rust 中检测主机操作系统类型(而不是目标操作系统)?

rust SQLx: "expected enum ` std::option::Option`"在查询中但不在类似查询中

c - 为什么当静态没有链接时const在链接过程中消失?

python - Python FFI 中的 Haskell 函数

pointers - 从 Rust FFI 中检索一个 float 指针

c - fscanf() 和 fgets() 等 C 函数如何记住文件中从何处开始读取?

C 从文件中读取一行的一列

c - 如何在 C 中动态分配字符串数组?