Передача строки из Rust в C вызывает segfault или ошибку - PullRequest
0 голосов
/ 29 сентября 2019

Я создаю привязку к libdpkg в Rust (в основном, чтобы узнать о Rust FFI), используя rust-bindgen.

Я столкнулся с проблемой в самом начале реализации.

У меня есть следующий код 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() все в порядке и работает нормально.

Однако, если я выполню Dpkg::new() в test_new2(), результатом будет либо segfault, либо следующая ошибка:

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

I'mхотя я не совсем уверен, почему.Следующая программа на 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, который вызываеттакие проблемы?

1 Ответ

2 голосов
/ 30 сентября 2019

Проблема была не в моем коде, а в том, что libdpkg не является потокобезопасным.

Это нормально с точки зрения реализации, но Rust запускает тесты параллельно, что вызывает ошибку segfault.

...