Warning about dereferencing a null pointer

The warning:

warning: dereferencing a null pointer
     --> src/generated/vulkan_bundle.rs:62648:14
      |
62648 |             &(*(::std::ptr::null::<VkPhysicalDeviceRayQueryFeaturesKHR>())).rayQuery as *const _
      |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
    assert_eq!(
        unsafe {
            &(*(::std::ptr::null::<VkPhysicalDeviceRayQueryFeaturesKHR>())).rayQuery as *const _
                as usize
        },
        16usize,
        concat!(
            "Offset of field: ",
            stringify!(VkPhysicalDeviceRayQueryFeaturesKHR),
            "::",
            stringify!(rayQuery)
        )
    );

Test results:

test generated::vulkan_bundle::bindgen_test_layout_VkPhysicalDeviceTimelineSemaphoreFeatures ... ok
test generated::vulkan_bundle::bindgen_test_layout_VkPhysicalDeviceVulkan12Features ... ok
test generated::vulkan_bundle::bindgen_test_layout_VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR ... ok

test result: ok. 560 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.74s

I got hundreds of warnings, in the tests generated by bindgen 0.59.1, that never appeared before, I recently updated to rust 1.55. The warning seems to make sense to me, a null pointer is created and then a member is accessed through it. The strange thing is that it runs normally. What could be happening?

Undefined Behavior is allowed to behave as expected

Yes, but a null pointer is created and used, shouldn't that cause the program crash?

The compiler is allowed to optimize the whole operation down to a constant, or in fact to anything else (that's what undefined behavior means).

I'm curious, is the original C code also dereferencing NULL or is this bindgen generating a bad dereference?

I got it. But wouldn't it make more sense to create an empty object with std::mem:zeroed()?
Is a layout test automatically generated by bindgen.

What it acutally does is &(TypedNull.field) as *FieldPtr as usize, so it doesn't really dereference the pointer, it just calculates the offset to field in a roundabout way (if null was a proper *TypedNull, what would be the adress of it's field).

Not sure if this is a good way of getting the offset or not, but I don't think it actually executes anything undefined, it just looks at it rather closely ...

Ugh, this seems to be a known issue with layout tests.

&((*null()).field) is definitely UB since it creates (temporarily) a reference that's not valid.

It's definitely a weird way to get the offset. The compiler and I were confused.

Creating an invalid pointer is not UB, only dereferencing it is. And yes, this code looks dangerously close to UB, but it doesn't actually dereference the bad pointer.

Creating an invalid reference, even without using it, is UB. Try running Miri on this example.

The right way to do this in Rust is the recently stabilized addr_of macro, which avoids dereferencing.

The addr_of documentation specifically calls out

Note, however, 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.

So more is needed to fix bindgen's mess here. I think you could do

let x = MaybeUninit::uninit();
unsafe { addr_of!((*x.as_ptr()).field) } as usize - x.as_ptr() as usize

Miri is okay with this version, at least.

MaybeUninit is certainly the mechanism used by the offset_of! macro, which there's been talk of upstreaming. Don't know if it would be worth pulling in for the generated code.