Pointer arithmetic on Windows

Given a pointer (ptr), on Windows rustc 1.53.0-nightly, the following code is displaying the exact same value for both pointers:

unsafe {
  println!("PTR: {:?} END: {:?}", ptr, ptr.add(0x8888));
}

After reading the docs for the add() function (pointer - Rust) I thought that the above should print two different values, and not the same value for both pointers. My initial thought is that I must misunderstand how rust is doing pointer arithmetic.

Can anyone say why the same value is being displayed for both PTR and END?

1 Like

I'd assume you violated the first bullet point of the safety comment, which means this is UB. All bets are of in that case.

4 Likes

I have narrowed the problem down to an issue with c_void pointers. The original code uses the WinPcap external library. (See libpnet/winpcap.rs at master · libpnet/libpnet · GitHub) I have distilled the issue down to a simple snippet that demonstrates the behavior where add() does not seem to work. I have tried using simple types like pointers into a constant string, which work fine, so I am assuming there is some sort of type wizardry going on with external C pointers that I do not understand.

        // from elsewhere:  pub type PVOID = *mut ctypes::c_void;
        unsafe {
            let mut b = Vec::new();
            b.resize(1000, 0u8);
            let bp: *mut ctypes::c_void = b.as_mut_ptr() as winpcap::PVOID;
            println!("PRE: {:?} PTR: {:?} END: {:?}", bp.sub(0x2), bp, bp.add(0x2));
        }

(All three values -- PRE, PTR, and END -- display the same value.)

Isn't this undefined behaviour? That adress is not in bounds of the vector's buffer.

2 Likes

If c_void is a zero sized type then this is expected behaviour, it is correctly adding 0 * 0x2 = 0 to the pointer.

5 Likes

I would expect bp.sub(0x2) to fail or be undefined, but bp and bp.add(0x2) both return the same value.

I believe the size associated with the c_void pointer derives from the type of 0u8 -- it is a pointer to a buffer of unsigned bytes.

By default, winapi::ctypes::c_void is defined as pub enum c_void {}, which has size 0.

(However, if the winapi/std Cargo feature is enabled, then this becomes an alias for std::ffi::c_void, which has size 1.)

1 Like

Success -- thank you! Enabling the std feature of the winapi dependency worked.

For reference, this change in Cargo.toml makes the pointers work as expected:

[target.'cfg(windows)'.dependencies]
# winapi = "0.3.8" # This will not work because the size of c_void defaults to 0.
winapi = { version = "0.3.8", features = [ "std" ] }

I only now see what you are saying -- I had thought c_void would have inferred the size from the 0u8 field from the resize.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.