Skip to content

unsafe_op_in_unsafe_fn not triggered for raw pointer dereferencing inside addr_of/addr_of_mut #112504

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

Open
nhusung opened this issue Jun 10, 2023 · 3 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@nhusung
Copy link

nhusung commented Jun 10, 2023

Code

#![forbid(unsafe_op_in_unsafe_fn)]

use core::ptr::addr_of;
use core::ptr::addr_of_mut;

pub struct Foo {
    field: u8
}

pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
    addr_of!((*ptr).field)
}

pub unsafe fn foo_mut(ptr: *mut Foo) -> *mut u8 {
    addr_of_mut!((*ptr).field)
}

Current output

<No error>

Desired output

error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
  --> src/lib.rs:11:5
   |
11 |     addr_of!((*ptr).field)
   |     ^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
   |
   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
   = note: this error originates in the macro `addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)
note: the lint level is defined here
  --> src/lib.rs:1:11
   |
1  | #![forbid(unsafe_op_in_unsafe_fn)]
   |           ^^^^^^^^^^^^^^^^^^^^^^

error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
  --> src/lib.rs:15:5
   |
15 |     addr_of_mut!((*ptr).field)
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
   |
   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
   = note: this error originates in the macro `addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0133`.

Rationale and extra context

When removing the unsafe keywords in the function declarations of the sample code, the compiler emits the errors found in the “desired output” box (except for the note on the unsafe_op_in_unsafe_fn lint). Hence, I was expecting the operations to be unsafe.1 However, with forbid(unsafe_op_in_unsafe_fn) and the functions marked unsafe, no errors are emitted at all.

Other cases

No response

Anything else?

Note that the unsafe_op_in_unsafe_fn lint is not entirely broken for the expr of addr_of!(expr). The following snippet emits an error for calling an unsafe function (but not for dereferencing the raw pointer):

#![forbid(unsafe_op_in_unsafe_fn)]

pub struct Foo {
    field: u8
}

unsafe fn unsafe_id<T>(x: T) -> T { x }

pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
    core::ptr::addr_of!((*unsafe_id(ptr)).field)
}

Playground


Note furthermore that inlining the addr_of!()/addr_of_mut!() macros (using #![feature(raw_ref_op)]) result in the expected behavior:

#![feature(raw_ref_op)]
#![forbid(unsafe_op_in_unsafe_fn)]

pub struct Foo {
    field: u8
}

pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
    &raw const (*ptr).field
}

pub unsafe fn foo_mut(ptr: *mut Foo) -> *mut u8 {
    &raw mut (*ptr).field
}

Playground

Footnotes

  1. I’m not a hundred percent sure about this point as we effectively perform pointer arithmetic only. Conceptually, I would like to understand addr_of!((*ptr).field) as not actually dereferencing ptr. But the docs explicitly state that “the expr in addr_of!(expr) is still subject to all the usual rules. In particular, addr_of!(*ptr::null()) is Undefined Behavior because it dereferences a null pointer.”

@nhusung nhusung added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 10, 2023
@nhusung
Copy link
Author

nhusung commented Jun 10, 2023

It seems like this issue is related to the macro definitions being located in a different crate. When copying the macro definitions, errors are generated accordingly (Playground). But if I have main.rs and lib.rs (in a binary application package called example) as below, the code compiles without error.

main.rs:

#![forbid(unsafe_op_in_unsafe_fn)]

pub struct Foo {
    field: u8,
}

fn main() {
    let mut x = Foo { field: 42 };
    println!("{:?}, {:?}", unsafe { foo(&x) }, unsafe { foo_mut(&mut x) });
}

pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
    example::addr_of!((*ptr).field)
}

pub unsafe fn foo_mut(ptr: *mut Foo) -> *mut u8 {
    example::addr_of_mut!((*ptr).field)
}

lib.rs:

#![feature(allow_internal_unstable)]
//#![feature(rustc_attrs)]
#![feature(decl_macro)]
//#![forbid(unsafe_op_in_unsafe_fn)]

//#[rustc_macro_transparency = "semitransparent"]
#[allow_internal_unstable(raw_ref_op)]
pub macro addr_of($place:expr) {
    &raw const $place
}

//#[rustc_macro_transparency = "semitransparent"]
#[allow_internal_unstable(raw_ref_op)]
pub macro addr_of_mut($place:expr) {
    &raw mut $place
}

Whether rustc_macro_transparency or #![forbid(unsafe_op_in_unsafe_fn)] in lib.rs is used or not does not seem to matter.

@Nemo157
Copy link
Member

Nemo157 commented Jun 11, 2023

One part of the issue is that the lint spans the entire statement, so the &raw mut tokens from the macro expansion suppress it. If the lint were scoped more tightly to the actual unsafe operation it should work still.

self.source_info = statement.source_info;

@wmmc88
Copy link

wmmc88 commented Mar 10, 2024

I just ran into this for a macro I've defined which has unsafe operations. It seems that this issue happens for any macro that does unsafe operations, not just addr_of/addr_of_mut that is mentionned in the OP. When defining these macros in other crates, they will properly error if not in an unsafe block or fn, but when in an unsafe fn, they will fail to trigger unsafe_op_in_unsafe_fn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants