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

improper_ctypes lint should check types referenced in C ABI function definitions #19834

Closed
tomjakubowski opened this issue Dec 14, 2014 · 22 comments
Assignees
Labels
A-FFI Area: Foreign function interface (FFI) A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-bug Category: This is a bug. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.

Comments

@tomjakubowski
Copy link
Contributor

For example, this compiles with no warnings or errors:

#![feature(lang_items)]
#![crate_type="staticlib"]
#![allow(missing_copy_implementations)]
#![deny(improper_ctypes)]
#![no_std]

pub struct Blah {
    pub x: i32
}

#[no_mangle]
pub extern "C" fn yikes(b: Blah) -> i32 {
    b.x
}

#[lang = "copy"] trait Copy {}
#[lang = "sized"] trait Sized {}
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }

Even though Blah lacks the #[repr(C)] attribute.

@jdm jdm added A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. I-wrong labels Dec 14, 2014
@tomjakubowski
Copy link
Contributor Author

To clarify: foreign functions (fn inside an extern block) are checked. Regular functions with a C ABI are not.

tomjakubowski added a commit to tomjakubowski/rust that referenced this issue Dec 29, 2014
It now checks extern fns in addition to foreign fns and checks `DefStruct`
defs as well.

There is room for improvement: for example, I believe that pointers
should be safe in extern fn signatures regardless of the pointee's
representation.

The `FIXME` on `rust_begin_unwind_fmt` is because I don't think it
should be, or needs to be, C ABI (cc @eddyb) and it takes a problematic
`fmt::Arguments` structure by value.

Fix rust-lang#20098, fix rust-lang#19834
@DanielKeep
Copy link
Contributor

This is still an issue. It just came up in a Stack Overflow question, with something roughly equivalent to this:

#[no_mangle]
pub extern fn print(s: String) {
    println!("{}", s);
}

This produces no warnings. For context, the user was trying to bind to this function from C#, which went about as well as you'd expect.

@arielb1
Copy link
Contributor

arielb1 commented Sep 28, 2015

@DanielKeep

Its even worse here - this is an extern "Rust" function, and these have their own ABI.

@TimNN
Copy link
Contributor

TimNN commented Sep 28, 2015

@arielb1 https://github.com/rust-lang/rust/blob/master/src/doc/trpl/ffi.md#calling-rust-code-from-c seems to imply that a pub extern fn something() {} uses the C calling convention by default.

Edit: and the reference seems to agree: https://github.com/rust-lang/rust/blob/master/src/doc/reference.md#extern-functions

@shepmaster
Copy link
Member

Continues to be a problem for people new to FFI:

#[no_mangle]
pub extern "C" fn hello(cs: CString) -> CString {
  // ...
}

The OP states:

Well, my reasoning is that if I can pass int32 back and forth + if CString can easily go in and out (commented code), then it should work.

If this generated a warning, there's more of a chance people might not do this.

@shepmaster
Copy link
Member

Should we start some kind of RFC for this?

/cc @nagisa from comment

@nagisa
Copy link
Member

nagisa commented Sep 16, 2016

See also #36464. A comment in that issue explains why extern fn stuff(with: NonCTypes) is completely valid code.

@tomjakubowski
Copy link
Contributor Author

What's the use case for that example? If it is a niche case, I think the cost of adding a #[allow(improper_ctypes)] for the rare case is worth catching bugs for the more common case: a Rust library which also exposes a C-compatible interface.

@nagisa
Copy link
Member

nagisa commented Sep 27, 2016

Lints in rustc shouldn’t have any false positives even in niche cases. Clippy has all the false positives you might want.

@retep998
Copy link
Member

retep998 commented May 17, 2017

I'm a firm believer that the lint should fire in this case. That niche usage does not mean we should let this massive footgun go by without any warning at all. In fact, if you so believe in that niche usage, find some examples in real crates that do that using non-repr(C) types.

@Mark-Simulacrum Mark-Simulacrum added C-bug Category: This is a bug. and removed I-wrong labels Jul 22, 2017
@kennytm kennytm added the A-FFI Area: Foreign function interface (FFI) label Feb 4, 2018
@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Feb 21, 2018

Would it be more acceptable for the lint to only apply to #[no_mangle] extern functions instead of all extern ones? That would cover the most common way to expose Rust functions to C (tough it would miss functions that are called from C through function pointers), while not firing on the hypothetical Rust function that has a C calling convention but is only called from Rust.

