Problem with calling C code


#1

Hello,
I am currently working on Posix Message Queue support for nix-rust. Some basic support is already there but now I have a strange bug which results in Signal 4 (illegal instruction) but goes away when some print statements are added. Strange. I assume I somehow messed up the unsafe code.

I have this C interface:

pub fn mq_getattr(mqdes: MQd, attr: *mut MqAttr) -> c_int;

and this struct:

 #[repr(C)]
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MqAttr {
    pub mq_flags: c_long,
    pub mq_maxmsg: c_long,
    pub mq_msgsize: c_long,
    pub mq_curmsgs: c_long,
}

This is my Rust function:

pub fn mq_getattr(mqdes: MQd) -> Result<MqAttr> {

    //println!("");  //If I remove the "//" and print the empty string the program does not crash
    let mut attr = MqAttr { mq_flags: 0, mq_maxmsg: 0, mq_msgsize: 0, mq_curmsgs: 0 };
    let res = unsafe { ffi::mq_getattr(mqdes, &mut attr as *mut MqAttr ) };
    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }
    Ok(attr)
}

and that is my test:

fn mq_set_and_get_attr() {

    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr { mq_flags: 0, mq_maxmsg: 10, mq_msgsize: MSG_SIZE, mq_curmsgs: 0 };
    let mq_name = &CString::new(b"/attr_test".as_ref()).unwrap();
    let mqd = mq_open(mq_name, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, &initial_attr).unwrap();
    let read_attr = mq_getattr(mqd);
    mq_close(mqd).unwrap();
}

I am sure that I have done something wrong with passing the mutable pointer to the C function. But I cannot figure it out.

What am I doing wrong here?

Markus


#2

If I remove the “//” and print the empty string the program does not crash

I’d wager you are running into undefined behavior here

You are passing

let mq_name = &CString::new(b"/attr_test".as_ref()).unwrap();

to the mq_open function. Your error might be in the mq_open function. There are very common pitfalls with passing CString objects to C. Please also show us the mq_open function.


#3

Thanks for the reply.

Here is the mq_open function:

 #[inline]
pub fn mq_open(name: &CString, oflag: MQ_OFlag, mode: Mode, attr: &MqAttr) -> Result<MQd> {
    let res = unsafe { ffi::mq_open(name.as_ptr(), oflag.bits(), mode.bits() as mode_t, attr as *const MqAttr) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res)
}

and here the C interface:

    pub fn mq_open(name: *const c_char, oflag: c_int, mode: mode_t, attr: *const MqAttr) -> MQd;

It can also be found here:

(The other code I am having problems with is not yet on github)


#4

are you sure the c-function’s signature matches your ffi-function’s signature?

I can’t see anything wrong on the rust-side. But the c-side might introduce anything… Maybe you are using the api in a wrong way or the signatures don’t match?


#5

For me the fff interfaction looks ok:

mp_open:

pub type MQd = c_int;
pub fn mq_open(name: *const c_char, oflag: c_int, mode: mode_t, attr: *const MqAttr) -> MQd;

and in C:

 mqd_t mq_open(const char *name, int oflag);

for the mq_getattr:

pub fn mq_getattr(mqdes: MQd, attr: *mut MqAttr) -> c_int;

C:

 int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

The C functions expert an mutable pointer to a MqAttr struct. I thought I messed this up somehow but I cannot see any error.

Markus


#6

There’s your source of undefined behavior:

vs

There are attr and mode parameters in the rust-ffi function signature but not in the c-function signature. This will definitely maybe probably surely never even possibly cause nasal demons


#7

Sorry, I posted the wrong C function. It is overloaded.
There is also

mqd_t mq_open(const char *name, int oflag, mode_t mode,
                     struct mq_attr *attr);

that should match the Rust function.

Or could that be the problem? Maybe Rust is trying the call the other, shorter C function I posted first.


#8

uhm… C does not have function overloading… are you sure that is C and not C++? (is there an extern "C" {} block around your functions in case you are compiling with a c++ compiler)


#9

Thanks for the hint. I will look closer at the mq_open definitions and how I did call it.
Somehow something got messed up :smile:


#10

mq_open uses varargs, not overloading, to support two different signatures.

mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode,
              struct mq_attr *attr);

The actual signature is (mqueue.h):

extern mqd_t mq_open (const char *__name, int __oflag, ...)
  __THROW __nonnull ((1));

#11

ah well… that definitely makes this undefined behavior due to a wrong ffi-binding.

Theres the va_list crate, but I think it only supports implementing a function that takes varargs, not calling one.


#12

Rust does support something like this:

 pub fn mq_open(name: *const c_char, oflag: c_int, ...) -> MQd;

at least this compiles. But the problem is still there :smile:
I have to dig deeper into calling vararg C functions.


#13

I finally found the problem. the mq_attr struct on linux has an additional member (a long[4] array) that is not documentet in the man pages. The Rust bidings were broken and that messed up the stack.
Yesterday evening I added it as a private member and create the Rust struct with “new” function that fills the private long array. Now everything seems to work.
At least from Rust using posix message queues should soon be safe. Pull request will follow.
Thanks to everyone for your help and suggestions.


#14

Could you please share how you are calling the varargs function? Did the ... work?

I would really appreciate if you told us.


#15

I’ll post the code on github this weekend.

What does seem to work is this:

pub fn mq_open(name: *const c_char, oflag: c_int, ...) -> MQd;

and calling it like this:

let res = unsafe { ffi::mq_open(name.as_ptr(), oflag.bits(), mode.bits() as mode_t, attr as *const MqAttr) };

#16

Thanks a lot!

Is it possible to omit the last two parameters (i.e. the varargs)?


#17

when I call it like this is compiles and seems to work:

let res2 = unsafe { ffi::mq_open(name.as_ptr(), oflag.bits()) };

The varargs are also used in the Rust standard lib. I found it here:

https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/c.rs
(line 126)

pub fn ioctl(fd: libc::c_int, req: libc::c_ulong, ...) -> libc::c_int;

I don’t know what the Rust compiler is doing here but I trust it :slight_smile:


#18

Bad Idea, never trust a compiler.… :wink: