Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how Rust array/slice types are translated for extern "C" functions. #30382

Closed
briansmith opened this issue Dec 15, 2015 · 15 comments
Closed
Labels
A-ffi Area: Foreign Function Interface (FFI)

Comments

@briansmith
Copy link

Currently I write:

extern {
    fn X25519(out_shared_key: *mut u8/*[32]*/, private_key: *const u8/*[32]*/,
              peers_public_value: *const u8/*[32]*/) -> libc::c_int;
}

I would like to write:

extern {
    fn X25519(out_shared_key: &mut [u8;32], private_key: &[u8;32],
              peers_public_value: &[u8;32]) -> libc::c_int;
}

The C declaration is:

int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
           const uint8_t peers_public_value[32]);

which is equivalent to:

int X25519(uint8_t *out_shared_key, const uint8_t *private_key,
           const uint8_t *peers_public_value);

The second form of the Rust ffi declaration is safer because it makes it clear that the function doesn't accept NULL pointers and it also makes it clear how long the input arrays are expected to be. However, the Rust reference doesn't document how it translates a reference to a slice or array to a C type, so using the second form is undefined behavior. I would like the Rust Reference to be amended to make it clear that it will call translate references to arrays/slices by passing the result of as_ptr/as_mut_ptr to the C function.

@Aatch Aatch added A-docs A-ffi Area: Foreign Function Interface (FFI) labels Dec 15, 2015
@Aatch
Copy link
Contributor

Aatch commented Dec 15, 2015

Note that this may require an RFC actually specifying this and related information.

@briansmith
Copy link
Author

Also, more generally, it should be documented that a reference will be translated to a pointer, so that extern "C" functions can use references instead of pointers to denote the non-acceptance of NULLs.

@retep998
Copy link
Member

Pointers to slices (aka &[T]) are fat and thus can't be used for FFI.

Pointers to fixed size arrays (aka &[T; N]) are thin and thus I imagine they should be perfectly okay even if it isn't spelled out in the documentation.

@steveklabnik
Copy link
Member

So, following up: what needs to be done here, exactly, and where should it go? FFI should say that fat pointers aren't usable?

@steveklabnik
Copy link
Member

I am going to give this one a close. I'm not going to do a whole ton of work on the existing FFI docs, focusing on the new one instead, and talking about fat vs thin pointers wrt FFI is a big thing.

@briansmith
Copy link
Author

I am going to give this one a close. I'm not going to do a whole ton of work on the existing FFI docs, focusing on the new one instead, and talking about fat vs thin pointers wrt FFI is a big thing.

I don't understand why we're so eager to close bugs without fixing them. It isn't a matter of "existing FFI docs" vs "new one" but the fact that it is unspecified what happens. Also, it isn't just a documentation bug because the compiler should be aligned with what ever is decided. In particular, is what the Rust compiler currently does right or wrong?

@arielb1
Copy link
Contributor

arielb1 commented Jan 26, 2016

Casting a fat pointer to a thin pointer returns the fat pointer's base. If the pointer was unsized from a thin pointer, that is the original thin pointer. If the pointer was created by from_raw_parts (e.g. from a Vec), it is the base parameter to that method. I was quite sure that this is sufficiently documented.

You are only supposed to use FFI-safe types for FFI. Fat pointers are not FFI-safe types, therefore their representation should not matter. Thin pointers are stored in the obvious way, but I think that is documented.

Slices/arrays are stored in memory in the obvious way (Rust requires that align | size so there is 1 such way). This is documented in the Rustonomicon:

However with the exception of arrays (which are densely packed and in-order), the layout of data is not by default specified in Rust.

@briansmith
Copy link
Author

You are only supposed to use FFI-safe types for FFI. Fat pointers are not FFI-safe types, therefore their representation should not matter.

  1. Where is the set of FFI-safe types documented?
  2. Assuming slices aren't FFI-safe types, why does the compiler let us pass non-FFI-safe types to foreign functions?
  3. Assuming slices aren't FFI-safe types, why not?

@bluss
Copy link
Member

bluss commented Jan 26, 2016

There's a lint for this, at least when declaring extern functions -- example on playpen

<anon>:4:26: 4:30 warning: found Rust slice type in foreign module, consider using a raw pointer instead, #[warn(improper_ctypes)] on by default
<anon>:4     fn other_find(text: &[u8], x: u8) -> Option<usize>;

@arielb1
Copy link
Contributor

arielb1 commented Jan 26, 2016

The representation of types is documented at https://doc.rust-lang.org/nightly/nomicon/data.html. In general, the rule is that types are FFI-safe ~ have a defined representation only if we say that they are that. Reading it again, it seems that we never say that primitives and thin pointers are FFI-safe - cc #31227 !

The currently most significant reason that fat pointers are not FFI safe is because we sometimes pass fat pointers as 2 arguments to a function instead of as a single 2-word argument.
For example, it may be possible to write memcpy as

extern {
    fn memcpy(dst: *mut u8, src: &[u8]); // THIS DOES NOT HAVE TO WORK!
}

@bluss
Copy link
Member

bluss commented Jan 29, 2016

I don't think the array part of this issue was answered. &mut [u8; 32] should be ffi safe, what more can we say?

@ustulation
Copy link

ustulation commented Jan 26, 2017

There's a lint for this, at least when declaring extern functions

I checked that and it works, i was wondering is there anything for the reverse - for e.g.: this.

The other function find does not give any warning. Though I understand that all it says is it follow C calling convention and no name-mangling none of which might warrant the parameter type warning, it is usually intended to be called form another language (with C compatibility) so should generate a warning in this case too ? Passing a callback from C for instance will be Undefined Behavior in this case. I was also stung by this:

#[no_mangle]
pub unsafe extern "C" fn(cb: extern "C" fn([u8; 32])) { // WRONG - should be fn(*const [u8; 32])
    let arr = [8u8; 32];
    cb(arr); // WRONG - should be &arr
}

Of-course looking back it was a stupid mistake - but i had subconciously assumed array would decay into a ptr like in C when callback was invoked. All my tests passed as they were written in rust. Only when interfacing with C and running into occassional seg-faults that i realised the mistake. If there was a warning or some language feature to mark that this function is not only for C ABI compatibility but also strictly for a call from C (like what extern "C" { } block does) it would be helpful.

Same with repr(C) - there is no warning for completely non-C struct having this attribute - i understand the reasoning from the docs for this but again if there is some lint/attribute to mark strict C only conformance then that would be great - as you know it's pita to debug C errors otherwise :(

@jbowens
Copy link

jbowens commented Dec 6, 2017

I don't think the array part of this issue was answered. &mut [u8; 32] should be ffi safe, what more can we say?

Was this ever addressed? I've been searching for some documentation on whether &mut [u8; 32] is ffi safe and came up short.

@bluss
Copy link
Member

bluss commented Dec 6, 2017

@jbowens, maybe we can discuss it on internals.. or go straight to a PR

@chfast
Copy link

chfast commented Nov 2, 2022

Has this been resolved since 5 years ago?
The compiler warns about [u8]

`extern` fn uses type `[u8]`, which is not FFI-safe

but not about the fixed-size array, e.g. [u8;16].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ffi Area: Foreign Function Interface (FFI)
Projects
None yet
Development

No branches or pull requests

9 participants