@retep998
Copy link
Member

retep998 commented Feb 21, 2018

@rkruppe In my opinion function pointers passed to C are much more common than Rust code calling other Rust functions that happen to use the C calling convention.

@hanna-kruppe
Copy link
Contributor

I agree. But #[no_mangle] extern functions are common enough that it's worthwhile warning about non-repr(C) types in them, even if the "Rust calling Rust with C ABI" objection rules out warning about all extern fns with non-repr(C) types.

Of course, if that objection is overruled, that would be great as well, but if not, let's try to at least catch some cases.

@shepmaster
Copy link
Member

Lints in rustc shouldn’t have any false positives even in niche cases.

I agree with the spirit of this statement, but not the absolute degree. Lints that are built into rustc and enabled by default should have a very low false positive rate, but not zero.

Discussing with @nagisa, I think that our understanding of the role of lints has changed a little bit in the last 2+ years:

nagisa: we lint against extern "C" { fn foo(x: String); }
nagisa: which makes sense because we are about 100% sure this function is being imported across FFI.
nagisa: We should perhaps lint against defining functions like [extern "C" fn foo(_: String) {}], with perhaps a lint that is different from the current one.
[...]
nagisa: Right, because extern "C" { fn ... } is also not quite 100% false-positive free.

I'd like to propose that we take the following course of action:

  1. Add a new lint that warns about using non -repr(C) structs in any extern "C" function defined in Rust code.
  2. Run it through crater.
  3. Based on crater feedback, maybe incorporate the no_mangle restriction.

Then, bikesheddable steps:

  1. Rename the current lint and put in a lint group as the current name for both lints.

@shepmaster
Copy link
Member

I've never done a lint before, but I might be able to try and do this one, if someone had any mentoring steps to provide. (or if you just do it, that's cool too!)

@varkor
Copy link
Member

varkor commented Feb 23, 2019

@shepmaster:
The code for linting for improper_ctypes is found in src/librustc_lint/types.rs. Specifically, the part here is the part that needs changing:
https://github.com/rust-lang/rust/blob/a9b907b5fae0c1cbf3447091746886b41ea374a5/src/librustc_lint/types.rs#L804-L820
The LateLintPass implementation has a check_foreign_item definition, which will end up calling the functions triggering the lint. However, extern "C" fn doesn't count as a foreign item (because it's not in an extern block).

You'll want to implement the check_fn method for ImproperCTypes. You should then be able to do something very similar to check_foreign_item, which should end up triggering the lints. (The check_type_for_ffi_and_report_errors function is the one that triggers the lints, so you can trace back from there if this doesn't work straight off.)

hir::FnHeader's abi field might be helpful:
https://github.com/rust-lang/rust/blob/a9b907b5fae0c1cbf3447091746886b41ea374a5/src/librustc/hir/mod.rs#L2236-L2242

@varkor varkor added E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. and removed E-needs-mentor labels Feb 23, 2019
@varkor
Copy link
Member

varkor commented Jun 3, 2019

@shepmaster: have you had a chance to try implementing this yet?

@shepmaster
Copy link
Member

I have not... it's just been sitting in a tab, waiting, lonely. If someone wants to steal it, please feel free!

@davidtwco
Copy link
Member

@varkor @shepmaster I had a go at implementing the instructions in issue-19834-improper-ctypes-in-extern-C-fn, but quickly ran into problems:

  • I had to #[allow(improper_ctypes)] on a bunch of places within the libproc_macro, libstd, libpanic_abort and libpanic_unwind.
  • typeck currently only prohibits generics on foreign functions, so by allowing improper_ctypes on regular functions (that have extern "C"), you quickly run into something like ICE: src/librustc_lint/types.rs:858: unexpected type in foreign function: T #65035 - I added basic support for generics to the improper_ctypes lint so I could #[allow(improper_ctypes)] away the remaining errors.

The branch doesn't fail any tests at the moment. Do either of you think it's worth continuing with this and opening a PR?

@hanna-kruppe
Copy link
Contributor

Neither of these problems is a showstopper in my opinion, please continue and open a PR. Also, I have comments about both of the issues you identified and how you addressed them but it would probably best to discuss that on the PR.

@davidtwco
Copy link
Member

@rkruppe opened as #65134

bors added a commit that referenced this issue Nov 4, 2019
…n-C-fn, r=rkruppe

