Skip to content

The improper_ctypes lint is very weak #36464

Description

@amluto

This code compiles without warnings. I think that the improper_ctypes lint should catch both of the extern declarations.

#[allow(dead_code)]

#[repr(C)]
enum NotSoGood {
    A,
    B(i32),
}

extern "C" {
    fn foo(a: [u8; 16]);
    fn bar(a: NotSoGood);
}

#[no_mangle]
pub extern fn test() {
    unsafe {
        foo([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
        bar(NotSoGood::B(0));
    }
}

The array case (foo) is particularly nasty. On brief, insufficiently careful inspection, it looks like it matches:

void foo(uint8_t a[16]);

But it actually doesn't match that and instead seems to try to pass the array in packed form in xmm0 on x86_64. This is extra nasty because I think I've caught rust-bindgen generating bindings like this.

Activity

  1. added
    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
    on Sep 14, 2016
  2. nagisa commented on Sep 14, 2016

    @nagisa
    Member

    I think that the improper_ctypes lint should catch both of the extern declarations.

    The enum should be caught, but the extern declarations should not, because code like following is entirely valid:

    // in rust
    #[link_name="bar"]
    pub extern fn stuff(a: NotSoGood) { ... }
    
    // somewhere else, still in rust
    extern { fn bar(a: NotSoGood); }
    

    To explain, making functions be called using non-Rust calling convention does not invalidate passing rust data types back into Rust for functions implemented with non-Rust calling convention.

  3. retep998 commented on Sep 14, 2016

    @retep998
    Contributor

    void foo(uint8_t a[16]); in C/C++ does not actually mean what it looks like. It actually means void foo(uint8_t* a);. So the fault lies with C/C++ being confusing in this case.

  4. amluto commented on Sep 14, 2016

    @amluto
    Author

    @nagisa: I agree that your code is valid, but you typed extern and not extern "C". There's a C ABI, and that ABI, as a practical matter, does not include passing arrays by value, since you can't pass arrays by value in C.

    Oddly, the actual x86_64 psABI does seem to think that arrays can be passed:

    The classification of aggregate (structures and arrays) and union types works as follows:  ...
    

    and I haven't found an example of Rust deviating from the psABI document, but I still suspect that almost (or maybe even exactly) 100% of cases where someone has actually attempted to pass an array into an extern "C" function are incorrect.

  5. nagisa commented on Sep 14, 2016

    @nagisa
    Member

    extern ≡ extern "C"

  6. amluto commented on Sep 14, 2016

    @amluto
    Author
  7. RalfJung commented on Jan 22, 2019

    @RalfJung
    Member

    @nagisa (I know I'm a bit late, what can you do) In your example, rustc actually will warn on the extern { fn } side of things. How is the trade-off different on the extern fn side?

  8. nagisa commented on Jan 22, 2019

    @nagisa
    Member

    The #[repr(C)] enum now has a formal definition, so this issue looks like it can be closed.

    In your example, rustc actually will warn on the extern { fn } side of things.

    Will it? All types in the example are repr(C), so there is nothing to warn about on either of the function definition or declaration.

  9. nagisa commented on Jan 22, 2019

    @nagisa
    Member

    Ah I guess there is still the case with sized arrays.

  10. reopened this on Jan 22, 2019
  11. added
    A-FFIArea: Foreign function interface (FFI)
    T-langRelevant to the language team
    on Nov 29, 2019
  12. est31 commented on Oct 28, 2020

    @est31
    Member

    Ah I guess there is still the case with sized arrays.

    Did #66305 fix this case? Can it be closed now?

  13. mversic commented on Aug 16, 2022

    @mversic

    The following code doesn't trigger improper_ctypes lint. Should it not?

    #[repr(transparent)]
    pub struct Array([u64; 2]);
    #[no_mangle]
    pub unsafe extern "C" fn function(arg: Array) -> Array {
        arg
    }

    Referred to in this issue

  14. mversic commented on Aug 16, 2022

    @mversic

    I guess the question above is: do arrays and structs follow the same calling convention? If no, then improper_ctypes lint should be improved to warn in the previous example

  15. Gankra commented on Aug 16, 2022

    @Gankra
    Contributor

    I believe this the ctypes lint isn't working correctly on that transparent array input, yes.

  16. workingjubilee commented on Oct 19, 2023

    @workingjubilee
    Member

    I have opened #116959 to cover the second case mentioned, because it is separate-ish. Closing.

  17. added
    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
    and removed
    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
    on Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-FFIArea: Foreign function interface (FFI)A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-enhancementCategory: An issue proposing an enhancement or a PR with one.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions