将字符串从 Rust 传递到 C 会导致段错误或错误
Passing a string from Rust to C causes a segfault or an error
我正在使用 rust-bindgen
.
在 Rust 中创建到 libdpkg
的绑定(主要是为了了解 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 并行运行测试会导致段错误。
我正在使用 rust-bindgen
.
libdpkg
的绑定(主要是为了了解 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 并行运行测试会导致段错误。