-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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 ABI compatibility #115476
document ABI compatibility #115476
Conversation
(rustbot has picked a reviewer for you, use r? to override) |
library/std/src/primitive_docs.rs
Outdated
/// - If `<T as Pointee>::Metadata == ()`, then `*const T`, `*mut T`, `&T`, `&mut T`, `Box<T>`, | ||
/// `NonNull<T>` are all ABI-compatible with each other. | ||
/// - Any two `fn()` types with the same `extern` ABI string are ABI-compatible with each other. | ||
/// - Any two 1-ZST types (types with size 0 and alignment 1) are ABI-compatible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8a2a026
to
7983afe
Compare
library/std/src/primitive_docs.rs
Outdated
/// - `i32` is ABI-compatible with `NonZeroI32`, and similar for all other integer types with their | ||
/// matching `NonZero*` type. | ||
/// - If `T` is guaranteed to be subject to the [null pointer | ||
/// optimization](option/index.html#representation), then `T` and `Option<T>` are ABI-compatible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure if this should be documented here, or with the NonZero*
and Option
types, respectively -- or both.
The Option
part is justified by an RFC. The same RFC also mentions Result
but so far we don't document any layout nor ABI guarantees for Result
so I left that out of this PR.
library/std/src/primitive_docs.rs
Outdated
/// - If `T1` and `T2` are ABI-compatible, then two `repr(C)` types that only differ because one | ||
/// field type was changed from `T1` to `T2` are ABI-compatible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This goes quite far and I'm a bit worried we might one day discover that it doesn't actually always work. But so far I haven't been able to find a counterexample... and chances are high that people will be relying on it even if we don't guarantee it, since it seems so natural (and since AFAIK it is true in C, at least in de-facto C -- I hear the standard has some odd caveats involving the name of the struct type).
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems reasonable to restrict some guarantees to just the ABIs that are guaranteed to exist on all platforms, which would include system
.
I'm not clear on why this particular guarantee would ever break, though, and I think you're right that people will rely on ABI compatibility being "recursive" or "covariant" in this sense. So if we do have reason to believe it's not guaranteed, we should definitely say so very loudly somewhere.
This comment has been minimized.
This comment has been minimized.
7983afe
to
ee09160
Compare
This comment has been minimized.
This comment has been minimized.
cddd5b7
to
b1a7983
Compare
5479e4c
to
81dadf6
Compare
library/std/src/primitive_docs.rs
Outdated
/// - If `T1` and `T2` are ABI-compatible, then two `repr(C)` types that only differ because one | ||
/// field type was changed from `T1` to `T2` are ABI-compatible. |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think there are a few style/clarity nits to iron out but i really like this documentation!
01838d4
to
f9bcda9
Compare
The T-lang design team discussed this PR briefly in triage meeting. We agreed with @RalfJung that this is something that needs documentation, and it appears that this text is not adding anything new, but rather documenting the existing rules as they are understood. Therefore, we are happy to go ahead with a T-lang FCP. In addition, however, we thought that this seemed like it may overlap with the expertise of T-opsem, and therefore in addition to the T-lang FCP, we also are nominating this for T-opsem to look at to ensure that they have a chance to sign off on the text as written here. @rfcbot fcp merge |
Team member @pnkfelix has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
Did you mean to include T-opsem in the FCP? |
No, I did not want to do that yet. I wanted to instead nominate this for T-opsem discussion and let them decide whether they need an FCP here. |
@rust-lang/lang FCP finished 3 weeks ago. Would be nice if someone could r+ :) |
@@ -1506,6 +1506,110 @@ mod prim_ref {} | |||
/// Note that all of this is not portable to platforms where function pointers and data pointers | |||
/// have different sizes. | |||
/// | |||
/// ### ABI compatibility | |||
/// | |||
/// Generally, when a function is declared with one signature and called via a function pointer with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't just about function pointers. If a function is declared external "ABI"
in Rust then the called function must have that ABI. Or if the function is declared just extern
or extern "C"
then it must have the default ABI (C compilers let you override the ABI of a function, or the function may be written in assembly language specifically for one calling convention).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do we document and guarantee what one has to do when linking multiple Rust objects together? Do we even support that with extern "Rust"
functions?
We have to put these docs somewhere, and function pointers are the only way to trigger these issues inside the language (without exotic things such as dlopen
or manually linking things together), so I figured this would make sense. If you can think of a better place where we can put this, I can move it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand from seeing other issues why you see "inside the language" as a useful initial scope because that's what seems to matter for the SIMD stuff. I don't object to that.
IME people are much more likely to run into ABI issues in cross-language situations since there are no guardrails at all, so I hope we at least are open to solving the issue for cross-language cases too. Perhaps that means adding similar language to the documentation of extern
and then factoring out the commonality to some top-level section of the language reference, in a future PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IME people are much more likely to run into ABI issues in cross-language situations
Fully agreed. It's also a much less defined space, you basically have to define "which C type and ABI is compatible with which Rust type and ABI" (and then potentially also which C++/Julia/whatever type, though I guess we can rely on those languages saying which C types their types are compatible with). This depends on the target and honestly I'm quickly out of my depth for those questions. ABI still surprises me with new nightmares every few weeks, so I'm sure there's more to come.
I do hope someone will pick this up eventually.
I hope we at least are open to solving the issue for cross-language cases too.
Of course we are, I didn't want to give any indication to the contrary! It's just not the problem I want to solve right now. It's not on my own personal todo list either, at the moment.
library/core/src/primitive_docs.rs
Outdated
/// alignment, they might be passed in different registers and hence not be ABI-compatible. | ||
/// | ||
/// ABI compatibility as a concern only arises in code that alters the type of function pointers, | ||
/// and in code that combines `#[target_feature]` with `extern fn`. Altering the type of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The case I run into the most is where the code that uses some feature (e.g. vector registers) is written in another language that doesn't have target_feature
(e.g. assembly language .S files) and that assumes that all CPU-supported features are available to use (e.g. vector registers). When porting to systems where this isn't the case (e.g. x86-64-unknown-none) I end up needing to audit all the extern "C"
declarations.
The other case I've run into is where I am porting assembly code (.S files) from Linux to Apple targets, where the assembly code uses registers (e.g. r18) that aren't safe to use on Apple targets.
In both cases, no function pointers are involved and there's no use of target_feature
(and also there are no vector types in the function signature).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this PR we are strictly only concerned with Rust-to-Rust calls. I agree that Rust-to-other/other-to-Rust calls are important, but they are also a huge topic. Let's not scope creep this PR, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't object to that idea. But this specific sentence is very misleading since it says "ABI compatibility as a concern only arises in" two situations when that's actually not true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair. I adjusted the wording.
library/core/src/primitive_docs.rs
Outdated
/// ABI string only differs in a trailing `-unwind`, independent of the rest of their signature. | ||
/// (Note that this is about the case of passing a function pointer as an argument to a function. | ||
/// The two pointers being ABI-compatible here means that the call successfully passes the | ||
/// pointer. When actually calling the pointer, of course the rest of the signature becomes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what you mean by "successfully passes the pointer." "calling the pointer" doesn't make sense to me either. Did you mean "calling the function through the pointer"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what you mean by "successfully passes the pointer."
There's no UB from passing the function pointer around.
IOW, if the caller has signature fn(fn())
, and the callee has signature fn(fn(i32) -> i32)
, then the function call itself is completely well-defined. Caller and callee are ABI compatible even though the only argument of this function has a different type in the two signatures.
Or to be completely concrete, this code does not have UB.
Of course if the callee actually calls the function pointer that it was given as argument, then the fact that the signatures are different matters.
"calling the pointer" doesn't make sense to me either.
When f: fn()
, then f()
is "calling the function pointer". At least that's how I would call that operation. You seem to call it "calling the function through the pointer".
With data pointers we say that we read and write the pointer, we don't always spell out "read and write the memory through the pointer". I am following the same principle for function pointers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This point here is a bit confusing since the question "are two fn ptr types A and B ABI-compatible" is ambiguous:
- it could mean "can I call a function of type A with a caller-side signature of type B"
- it could mean "can I call a function of type
fn(A)
with a caller-side signature of typefn(B)
"
Usually when we say, e.g., "u32
and NonZeroU32
are ABI-compatible", we mean the latter, but when the two types in questions are themselves function pointers then the terminology becomes unclear.
I'd be happy for suggestions for how to word this more clearly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This point here is a bit confusing since the question "are two fn ptr types A and B ABI-compatible" is ambiguous:
it could mean "can I call a function of type A with a caller-side signature of type B"
it could mean "can I call a function of type fn(A) with a caller-side signature of type fn(B)"Usually when we say, e.g., "u32 and NonZeroU32 are ABI-compatible", we mean the latter, but when
the two types in questions are themselves function pointers then the terminology becomes unclear.I'd be happy for suggestions for how to word this more clearly.
First, I admit I don't think it is good to guarantee anything about transmuting function pointers with incompatible signatures, as I don't understand the motivation driving us to make such guarantees. So one way to clarify things would be to just have one set of rules for ABI compatibility / transmutation for function types for now, and then maybe follow up later with a proposal to guarantee transmutations of incompatible function types work.
usually when we say, e.g., "u32 and NonZeroU32 are ABI-compatible",
If you reserve "ABI compatibility" to be strictly about function calls, function declarations, function definitions, and function pointers, then we could have a separate term "transmutable, e.g. "NonZeroU32 is transmutable to u32" (where "transmutable" means the result of the transmutation is well-defined), and we could have a separate term "A is argument-compatible with B" to talk about where an argument of type A can be passed for a function parameter of type B. And hopefully we would have rules like "Any type A that is transmutable to B is argument-compatible with B." Then you could use "argument-compatible" as part of the definition of ABI-compatibility, in particular each argument in a function-call must be argument-compatible with corresponding parameter in the function's definition (and later, declaration), and (target_feature stuff, etc.).
In other words, don't use "ABI-compatible" and "ABI compatibility" terms to talk about argument/parameter compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, I admit I don't think it is good to guarantee anything about transmuting function pointers with incompatible signatures, as I don't understand the motivation driving us to make such guarantees. So one way to clarify things would be to just have one set of rules for ABI compatibility / transmutation for function types for now, and then maybe follow up later with a proposal to guarantee transmutations of incompatible function types work.
I think it would be really strange to say that *const i32
and *mut u8
are ABI-compatible, but fn()
and fn(i32)
are not. Both are just pointer types with different pointee information. It'd be very surprising to require the function signatures to be ABI compatible in order to have the pointers be ABI compatible.
transmutable
That's a very bad term for this situation. i32
is transmutable to u32
but they are not ABI compatible. The PR explicitly talks about this.
For better or worse, "ABI compatible" is already in common use for this concept, in questions like "are u32
and char
ABI compaible?" I think it's not a bad term, it's only a bit annoying for this specific case of ABI compatibility of function pointer types.
I have updated the text to use an example instead of remaining so abstract, I hope that helps.
/// declaration site; for the caller we consider the features enabled at the call site. | ||
/// - Neither any argument nor the return value involves a SIMD type (`#[repr(simd)]`) that is not | ||
/// behind a pointer indirection (i.e., `*mut __m256` is fine, but `(i32, __m256)` is not). | ||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean we need to declare the target features in every extern "C"
declaration?
In general it would be helpful to generalize the discussion here to also handle cases where pointers are not involved but instead extern "ABI"
is used to declare a non-Rust function that is then called from Rust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above I strongly want to avoid scope creep to non-Rust calls here. That's a much more complicated discussion.
38cdc45
to
9db35cb
Compare
and add an and
9db35cb
to
52d22ea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r=me with this comment resolved (either way)
library/core/src/primitive_docs.rs
Outdated
/// is also used rarely. So, most likely you do not have to worry about ABI compatibility. | ||
/// | ||
/// But assuming such circumstances, what are the rules? For this section, we are specifically | ||
/// concerned with the case where both the caller and the callee are defined in Rust. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// concerned with the case where both the caller and the callee are defined in Rust. | |
/// concerned with the case where both the caller and the callee are defined in Rust (and using the same compiler version). |
Right? We're not making guarantees about inter-op between future and past compiler versions here, in terms of what the ABI of some primitive is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you start linking separately compiled Rust objects with each other there's a ton of things you need to consider. Using the same Rust version is necessary but far from sufficient. I don't even know the full list of things, but at the very least you must use the same target triple, and there's a bunch of other flags that must be set the same, as far as I understand.
I'm not sure if we want to get into listing all these requirements here. I want to discuss ABI, not linking.
@bors r=Mark-Simulacrum rollup |
…iaskrgr Rollup of 3 pull requests Successful merges: - rust-lang#115476 (document ABI compatibility) - rust-lang#117688 (Misc changes to StableMIR required to Kani use case.) - rust-lang#117998 (On resolve error of `[rest..]`, suggest `[rest @ ..]`) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of rust-lang#115476 - RalfJung:abi-compat-docs, r=Mark-Simulacrum document ABI compatibility I don't think we have any central place where we document our ABI compatibility rules, so let's create one. The `fn()` pointer type seems like a good place since ABI questions can only become relevant when invoking a function through a function pointer. This will likely need T-lang FCP.
…tmcm do not allow ABI mismatches inside repr(C) types In rust-lang#115476 we allowed ABI mismatches inside `repr(C)` types. This wasn't really discussed much; I added it because from how I understand calling conventions, this should actually be safe in practice. However I entirely forgot to actually allow this in Miri, and in the mean time I have learned that too much ABI compatibility can be a problem for CFI (it can reject fewer calls so that gives an attacker more room to play with). So I propose we take back that part about ABI compatibility in `repr(C)`. It is anyway something that C and C++ do not allow, as far as I understand. In the future we might want to introduce a class of ABI compatibilities where we say "this is a bug and it may lead to aborting the process, but it won't lead to arbitrary misbehavior -- worst case it'll just transmute the arguments from the caller type to the callee type". That would give CFI leeway to reject such calls without introducing the risk of arbitrary UB. (The UB can still happen if the transmute leads to bad results, of course, but it wouldn't be due to ABI weirdness.) rust-lang#115476 hasn't reached beta yet so if we land this before Dec 22nd we can just pretend this all never happened. ;) Otherwise we should do a beta backport (of the docs change at least). Cc `@rust-lang/opsem` `@rust-lang/types`
do not allow ABI mismatches inside repr(C) types In rust-lang/rust#115476 we allowed ABI mismatches inside `repr(C)` types. This wasn't really discussed much; I added it because from how I understand calling conventions, this should actually be safe in practice. However I entirely forgot to actually allow this in Miri, and in the mean time I have learned that too much ABI compatibility can be a problem for CFI (it can reject fewer calls so that gives an attacker more room to play with). So I propose we take back that part about ABI compatibility in `repr(C)`. It is anyway something that C and C++ do not allow, as far as I understand. In the future we might want to introduce a class of ABI compatibilities where we say "this is a bug and it may lead to aborting the process, but it won't lead to arbitrary misbehavior -- worst case it'll just transmute the arguments from the caller type to the callee type". That would give CFI leeway to reject such calls without introducing the risk of arbitrary UB. (The UB can still happen if the transmute leads to bad results, of course, but it wouldn't be due to ABI weirdness.) #115476 hasn't reached beta yet so if we land this before Dec 22nd we can just pretend this all never happened. ;) Otherwise we should do a beta backport (of the docs change at least). Cc `@rust-lang/opsem` `@rust-lang/types`
Pkgsrc changes: * Adapt checksums and patches. Upstream chnages: Version 1.76.0 (2024-02-08) ========================== Language -------- - [Document Rust ABI compatibility between various types] (rust-lang/rust#115476) - [Also: guarantee that char and u32 are ABI-compatible] (rust-lang/rust#118032) - [Warn against ambiguous wide pointer comparisons] (rust-lang/rust#117758) Compiler -------- - [Lint pinned `#[must_use]` pointers (in particular, `Box<T>` where `T` is `#[must_use]`) in `unused_must_use`.] (rust-lang/rust#118054) - [Soundness fix: fix computing the offset of an unsized field in a packed struct] (rust-lang/rust#118540) - [Soundness fix: fix dynamic size/align computation logic for packed types with dyn Trait tail] (rust-lang/rust#118538) - [Add `$message_type` field to distinguish json diagnostic outputs] (rust-lang/rust#115691) - [Enable Rust to use the EHCont security feature of Windows] (rust-lang/rust#118013) - [Add tier 3 {x86_64,i686}-win7-windows-msvc targets] (rust-lang/rust#118150) - [Add tier 3 aarch64-apple-watchos target] (rust-lang/rust#119074) - [Add tier 3 arm64e-apple-ios & arm64e-apple-darwin targets] (rust-lang/rust#115526) Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries --------- - [Add a column number to `dbg!()`] (rust-lang/rust#114962) - [Add `std::hash::{DefaultHasher, RandomState}` exports] (rust-lang/rust#115694) - [Fix rounding issue with exponents in fmt] (rust-lang/rust#116301) - [Add T: ?Sized to `RwLockReadGuard` and `RwLockWriteGuard`'s Debug impls.] (rust-lang/rust#117138) - [Windows: Allow `File::create` to work on hidden files] (rust-lang/rust#116438) Stabilized APIs --------------- - [`Arc::unwrap_or_clone`] (https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.unwrap_or_clone) - [`Rc::unwrap_or_clone`] (https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.unwrap_or_clone) - [`Result::inspect`] (https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect) - [`Result::inspect_err`] (https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect_err) - [`Option::inspect`] (https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.inspect) - [`type_name_of_val`] (https://doc.rust-lang.org/stable/std/any/fn.type_name_of_val.html) - [`std::hash::{DefaultHasher, RandomState}`] (https://doc.rust-lang.org/stable/std/hash/index.html#structs) These were previously available only through `std::collections::hash_map`. - [`ptr::{from_ref, from_mut}`] (https://doc.rust-lang.org/stable/std/ptr/fn.from_ref.html) - [`ptr::addr_eq`](https://doc.rust-lang.org/stable/std/ptr/fn.addr_eq.html) Cargo ----- See [Cargo release notes] (https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#cargo-176-2024-02-08). Rustdoc ------- - [Don't merge cfg and doc(cfg) attributes for re-exports] (rust-lang/rust#113091) - [rustdoc: allow resizing the sidebar / hiding the top bar] (rust-lang/rust#115660) - [rustdoc-search: add support for traits and associated types] (rust-lang/rust#116085) - [rustdoc: Add highlighting for comments in items declaration] (rust-lang/rust#117869) Compatibility Notes ------------------- - [Add allow-by-default lint for unit bindings] (rust-lang/rust#112380) This is expected to be upgraded to a warning by default in a future Rust release. Some macros emit bindings with type `()` with user-provided spans, which means that this lint will warn for user code. - [Remove x86_64-sun-solaris target.] (rust-lang/rust#118091) - [Remove asmjs-unknown-emscripten target] (rust-lang/rust#117338) - [Report errors in jobserver inherited through environment variables] (rust-lang/rust#113730) This [may warn](rust-lang/rust#120515) on benign problems too. - [Update the minimum external LLVM to 16.] (rust-lang/rust#117947) - [Improve `print_tts`](rust-lang/rust#114571) This change can break some naive manual parsing of token trees in proc macro code which expect a particular structure after `.to_string()`, rather than just arbitrary Rust code. - [Make `IMPLIED_BOUNDS_ENTAILMENT` into a hard error from a lint] (rust-lang/rust#117984) - [Vec's allocation behavior was changed when collecting some iterators] (rust-lang/rust#110353) Allocation behavior is currently not specified, nevertheless changes can be surprising. See [`impl FromIterator for Vec`] (https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#impl-FromIterator%3CT%3E-for-Vec%3CT%3E) for more details. - [Properly reject `default` on free const items] (rust-lang/rust#117818)
do not allow ABI mismatches inside repr(C) types In rust-lang/rust#115476 we allowed ABI mismatches inside `repr(C)` types. This wasn't really discussed much; I added it because from how I understand calling conventions, this should actually be safe in practice. However I entirely forgot to actually allow this in Miri, and in the mean time I have learned that too much ABI compatibility can be a problem for CFI (it can reject fewer calls so that gives an attacker more room to play with). So I propose we take back that part about ABI compatibility in `repr(C)`. It is anyway something that C and C++ do not allow, as far as I understand. In the future we might want to introduce a class of ABI compatibilities where we say "this is a bug and it may lead to aborting the process, but it won't lead to arbitrary misbehavior -- worst case it'll just transmute the arguments from the caller type to the callee type". That would give CFI leeway to reject such calls without introducing the risk of arbitrary UB. (The UB can still happen if the transmute leads to bad results, of course, but it wouldn't be due to ABI weirdness.) #115476 hasn't reached beta yet so if we land this before Dec 22nd we can just pretend this all never happened. ;) Otherwise we should do a beta backport (of the docs change at least). Cc `@rust-lang/opsem` `@rust-lang/types`
do not allow ABI mismatches inside repr(C) types In rust-lang/rust#115476 we allowed ABI mismatches inside `repr(C)` types. This wasn't really discussed much; I added it because from how I understand calling conventions, this should actually be safe in practice. However I entirely forgot to actually allow this in Miri, and in the mean time I have learned that too much ABI compatibility can be a problem for CFI (it can reject fewer calls so that gives an attacker more room to play with). So I propose we take back that part about ABI compatibility in `repr(C)`. It is anyway something that C and C++ do not allow, as far as I understand. In the future we might want to introduce a class of ABI compatibilities where we say "this is a bug and it may lead to aborting the process, but it won't lead to arbitrary misbehavior -- worst case it'll just transmute the arguments from the caller type to the callee type". That would give CFI leeway to reject such calls without introducing the risk of arbitrary UB. (The UB can still happen if the transmute leads to bad results, of course, but it wouldn't be due to ABI weirdness.) #115476 hasn't reached beta yet so if we land this before Dec 22nd we can just pretend this all never happened. ;) Otherwise we should do a beta backport (of the docs change at least). Cc `@rust-lang/opsem` `@rust-lang/types`
I don't think we have any central place where we document our ABI compatibility rules, so let's create one. The
fn()
pointer type seems like a good place since ABI questions can only become relevant when invoking a function through a function pointer.This will likely need T-lang FCP.