Rust C 互操作分段错误(斜杠 STATUS_ACCESS_VIOLATION) [英] Rust C interop segmentation fault (slash STATUS_ACCESS_VIOLATION)

查看:72
本文介绍了Rust C 互操作分段错误(斜杠 STATUS_ACCESS_VIOLATION)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在本地出现 2 个错误(两种情况下的代码实际上相同):

PS C:\Users\jonat\Projects\latex_classifier>货物测试--发布在 0.07 秒内完成发布 [优化] 目标运行单元测试 (target\release\deps\latex_classifier-4191f26efde0cf48.exe)运行 0 个测试测试结果:好的.0 通过;0 失败;0 忽略;0 测量;0 过滤掉;在 0.00 秒内完成运行测试\tests.rs (target\release\deps\tests-aaa96da54d1827ef.exe)运行 1 个测试错误:测试失败,重新运行通过--test 测试"引起的:进程没有成功退出:`C:\Users\jonat\Projects\latex_classifier\target\release\deps\tests-aaa96da54d1827ef.exe`(退出代码:0xc0000005,STATUS_ACCESS_VIOLATION)PS C:\Users\jonat\Projects\latex_classifier>

在操场上:

 编译playground v0.0.1 (/playground)在 2.09 秒内完成发布 [优化] 目标运行 `target/release/playground`超时:受监控的命令转储核心/playground/tools/entrypoint.sh: line 11: 7 Segmentation fault timeout --signal=KILL ${timeout} "$@";

为了在 Python 中调用一些 Rust 函数,我使用了 C 接口 CFFI,这似乎需要很多指针:

<块引用>

ctype 通常是一些描述 C 类型的常量字符串.它必须是指针或数组类型.

而且我需要返回一个相对复杂的结构:CArray>

我发现 Python 在尝试某些访问时崩溃,因此我在 Rust 中对其进行了测试,这导致了这些错误.

游乐场链接:https:///play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=5609cf66ff7ee1104e978ce4545f9679

游乐场代码:

#![feature(vec_into_raw_parts)]#![允许(未使用)]fn 主(){让段 = test_function();不安全{println!("segment: {:.?}",segment);让行 = std::slice::from_raw_parts(segment.ptr, segment.size);println!("行:{:.?}",lines);对于 line.iter() {让符号 = std::slice::from_raw_parts(line.ptr, line.size);对于符号中的 s.iter() {println!("{:.?}",*s.bound);//问题://下面的行不起作用println!("{:.?}",*(*s.bound).min);}}}}#[no_mangle]酒吧外部C"fn test_function() ->CArray>{//只是一些测试数据让温度:Vec, Bound)>= vec![vec![(vec![1,2,3,4,5,6,7],Bound::new())],vec![(vec![1,2,3,4,5,6,7],Bound::new()),(vec![1,2,3,4,5,6,7],Bound::new()),(vec![1,2,3,4,5,6,7],Bound::new())]];CArray::new(temp.into_iter().map(|sl| {CArray::new(sl.into_iter().map(|(s, b)| SymbolPixels {像素:&CArray::new(s),绑定:&ReturnBound::new(b)}).collect::>())}).collect::<Vec<CArray<SymbolPixels>>>())}#[派生(克隆,调试)]pub struct Bound{酒吧分钟:点<T>,酒吧最大:点<T>,}//这个 impl 只是为了方便测试impl Bound{fn new() ->绑定<使用大小>{Bound { min: Point { x:1, y:2 }, max: Point { x:5, y:9 } }}}#[repr(C)]#[派生(调试)]pub struct CArray{pub ptr: *const T,酒吧大小:使用,}实施<T>CArray T{pub fn new(v: Vec) ->自己 {让 (ptr,size,_) = v.into_raw_parts();数组{ptr:ptr,尺寸:尺寸,}}}#[repr(C)]#[派生(调试)]酒吧结构符号像素{酒吧像素:*const CArray<u8>,酒吧绑定:*const ReturnBound}#[repr(C)]#[派生(调试)]酒吧结构返回绑定{pub min: *const Point,酒吧最大:*const Point<u32>,}impl 返回绑定 {pub fn new(b: Bound) ->自己 {返回边界{min: &Point { x: b.min.x as u32, y: b.min.y as u32 },max: &Point { x: b.max.x 作为 u32, y: b.max.y 作为 u32 }}}}#[repr(C)]#[派生(复制,克隆,调试)]pub struct Point<T: Ord>{酒吧 x: T,酒馆:T,}

