Skip to content

Latest commit

 

History

History
92 lines (64 loc) · 4.79 KB

0000-unaligned-fn-ptr.md

File metadata and controls

92 lines (64 loc) · 4.79 KB

Summary

Declare that function pointers do not make any alignment or other requirements to be "valid", beyond being non-null.

Motivation

Rust requires every value that is "produced" to be "valid" according to its type, even if the value is never actually "used" (see Reference, Nomicon). Unsafe code needs to know these rules to be sure it does not cause undefined behavior. So what should the rules be for function pointers (fn types)? They definitely must not be null, but is there anything else? Possible options for additional requirements include alignment, and some kind of well-formedness of the data it points to ("it must be executable code", or something like that). This is currently an open question.

The motivation to settle this question is that we have to say something in the Reference and the Nomicon that is at least a reasonable "upper bound" for the validity requirements of function pointers. Otherwise, unsafe code creating function pointers is left without rules to follow. With this PR for the Reference, we have reasonable upper bounds for most types of Rust (these have not gone through a formal consensus process, but to my knowledge they form an upper bound for every proposal that has been made for validity of any type thus far). The only exception is function pointers. Saying anything about their alignment requirements is actually non-trivial: we would have to introduce the notion of a platform-dependent "function alignment", and then ask users to look that up for the platforms they are targeting. Preferably, there would be way for code to actually query this alignment requirement (similar to mem::align_of). To avoid having to create all that infrastructure, I propose we instead declare that function pointers do not have to be aligned.

On most current platforms, we could not make any alignment requirement anyway: on x86, there is no alignment for code, and while ARM has 2- or 4-byte alignment (depending on platform details), it uses the least significant bit to encode the instruction set (thumb or not) of the pointee, so function pointers themselves are not guaranteed to be aligned.

Beyond alignment, while we could require the function pointer to point to a "valid sequence of instructions", it is unclear what the benefit of that would be.

Hence, given the lack of a motivation for making any non-null function pointers invalid, and in order to simplify the rules unsafe code has to follow and remove needless footguns, we propose to specify null as the only value that is invalid for function pointers.

Guide-level explanation

When transmuting data to a function pointer, you must make sure it is non-null:

fn bad() {
    let x: fn() = unsafe { std::mem::transmute(0usize) }; // This is UB!
}

Transmuting any non-null value to a function pointer is defined behavior.

fn good() {
    let x: fn() = unsafe { std::mem::transmute(1usize) }; // This is not UB.
}

Further requirements, outside the scope of this RFC, come into play when the function pointer gets called.

Reference-level explanation

A function pointer is "valid" (in the sense that it can be produced without causing immediate UB) if and only if it is non-null.

Drawbacks

This closes the door to potential optimizations based on more invalid values for function pointers. For example, if function pointers had to be 2-aligned, Option<Option<fn()>> could be pointer-sized because there is a niche of size 2 in fn().

Rationale and alternatives

The rationale was given further up in the motivation.

The alternative is to require function pointers to be aligned to some platform-specific quantity. In that case we should specify what that quantity is for our platforms.

Prior art

To our knowledge, no other language makes it undefined behavior to merely create (but not use) an unaligned or otherwise "bad" function pointer.

Unresolved questions

None we can think of.

Future possibilities

What exactly are the rules for calling a function pointer? This is deliberately out of scope of this RFC.