Почему указатель Box передается в C и обратно в Rust segfault? - PullRequest
0 голосов
/ 08 февраля 2019

Некоторые C-коды вызывают вызов Rust open ниже, который возвращает указатель.Позже код C передает точно такой же указатель обратно в функцию close, которая пытается удалить ее (освободить).Это segfaults в free(3).Почему?

use std::os::raw::{c_int, c_void};

struct Handle;

extern "C" fn open(_readonly: c_int) -> *mut c_void {
    let h = Handle;
    let h = Box::new(h);
    return Box::into_raw(h) as *mut c_void;
}

extern "C" fn close(h: *mut c_void) {
    let h = unsafe { Box::from_raw(h) };
    // XXX This segfaults - why?
    drop(h);
}

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

В close вы заканчиваете тем, что создали Box<c_void> вместо Box<Handle>, потому что вы не бросили *mut c_void обратно к *mut Handle до вызова Box::from_raw.

fn close(h: *mut c_void) {
    let h = unsafe { Box::from_raw(h as *mut Handle) };
    drop(h);
}

Кстати, Box фактически не выделяет никакой памяти для типа нулевого размера (например, Handle здесь) и вместо этого использует фиксированное ненулевое значение указателя (которое в текущей реализации являетсявыравнивание типа; по умолчанию тип с нулевым размером имеет выравнивание 1).Деструктор для упакованного типа нулевого размера знает, что он не пытается освободить память по этому фиктивному адресу памяти, но c_void не является типом нулевого размера (он имеет размер 1), поэтому деструктор для Box<c_void> пытается освободитьпамять по адресу 0x1, которая вызывает ошибку.

0 голосов
/ 08 февраля 2019

Проблема в том, что вы не приводили указатель обратно к указателю Handle при преобразовании его обратно в Box и получили Box неправильного типа.

Это работает:

fn close(h: *mut c_void) {
    let h = unsafe { Box::from_raw(h as *mut Handle) };
    //                               ^^^^^^^^^^^^^^
    drop(h);
}

В вашем коде h является std::boxed::Box<std::ffi::c_void>.

...