rust-lang / rust-lang/rust

Issue with function alignment in `const` combined with `is_null` on platforms with function pointer value not agreeing with function alignment.

Open
#144,661 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-const-eval C-bug F-fn_align requires-nightly T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code:

Running on armv5te-unknown-linux-gnueabi (and probably any other arm target with #[instruction_set(arm::t32)], or something similar on targets which use thumb mode by default, like thumbv7neon-unknown-linux-gnueabihf).

#![feature(fn_align)]
// This also occurs if using `-Zmin-function-alignment=2` on the CLI instead of `#[rustc_align(2)]` on just `foo`.

#[rustc_align(2)]
#[instruction_set(arm::t32)]
fn foo() {}

// If the assert succeeds, make sure this is the same value that `dbg!` prints below.
const EXPECTED_ADDR: usize = 0x00408aed;

const FOO_PTR: *const () = unsafe { std::mem::transmute::<fn(), *const ()>(foo) };
const MAYBE_NULL: *const () = FOO_PTR.wrapping_byte_sub(EXPECTED_ADDR);

fn main() {
    dbg!(FOO_PTR);
    let is_expected_addr_const = const { MAYBE_NULL.is_null() };
    let is_expected_addr = { MAYBE_NULL.is_null() };
    assert_eq!(is_expected_addr_const, is_expected_addr);
    println!("Hello, world!");
}

I expected to see this happen: The assert_eq succeeds (or compilation fails): the same value should not be non-null at compile time and null at runtime.

Instead, this happened:

[src/main.rs:18:5] FOO_PTR = 0x00408aed

thread 'main' panicked at src/main.rs:21:5:
assertion `left == right` failed
  left: false
 right: true
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Because foo is annotated #[rustc_align(2)], consteval believes that any odd offset from its address cannot be null. However, on ARM, all functions are aligned to at least 2, and the low bit of function pointers indicate which instruction set to use (0 = a32, 1 = t32 (thumb)). Since foo is #[instruction_set(arm::t32)], the low bit of a function pointer to it is thus odd at runtime, and thus subtracting the address it ends up at results in a null pointer.

ARM mode instructions are located on 4-byte boundaries. Thumb mode instructions are located on 2-byte boundaries. In the ARM architecture, bit 0 of a function pointer indicates the mode (ARM or Thumb) of the called function, rather than a memory location. When bit 0 = 1, Thumb mode is selected.

This also occurs if using -Zmin-function-alignment=2 on the CLI instead of #[rustc_align(2)] on just foo.

Tracking issue for fn_align: https://github.com/rust-lang/rust/pull/140261

Proposed stabilization PR for -Cmin-function-alignment: https://github.com/rust-lang/rust/pull/142824

Meta

rustc --version --verbose:

rustc 1.90.0-nightly (498ae9fed 2025-07-28)
binary: rustc
commit-hash: 498ae9fed2e7d90821d70a048f3770f91af08957
commit-date: 2025-07-28
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.8

I think the most "principled" solution for this would be for consteval to know about ARM (and maybe any other platform)'s function aligning/pointer tagging, so that FOO_PTR will be at offset 1 from the align-2 allocation for foo in consteval, and any arm::a32 function's function pointer would be at offset 0 from a its 4-byte-aligned allocation.

As a simpler workaround, maybe consteval could just make all function allocations be align-1 for consteval purposes, even if they have a higher alignment?

@rustbot label +A-const-eval +requires-nightly

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reproducer in src/main.rs and run it on armv5te-unknown-linux-gnueabi or another Thumb-mode target, comparing const and runtime is_null results. Investigate consteval's handling of function allocation alignment and tagged function pointers; done means the results agree or compilation rejects the unsupported case.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.