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

Can't define unsafe extern "C" fn #10025

Closed
kmcallister opened this issue Oct 23, 2013 · 7 comments · Fixed by #14005
Closed

Can't define unsafe extern "C" fn #10025

kmcallister opened this issue Oct 23, 2013 · 7 comments · Fixed by #14005
Milestone

Comments

@kmcallister
Copy link
Contributor

Both

unsafe extern "C" fn foobar() {
    *(0 as *mut u8) = 0;
}

and

extern "C" unsafe fn foobar() {
    *(0 as *mut u8) = 0;
}

give me a syntax error. But it's not the case that an extern "C" function is automatically unsafe. Dropping the unsafe keyword gives me

error: dereference of unsafe pointer requires unsafe function or block

And this code compiles and segfaults:

extern "C" fn foobar() {
    unsafe {
        *(0 as *mut u8) = 0;
    }
}

#[fixed_stack_segment]
fn main() {
    foobar()
}

So I can't declare an extern "C" function (for external linkage, with #[no_mangle]) without also allowing safe Rust code to call that function, breaking memory safety.

@alexcrichton
Copy link
Member

I don't quite understand how this breaks memory safety, you explicitly have an unsafe block meaning that it's up to you to guarantee safety (which doesn't happen in this code). It also makes sense to me that you can write an extern "C" function which is indeed safe, you don't necessarily have to force the function to always be unsafe.

It seems like a bug that you can't declare an extern unsafe function, but other than that I'm not sure that there's a problem here.

@kmcallister
Copy link
Contributor Author

Right, the bug is that I can't declare a function to be called from C, without allowing safe Rust code to call it too.

The memory unsafety results from the workaround of declaring a safe extern "C" function and using an unsafe block. I can't satisfy the proof obligation of the unsafe block, but there is no other way to write the function.

I agree that you should be able to declare safe extern "C" functions too. But if all extern "C" functions were automatically unsafe then it would at least solve my problem, which is why I included an example to show that isn't the case.

(This came up when I was defining memset in Rust, to satisfy memset calls emitted by rustc/LLVM, in a no-libc environment. I don't want to let safe Rust code call that memset too.)

@brson
Copy link
Contributor

brson commented Oct 24, 2013

There seems to be a discrepancy between extern "C" fns defined in Rust and those that bind to native functions. The latter are supposed to be unsafe by default, and since they're the same type, so should the former.

@brson
Copy link
Contributor

brson commented Oct 24, 2013

Nominating.

@nikomatsakis
Copy link
Contributor

Hmm. It seems to me that for maximum precision, we would set it up like so:

  1. functions declared in an extern block should have the type unsafe extern "C" fn().
  2. Rust functions declared with extern would have the type extern "C" fn().
  3. Rust functions can be declared unsafe as well as extern.
  4. Calling a bare fn is unsafe iff it is unsafe, and does not consider the ABI.

The only downside of this I can see is that the type of C functions is cumbersome. Note that the type system is mostly setup this way (that is, types like unsafe extern "C" fn exist) so it shouldn't be too hard to get it into this state.

@catamorphism
Copy link
Contributor

1.0, backwards-compat

@lilyball
Copy link
Contributor

lilyball commented Dec 4, 2013

I just ran into this. I agree with @nikomatsakis's analysis. The type of C functions may be cumbersome, but nobody declaring an FFI function would have to type unsafe extern "C" fn(). The only time anyone should have to type that is when they're implementing an extern "C" function Rust that needs to be unsafe.

alexcrichton added a commit to alexcrichton/rust that referenced this issue May 7, 2014
Previously, the parser would not allow you to simultaneously implement a
function with a different abi as well as being unsafe at the same time. This
extends the parser to allow functions of the form:

    unsafe extern fn foo() {
        // ...
    }

The closure type grammar was also changed to reflect this reversal, types
previously written as "extern unsafe fn()" must now be written as
"unsafe extern fn()". The parser currently has a hack which allows the old
style, but this will go away once a snapshot has landed.

Closes rust-lang#10025

[breaking-change]
bors added a commit that referenced this issue May 7, 2014
Previously, the parser would not allow you to simultaneously implement a
function with a different abi as well as being unsafe at the same time. This
extends the parser to allow functions of the form:

    unsafe extern fn foo() {
        // ...
    }

The closure type grammar was also changed to reflect this reversal, types
previously written as "extern unsafe fn()" must now be written as
"unsafe extern fn()". The parser currently has a hack which allows the old
style, but this will go away once a snapshot has landed.

Closes #10025

[breaking-change]
michaelwoerister pushed a commit to michaelwoerister/rust that referenced this issue Jun 5, 2014
Previously, the parser would not allow you to simultaneously implement a
function with a different abi as well as being unsafe at the same time. This
extends the parser to allow functions of the form:

    unsafe extern fn foo() {
        // ...
    }

The closure type grammar was also changed to reflect this reversal, types
previously written as "extern unsafe fn()" must now be written as
"unsafe extern fn()". The parser currently has a hack which allows the old
style, but this will go away once a snapshot has landed.

Closes rust-lang#10025

[breaking-change]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants