Reputation: 3243
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.
Upvotes: 0
Views: 802
Reputation: 1660
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
Upvotes: 1