#status access violation runtime error

19 messages · Page 1 of 1 (latest)

limpid citrus
#

Hello,

I'm trying to write a rust binding to a C library, and I'm getting exit code: 0xc0000005, STATUS_ACCESS_VIOLATION. I know this is a memory issue, but I can't spot where the problem is coming from. I've commented out all the code which I think might be causing the problem but... no luck.

the full code is here: https://github.com/ua-kxie/paprika/tree/dev. But I'll explain what I believe the code to be doing below:

my main function. note the comments annotating the problem lines

fn main() {
    let ngspice = paprika::NgSpice::new();  // initialize library
    let mut manager = NgSpiceManager::new();  // initialize manager

    let a = paprika::PkVecinfoall{
        count: 1,
    };
    manager.test(a);  // this is fine

    ngspice.init(&manager);  // registers manager (callbacks) to the library
    ngspice.command("source dcop1.cir");  // this is not fine
}

manager.test(a) is defined as follows:

    fn test(&mut self, a: paprika::PkVecinfoall) {
        self.vec_pkvecinfoall.push(a);
    }

ngspice.command("source dcop1.cir") sends a command to the external library, which in turn calls the following callback:

    fn cb_send_init_data(&mut self, pkvecinfoall: paprika::PkVecinfoall, count: i32, id: i32) {
        let a = paprika::PkVecinfoall{
            count: 1,
        };
        println!("{:p}", &a);
        self.vec_pkvecinfoall.push(a);  // if I comment out this line, the program exits normally
    }

normally it would push pkvecinfoall into the vector, but trying to pinpoint the problem I've created a stripped down version of the structure locally without unsafe{}. As best as I can tell the program crashes when it tries to push a into the vector.

#

cb_send_init_data is called from callback wrapper which contains an unsafe block. This wrapper function is called directly from the C lib.

extern fn cbw_send_init_data<T>(pvecinfoall: *const NgVecinfoall, count: c_int, id: c_int, user: *const c_void) -> c_int where T: NgSpiceManager{
    unsafe {
        let pkvecinfoall = PkVecinfoall{
            count: 1,
        };
        <T as NgSpiceManager>::cb_send_init_data(&mut *(user as *mut T), pkvecinfoall, count, id);
    }
    return 0;
}
#

additional info:
code compiles and runs until STATUS_ACCESS_VIOLATION with cargo run. it exits correctly running on lldb debugger in vscode (although I can tell from the variables viewer that its not running correctly - the .push(a) from the callback seems not to have run)

#

there's also a very similar piece of code which runs correctly as far as I can tell:

    fn cb_send_data(&mut self, pkvecvaluesall: paprika::PkVecvaluesall, count: i32, id: i32) {
self.vec_pkvecvalsall.push(pkvecvaluesall);
    }

The struct is a bit different, but thats all. Maybe there's some small trivial difference I've missed?

wet palm
#

in command, are you sending CString directly to C?

limpid citrus
#
    pub fn command(&self, command: &str) -> bool {
        (self.api.command)(std::ffi::CString::new(command).unwrap())
    }

I guess so

wet palm
#

CString doesn't have ffi-safe layout

#

you should pass raw pointer to u8/i8 instead

#

also, be careful not to accidently drop CString before passing the pointer to function

#

e.g. let ptr = CString::new(s).unwrap().as_ptr(); is bad, because pointers will point to deallocated memory after the assignment. you need to assign CString itself to variable, then get pointer from it

limpid citrus
#
    pub fn command(&self, command: &str) -> bool {
        let cmdstr = std::ffi::CString::new(command).unwrap().into_raw();
        let ret = (self.api.command)(cmdstr);
        let handle = unsafe{std::ffi::CString::from_raw(cmdstr);};
        ret
    }

something like this? I remember seeing into_raw/from_raw from the docs now

wet palm
#

you don't need to convert it back-and-forth to pointer: ```rust
pub fn command(&self, command: &str) -> bool {
let cmdstr = std::ffi::CString::new(command).unwrap();
let ret = (self.api.command)(cmdstr.as_ptr());
ret
}

#

also, i think that should be extern fn() pointer. without the extern, it will assume it uses rust's internal call convention

limpid citrus
#

ah ok, I understand the .as_ptr thing now

#

ahh rats I forget that too

#

thanks so much for your help, I'll let you know in a bit if it works

limpid citrus
#

ok, so it's still not working ferrisClueless

#

just to clarify, where I forgot to put extern was in the fn type def type NgSpiceCommand = extern fn(*const c_char) -> bool;. just to be sure I added extern to pub fn command as well but it didnt make a difference

limpid citrus
#

for posterity, this issue is fixed now. The mistake I made was in the extern function declaration, where I put into an additional input argument. As a result the caller pointer I was interpreting became garbage.