我不知道要在这里提供哪些其他信息,所以如果我遗漏了任何信息,请发表评论.

非常感谢有关此主题的任何帮助.

解决方案

当我使用 'cargo run' 运行代码时,我得到了输出:

segment: CArray { ptr: 0x55ab1e3fac80, size: 2 }行:[CArray { ptr: 0x55ab1e3facd0, size: 1 }, CArray { ptr: 0x55ab1e3fad20, size: 3 }]ReturnBound { min: 0x8, max: 0x55ab1d4a554e }分段故障

其中 ReturnBound.min 应该是一个指针,但 0x8 不是一个有效的指针.注释指出的 println 试图取消引用这个无效的指针,这导致了段错误.

看起来没有分配后备内存,因此您存储在数据结构中的指针指向在您访问之前已释放的内存.这在处理原始指针时很危险,因为它们是未经检查的,而且您不会收到编译器错误

例如,在:

pub fn new(b: Bound) ->自己 {返回边界{min: &Point { x: b.min.x as u32, y: b.min.y as u32 },max: &Point { x: b.max.x 作为 u32, y: b.max.y 作为 u32 }}} <-- 两个Point结构体后面的数据会被丢到这里

Point 在堆栈上分配,对该数据的引用存储在 ReturnBound 的最小值和最大值中.然后返回 ReturnBound,但分配给 Point 结构的数据将在 new() 函数结束时删除,然后该内存将被其他事物使用.存储在 CArray 结构中的 Vec 数据也是如此.为了解决这个问题,您可以使用 libc::malloc(因为它与 C 接口),或者您可以使用 Box::leak() 或其他一些技巧来防止分配的内存被丢弃.

您也可以重构它以避免需要指针,但这取决于您到底要与什么接口

Getting 2 errors (code is practically identical in both cases), locally:

PS C:\Users\jonat\Projects\latex_classifier> cargo test --release
    Finished release [optimized] target(s) in 0.07s
     Running unittests (target\release\deps\latex_classifier-4191f26efde0cf48.exe)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests\tests.rs (target\release\deps\tests-aaa96da54d1827ef.exe)

running 1 test
error: test failed, to rerun pass '--test tests'

Caused by:
  process didn't exit successfully: `C:\Users\jonat\Projects\latex_classifier\target\release\deps\tests-aaa96da54d1827ef.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
PS C:\Users\jonat\Projects\latex_classifier> 

In playground:

   Compiling playground v0.0.1 (/playground)
    Finished release [optimized] target(s) in 2.09s
     Running `target/release/playground`
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11:     7 Segmentation fault      timeout --signal=KILL ${timeout} "$@"

To call some Rust functions in Python I'm using a C interface CFFI, this seems to require a lot of pointers:

The ctype is usually some constant string describing the C type. It must be a pointer or array type.

And I need to return a relatively complex structure: CArray<CArray<SymbolPixels>>

I found Python was crashing when attempting some accesses, so I tested it in Rust which lead to these errors.

Playground link: https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=5609cf66ff7ee1104e978ce4545f9679

The playground code:

#![feature(vec_into_raw_parts)]

#![allow(unused)]
fn main() {
    let segment = test_function();
    unsafe {
        println!("segment: {:.?}",segment);
        let lines = std::slice::from_raw_parts(segment.ptr, segment.size);
        println!("lines: {:.?}",lines);
        for line in lines.iter() {
            let symbols = std::slice::from_raw_parts(line.ptr, line.size);
            for s in symbols.iter() {
                println!("{:.?}",*s.bound);
                // THE PROBLEM:
                // The below line doesn't work
                println!("{:.?}",*(*s.bound).min);
            }
        }
    }
}

