Rust Win32 FFI:违反用户模式数据执行保护 (DEP)

Rust Win32 FFI: User-mode data execution prevention (DEP) violation

我正在尝试将 ID3D11Device 实例从 Rust 传递到 C FFI 库 (FFMPEG)。

我做了这个示例代码:

pub fn create_d3d11_device(&mut self, device: &mut Box<windows::Win32::Graphics::Direct3D11::ID3D11Device>, context: &mut Box<windows::Win32::Graphics::Direct3D11::ID3D11DeviceContext>) {
            let av_device : Box<AVBufferRef> = self.alloc(HwDeviceType::D3d11va);
            unsafe {
                let device_context = Box::from_raw(av_device.data as *mut AVHWDeviceContext);
                let mut d3d11_device_context = Box::from_raw(device_context.hwctx as *mut AVD3D11VADeviceContext);
                d3d11_device_context.device = device.as_mut() as *mut _;
                d3d11_device_context.device_context = context.as_mut() as *mut _;
                let avp = Box::into_raw(av_device);
                av_hwdevice_ctx_init(avp);
                self.av_hwdevice = Some(Box::from_raw(avp));
            }
        }

在 Rust 方面,设备确实可以工作,但在 C 方面,当 FFMEPG 调用 ID3D11DeviceContext_QueryInterface 应用程序崩溃并出现以下错误:Exception 0xc0000005 encountered at address 0x7ff9fb99ad38: User-mode data execution prevention (DEP) violation at location 0x7ff9fb99ad38

地址实际上是 QueryInterface 的 lpVtbl 的指针,如下所示:

地址的反汇编看起来也是正确的(这是在另一个调试会话中完成的):

(lldb) disassemble --start-address 0x00007ffffdf3ad38
    0x7ffffdf3ad38: addb   %ah, 0x7ffffd(%rdi,%riz,8)
    0x7ffffdf3ad3f: addb   %al, (%rax)
    0x7ffffdf3ad41: movabsl -0x591fffff80000219, %eax
    0x7ffffdf3ad4a: outl   %eax, [=11=]xfd

你有任何进一步调试的指示吗?

编辑: 我做了一个最小复制样本。有趣的是,这不会导致 DEP 违规,而只会导致段错误。

C端:

int test_ffi(ID3D11Device *device){
    ID3D11DeviceContext *context;
    device->lpVtbl->GetImmediateContext(device, &context);
    if (!context) return 1;
    return 0;
}

在 Rust 方面:

unsafe fn main_rust(){
    let mut device = None;
    let mut device_context = None;
    let _ = match windows::Win32::Graphics::Direct3D11::D3D11CreateDevice(None, D3D_DRIVER_TYPE_HARDWARE, OtherHinstance::default(), D3D11_CREATE_DEVICE_DEBUG, &[], D3D11_SDK_VERSION, &mut device, std::ptr::null_mut(), &mut device_context) {
        Ok(e) => e,
        Err(e) => panic!("Creation Failed: {}", e)
    };
    let mut device = match device {
        Some(e) => e,
        None => panic!("Creation Failed2")
    };
    let mut f2 : ID3D11Device = transmute_copy(&device); //Transmuting the WinAPI into a bindgen ID3D11Device
    test_ffi(&mut f2);
}

bindgen build.rs:

extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
    // Tell cargo to tell rustc to link the system bzip2
    // shared library.
    println!("cargo:rustc-link-lib=ffi_demoLIB");
    println!("cargo:rustc-link-lib=d3d11");

    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=library.h");

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header("library.h")
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .blacklist_type("_IMAGE_TLS_DIRECTORY64")
        .blacklist_type("IMAGE_TLS_DIRECTORY64")
        .blacklist_type("PIMAGE_TLS_DIRECTORY64")
        .blacklist_type("IMAGE_TLS_DIRECTORY")
        .blacklist_type("PIMAGE_TLS_DIRECTORY")
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

完整的回购可以在这里找到:https://github.com/TheElixZammuto/demo-ffi

根据 https://github.com/microsoft/windows-rs/issues/1710#issuecomment-1111522946,我的错误是我正在转换结构,而我应该做的是转换引用:

    let f2 : &mut ID3D11Device = transmute_copy(&mut device); //Transmuting the WinAPI into a bindgen ID3D11Device
    test_ffi(f2);