Fix double drop raised in issue #13

Tested with the wdk_mutex_test crate and added new test types with inner boxed data, all tests passed.
This commit is contained in:
flux
2026-01-24 10:24:45 +00:00
parent a0ab28a4fc
commit 8772a4f949
4 changed files with 51 additions and 17 deletions
Generated
+1 -1
View File
@@ -741,7 +741,7 @@ dependencies = [
[[package]]
name = "wdk-mutex"
version = "1.3.1"
version = "1.3.2"
dependencies = [
"wdk",
"wdk-alloc",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "wdk-mutex"
version = "1.3.1"
version = "1.3.2"
edition = "2021"
authors = ["Flux Sec <fluxsec@proton.me>"]
license = "MIT"
+26 -9
View File
@@ -2,10 +2,7 @@
use alloc::boxed::Box;
use core::{
ffi::c_void,
fmt::Display,
ops::{Deref, DerefMut},
ptr::{self, drop_in_place},
ffi::c_void, fmt::Display, mem::ManuallyDrop, ops::{Deref, DerefMut}, ptr::{self, drop_in_place}
};
use wdk_sys::{
ntddk::{
@@ -238,6 +235,10 @@ impl<T> FastMutex<T> {
/// consider using [`Self::to_owned_box`] instead.
///
/// # Safety
///
/// This function moves `T` out of the mutex without running `Drop` on the original
/// in-place value. The returned `T` remains fully owned by the caller and will be
/// dropped normally.
///
/// - **Single Ownership Guarantee:** After calling [`Self::to_owned`], ensure that
/// no other references (especially static or global ones) attempt to access the
@@ -246,7 +247,7 @@ impl<T> FastMutex<T> {
/// - **Exclusive Access:** This function should only be called when you can guarantee
/// that there will be no further access to the protected `T`. Violating this can
/// lead to undefined behavior since the memory is freed after the call.
///
///
/// # Example
///
/// ```
@@ -256,11 +257,17 @@ impl<T> FastMutex<T> {
/// }
/// ```
pub unsafe fn to_owned(self) -> T {
let data_read = unsafe { ptr::read(&(*self.inner).data) };
let manually_dropped = ManuallyDrop::new(self);
let data_read = unsafe { ptr::read(&(*manually_dropped.inner).data) };
// Free the mutex allocation without using drop semantics which could cause an
// accidental double drop of the underlying `T`.
unsafe { ExFreePool(manually_dropped.inner as _) };
data_read
}
/// Consumes the mutex and returns an owned `Box<T>` containing the protected data (`T`).
/// Consumes the mutex and returns an owned `Box<T>` containing the protected data (`T`).
///
/// This method is an alternative to [`Self::to_owned`] and is particularly useful when
/// dealing with large data types. By returning a `Box<T>`, the data is pool-allocated,
@@ -268,6 +275,10 @@ impl<T> FastMutex<T> {
///
/// # Safety
///
/// This function moves `T` out of the mutex without running `Drop` on the original
/// in-place value. The returned `T` remains fully owned by the caller and will be
/// dropped normally.
///
/// - **Single Ownership Guarantee:** After calling [`Self::to_owned_box`], ensure that
/// no other references (especially static or global ones) attempt to access the
/// underlying mutex. This is because the mutexes memory is deallocated once this
@@ -275,7 +286,7 @@ impl<T> FastMutex<T> {
/// - **Exclusive Access:** This function should only be called when you can guarantee
/// that there will be no further access to the protected `T`. Violating this can
/// lead to undefined behavior since the memory is freed after the call.
///
///
/// # Example
///
/// ```rust
@@ -285,7 +296,13 @@ impl<T> FastMutex<T> {
/// }
/// ```
pub unsafe fn to_owned_box(self) -> Box<T> {
let data_read = unsafe { ptr::read(&(*self.inner).data) };
let manually_dropped = ManuallyDrop::new(self);
let data_read = unsafe { ptr::read(&(*manually_dropped.inner).data) };
// Free the mutex allocation without using drop semantics which could cause an
// accidental double drop of the underlying `T`.
unsafe { ExFreePool(manually_dropped.inner as _) };
Box::new(data_read)
}
}
+23 -6
View File
@@ -2,10 +2,7 @@
use alloc::boxed::Box;
use core::{
ffi::c_void,
fmt::Display,
ops::{Deref, DerefMut},
ptr::{self, drop_in_place, null_mut},
ffi::c_void, fmt::Display, mem::ManuallyDrop, ops::{Deref, DerefMut}, ptr::{self, drop_in_place, null_mut}
};
use wdk_sys::{
ntddk::{
@@ -231,6 +228,10 @@ impl<T> KMutex<T> {
///
/// # Safety
///
/// This function moves `T` out of the mutex without running `Drop` on the original
/// in-place value. The returned `T` remains fully owned by the caller and will be
/// dropped normally.
///
/// - **Single Ownership Guarantee:** After calling [`Self::to_owned`], ensure that
/// no other references (especially static or global ones) attempt to access the
/// underlying mutex. This is because the mutexes memory is deallocated once this
@@ -248,7 +249,13 @@ impl<T> KMutex<T> {
/// }
/// ```
pub unsafe fn to_owned(self) -> T {
let data_read = unsafe { ptr::read(&(*self.inner).data) };
let manually_dropped = ManuallyDrop::new(self);
let data_read = unsafe { ptr::read(&(*manually_dropped.inner).data) };
// Free the mutex allocation without using drop semantics which could cause an
// accidental double drop of the underlying `T`.
unsafe { ExFreePool(manually_dropped.inner as _) };
data_read
}
@@ -259,6 +266,10 @@ impl<T> KMutex<T> {
/// avoiding potential stack overflows associated with large stack allocations.
///
/// # Safety
///
/// This function moves `T` out of the mutex without running `Drop` on the original
/// in-place value. The returned `T` remains fully owned by the caller and will be
/// dropped normally.
///
/// - **Single Ownership Guarantee:** After calling [`Self::to_owned_box`], ensure that
/// no other references (especially static or global ones) attempt to access the
@@ -277,7 +288,13 @@ impl<T> KMutex<T> {
/// }
/// ```
pub unsafe fn to_owned_box(self) -> Box<T> {
let data_read = unsafe { ptr::read(&(*self.inner).data) };
let manually_dropped = ManuallyDrop::new(self);
let data_read = unsafe { ptr::read(&(*manually_dropped.inner).data) };
// Free the mutex allocation without using drop semantics which could cause an
// accidental double drop of the underlying `T`.
unsafe { ExFreePool(manually_dropped.inner as _) };
Box::new(data_read)
}
}