#[no_mangle]
pub extern "C" fn test_function() -> CArray<CArray<SymbolPixels>> {
    // Just some test data
    let temp: Vec<Vec<(Vec<u8>, Bound<usize>)>> = vec![
        vec![(vec![1,2,3,4,5,6,7],Bound::new())],
        vec![(vec![1,2,3,4,5,6,7],Bound::new()),(vec![1,2,3,4,5,6,7],Bound::new()),(vec![1,2,3,4,5,6,7],Bound::new())]
    ];
    CArray::new(temp.into_iter().map(|sl| {
        CArray::new(sl.into_iter().map(|(s, b)| SymbolPixels { 
            pixels: &CArray::new(s), 
            bound: &ReturnBound::new(b)
        }).collect::<Vec<SymbolPixels>>())
    }).collect::<Vec<CArray<SymbolPixels>>>())
}


#[derive(Clone, Debug)]
pub struct Bound<T: Ord + Copy> {
    pub min: Point<T>,
    pub max: Point<T>,
}
// This impl is just for easy testing here
impl Bound<usize> {
    fn new() -> Bound<usize> {
        Bound { min: Point { x:1, y:2 }, max: Point { x:5, y:9 } }
    }
}

#[repr(C)]
#[derive(Debug)]
pub struct CArray<T> {
    pub ptr: *const T,
    pub size: usize,
}
impl<T> CArray<T> {
    pub fn new(v: Vec<T>) -> Self {
        let (ptr,size,_) = v.into_raw_parts();
        CArray {
            ptr: ptr,
            size: size,
        }
    }
}

#[repr(C)]
#[derive(Debug)]
pub struct SymbolPixels {
    pub pixels: *const CArray<u8>,
    pub bound: *const ReturnBound
}

#[repr(C)]
#[derive(Debug)]
pub struct ReturnBound {
    pub min: *const Point<u32>,
    pub max: *const Point<u32>,
}
impl ReturnBound {
    pub fn new(b: Bound<usize>) -> Self {
        ReturnBound { 
            min: &Point { x: b.min.x as u32, y: b.min.y as u32 },
            max: &Point { x: b.max.x as u32, y: b.max.y as u32 }
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Point<T: Ord> {
    pub x: T,
    pub y: T,
}

I'm not sure what other information to give here, so if I'm missing anything please drop a comment.

Would really appreciate any help on this topic.

解决方案

When I run the code using 'cargo run', I'm getting the output:

segment: CArray { ptr: 0x55ab1e3fac80, size: 2 }
lines: [CArray { ptr: 0x55ab1e3facd0, size: 1 }, CArray { ptr: 0x55ab1e3fad20, size: 3 }]
ReturnBound { min: 0x8, max: 0x55ab1d4a554e }
Segmentation fault

where ReturnBound.min should be a pointer, but 0x8 is not a valid pointer. The println that's pointed out by the comment is trying to dereference this invalid pointer, which is causing the segfault.

It looks like the backing memory isn't being allocated, and so the pointers you're storing in the data structs are pointing to memory that is freed before you're accessing it. This is a danger when dealing with raw pointers because they are unchecked, and you wont get a compiler error

As an example, in:

pub fn new(b: Bound<usize>) -> Self {
    ReturnBound { 
        min: &Point { x: b.min.x as u32, y: b.min.y as u32 },
        max: &Point { x: b.max.x as u32, y: b.max.y as u32 }
    }
} <-- the data behind the two Point structs will be dropped here

The Point is allocated on the stack, and a reference to that data is stored in the min and max values of the ReturnBound. The ReturnBound is then returned, but the data allocated for the Point structs will be dropped at the end of the new() function, and that memory will then be used by other things. The same goes for the Vec data that you're storing in the CArray structs. In order to get around this, you could use libc::malloc (since this is interfacing to C), or you could possible use Box::leak() or some of the other tricks to prevent allocated memory from being dropped.

You might also be able to refactor this to avoid the need for pointers, but that depends on what exactly you're interfacing to

这篇关于Rust C 互操作分段错误(斜杠 STATUS_ACCESS_VIOLATION)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