chore(safety): close the three crate-level lint gaps (WP2b)

pf-vkhdr-layer — the sharpest gap: an implicit layer injected into every
Vulkan game process, 32 unsafe usages, zero SAFETY comments, own workspace so
no lint table reached it, and an explicit missing_safety_doc allow. Now: a
[lints] table (unsafe_op_in_unsafe_fn + clippy::undocumented_unsafe_blocks,
both deny), the allow removed, every unsafe operation in an explicit block
with a real proof (loader layer protocol / Vulkan valid-usage), # Safety docs
on the contract-carrying fns, const layout asserts for the SurfaceFormat2Raw
mirror, the five helpers with no caller-facing contract demoted to safe fns,
and the two redundant `unsafe impl Send` deleted (fn pointers and vk handles
are Send intrinsically — the type-check proves it).

wdk-probe — 21 unsafe blocks, 12 proofs: the 9 missing SAFETY comments are
written (the iddcx_rt.rs DDI slot-dispatch ones are about table population
and PFN/index pairing, not pattern fill), the sibling denies added at the
crate root, missing_safety_doc allow dropped, # Safety on DriverEntry, and
the crate joins windows-drivers.yml's clippy list — it was the only driver
crate not in it.

pf-clipboard — the undocumented_unsafe_blocks deny moves from host/windows.rs
to the crate root so host/wayland.rs (4 blocks), host/mutter.rs (2) and any
future backend under host/ are covered on creation. All existing blocks
already carry proofs; free today, structural tomorrow.

Verified here: pf-vkhdr-layer cargo fmt --check + clippy --release
-D warnings at x86_64-pc-windows-msvc. wdk-probe and pf-clipboard compile
checks need the WDK/Linux boxes and ride the .133/.25 gate.
This commit is contained in:
2026-08-11 23:10:00 +02:00
parent 0c2ac333ae
commit 23fa03b051
7 changed files with 451 additions and 166 deletions
+4 -3
View File
@@ -159,9 +159,10 @@ jobs:
# The gamepad drivers' business logic is 100% safe (it moved onto pf-umdf-util, the audited
# unsafe layer); pf-vdisplay + wdk-iddcx are inherently FFI-bound but every `unsafe {}` carries a
# `// SAFETY:` proof. Both invariants are lint-gated (`unsafe_op_in_unsafe_fn` +
# `undocumented_unsafe_blocks`); this step keeps them from regressing. (wdk-probe is a
# toolchain-only probe crate and is excluded.)
run: cargo clippy -p pf-umdf-util -p pf-xusb -p pf-gamepad -p pf-mouse -p wdk-iddcx -p pf-vdisplay --all-targets -- -D warnings
# `undocumented_unsafe_blocks`); this step keeps them from regressing. wdk-probe is a
# toolchain-only probe crate, but it holds real DDI slot-dispatch unsafe (iddcx_rt.rs), so it
# runs the same gates.
run: cargo clippy -p pf-umdf-util -p pf-xusb -p pf-gamepad -p pf-mouse -p wdk-iddcx -p pf-vdisplay -p wdk-probe --all-targets -- -D warnings
- name: cargo fmt --check the safe-layer + gamepad/mouse drivers
run: cargo fmt -p pf-umdf-util -p pf-xusb -p pf-gamepad -p pf-mouse --check
- name: Inspect /INTEGRITYCHECK (before) — expect FORCE_INTEGRITY set by wdk-build
+2 -2
View File
@@ -17,8 +17,8 @@
//! (`PostMessage` is the documented thread-safe way to poke a message loop). Per-window state hangs
//! off `GWLP_USERDATA`, so multiple concurrent sessions each get their own window + state.
// Every `unsafe` block in this file carries a `// SAFETY:` proof; enforce it (unsafe-proof program).
#![deny(clippy::undocumented_unsafe_blocks)]
// Every `unsafe` block in this file carries a `// SAFETY:` proof; the deny enforcing it sits at
// the crate root (lib.rs), covering every backend.
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
+4
View File
@@ -10,6 +10,10 @@
//! [`spawn_decline_loop`] — so its control loop compiles unchanged on every host platform; the
//! platform split lives entirely behind [`start`].
// Crate-wide: every `unsafe` block in any backend carries a `// SAFETY:` proof (unsafe-proof
// program). At the root — not per-module — so a new backend under `host/` is covered on creation.
#![deny(clippy::undocumented_unsafe_blocks)]
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
@@ -26,6 +26,10 @@ unsafe fn ddi<T: Copy>(index: i32) -> T {
let table = (&raw const IddFunctions).cast::<PFN_IDD_CX>();
// SAFETY: `index` is a valid IddCx table slot; the slot holds a `PFN_*` whose layout is `T`.
let slot = unsafe { table.add(index as usize) };
// SAFETY: `slot` is in bounds of the stub-populated table (per this function's contract),
// pointer-aligned (a table of `PFN_IDD_CX` entries), and initialized by `IddCxStub` at driver
// load; every `PFN_*` is an `Option<extern fn>`, for which both null (None) and a populated
// entry are valid values to read as `T`.
unsafe { slot.cast::<T>().read() }
}
@@ -45,8 +49,12 @@ pub unsafe fn IddCxDeviceInitConfig(
device_init: PWDFDEVICE_INIT,
config: *const IDD_CX_CLIENT_CONFIG,
) -> NTSTATUS {
// SAFETY: index and PFN type name the same DDI (IddCxDeviceInitConfig), and this function's
// contract — call only during EvtDriverDeviceAdd — implies the driver is loaded and the
// stub's table populated.
let f: PFN_IDDCXDEVICEINITCONFIG =
unsafe { ddi(_IDDFUNCENUM::IddCxDeviceInitConfigTableIndex) };
// SAFETY: the driver is loaded (same premise as above), so the stub globals are set.
let g = unsafe { globals() };
// SAFETY: dispatching a populated DDI with the stub globals and caller-valid args.
unsafe { (f.unwrap())(g, device_init, config) }
@@ -55,8 +63,12 @@ pub unsafe fn IddCxDeviceInitConfig(
/// # Safety
/// `device` must be a valid `WDFDEVICE` previously configured via [`IddCxDeviceInitConfig`].
pub unsafe fn IddCxDeviceInitialize(device: WDFDEVICE) -> NTSTATUS {
// SAFETY: index and PFN type name the same DDI (IddCxDeviceInitialize), and this function's
// contract — `device` came out of a WdfDeviceCreate on an IddCx-configured init — implies
// the driver is loaded and the stub's table populated.
let f: PFN_IDDCXDEVICEINITIALIZE =
unsafe { ddi(_IDDFUNCENUM::IddCxDeviceInitializeTableIndex) };
// SAFETY: the driver is loaded (same premise as above), so the stub globals are set.
let g = unsafe { globals() };
// SAFETY: dispatching a populated DDI with the stub globals and a caller-valid device.
unsafe { (f.unwrap())(g, device) }
@@ -68,8 +80,12 @@ pub unsafe fn IddCxAdapterInitAsync(
in_args: *const IDARG_IN_ADAPTER_INIT,
out_args: *mut IDARG_OUT_ADAPTER_INIT,
) -> NTSTATUS {
// SAFETY: index and PFN type name the same DDI (IddCxAdapterInitAsync), and IddCx DDIs are
// only reachable from framework callbacks — i.e. with the driver loaded and the stub's
// table populated.
let f: PFN_IDDCXADAPTERINITASYNC =
unsafe { ddi(_IDDFUNCENUM::IddCxAdapterInitAsyncTableIndex) };
// SAFETY: the driver is loaded (same premise as above), so the stub globals are set.
let g = unsafe { globals() };
// SAFETY: dispatching a populated DDI with the stub globals and caller-valid args.
unsafe { (f.unwrap())(g, in_args, out_args) }
+13 -2
View File
@@ -4,7 +4,9 @@
//! stub link AND prove the `iddcx` subset is callable + links against `IddCxStub`. Also force-links the
//! shared `pf-driver-proto` ABI crate (no_std + bytemuck) across the workspace boundary.
#![allow(non_snake_case, clippy::missing_safety_doc)]
#![allow(non_snake_case)]
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(clippy::undocumented_unsafe_blocks)]
mod iddcx_rt;
mod iddcx_surface_assert;
@@ -29,6 +31,9 @@ static PROTO_GUID_LO: u64 = pf_driver_proto::PF_VDISPLAY_INTERFACE_GUID_U128 as
#[unsafe(no_mangle)]
pub static IddMinimumVersionRequired: ULONG = 4;
/// # Safety
/// Called by the UMDF host at driver load: `driver` and `registry_path` must be the
/// loader-provided driver object and registry path, both valid for the duration of the call.
#[unsafe(export_name = "DriverEntry")]
pub unsafe extern "system" fn driver_entry(
driver: PDRIVER_OBJECT,
@@ -57,6 +62,9 @@ extern "C" fn evt_device_add(_driver: WDFDRIVER, mut device_init: PWDFDEVICE_INI
// IddCxStub (CI), and that a self-signed IddCx driver loads + dispatches (box); the full callback
// surface + a valid adapter come with the driver port. At runtime IddCxDeviceInitConfig will reject
// the null callbacks, but the call site is what links IddCxStub and exercises table dispatch.
// SAFETY: IDD_CX_CLIENT_CONFIG is a C aggregate of integers, pointers, and nullable
// callbacks (`Option<extern fn>`) — all-zero is a valid value (null callbacks, see above);
// Size is stamped on the next line.
let mut cfg: IDD_CX_CLIENT_CONFIG = unsafe { core::mem::zeroed() };
cfg.Size = core::mem::size_of::<IDD_CX_CLIENT_CONFIG>() as ULONG;
// SAFETY: device_init is the framework-provided init; cfg is a valid (if minimal) config.
@@ -76,8 +84,11 @@ extern "C" fn evt_device_add(_driver: WDFDRIVER, mut device_init: PWDFDEVICE_INI
// SAFETY: device is the just-created WDFDEVICE.
let _ = unsafe { iddcx_rt::IddCxDeviceInitialize(device) };
// SAFETY: zeroed adapter-init args — a link/dispatch reference, not a valid adapter (see above).
// SAFETY: IDARG_IN_ADAPTER_INIT is a C aggregate of handles/pointers/integers — all-zero is
// a valid value (a link/dispatch reference, not a valid adapter; see above).
let in_args: IDARG_IN_ADAPTER_INIT = unsafe { core::mem::zeroed() };
// SAFETY: IDARG_OUT_ADAPTER_INIT is a C aggregate of handles — all-zero is a valid value,
// and the call below only writes through it.
let mut out_args: IDARG_OUT_ADAPTER_INIT = unsafe { core::mem::zeroed() };
// SAFETY: in/out args are valid local storage for the call.
let _ = unsafe { iddcx_rt::IddCxAdapterInitAsync(&in_args, &mut out_args) };
@@ -13,6 +13,14 @@ crate-type = ["cdylib"]
[dependencies]
ash = "=0.38.0+1.3.281"
# This package is its own workspace (see below), so the main workspace's lint tables never reach
# it — the unsafe discipline has to be restated here or it silently doesn't apply.
[lints.rust]
unsafe_op_in_unsafe_fn = "deny"
[lints.clippy]
undocumented_unsafe_blocks = "deny"
[profile.release]
opt-level = 2
panic = "abort"
+404 -159
View File
@@ -23,9 +23,17 @@
//! Off-switches: the loader-standard `DISABLE_PF_VKHDR=1` (disables the whole layer), and
//! `PF_VKHDR_EXCLUDE` (comma/semicolon list of exe basenames to skip — defaults include known
//! kernel-anti-cheat titles). `PF_VKHDR_LOG=1` enables a debug log in `%TEMP%\pf_vkhdr_layer.log`.
//!
//! ## Safety model
//! This cdylib runs inside someone else's process, called by the Vulkan loader. Two contract
//! sources cover every unsafe operation here: the **loader layer protocol** (negotiate struct,
//! per-layer chain links, dispatchable handles whose first word is the loader's dispatch-table
//! pointer) and **Vulkan valid-usage rules** the application must already uphold for the ICD
//! (NUL-terminated command names, valid create-info chains, count/array pairs in the two-call
//! idiom). Every `unsafe` block cites the specific clause it leans on. The layer targets x86_64
//! only (the two hand-computed struct offsets below say so explicitly).
#![allow(non_snake_case)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::too_many_arguments)]
// HWND / HMONITOR etc. deliberately mirror the Win32 names.
#![allow(clippy::upper_case_acronyms)]
@@ -97,6 +105,14 @@ struct SurfaceFormat2Raw {
p_next: *mut c_void,
surface_format: vk::SurfaceFormatKHR,
}
// Layout proof for the mirror: arrays of it are handed to the ICD and stamped over the caller's
// VkSurfaceFormat2KHR array, so a size or alignment drift would corrupt either side.
const _: () = assert!(
std::mem::size_of::<SurfaceFormat2Raw>() == std::mem::size_of::<vk::SurfaceFormat2KHR>()
);
const _: () = assert!(
std::mem::align_of::<SurfaceFormat2Raw>() == std::mem::align_of::<vk::SurfaceFormat2KHR>()
);
// ---- ICD function-pointer typedefs we call down to (raw pointers, no lifetimes) ----
type FnCreateInstance =
@@ -128,6 +144,9 @@ type FnCreateWin32Surface = unsafe extern "system" fn(
) -> vk::Result;
type FnDestroySurface = unsafe extern "system" fn(vk::Instance, vk::SurfaceKHR, *const c_void);
// Both maps' values are raw `extern "system"` fn pointers plus plain handles; fn pointers are
// process-global code addresses and implement Send/Sync intrinsically, so the auto traits hold
// and no `unsafe impl Send` is needed (two used to sit here as unproven markers).
struct InstanceData {
instance: vk::Instance,
next_gipa: PfnGipa,
@@ -138,12 +157,10 @@ struct InstanceData {
create_win32_surface: Option<FnCreateWin32Surface>,
destroy_surface: Option<FnDestroySurface>,
}
unsafe impl Send for InstanceData {}
struct DeviceData {
next_gdpa: PfnGdpa,
}
unsafe impl Send for DeviceData {}
fn instances() -> &'static Mutex<HashMap<usize, InstanceData>> {
static M: OnceLock<Mutex<HashMap<usize, InstanceData>>> = OnceLock::new();
@@ -159,21 +176,47 @@ fn surface_hwnds() -> &'static Mutex<HashMap<u64, isize>> {
M.get_or_init(|| Mutex::new(HashMap::new()))
}
// dispatch key = first pointer word of a dispatchable handle (loader dispatch table ptr).
/// Dispatch key of a dispatchable handle.
///
/// # Safety
/// `raw` must be the value of a **live dispatchable** Vulkan handle (`VkInstance`,
/// `VkPhysicalDevice`, `VkDevice`). The loader ABI mandates that every dispatchable object's
/// first pointer-sized word is the loader's dispatch-table pointer, which is what makes the read
/// in-bounds and initialized — and what makes it a stable per-chain key.
#[inline]
unsafe fn key(raw: u64) -> usize {
*(raw as usize as *const usize)
// SAFETY: per this function's contract, `raw` points at a live dispatchable object whose
// first pointer-sized word exists and is initialized (the loader wrote it at creation).
unsafe { *(raw as usize as *const usize) }
}
/// Reinterpret a function address as the loader's type-erased void-function pointer.
///
/// # Safety
/// `p` must be the address of an `extern "system"` function. Whoever receives the returned PFN
/// must cast it back to the exact prototype of the command it was queried under before calling —
/// which is precisely what the Vulkan `*ProcAddr` contract obliges callers to do.
#[inline]
unsafe fn as_pfn(p: *const c_void) -> vk::PFN_vkVoidFunction {
Some(std::mem::transmute::<
*const c_void,
unsafe extern "system" fn(),
>(p))
// SAFETY: data and function pointers have identical size and representation on all Windows
// targets, and per this function's contract `p` is a real `extern "system"` fn address.
Some(unsafe { std::mem::transmute::<*const c_void, unsafe extern "system" fn()>(p) })
}
/// Resolve `name` down-chain and reinterpret the result as fn-pointer type `T`.
///
/// # Safety
/// `gipa` must be a valid `vkGetInstanceProcAddr` for `inst` (or for the null instance when
/// resolving global commands), and `T` must be the exact fn-pointer prototype of the Vulkan
/// command named by `name` — `transmute_copy` erases all type checking between them.
#[inline]
unsafe fn resolve<T: Copy>(gipa: PfnGipa, inst: vk::Instance, name: &CStr) -> Option<T> {
gipa(inst, name.as_ptr()).map(|f| std::mem::transmute_copy::<_, T>(&f))
// SAFETY: per this function's contract `gipa` is a valid loader/ICD GetInstanceProcAddr for
// `inst`, and `name` is a live NUL-terminated string for the duration of the call.
let f = unsafe { gipa(inst, name.as_ptr()) };
// SAFETY: `f` was returned for `name`, and per this function's contract `T` is that
// command's exact prototype; both sides are fn pointers of identical size.
f.map(|f| unsafe { std::mem::transmute_copy::<_, T>(&f) })
}
fn log(msg: &str) {
@@ -356,21 +399,34 @@ mod hdr {
const GET_ADVANCED_COLOR_INFO: i32 = 9;
const MONITOR_DEFAULTTONEAREST: u32 = 2;
unsafe fn active_paths() -> Vec<PathInfo> {
// Safe fn: every invariant below is local (out-params point at live locals / exact-length
// Vecs); callers carry no obligations.
fn active_paths() -> Vec<PathInfo> {
let (mut np, mut nm) = (0u32, 0u32);
if GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut np, &mut nm) != 0 || np == 0 {
// SAFETY: both out-pointers come from live local `u32`s that outlive the call.
if unsafe { GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut np, &mut nm) } != 0
|| np == 0
{
return Vec::new();
}
let mut pa: Vec<PathInfo> = vec![std::mem::zeroed(); np as usize];
let mut ma: Vec<ModeInfo> = vec![std::mem::zeroed(); nm as usize];
if QueryDisplayConfig(
QDC_ONLY_ACTIVE_PATHS,
&mut np,
pa.as_mut_ptr(),
&mut nm,
ma.as_mut_ptr(),
std::ptr::null_mut(),
) != 0
// SAFETY: PathInfo is a #[repr(C)] aggregate of integers — all-zero is a valid value.
let mut pa: Vec<PathInfo> = vec![unsafe { std::mem::zeroed() }; np as usize];
// SAFETY: ModeInfo is an opaque byte blob — all-zero is a valid value.
let mut ma: Vec<ModeInfo> = vec![unsafe { std::mem::zeroed() }; nm as usize];
// SAFETY: `pa`/`ma` hold exactly `np`/`nm` elements and those counts are passed by
// pointer as the in/out capacities: QueryDisplayConfig writes at most that many entries
// (if the topology grew between the two calls it returns ERROR_INSUFFICIENT_BUFFER
// rather than writing past the end) and shrinks np/nm to what it actually wrote.
if unsafe {
QueryDisplayConfig(
QDC_ONLY_ACTIVE_PATHS,
&mut np,
pa.as_mut_ptr(),
&mut nm,
ma.as_mut_ptr(),
std::ptr::null_mut(),
)
} != 0
{
return Vec::new();
}
@@ -378,38 +434,52 @@ mod hdr {
pa
}
unsafe fn target_hdr_enabled(p: &PathInfo) -> bool {
let mut ai: AdvInfo = std::mem::zeroed();
fn target_hdr_enabled(p: &PathInfo) -> bool {
// SAFETY: AdvInfo is a #[repr(C)] aggregate of integers — all-zero is a valid value.
let mut ai: AdvInfo = unsafe { std::mem::zeroed() };
ai.header.typ = GET_ADVANCED_COLOR_INFO;
ai.header.size = std::mem::size_of::<AdvInfo>() as u32;
ai.header.adapter = p.tgt.adapter;
ai.header.id = p.tgt.id;
if DisplayConfigGetDeviceInfo(&mut ai as *mut _ as *mut c_void) != 0 {
// SAFETY: the request header carries this struct's exact size, which is the documented
// bound for DisplayConfigGetDeviceInfo's write; `ai` is a live local across the call.
if unsafe { DisplayConfigGetDeviceInfo(&mut ai as *mut _ as *mut c_void) } != 0 {
return false;
}
// value bitfield: bit0 advancedColorSupported, bit1 advancedColorEnabled.
(ai.value & 0b10) != 0
}
unsafe fn source_gdi(p: &PathInfo) -> [u16; 32] {
let mut sn: SourceName = std::mem::zeroed();
fn source_gdi(p: &PathInfo) -> [u16; 32] {
// SAFETY: SourceName is a #[repr(C)] aggregate of integers — all-zero is a valid value.
let mut sn: SourceName = unsafe { std::mem::zeroed() };
sn.header.typ = GET_SOURCE_NAME;
sn.header.size = std::mem::size_of::<SourceName>() as u32;
sn.header.adapter = p.src.adapter;
sn.header.id = p.src.id;
let _ = DisplayConfigGetDeviceInfo(&mut sn as *mut _ as *mut c_void);
// SAFETY: the request header carries this struct's exact size, which is the documented
// bound for DisplayConfigGetDeviceInfo's write; `sn` is a live local across the call.
let _ = unsafe { DisplayConfigGetDeviceInfo(&mut sn as *mut _ as *mut c_void) };
sn.gdi
}
/// Is HDR (Windows advanced color) currently enabled on the display this surface lives on?
/// `hwnd == 0`/unknown falls back to "any active display has HDR enabled".
pub unsafe fn enabled_for(hwnd: HWND) -> bool {
///
/// Safe fn: `MonitorFromWindow` with `DEFAULTTONEAREST` tolerates any HWND value — including
/// a destroyed or foreign one (our map can be stale) — so callers carry no obligations.
pub fn enabled_for(hwnd: HWND) -> bool {
let paths = active_paths();
if hwnd != 0 {
let mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
let mut mi: MonitorInfoExW = std::mem::zeroed();
// SAFETY: MonitorFromWindow accepts arbitrary HWND values with DEFAULTTONEAREST
// (falling back to the nearest/primary monitor) and takes nothing by pointer.
let mon = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };
// SAFETY: MonitorInfoExW is a #[repr(C)] aggregate of integers — all-zero is valid.
let mut mi: MonitorInfoExW = unsafe { std::mem::zeroed() };
mi.cb = std::mem::size_of::<MonitorInfoExW>() as u32;
if GetMonitorInfoW(mon, &mut mi) != 0 {
// SAFETY: `mi.cb` carries the struct's exact size, which is the documented bound for
// GetMonitorInfoW's write; `mi` is a live local across the call.
if unsafe { GetMonitorInfoW(mon, &mut mi) } != 0 {
for p in &paths {
if source_gdi(p) == mi.sz_device {
return target_hdr_enabled(p);
@@ -417,12 +487,13 @@ mod hdr {
}
}
}
paths.iter().any(|p| target_hdr_enabled(p))
paths.iter().any(target_hdr_enabled)
}
}
/// Should we inject HDR formats for this surface right now?
unsafe fn should_inject(surface: vk::SurfaceKHR) -> bool {
/// Should we inject HDR formats for this surface right now? Safe fn — the surface handle is only
/// used as a map key, and a stale/unknown HWND degrades to the any-display fallback.
fn should_inject(surface: vk::SurfaceKHR) -> bool {
if !injection_allowed_for_process() {
return false;
}
@@ -436,6 +507,12 @@ unsafe fn should_inject(surface: vk::SurfaceKHR) -> bool {
// ---- entry point ----
/// Layer negotiation entry point; the loader calls this export when it loads the DLL.
///
/// # Safety
/// `p` must be null or point to a live `NegotiateLayerInterface` the caller has exclusive access
/// to for the duration of the call. The Vulkan loader — the only intended caller of this
/// export — guarantees exactly that for the negotiate handshake.
#[no_mangle]
pub unsafe extern "system" fn vkNegotiateLoaderLayerInterfaceVersion(
p: *mut NegotiateLayerInterface,
@@ -443,7 +520,9 @@ pub unsafe extern "system" fn vkNegotiateLoaderLayerInterfaceVersion(
if p.is_null() {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
let s = &mut *p;
// SAFETY: `p` is non-null, and per this export's contract the loader hands us exclusive
// access to a live NegotiateLayerInterface for the duration of the call.
let s = unsafe { &mut *p };
if s.s_type != LAYER_NEGOTIATE_INTERFACE_STRUCT {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
@@ -466,27 +545,46 @@ unsafe extern "system" fn layer_gipa(
if p_name.is_null() {
return None;
}
match CStr::from_ptr(p_name).to_bytes() {
b"vkGetInstanceProcAddr" => as_pfn(layer_gipa as *const c_void),
b"vkGetDeviceProcAddr" => as_pfn(layer_gdpa as *const c_void),
b"vkCreateInstance" => as_pfn(create_instance as *const c_void),
b"vkDestroyInstance" => as_pfn(destroy_instance as *const c_void),
b"vkCreateDevice" => as_pfn(create_device as *const c_void),
b"vkGetPhysicalDeviceSurfaceFormatsKHR" => as_pfn(get_surface_formats as *const c_void),
b"vkGetPhysicalDeviceSurfaceFormats2KHR" => as_pfn(get_surface_formats2 as *const c_void),
b"vkCreateWin32SurfaceKHR" => as_pfn(create_win32_surface as *const c_void),
b"vkDestroySurfaceKHR" => as_pfn(destroy_surface as *const c_void),
_ => {
if instance == vk::Instance::null() {
return None;
// SAFETY: `p_name` is non-null, and vkGetInstanceProcAddr's valid-usage rules require pName
// to be a valid NUL-terminated string for the duration of the call.
let name = unsafe { CStr::from_ptr(p_name) }.to_bytes();
// SAFETY: every arm hands `as_pfn` the address of the `extern "system"` hook this layer
// substitutes for exactly that command name; the *ProcAddr contract obliges the caller to
// cast the returned PFN back to the named command's prototype before invoking it, which is
// what makes the type-erased transmute inside `as_pfn` sound for each arm.
let hook = unsafe {
match name {
b"vkGetInstanceProcAddr" => as_pfn(layer_gipa as *const c_void),
b"vkGetDeviceProcAddr" => as_pfn(layer_gdpa as *const c_void),
b"vkCreateInstance" => as_pfn(create_instance as *const c_void),
b"vkDestroyInstance" => as_pfn(destroy_instance as *const c_void),
b"vkCreateDevice" => as_pfn(create_device as *const c_void),
b"vkGetPhysicalDeviceSurfaceFormatsKHR" => as_pfn(get_surface_formats as *const c_void),
b"vkGetPhysicalDeviceSurfaceFormats2KHR" => {
as_pfn(get_surface_formats2 as *const c_void)
}
let next = {
let g = instances().lock().ok()?;
g.get(&key(instance.as_raw())).map(|d| d.next_gipa)
};
next.and_then(|gipa| gipa(instance, p_name))
b"vkCreateWin32SurfaceKHR" => as_pfn(create_win32_surface as *const c_void),
b"vkDestroySurfaceKHR" => as_pfn(destroy_surface as *const c_void),
_ => None,
}
};
if hook.is_some() {
return hook;
}
if instance == vk::Instance::null() {
return None;
}
let next = {
let g = instances().lock().ok()?;
// SAFETY: `instance` is non-null, and vkGetInstanceProcAddr's valid-usage rules make a
// non-null instance argument a live instance handle — a dispatchable object whose first
// word is the dispatch key.
g.get(&unsafe { key(instance.as_raw()) })
.map(|d| d.next_gipa)
};
// SAFETY: `next` is the down-chain GetInstanceProcAddr captured from the loader's link at
// create_instance for this very chain; `p_name` is still valid NUL-terminated.
next.and_then(|gipa| unsafe { gipa(instance, p_name) })
}
unsafe extern "system" fn layer_gpdpa(
@@ -496,20 +594,37 @@ unsafe extern "system" fn layer_gpdpa(
if p_name.is_null() {
return None;
}
match CStr::from_ptr(p_name).to_bytes() {
b"vkGetPhysicalDeviceSurfaceFormatsKHR" => as_pfn(get_surface_formats as *const c_void),
b"vkGetPhysicalDeviceSurfaceFormats2KHR" => as_pfn(get_surface_formats2 as *const c_void),
_ => {
if instance == vk::Instance::null() {
return None;
// SAFETY: `p_name` is non-null, and the GetPhysicalDeviceProcAddr contract mirrors
// vkGetInstanceProcAddr's: pName is a valid NUL-terminated string for the call.
let name = unsafe { CStr::from_ptr(p_name) }.to_bytes();
// SAFETY: both arms hand `as_pfn` the address of the `extern "system"` hook this layer
// substitutes for exactly that command name; the *ProcAddr contract obliges the caller to
// cast the returned PFN back to that command's prototype before invoking it.
let hook = unsafe {
match name {
b"vkGetPhysicalDeviceSurfaceFormatsKHR" => as_pfn(get_surface_formats as *const c_void),
b"vkGetPhysicalDeviceSurfaceFormats2KHR" => {
as_pfn(get_surface_formats2 as *const c_void)
}
let next = {
let g = instances().lock().ok()?;
g.get(&key(instance.as_raw())).and_then(|d| d.next_gpdpa)
};
next.and_then(|gpdpa| gpdpa(instance, p_name))
_ => None,
}
};
if hook.is_some() {
return hook;
}
if instance == vk::Instance::null() {
return None;
}
let next = {
let g = instances().lock().ok()?;
// SAFETY: `instance` is non-null and (per the caller's contract) a live instance
// handle — a dispatchable object whose first word is the dispatch key.
g.get(&unsafe { key(instance.as_raw()) })
.and_then(|d| d.next_gpdpa)
};
// SAFETY: `next` is the down-chain GPDPA captured from the loader's link at create_instance
// for this very chain; `p_name` is still valid NUL-terminated.
next.and_then(|gpdpa| unsafe { gpdpa(instance, p_name) })
}
unsafe extern "system" fn layer_gdpa(
@@ -519,8 +634,13 @@ unsafe extern "system" fn layer_gdpa(
if p_name.is_null() {
return None;
}
if CStr::from_ptr(p_name).to_bytes() == b"vkGetDeviceProcAddr" {
return as_pfn(layer_gdpa as *const c_void);
// SAFETY: `p_name` is non-null, and vkGetDeviceProcAddr's valid-usage rules require pName to
// be a valid NUL-terminated string for the duration of the call.
let name = unsafe { CStr::from_ptr(p_name) }.to_bytes();
if name == b"vkGetDeviceProcAddr" {
// SAFETY: `layer_gdpa` is an `extern "system"` fn whose prototype is exactly what the
// caller will cast the returned PFN back to for this name (*ProcAddr contract).
return unsafe { as_pfn(layer_gdpa as *const c_void) };
}
if device == vk::Device::null() {
return None;
@@ -530,23 +650,38 @@ unsafe extern "system" fn layer_gdpa(
Ok(g) => g,
Err(_) => return None,
};
g.get(&key(device.as_raw())).map(|d| d.next_gdpa)
// SAFETY: `device` is non-null, and vkGetDeviceProcAddr's valid-usage rules make it a
// live device handle — a dispatchable object whose first word is the dispatch key.
g.get(&unsafe { key(device.as_raw()) }).map(|d| d.next_gdpa)
};
next.and_then(|gdpa| gdpa(device, p_name))
// SAFETY: `next` is the down-chain GetDeviceProcAddr captured from the loader's link at
// create_device for this very chain; `p_name` is still valid NUL-terminated.
next.and_then(|gdpa| unsafe { gdpa(device, p_name) })
}
// ---- instance chain ----
/// Walk `p_ci`'s pNext chain for the loader's layer-link node.
///
/// # Safety
/// `p_ci` must point to a valid create-info struct whose `pNext` chain is a well-formed list in
/// which every node begins with the `{sType, pNext}` header — the loader builds exactly that
/// chain for the create call that invokes us.
unsafe fn find_instance_link(p_ci: *const c_void) -> *mut LayerInstanceCreateInfo {
let mut node = (*(p_ci as *const BaseIn)).p_next as *const BaseIn;
while !node.is_null() {
if (*node).s_type.as_raw() == LOADER_INSTANCE_CREATE_INFO {
let lci = node as *mut LayerInstanceCreateInfo;
if (*lci).function == VK_LAYER_LINK_INFO {
return lci;
// SAFETY: per this function's contract, `p_ci` and every non-null `pNext` reached from it
// point at nodes beginning with a {sType, pNext} header; a node whose sType says it is the
// loader's instance create-info really is a LayerInstanceCreateInfo (the loader wrote it).
unsafe {
let mut node = (*(p_ci as *const BaseIn)).p_next as *const BaseIn;
while !node.is_null() {
if (*node).s_type.as_raw() == LOADER_INSTANCE_CREATE_INFO {
let lci = node as *mut LayerInstanceCreateInfo;
if (*lci).function == VK_LAYER_LINK_INFO {
return lci;
}
}
node = (*node).p_next as *const BaseIn;
}
node = (*node).p_next as *const BaseIn;
}
ptr::null_mut()
}
@@ -556,40 +691,67 @@ unsafe extern "system" fn create_instance(
p_alloc: *const c_void,
p_inst: *mut vk::Instance,
) -> vk::Result {
let lci = find_instance_link(p_ci);
// SAFETY: the loader invokes this hook with a valid VkInstanceCreateInfo whose pNext chain
// it built — the chain find_instance_link's contract requires.
let lci = unsafe { find_instance_link(p_ci) };
if lci.is_null() {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
let link = (*lci).u;
if link.is_null() {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
let next_gipa = (*link).next_gipa;
let next_gpdpa = (*link).next_gpdpa;
(*lci).u = (*link).p_next;
// SAFETY: `lci` is non-null and points into the loader's live chain. Its `u` link (checked
// non-null before use) is this layer's link entry; reading the down-chain pointers out of it
// and advancing `(*lci).u` to the next link is the layer protocol every layer must follow
// before calling down.
let (next_gipa, next_gpdpa) = unsafe {
let link = (*lci).u;
if link.is_null() {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
let gipa = (*link).next_gipa;
let gpdpa = (*link).next_gpdpa;
(*lci).u = (*link).p_next;
(gipa, gpdpa)
};
// SAFETY: `next_gipa` is the loader-supplied down-chain GIPA; vkCreateInstance is a global
// command resolvable from the null instance, and FnCreateInstance mirrors its prototype.
let create: FnCreateInstance =
match resolve(next_gipa, vk::Instance::null(), c"vkCreateInstance") {
match unsafe { resolve(next_gipa, vk::Instance::null(), c"vkCreateInstance") } {
Some(f) => f,
None => return vk::Result::ERROR_INITIALIZATION_FAILED,
};
let res = create(p_ci, p_alloc, p_inst);
// SAFETY: forwarding the loader's own arguments unchanged to the down-chain create, which
// expects exactly this (p_ci with the link advanced, the caller's allocator, the caller's
// out-pointer).
let res = unsafe { create(p_ci, p_alloc, p_inst) };
if res != vk::Result::SUCCESS {
return res;
}
let inst = *p_inst;
let data = InstanceData {
instance: inst,
next_gipa,
next_gpdpa,
destroy_instance: resolve(next_gipa, inst, c"vkDestroyInstance"),
get_surface_formats: resolve(next_gipa, inst, c"vkGetPhysicalDeviceSurfaceFormatsKHR"),
get_surface_formats2: resolve(next_gipa, inst, c"vkGetPhysicalDeviceSurfaceFormats2KHR"),
create_win32_surface: resolve(next_gipa, inst, c"vkCreateWin32SurfaceKHR"),
destroy_surface: resolve(next_gipa, inst, c"vkDestroySurfaceKHR"),
// SAFETY: vkCreateInstance requires pInstance to be a valid pointer, and on SUCCESS the
// down-chain just wrote the new instance handle through it.
let inst = unsafe { *p_inst };
// SAFETY: `inst` is the live instance created above; `next_gipa` resolves its
// instance-level commands, and every Fn* typedef used here mirrors the prototype of the
// exact command name it is resolved from.
let data = unsafe {
InstanceData {
instance: inst,
next_gipa,
next_gpdpa,
destroy_instance: resolve(next_gipa, inst, c"vkDestroyInstance"),
get_surface_formats: resolve(next_gipa, inst, c"vkGetPhysicalDeviceSurfaceFormatsKHR"),
get_surface_formats2: resolve(
next_gipa,
inst,
c"vkGetPhysicalDeviceSurfaceFormats2KHR",
),
create_win32_surface: resolve(next_gipa, inst, c"vkCreateWin32SurfaceKHR"),
destroy_surface: resolve(next_gipa, inst, c"vkDestroySurfaceKHR"),
}
};
if let Ok(mut g) = instances().lock() {
g.insert(key(inst.as_raw()), data);
// SAFETY: `inst` is the live dispatchable handle created above — its first word is the
// dispatch key.
g.insert(unsafe { key(inst.as_raw()) }, data);
}
log("create_instance: hooked");
vk::Result::SUCCESS
@@ -602,26 +764,40 @@ unsafe extern "system" fn destroy_instance(inst: vk::Instance, p_alloc: *const c
let data = instances()
.lock()
.ok()
.and_then(|mut g| g.remove(&key(inst.as_raw())));
// SAFETY: `inst` is non-null, and vkDestroyInstance requires a live instance handle —
// still live during this call — whose first word is the dispatch key.
.and_then(|mut g| g.remove(&unsafe { key(inst.as_raw()) }));
if let Some(d) = data {
if let Some(f) = d.destroy_instance {
f(inst, p_alloc);
// SAFETY: `f` is the down-chain vkDestroyInstance resolved for this very instance at
// create time; forwarding the caller's own arguments unchanged.
unsafe { f(inst, p_alloc) };
}
}
}
// ---- device chain (pass-through; keeps device-level dispatch working) ----
/// Walk `p_ci`'s pNext chain for the loader's device layer-link node.
///
/// # Safety
/// Same contract as [`find_instance_link`]: `p_ci` must point to a valid create-info struct
/// whose `pNext` chain is a well-formed list of `{sType, pNext}`-headed nodes.
unsafe fn find_device_link(p_ci: *const c_void) -> *mut LayerDeviceCreateInfo {
let mut node = (*(p_ci as *const BaseIn)).p_next as *const BaseIn;
while !node.is_null() {
if (*node).s_type.as_raw() == LOADER_DEVICE_CREATE_INFO {
let lci = node as *mut LayerDeviceCreateInfo;
if (*lci).function == VK_LAYER_LINK_INFO {
return lci;
// SAFETY: per this function's contract, `p_ci` and every non-null `pNext` reached from it
// point at nodes beginning with a {sType, pNext} header; a node whose sType says it is the
// loader's device create-info really is a LayerDeviceCreateInfo (the loader wrote it).
unsafe {
let mut node = (*(p_ci as *const BaseIn)).p_next as *const BaseIn;
while !node.is_null() {
if (*node).s_type.as_raw() == LOADER_DEVICE_CREATE_INFO {
let lci = node as *mut LayerDeviceCreateInfo;
if (*lci).function == VK_LAYER_LINK_INFO {
return lci;
}
}
node = (*node).p_next as *const BaseIn;
}
node = (*node).p_next as *const BaseIn;
}
ptr::null_mut()
}
@@ -632,35 +808,54 @@ unsafe extern "system" fn create_device(
p_alloc: *const c_void,
p_dev: *mut vk::Device,
) -> vk::Result {
let lci = find_device_link(p_ci);
// SAFETY: the loader invokes this hook with a valid VkDeviceCreateInfo whose pNext chain it
// built — the chain find_device_link's contract requires.
let lci = unsafe { find_device_link(p_ci) };
if lci.is_null() {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
let link = (*lci).u;
if link.is_null() {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
let next_gipa = (*link).next_gipa;
let next_gdpa = (*link).next_gdpa;
(*lci).u = (*link).p_next;
// SAFETY: `lci` is non-null and points into the loader's live chain. Its `u` link (checked
// non-null before use) is this layer's link entry; reading the down-chain pointers and
// advancing `(*lci).u` is the layer protocol, exactly as in create_instance.
let (next_gipa, next_gdpa) = unsafe {
let link = (*lci).u;
if link.is_null() {
return vk::Result::ERROR_INITIALIZATION_FAILED;
}
let gipa = (*link).next_gipa;
let gdpa = (*link).next_gdpa;
(*lci).u = (*link).p_next;
(gipa, gdpa)
};
let inst = instances()
.lock()
.ok()
.and_then(|g| g.get(&key(pdev.as_raw())).map(|d| d.instance))
// SAFETY: vkCreateDevice requires `pdev` to be a live physical-device handle — a
// dispatchable object sharing its instance's dispatch table, so its first word is the
// same dispatch key create_instance stored.
.and_then(|g| g.get(&unsafe { key(pdev.as_raw()) }).map(|d| d.instance))
.unwrap_or(vk::Instance::null());
let create: FnCreateDevice = match resolve(next_gipa, inst, c"vkCreateDevice") {
// SAFETY: `next_gipa` is the loader-supplied down-chain GIPA for this create call, `inst` is
// the (possibly null) instance owning `pdev`, and FnCreateDevice mirrors vkCreateDevice's
// prototype.
let create: FnCreateDevice = match unsafe { resolve(next_gipa, inst, c"vkCreateDevice") } {
Some(f) => f,
None => return vk::Result::ERROR_INITIALIZATION_FAILED,
};
let res = create(pdev, p_ci, p_alloc, p_dev);
// SAFETY: forwarding the loader's own arguments unchanged to the down-chain create.
let res = unsafe { create(pdev, p_ci, p_alloc, p_dev) };
if res != vk::Result::SUCCESS {
return res;
}
let dev = *p_dev;
// SAFETY: vkCreateDevice requires pDevice to be a valid pointer, and on SUCCESS the
// down-chain just wrote the new device handle through it.
let dev = unsafe { *p_dev };
if let Ok(mut g) = devices().lock() {
g.insert(key(dev.as_raw()), DeviceData { next_gdpa });
// SAFETY: `dev` is the live dispatchable handle created above — its first word is the
// dispatch key.
g.insert(unsafe { key(dev.as_raw()) }, DeviceData { next_gdpa });
}
vk::Result::SUCCESS
}
@@ -674,19 +869,28 @@ unsafe extern "system" fn create_win32_surface(
p_surface: *mut vk::SurfaceKHR,
) -> vk::Result {
let down = instances().lock().ok().and_then(|g| {
g.get(&key(inst.as_raw()))
// SAFETY: vkCreateWin32SurfaceKHR requires `inst` to be a live instance handle — a
// dispatchable object whose first word is the dispatch key.
g.get(&unsafe { key(inst.as_raw()) })
.and_then(|d| d.create_win32_surface)
});
let down = match down {
Some(f) => f,
None => return vk::Result::ERROR_EXTENSION_NOT_PRESENT,
};
let res = down(inst, p_ci, p_alloc, p_surface);
// SAFETY: `down` is the down-chain vkCreateWin32SurfaceKHR resolved for this instance at
// create time; forwarding the caller's own arguments unchanged.
let res = unsafe { down(inst, p_ci, p_alloc, p_surface) };
if res == vk::Result::SUCCESS {
// VkWin32SurfaceCreateInfoKHR: sType@0, pNext@8, flags@16, hinstance@24, hwnd@32
let hwnd = *((p_ci as *const u8).add(32) as *const isize);
// SAFETY: the down-chain call succeeded, so `p_ci` pointed at a valid
// VkWin32SurfaceCreateInfoKHR for the whole call (the ICD just consumed it). On x86_64
// its layout is sType(4+4 pad)@0, pNext@8, flags(4+4 pad)@16, hinstance@24, hwnd@32 —
// so `p_ci + 32` is an in-bounds, 8-aligned read of the HWND field.
let hwnd = unsafe { *((p_ci as *const u8).add(32) as *const isize) };
if let Ok(mut m) = surface_hwnds().lock() {
m.insert((*p_surface).as_raw(), hwnd);
// SAFETY: vkCreateWin32SurfaceKHR requires pSurface to be a valid pointer, and on
// SUCCESS the down-chain just wrote the new surface handle through it.
m.insert(unsafe { *p_surface }.as_raw(), hwnd);
}
}
res
@@ -703,9 +907,16 @@ unsafe extern "system" fn destroy_surface(
let down = instances()
.lock()
.ok()
.and_then(|g| g.get(&key(inst.as_raw())).and_then(|d| d.destroy_surface));
// SAFETY: vkDestroySurfaceKHR requires `inst` to be a live instance handle — a
// dispatchable object whose first word is the dispatch key.
.and_then(|g| {
g.get(&unsafe { key(inst.as_raw()) })
.and_then(|d| d.destroy_surface)
});
if let Some(f) = down {
f(inst, surface, p_alloc);
// SAFETY: `f` is the down-chain vkDestroySurfaceKHR resolved for this instance at
// create time; forwarding the caller's own arguments unchanged.
unsafe { f(inst, surface, p_alloc) };
}
}
@@ -718,7 +929,9 @@ unsafe extern "system" fn get_surface_formats(
p_formats: *mut vk::SurfaceFormatKHR,
) -> vk::Result {
let down = instances().lock().ok().and_then(|g| {
g.get(&key(pdev.as_raw()))
// SAFETY: vkGetPhysicalDeviceSurfaceFormatsKHR requires `pdev` to be a live
// physical-device handle — a dispatchable object whose first word is the dispatch key.
g.get(&unsafe { key(pdev.as_raw()) })
.and_then(|d| d.get_surface_formats)
});
let down = match down {
@@ -727,13 +940,17 @@ unsafe extern "system" fn get_surface_formats(
};
let mut n = 0u32;
let r = down(pdev, surface, &mut n, ptr::null_mut());
// SAFETY: count-query form of the two-call idiom — null pFormats plus a count out-pointer
// backed by the live local `n`; the caller's pdev/surface are forwarded unchanged.
let r = unsafe { down(pdev, surface, &mut n, ptr::null_mut()) };
if r != vk::Result::SUCCESS {
return r;
}
let mut real = vec![vk::SurfaceFormatKHR::default(); n as usize];
if n > 0 {
let r = down(pdev, surface, &mut n, real.as_mut_ptr());
// SAFETY: `real` holds exactly `n` elements and `n` is passed as the in/out capacity, so
// the down-chain writes at most `n` entries and shrinks `n` to what it wrote.
let r = unsafe { down(pdev, surface, &mut n, real.as_mut_ptr()) };
if r != vk::Result::SUCCESS {
return r;
}
@@ -753,16 +970,24 @@ unsafe extern "system" fn get_surface_formats(
}
if p_formats.is_null() {
*p_count = aug.len() as u32;
// SAFETY: vkGetPhysicalDeviceSurfaceFormatsKHR requires pSurfaceFormatCount to be a
// valid u32 pointer in both call forms.
unsafe { *p_count = aug.len() as u32 };
return vk::Result::SUCCESS;
}
let m = (*p_count as usize).min(aug.len());
ptr::copy_nonoverlapping(aug.as_ptr(), p_formats, m);
*p_count = m as u32;
if m < aug.len() {
vk::Result::INCOMPLETE
} else {
vk::Result::SUCCESS
// SAFETY: with a non-null pSurfaceFormats the spec requires *pSurfaceFormatCount to be
// readable and pSurfaceFormats to point at at least that many elements; `m` is clamped to
// both that capacity and our own length, and the caller's buffer cannot overlap the
// freshly-allocated `aug`.
unsafe {
let m = (*p_count as usize).min(aug.len());
ptr::copy_nonoverlapping(aug.as_ptr(), p_formats, m);
*p_count = m as u32;
if m < aug.len() {
vk::Result::INCOMPLETE
} else {
vk::Result::SUCCESS
}
}
}
@@ -773,7 +998,9 @@ unsafe extern "system" fn get_surface_formats2(
p_formats: *mut c_void,
) -> vk::Result {
let down = instances().lock().ok().and_then(|g| {
g.get(&key(pdev.as_raw()))
// SAFETY: vkGetPhysicalDeviceSurfaceFormats2KHR requires `pdev` to be a live
// physical-device handle — a dispatchable object whose first word is the dispatch key.
g.get(&unsafe { key(pdev.as_raw()) })
.and_then(|d| d.get_surface_formats2)
});
let down = match down {
@@ -782,7 +1009,10 @@ unsafe extern "system" fn get_surface_formats2(
};
let mut n = 0u32;
let r = down(pdev, p_info, &mut n, ptr::null_mut());
// SAFETY: count-query form of the two-call idiom — null pSurfaceFormats plus a count
// out-pointer backed by the live local `n`; the caller's pdev/pSurfaceInfo are forwarded
// unchanged (the spec requires pSurfaceInfo to stay valid for the call).
let r = unsafe { down(pdev, p_info, &mut n, ptr::null_mut()) };
if r != vk::Result::SUCCESS {
return r;
}
@@ -794,15 +1024,21 @@ unsafe extern "system" fn get_surface_formats2(
})
.collect();
if n > 0 {
let r = down(pdev, p_info, &mut n, real.as_mut_ptr() as *mut c_void);
// SAFETY: `real` holds exactly `n` properly-stamped VkSurfaceFormat2KHR mirrors
// (SurfaceFormat2Raw layout-asserted at the type), and `n` is passed as the in/out
// capacity, so the down-chain writes at most `n` entries.
let r = unsafe { down(pdev, p_info, &mut n, real.as_mut_ptr() as *mut c_void) };
if r != vk::Result::SUCCESS {
return r;
}
}
real.truncate(n as usize);
// VkPhysicalDeviceSurfaceInfo2KHR: sType@0, pNext@8, surface@16
let surface = vk::SurfaceKHR::from_raw(*((p_info as *const u8).add(16) as *const u64));
// SAFETY: the spec requires pSurfaceInfo to point at a valid VkPhysicalDeviceSurfaceInfo2KHR
// for the whole call. On x86_64 its layout is sType(4+4 pad)@0, pNext@8, surface(u64)@16 —
// so `p_info + 16` is an in-bounds, 8-aligned read of the non-dispatchable surface handle.
let surface =
vk::SurfaceKHR::from_raw(unsafe { *((p_info as *const u8).add(16) as *const u64) });
let mut extras: Vec<vk::SurfaceFormatKHR> = Vec::new();
if !real.is_empty() && should_inject(surface) {
@@ -817,25 +1053,34 @@ unsafe extern "system" fn get_surface_formats2(
let total = real.len() + extras.len();
if p_formats.is_null() {
*p_count = total as u32;
// SAFETY: vkGetPhysicalDeviceSurfaceFormats2KHR requires pSurfaceFormatCount to be a
// valid u32 pointer in both call forms.
unsafe { *p_count = total as u32 };
return vk::Result::SUCCESS;
}
let m = (*p_count as usize).min(total);
let out = p_formats as *mut SurfaceFormat2Raw;
for i in 0..m {
let sf = if i < real.len() {
real[i].surface_format
// SAFETY: with a non-null pSurfaceFormats the spec requires *pSurfaceFormatCount to be
// readable and pSurfaceFormats to point at at least that many VkSurfaceFormat2KHR — whose
// layout SurfaceFormat2Raw mirrors (asserted at the type). `m` is clamped to both bounds.
// Only sType and surfaceFormat are stamped per element; each element's pNext chain is left
// exactly as the caller built it.
unsafe {
let m = (*p_count as usize).min(total);
let out = p_formats as *mut SurfaceFormat2Raw;
for i in 0..m {
let sf = if i < real.len() {
real[i].surface_format
} else {
extras[i - real.len()]
};
let dst = out.add(i);
(*dst).s_type = vk::StructureType::SURFACE_FORMAT_2_KHR;
(*dst).surface_format = sf;
}
*p_count = m as u32;
if m < total {
vk::Result::INCOMPLETE
} else {
extras[i - real.len()]
};
let dst = out.add(i);
(*dst).s_type = vk::StructureType::SURFACE_FORMAT_2_KHR;
(*dst).surface_format = sf;
}
*p_count = m as u32;
if m < total {
vk::Result::INCOMPLETE
} else {
vk::Result::SUCCESS
vk::Result::SUCCESS
}
}
}