improper_ctypes: `extern "C"` fns

cc #19834. Fixes #65867.

This pull request implements the change [described in this comment](#19834 (comment)).

cc @rkruppe @varkor @shepmaster
bors added a commit that referenced this issue Nov 6, 2019
…n-C-fn, r=rkruppe

improper_ctypes: `extern "C"` fns

cc #19834. Fixes #65867.

This pull request implements the change [described in this comment](#19834 (comment)).

cc @rkruppe @varkor @shepmaster
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Jun 23, 2020
…es-declarations, r=lcnr,varkor

`improper_ctypes_definitions` lint

Addresses rust-lang#19834, rust-lang#66220, and rust-lang#66373.

This PR takes another attempt at rust-lang#65134 (reverted in rust-lang#66378). Instead of modifying the existing `improper_ctypes` lint to consider `extern "C" fn` definitions in addition to `extern "C" {}` declarations, this PR adds a new lint - `improper_ctypes_definitions` - which only applies to `extern "C" fn` definitions.

In addition, the `improper_ctype_definitions` lint differs from `improper_ctypes` by considering `*T` and `&T` (where `T: Sized`) FFI-safe (addressing rust-lang#66220).

There wasn't a clear consensus in rust-lang#66220 (where the issues with rust-lang#65134 were primarily discussed) on the approach to take, but there has [been some discussion in Zulip](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/.2366220.20improper_ctypes.20definitions.20vs.20declarations/near/198903086). I fully expect that we'll want to iterate on this before landing.

cc @varkor + @shepmaster (from rust-lang#19834) @hanna-kruppe (active in discussing rust-lang#66220), @SimonSapin (rust-lang#65134 caused problems for Servo, want to make sure that this PR doesn't)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Jun 25, 2020
…es-declarations, r=lcnr,varkor

`improper_ctypes_definitions` lint

Addresses rust-lang#19834, rust-lang#66220, and rust-lang#66373.

This PR takes another attempt at rust-lang#65134 (reverted in rust-lang#66378). Instead of modifying the existing `improper_ctypes` lint to consider `extern "C" fn` definitions in addition to `extern "C" {}` declarations, this PR adds a new lint - `improper_ctypes_definitions` - which only applies to `extern "C" fn` definitions.

In addition, the `improper_ctype_definitions` lint differs from `improper_ctypes` by considering `*T` and `&T` (where `T: Sized`) FFI-safe (addressing rust-lang#66220).

There wasn't a clear consensus in rust-lang#66220 (where the issues with rust-lang#65134 were primarily discussed) on the approach to take, but there has [been some discussion in Zulip](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/.2366220.20improper_ctypes.20definitions.20vs.20declarations/near/198903086). I fully expect that we'll want to iterate on this before landing.

cc @varkor + @shepmaster (from rust-lang#19834) @hanna-kruppe (active in discussing rust-lang#66220), @SimonSapin (rust-lang#65134 caused problems for Servo, want to make sure that this PR doesn't)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Jun 25, 2020
…es-declarations, r=lcnr,varkor

`improper_ctypes_definitions` lint

Addresses rust-lang#19834, rust-lang#66220, and rust-lang#66373.

This PR takes another attempt at rust-lang#65134 (reverted in rust-lang#66378). Instead of modifying the existing `improper_ctypes` lint to consider `extern "C" fn` definitions in addition to `extern "C" {}` declarations, this PR adds a new lint - `improper_ctypes_definitions` - which only applies to `extern "C" fn` definitions.

In addition, the `improper_ctype_definitions` lint differs from `improper_ctypes` by considering `*T` and `&T` (where `T: Sized`) FFI-safe (addressing rust-lang#66220).

There wasn't a clear consensus in rust-lang#66220 (where the issues with rust-lang#65134 were primarily discussed) on the approach to take, but there has [been some discussion in Zulip](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/.2366220.20improper_ctypes.20definitions.20vs.20declarations/near/198903086). I fully expect that we'll want to iterate on this before landing.

cc @varkor + @shepmaster (from rust-lang#19834) @hanna-kruppe (active in discussing rust-lang#66220), @SimonSapin (rust-lang#65134 caused problems for Servo, want to make sure that this PR doesn't)
@davidtwco
Copy link
Member

Closing, extern fns are handled in #72700.

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) A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-bug Category: This is a bug. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
Projects
None yet
Development

Successfully merging a pull request may close this issue.