Skip to content

fn_addr_eq docs suggest incorrect reasoning #160202

Description

@RalfJung

The docs for fn_addr_eq say

Despite these false positives and false negatives, this comparison can still be useful. Specifically, if

  • T is the same type as U, T is a subtype of U, or U is a subtype of T, and
  • ptr::fn_addr_eq(f, g) returns true,

then calling f and calling g will be equivalent.

This is not correct, I think.
Consider this example:

unsafe fn f(x: i32) -> i32 { x }
unsafe fn g(x: i32) -> i32 { std::hint::assert_unchecked(x != 0); x }

fn main() {
  let fptr = f as unsafe fn(i32) -> i32;
  let gptr = g as unsafe fn(i32) -> i32;
  if std::ptr::fn_addr_eq(fptr, gptr) {
    // SAFETY: Calling `fptr(0)` here would be allowed, and according to the `fn_addr_eq`
    // docs, inside this `if` calling `gptr` is equivalent to calling `fptr`.
    unsafe { gptr(0) };
  }
}

The compiler may now do the following:

  • Realize that gptr points to g, and use that to inline the call to g.
  • Optimize the then branch to std::hint::assert_unchecked(false), and then replace it by arbitrary code (e.g. a trap).
  • Later during compilation, merge f and g into one function since they compile to the same assembly.

Now if we run this program we hit the trap, i.e., we have exhibited UB.
(See rust-lang/unsafe-code-guidelines#589 for a wider discussion of the problem. When discussing this we were not aware that our libs docs actually make any claims in this regard.)

Cc @rust-lang/opsem @rust-lang/libs-api

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-docsArea: Documentation for any part of the project, including the compiler, standard library, and toolsC-bugCategory: This is a bug.T-libsRelevant to the library team, which will review and decide on the PR/issue.T-opsemRelevant to the opsem team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions