From d41c1ff50a2871d194e522ce3a1a2c415c3e5356 Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Thu, 12 Oct 2023 18:12:29 -0700 Subject: [PATCH] style: restrict to one unsafe operation per block --- crates/sample-kmdf-driver/src/lib.rs | 81 +++++++++++++++---- crates/wdk-alloc/src/lib.rs | 18 ++++- crates/wdk-build/src/lib.rs | 2 + crates/wdk-build/src/utils.rs | 71 +++++++++++----- crates/wdk-macros/src/lib.rs | 54 ++++++++++--- .../macrotest/wdf_device_create.expanded.rs | 29 ++++--- ...device_create_device_interface.expanded.rs | 29 ++++--- .../macrotest/wdf_driver_create.expanded.rs | 33 +++++--- .../macrotest/wdf_device_create.expanded.rs | 29 ++++--- ...device_create_device_interface.expanded.rs | 29 ++++--- .../macrotest/wdf_driver_create.expanded.rs | 33 +++++--- crates/wdk-panic/src/lib.rs | 2 + crates/wdk-sys/src/lib.rs | 20 +++-- crates/wdk-sys/src/types.rs | 2 + crates/wdk/src/lib.rs | 2 + crates/wdk/src/wdf/spinlock.rs | 56 +++++++------ crates/wdk/src/wdf/timer.rs | 53 +++++++----- 17 files changed, 378 insertions(+), 165 deletions(-) diff --git a/crates/sample-kmdf-driver/src/lib.rs b/crates/sample-kmdf-driver/src/lib.rs index 255352b8..39297a47 100644 --- a/crates/sample-kmdf-driver/src/lib.rs +++ b/crates/sample-kmdf-driver/src/lib.rs @@ -4,11 +4,14 @@ #![no_std] #![cfg_attr(feature = "nightly", feature(hint_must_use))] #![deny(warnings)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(clippy::nursery)] #![deny(clippy::cargo)] +#![deny(clippy::undocumented_unsafe_blocks)] #![deny(clippy::unnecessary_safety_doc)] +#![deny(clippy::multiple_unsafe_ops_per_block)] extern crate alloc; @@ -28,6 +31,8 @@ use wdk_sys::{ NTSTATUS, PCUNICODE_STRING, ULONG, + UNICODE_STRING, + WCHAR, WDFDEVICE, WDFDEVICE_INIT, WDFDRIVER, @@ -54,6 +59,9 @@ pub unsafe extern "system" fn driver_entry( ) -> NTSTATUS { // This is an example of directly using DbgPrint binding to print let string = CString::new("Hello World!\n").unwrap(); + + // SAFETY: This is safe because `string` is a valid pointer to a null-terminated + // string unsafe { DbgPrint(string.as_ptr()); } @@ -81,24 +89,60 @@ pub unsafe extern "system" fn driver_entry( let driver_attributes = WDF_NO_OBJECT_ATTRIBUTES; let driver_handle_output = WDF_NO_HANDLE.cast::<*mut wdk_sys::WDFDRIVER__>(); - let wdf_driver_create_ntstatus = unsafe { - call_unsafe_wdf_function_binding!( + let wdf_driver_create_ntstatus; + // SAFETY: This is safe because: + // 1. `driver` is provided by `DriverEntry` and is never null + // 2. `registry_path` is provided by `DriverEntry` and is never null + // 3. `driver_attributes` is allowed to be null + // 4. `driver_config` is a valid pointer to a valid `WDF_DRIVER_CONFIG` + // 5. `driver_handle_output` is expected to be null + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + wdf_driver_create_ntstatus = call_unsafe_wdf_function_binding!( WdfDriverCreate, driver as wdk_sys::PDRIVER_OBJECT, registry_path, driver_attributes, &mut driver_config, driver_handle_output, - ) - }; + ); + } // Translate UTF16 string to rust string - let registry_path = String::from_utf16_lossy(unsafe { - slice::from_raw_parts( - (*registry_path).Buffer, - (*registry_path).Length as usize / core::mem::size_of_val(&(*(*registry_path).Buffer)), - ) - }); + let registry_path: UNICODE_STRING = + // SAFETY: This dereference is safe since `registry_path` is: + // * provided by `DriverEntry` and is never null + // * a valid pointer to a `UNICODE_STRING` + unsafe { *registry_path }; + let number_of_slice_elements = { + registry_path.Length as usize + / core::mem::size_of_val( + // SAFETY: This dereference is safe since `Buffer` is: + // * provided by `DriverEntry` and is never null + // * a valid pointer to `Buffer`'s type + &unsafe { *registry_path.Buffer }, + ) + }; + + let registry_path = String::from_utf16_lossy( + // SAFETY: This is safe because: + // 1. `registry_path.Buffer` is valid for reads for `number_of_slice_elements` * + // `core::mem::size_of::()` bytes, and is guaranteed to be aligned and it + // must be properly aligned. + // 2. `registry_path.Buffer` points to `number_of_slice_elements` consecutive + // properly initialized values of type `WCHAR`. + // 3. Windows does not mutate the memory referenced by the returned slice for for + // its entire lifetime. + // 4. The total size, `number_of_slice_elements` * `core::mem::size_of::()`, + // of the slice must be no larger than `isize::MAX`. This is proven by the below + // `debug_assert!`. + unsafe { + debug_assert!( + isize::try_from(number_of_slice_elements * core::mem::size_of::()).is_ok() + ); + slice::from_raw_parts(registry_path.Buffer, number_of_slice_elements) + }, + ); // It is much better to use the println macro that has an implementation in // wdk::print.rs to call DbgPrint. The println! implementation in @@ -117,16 +161,23 @@ extern "C" fn evt_driver_device_add( let mut device_handle_output: WDFDEVICE = WDF_NO_HANDLE.cast(); - let ntstatus = unsafe { - wdk_macros::call_unsafe_wdf_function_binding!( + let ntstatus; + // SAFETY: This is safe because: + // 1. `device_init` is provided by `EvtDriverDeviceAdd` and is never null + // 2. the argument receiving `WDF_NO_OBJECT_ATTRIBUTES` is allowed to be + // null + // 3. `device_handle_output` is expected to be null + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + ntstatus = wdk_macros::call_unsafe_wdf_function_binding!( WdfDeviceCreate, &mut device_init, WDF_NO_OBJECT_ATTRIBUTES, &mut device_handle_output, - ) - }; - println!("WdfDeviceCreate NTSTATUS: {ntstatus:#02x}"); + ); + } + println!("WdfDeviceCreate NTSTATUS: {ntstatus:#02x}"); ntstatus } diff --git a/crates/wdk-alloc/src/lib.rs b/crates/wdk-alloc/src/lib.rs index 6b8f930c..ff06a741 100644 --- a/crates/wdk-alloc/src/lib.rs +++ b/crates/wdk-alloc/src/lib.rs @@ -17,10 +17,12 @@ #![no_std] #![deny(warnings)] #![deny(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(clippy::nursery)] #![deny(clippy::cargo)] +#![deny(clippy::multiple_unsafe_ops_per_block)] #![deny(clippy::undocumented_unsafe_blocks)] #![deny(clippy::unnecessary_safety_doc)] #![deny(rustdoc::broken_intra_doc_links)] @@ -45,6 +47,10 @@ use wdk_sys::{ /// Allocator implementation to use with `#[global_allocator]` to allow use of /// [`core::alloc`]. +/// +/// # Safety +/// This allocator is only safe to use for allocations happening at `IRQL` <= +/// `DISPATCH_LEVEL` pub struct WDKAllocator; // The value of memory tags are stored in little-endian order, so it is @@ -67,7 +73,11 @@ lazy_static! { // supported) unsafe impl GlobalAlloc for WDKAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let ptr = ExAllocatePool2(POOL_FLAG_NON_PAGED, layout.size() as SIZE_T, *RUST_TAG); + let ptr = + // SAFETY: `ExAllocatePool2` is safe to call from any `IRQL` <= `DISPATCH_LEVEL` since its allocating from `POOL_FLAG_NON_PAGED` + unsafe { + ExAllocatePool2(POOL_FLAG_NON_PAGED, layout.size() as SIZE_T, *RUST_TAG) + }; if ptr.is_null() { return core::ptr::null_mut(); } @@ -75,6 +85,10 @@ unsafe impl GlobalAlloc for WDKAllocator { } unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - ExFreePool(ptr.cast()); + // SAFETY: `ExFreePool` is safe to call from any `IRQL` <= `DISPATCH_LEVEL` + // since its freeing memory allocated from `POOL_FLAG_NON_PAGED` in `alloc` + unsafe { + ExFreePool(ptr.cast()); + } } } diff --git a/crates/wdk-build/src/lib.rs b/crates/wdk-build/src/lib.rs index 0a5fabe2..e86108c3 100644 --- a/crates/wdk-build/src/lib.rs +++ b/crates/wdk-build/src/lib.rs @@ -12,10 +12,12 @@ #![cfg_attr(nightly_toolchain, feature(assert_matches))] #![deny(warnings)] #![deny(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(clippy::nursery)] #![deny(clippy::cargo)] +#![deny(clippy::multiple_unsafe_ops_per_block)] #![deny(clippy::undocumented_unsafe_blocks)] #![deny(clippy::unnecessary_safety_doc)] #![deny(rustdoc::broken_intra_doc_links)] diff --git a/crates/wdk-build/src/utils.rs b/crates/wdk-build/src/utils.rs index b9e76ba1..d8fa6fcb 100644 --- a/crates/wdk-build/src/utils.rs +++ b/crates/wdk-build/src/utils.rs @@ -170,10 +170,18 @@ fn read_registry_key_string_value( ) -> Option { let mut opened_key_handle = HKEY::default(); let mut len = 0; - // SAFETY: FIXME seperate unsafe blocks - unsafe { - if RegOpenKeyExA(key_handle, sub_key, 0, KEY_READ, &mut opened_key_handle).is_ok() { - if RegGetValueA( + if + // SAFETY: `&mut opened_key_handle` is coerced to a &raw mut, so the address passed as the + // argument is always valid. `&mut opened_key_handle` is coerced to a pointer of the correct + // type. + unsafe { RegOpenKeyExA(key_handle, sub_key, 0, KEY_READ, &mut opened_key_handle) }.is_ok() { + if + // SAFETY: `opened_key_handle` is valid key opened with the `KEY_QUERY_VALUE` access right + // (included in `KEY_READ`). `&mut len` is coerced to a &raw mut, so the address passed as + // the argument is always valid. `&mut len` is coerced to a pointer of the correct + // type. + unsafe { + RegGetValueA( opened_key_handle, None, value, @@ -182,10 +190,19 @@ fn read_registry_key_string_value( None, Some(&mut len), ) - .is_ok() - { - let mut buffer = vec![0u8; len as usize]; - if RegGetValueA( + } + .is_ok() + { + let mut buffer = vec![0u8; len as usize]; + if + // SAFETY: `opened_key_handle` is valid key opened with the `KEY_QUERY_VALUE` access + // right (included in `KEY_READ`). `&mut buffer` is coerced to a &raw mut, + // so the address passed as the argument is always valid. `&mut buffer` is + // coerced to a pointer of the correct type. `&mut len` is coerced to a &raw + // mut, so the address passed as the argument is always valid. `&mut len` is + // coerced to a pointer of the correct type. + unsafe { + RegGetValueA( opened_key_handle, None, value, @@ -194,21 +211,31 @@ fn read_registry_key_string_value( Some(buffer.as_mut_ptr().cast()), Some(&mut len), ) - .is_ok() - { - RegCloseKey(opened_key_handle) - .expect("opened_key_handle should be successfully closed"); - return Some( - CStr::from_bytes_with_nul_unchecked(&buffer[..len as usize]) - .to_str() - .expect("Registry value should be parseable as utf8") - .to_string(), - ); - } } - RegCloseKey(opened_key_handle) - .expect(r"opened_key_handle should be successfully closed"); + .is_ok() + { + // SAFETY: `opened_key_handle` is valid opened key that was opened by + // `RegOpenKeyExA` + unsafe { RegCloseKey(opened_key_handle) } + .expect("opened_key_handle should be successfully closed"); + return Some( + CStr::from_bytes_with_nul(&buffer[..len as usize]) + .expect( + "RegGetValueA should always return a null terminated string. The read \ + string (REG_SZ) from the registry should not contain any interior \ + nulls.", + ) + .to_str() + .expect("Registry value should be parseable as UTF8") + .to_string(), + ); + } } + + // SAFETY: `opened_key_handle` is valid opened key that was opened by + // `RegOpenKeyExA` + unsafe { RegCloseKey(opened_key_handle) } + .expect(r"opened_key_handle should be successfully closed"); } None } @@ -304,7 +331,7 @@ mod tests { #[test] fn read_reg_key_programfilesdir() { let program_files_dir = - // SAFETY: FOLDERID_ProgramFiles is a constant from the windows crate, so dereference a pointer re-borrowed from its reference is always valid + // SAFETY: FOLDERID_ProgramFiles is a constant from the windows crate, so the pointer (resulting from its reference being coerced) is always valid to be dereferenced unsafe { SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, None) } .expect("Program Files Folder should always resolve via SHGetKnownFolderPath."); diff --git a/crates/wdk-macros/src/lib.rs b/crates/wdk-macros/src/lib.rs index 11cfcfe1..84dc769e 100644 --- a/crates/wdk-macros/src/lib.rs +++ b/crates/wdk-macros/src/lib.rs @@ -6,10 +6,12 @@ #![cfg_attr(feature = "nightly", feature(hint_must_use))] #![deny(warnings)] #![deny(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(clippy::nursery)] #![deny(clippy::cargo)] +#![deny(clippy::multiple_unsafe_ops_per_block)] #![deny(clippy::undocumented_unsafe_blocks)] #![deny(clippy::unnecessary_safety_doc)] #![deny(rustdoc::broken_intra_doc_links)] @@ -41,6 +43,11 @@ use syn::{ /// from the WDF function table, and then calls it with the arguments passed to /// it /// +/// # Safety +/// Function arguments must abide by any rules outlined in the WDF +/// documentation. This macro does not perform any validation of the arguments +/// passed to it., beyond type validation. +/// /// # Examples /// /// ```rust, no_run @@ -70,6 +77,7 @@ use syn::{ /// } /// } /// ``` +#[allow(clippy::unnecessary_safety_doc)] #[proc_macro] pub fn call_unsafe_wdf_function_binding(input_tokens: TokenStream) -> TokenStream { call_unsafe_wdf_function_binding_impl(TokenStream2::from(input_tokens)).into() @@ -108,20 +116,48 @@ fn call_unsafe_wdf_function_binding_impl(input_tokens: TokenStream2) -> TokenStr Err(err) => return err.to_compile_error(), }; + // let inner_attribute_macros = proc_macro2::TokenStream::from_str( + // "#![allow(unused_unsafe)]\n\ + // #![allow(clippy::multiple_unsafe_ops_per_block)]", + // ).expect("inner_attribute_macros must be convertible to a valid + // TokenStream"); + let wdf_function_call_tokens = quote! { { + // Force the macro to require an unsafe block + unsafe fn force_unsafe(){} + force_unsafe(); + // Get handle to WDF function from the function table - let wdf_function: wdk_sys::#function_pointer_type = Some(core::mem::transmute( - // FIXME: investigate why _WDFFUNCENUM does not have a generated type alias without the underscore prefix - wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::#function_table_index as usize], - )); + let wdf_function: wdk_sys::#function_pointer_type = Some( + // SAFETY: This `transmute` from a no-argument function pointer to a function pointer with the correct + // arguments for the WDF function is safe befause WDF maintains the strict mapping between the + // function table index and the correct function pointer type. + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + core::mem::transmute( + // FIXME: investigate why _WDFFUNCENUM does not have a generated type alias without the underscore prefix + wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::#function_table_index as usize], + ) + } + ); - // Call the WDF function with the supplied args. This mirrors what happens in the inlined WDF function in the various wdf headers(ex. wdfdriver.h) + // Call the WDF function with the supplied args. This mirrors what happens in the inlined WDF function in + // the various wdf headers(ex. wdfdriver.h) if let Some(wdf_function) = wdf_function { - (wdf_function)( - wdk_sys::WdfDriverGlobals, - #function_arguments - ) + // SAFETY: The WDF function pointer is always valid because its an entry in + // `wdk_sys::WDF_FUNCTION_TABLE` indexed by `function_table_index` and guarded by the type-safety of + // `function_pointer_type`. The passed arguments are also guaranteed to be of a compatible type due to + // `function_pointer_type`. + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + (wdf_function)( + wdk_sys::WdfDriverGlobals, + #function_arguments + ) + } } else { unreachable!("Option should never be None"); } diff --git a/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create.expanded.rs b/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create.expanded.rs index d8bd66c1..e368767f 100644 --- a/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create.expanded.rs +++ b/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create.expanded.rs @@ -8,19 +8,28 @@ extern "C" fn evt_driver_device_add( let mut device_handle_output: WDFDEVICE = WDF_NO_HANDLE.cast(); unsafe { core::hint::must_use({ + unsafe fn force_unsafe() {} + force_unsafe(); let wdf_function: wdk_sys::PFN_WDFDEVICECREATE = Some( - core::mem::transmute( - wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateTableIndex - as usize], - ), + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + core::mem::transmute( + wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateTableIndex + as usize], + ) + }, ); if let Some(wdf_function) = wdf_function { - (wdf_function)( - wdk_sys::WdfDriverGlobals, - &mut device_init, - WDF_NO_OBJECT_ATTRIBUTES, - &mut device_handle_output, - ) + #[allow(unused_unsafe)] #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + (wdf_function)( + wdk_sys::WdfDriverGlobals, + &mut device_init, + WDF_NO_OBJECT_ATTRIBUTES, + &mut device_handle_output, + ) + } } else { { ::core::panicking::panic_fmt( diff --git a/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create_device_interface.expanded.rs b/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create_device_interface.expanded.rs index bf9acff4..d1b07eb8 100644 --- a/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create_device_interface.expanded.rs +++ b/crates/wdk-macros/tests/nightly/macrotest/wdf_device_create_device_interface.expanded.rs @@ -10,19 +10,28 @@ const GUID_DEVINTERFACE_COMPORT: GUID = GUID { fn create_device_interface(wdf_device: WDFDEVICE) -> NTSTATUS { unsafe { core::hint::must_use({ + unsafe fn force_unsafe() {} + force_unsafe(); let wdf_function: wdk_sys::PFN_WDFDEVICECREATEDEVICEINTERFACE = Some( - core::mem::transmute( - wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateDeviceInterfaceTableIndex - as usize], - ), + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + core::mem::transmute( + wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateDeviceInterfaceTableIndex + as usize], + ) + }, ); if let Some(wdf_function) = wdf_function { - (wdf_function)( - wdk_sys::WdfDriverGlobals, - wdf_device, - &GUID_DEVINTERFACE_COMPORT, - core::ptr::null(), - ) + #[allow(unused_unsafe)] #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + (wdf_function)( + wdk_sys::WdfDriverGlobals, + wdf_device, + &GUID_DEVINTERFACE_COMPORT, + core::ptr::null(), + ) + } } else { { ::core::panicking::panic_fmt( diff --git a/crates/wdk-macros/tests/nightly/macrotest/wdf_driver_create.expanded.rs b/crates/wdk-macros/tests/nightly/macrotest/wdf_driver_create.expanded.rs index faefb727..9764e38a 100644 --- a/crates/wdk-macros/tests/nightly/macrotest/wdf_driver_create.expanded.rs +++ b/crates/wdk-macros/tests/nightly/macrotest/wdf_driver_create.expanded.rs @@ -13,21 +13,30 @@ pub extern "system" fn driver_entry( let driver_handle_output = WDF_NO_HANDLE as *mut WDFDRIVER; unsafe { core::hint::must_use({ + unsafe fn force_unsafe() {} + force_unsafe(); let wdf_function: wdk_sys::PFN_WDFDRIVERCREATE = Some( - core::mem::transmute( - wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDriverCreateTableIndex - as usize], - ), + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + core::mem::transmute( + wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDriverCreateTableIndex + as usize], + ) + }, ); if let Some(wdf_function) = wdf_function { - (wdf_function)( - wdk_sys::WdfDriverGlobals, - driver as PDRIVER_OBJECT, - registry_path, - WDF_NO_OBJECT_ATTRIBUTES, - &mut driver_config, - driver_handle_output, - ) + #[allow(unused_unsafe)] #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + (wdf_function)( + wdk_sys::WdfDriverGlobals, + driver as PDRIVER_OBJECT, + registry_path, + WDF_NO_OBJECT_ATTRIBUTES, + &mut driver_config, + driver_handle_output, + ) + } } else { { ::core::panicking::panic_fmt( diff --git a/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create.expanded.rs b/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create.expanded.rs index a6380517..d667df58 100644 --- a/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create.expanded.rs +++ b/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create.expanded.rs @@ -7,19 +7,28 @@ extern "C" fn evt_driver_device_add( let mut device_handle_output: WDFDEVICE = WDF_NO_HANDLE.cast(); unsafe { { + unsafe fn force_unsafe() {} + force_unsafe(); let wdf_function: wdk_sys::PFN_WDFDEVICECREATE = Some( - core::mem::transmute( - wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateTableIndex - as usize], - ), + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + core::mem::transmute( + wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateTableIndex + as usize], + ) + }, ); if let Some(wdf_function) = wdf_function { - (wdf_function)( - wdk_sys::WdfDriverGlobals, - &mut device_init, - WDF_NO_OBJECT_ATTRIBUTES, - &mut device_handle_output, - ) + #[allow(unused_unsafe)] #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + (wdf_function)( + wdk_sys::WdfDriverGlobals, + &mut device_init, + WDF_NO_OBJECT_ATTRIBUTES, + &mut device_handle_output, + ) + } } else { { ::core::panicking::panic_fmt( diff --git a/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create_device_interface.expanded.rs b/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create_device_interface.expanded.rs index 426f3c69..2fa5f7bf 100644 --- a/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create_device_interface.expanded.rs +++ b/crates/wdk-macros/tests/non-nightly/macrotest/wdf_device_create_device_interface.expanded.rs @@ -9,19 +9,28 @@ const GUID_DEVINTERFACE_COMPORT: GUID = GUID { fn create_device_interface(wdf_device: WDFDEVICE) -> NTSTATUS { unsafe { { + unsafe fn force_unsafe() {} + force_unsafe(); let wdf_function: wdk_sys::PFN_WDFDEVICECREATEDEVICEINTERFACE = Some( - core::mem::transmute( - wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateDeviceInterfaceTableIndex - as usize], - ), + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + core::mem::transmute( + wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDeviceCreateDeviceInterfaceTableIndex + as usize], + ) + }, ); if let Some(wdf_function) = wdf_function { - (wdf_function)( - wdk_sys::WdfDriverGlobals, - wdf_device, - &GUID_DEVINTERFACE_COMPORT, - core::ptr::null(), - ) + #[allow(unused_unsafe)] #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + (wdf_function)( + wdk_sys::WdfDriverGlobals, + wdf_device, + &GUID_DEVINTERFACE_COMPORT, + core::ptr::null(), + ) + } } else { { ::core::panicking::panic_fmt( diff --git a/crates/wdk-macros/tests/non-nightly/macrotest/wdf_driver_create.expanded.rs b/crates/wdk-macros/tests/non-nightly/macrotest/wdf_driver_create.expanded.rs index 7a66de49..8370b086 100644 --- a/crates/wdk-macros/tests/non-nightly/macrotest/wdf_driver_create.expanded.rs +++ b/crates/wdk-macros/tests/non-nightly/macrotest/wdf_driver_create.expanded.rs @@ -12,21 +12,30 @@ pub extern "system" fn driver_entry( let driver_handle_output = WDF_NO_HANDLE as *mut WDFDRIVER; unsafe { { + unsafe fn force_unsafe() {} + force_unsafe(); let wdf_function: wdk_sys::PFN_WDFDRIVERCREATE = Some( - core::mem::transmute( - wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDriverCreateTableIndex - as usize], - ), + #[allow(unused_unsafe)] + #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + core::mem::transmute( + wdk_sys::WDF_FUNCTION_TABLE[wdk_sys::_WDFFUNCENUM::WdfDriverCreateTableIndex + as usize], + ) + }, ); if let Some(wdf_function) = wdf_function { - (wdf_function)( - wdk_sys::WdfDriverGlobals, - driver as PDRIVER_OBJECT, - registry_path, - WDF_NO_OBJECT_ATTRIBUTES, - &mut driver_config, - driver_handle_output, - ) + #[allow(unused_unsafe)] #[allow(clippy::multiple_unsafe_ops_per_block)] + unsafe { + (wdf_function)( + wdk_sys::WdfDriverGlobals, + driver as PDRIVER_OBJECT, + registry_path, + WDF_NO_OBJECT_ATTRIBUTES, + &mut driver_config, + driver_handle_output, + ) + } } else { { ::core::panicking::panic_fmt( diff --git a/crates/wdk-panic/src/lib.rs b/crates/wdk-panic/src/lib.rs index c0e81179..1dfaf872 100644 --- a/crates/wdk-panic/src/lib.rs +++ b/crates/wdk-panic/src/lib.rs @@ -5,10 +5,12 @@ #![no_std] #![deny(warnings)] #![deny(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(clippy::nursery)] #![deny(clippy::cargo)] +#![deny(clippy::multiple_unsafe_ops_per_block)] #![deny(clippy::undocumented_unsafe_blocks)] #![deny(clippy::unnecessary_safety_doc)] #![deny(rustdoc::broken_intra_doc_links)] diff --git a/crates/wdk-sys/src/lib.rs b/crates/wdk-sys/src/lib.rs index 8deb561d..01ed616d 100644 --- a/crates/wdk-sys/src/lib.rs +++ b/crates/wdk-sys/src/lib.rs @@ -6,10 +6,12 @@ #![no_std] #![deny(warnings)] #![deny(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(clippy::nursery)] #![deny(clippy::cargo)] +#![deny(clippy::multiple_unsafe_ops_per_block)] #![deny(clippy::undocumented_unsafe_blocks)] #![deny(clippy::unnecessary_safety_doc)] #![deny(rustdoc::broken_intra_doc_links)] @@ -57,12 +59,11 @@ pub extern "system" fn __CxxFrameHandler3() -> i32 { lazy_static! { #[allow(missing_docs)] pub static ref WDF_FUNCTION_TABLE: &'static [WDFFUNC] = { - debug_assert!( - isize::try_from( - // SAFETY: `WdfFunctionCount` is generated as a mutable static, but is not supposed to be mutated by WDF. - unsafe { WdfFunctionCount } as usize * core::mem::size_of::() - ).is_ok() - ); + // SAFETY: `WdfFunctions_01033` is generated as a mutable static, but is not supposed to be ever mutated by WDF. + let wdf_function_table = unsafe { WdfFunctions_01033 }; + + // SAFETY: `WdfFunctionCount` is generated as a mutable static, but is not supposed to be ever mutated by WDF. + let wdf_function_count = unsafe { WdfFunctionCount } as usize; // SAFETY: This is safe because: // 1. `WdfFunctions_01033` is valid for reads for `WdfFunctionCount` * `core::mem::size_of::()` @@ -71,8 +72,11 @@ lazy_static! { // type `WDFFUNC`. // 3. WDF does not mutate the memory referenced by the returned slice for for its entire `'static' lifetime. // 4. The total size, `WdfFunctionCount` * `core::mem::size_of::()`, of the slice must be no - // larger than `isize::MAX`. This is proven by the above `debug_assert!`. - unsafe { core::slice::from_raw_parts(WdfFunctions_01033, WdfFunctionCount as usize) } + // larger than `isize::MAX`. This is proven by the below `debug_assert!`. + unsafe { + debug_assert!(isize::try_from(wdf_function_count * core::mem::size_of::()).is_ok()); + core::slice::from_raw_parts(wdf_function_table, wdf_function_count) + } }; } diff --git a/crates/wdk-sys/src/types.rs b/crates/wdk-sys/src/types.rs index 35cacbaa..fa150237 100644 --- a/crates/wdk-sys/src/types.rs +++ b/crates/wdk-sys/src/types.rs @@ -5,6 +5,7 @@ #[allow(non_upper_case_globals)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] +#[allow(unsafe_op_in_unsafe_fn)] #[allow(clippy::cast_lossless)] #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cognitive_complexity)] @@ -14,6 +15,7 @@ #[allow(clippy::missing_safety_doc)] #[allow(clippy::missing_const_for_fn)] #[allow(clippy::module_name_repetitions)] +#[allow(clippy::multiple_unsafe_ops_per_block)] #[allow(clippy::must_use_candidate)] #[allow(clippy::not_unsafe_ptr_arg_deref)] #[allow(clippy::ptr_as_ptr)] diff --git a/crates/wdk/src/lib.rs b/crates/wdk/src/lib.rs index daef75e8..21dd37d6 100644 --- a/crates/wdk/src/lib.rs +++ b/crates/wdk/src/lib.rs @@ -8,10 +8,12 @@ #![cfg_attr(feature = "nightly", feature(hint_must_use))] #![deny(warnings)] #![deny(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(clippy::nursery)] #![deny(clippy::cargo)] +#![deny(clippy::multiple_unsafe_ops_per_block)] #![deny(clippy::undocumented_unsafe_blocks)] #![deny(clippy::unnecessary_safety_doc)] #![deny(rustdoc::broken_intra_doc_links)] diff --git a/crates/wdk/src/wdf/spinlock.rs b/crates/wdk/src/wdf/spinlock.rs index c047db08..a5a2e8ce 100644 --- a/crates/wdk/src/wdf/spinlock.rs +++ b/crates/wdk/src/wdf/spinlock.rs @@ -31,15 +31,19 @@ impl SpinLock { let mut spin_lock = Self { wdf_spin_lock: core::ptr::null_mut(), }; - let nt_status = - // SAFETY: The resulting ffi object is stored in a private member and not accessible outside of this module, and this module guarantees that it is always in a valid state. - unsafe { - macros::call_unsafe_wdf_function_binding!( - WdfSpinLockCreate, - attributes, - &mut spin_lock.wdf_spin_lock, - ) - }; + + let nt_status; + // SAFETY: The resulting ffi object is stored in a private member and not + // accessible outside of this module, and this module guarantees that it is + // always in a valid state. + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + nt_status = macros::call_unsafe_wdf_function_binding!( + WdfSpinLockCreate, + attributes, + &mut spin_lock.wdf_spin_lock, + ); + } nt_success(nt_status).then_some(spin_lock).ok_or(nt_status) } @@ -55,25 +59,27 @@ impl SpinLock { /// Acquire the spinlock pub fn acquire(&self) { - let [()] = - // SAFETY: `wdf_spin_lock` is a private member of `SpinLock`, originally created by WDF, and this module guarantees that it is always in a valid state. - unsafe { - [macros::call_unsafe_wdf_function_binding!( - WdfSpinLockAcquire, - self.wdf_spin_lock - )] - }; + // SAFETY: `wdf_spin_lock` is a private member of `SpinLock`, originally created + // by WDF, and this module guarantees that it is always in a valid state. + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + let [()] = [macros::call_unsafe_wdf_function_binding!( + WdfSpinLockAcquire, + self.wdf_spin_lock + )]; + } } /// Release the spinlock pub fn release(&self) { - let [()] = - // SAFETY: `wdf_spin_lock` is a private member of `SpinLock`, originally created by WDF, and this module guarantees that it is always in a valid state. - unsafe { - [macros::call_unsafe_wdf_function_binding!( - WdfSpinLockRelease, - self.wdf_spin_lock - )] - }; + // SAFETY: `wdf_spin_lock` is a private member of `SpinLock`, originally created + // by WDF, and this module guarantees that it is always in a valid state. + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + let [()] = [macros::call_unsafe_wdf_function_binding!( + WdfSpinLockRelease, + self.wdf_spin_lock + )]; + } } } diff --git a/crates/wdk/src/wdf/timer.rs b/crates/wdk/src/wdf/timer.rs index 237e8ebb..c3e1bbfa 100644 --- a/crates/wdk/src/wdf/timer.rs +++ b/crates/wdk/src/wdf/timer.rs @@ -22,16 +22,20 @@ impl Timer { let mut timer = Self { wdf_timer: core::ptr::null_mut(), }; - let nt_status = - // SAFETY: The resulting ffi object is stored in a private member and not accessible outside of this module, and this module guarantees that it is always in a valid state. - unsafe { - macros::call_unsafe_wdf_function_binding!( - WdfTimerCreate, - timer_config, - attributes, - &mut timer.wdf_timer, - ) - }; + + let nt_status; + // SAFETY: The resulting ffi object is stored in a private member and not + // accessible outside of this module, and this module guarantees that it is + // always in a valid state. + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + nt_status = macros::call_unsafe_wdf_function_binding!( + WdfTimerCreate, + timer_config, + attributes, + &mut timer.wdf_timer, + ); + } nt_success(nt_status).then_some(timer).ok_or(nt_status) } @@ -49,21 +53,30 @@ impl Timer { /// Start the [`Timer`]'s clock pub fn start(&self, due_time: i64) -> bool { - let result = - // SAFETY: `wdf_timer` is a private member of `Timer`, originally created by WDF, and this module guarantees that it is always in a valid state. - unsafe { - macros::call_unsafe_wdf_function_binding!(WdfTimerStart, self.wdf_timer, due_time) - }; + let result; + // SAFETY: `wdf_timer` is a private member of `Timer`, originally created by + // WDF, and this module guarantees that it is always in a valid state. + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + result = + macros::call_unsafe_wdf_function_binding!(WdfTimerStart, self.wdf_timer, due_time); + } result != 0 } /// Stop the [`Timer`]'s clock pub fn stop(&self, wait: bool) -> bool { - let result = - // SAFETY: `wdf_timer` is a private member of `Timer`, originally created by WDF, and this module guarantees that it is always in a valid state. - unsafe { - macros::call_unsafe_wdf_function_binding!(WdfTimerStop, self.wdf_timer, u8::from(wait)) - }; + let result; + // SAFETY: `wdf_timer` is a private member of `Timer`, originally created by + // WDF, and this module guarantees that it is always in a valid state. + unsafe { + #![allow(clippy::multiple_unsafe_ops_per_block)] + result = macros::call_unsafe_wdf_function_binding!( + WdfTimerStop, + self.wdf_timer, + u8::from(wait) + ); + } result != 0 } }