Skip to content

Commit 8f6f696

Browse files
authored
Merge pull request #5896 from nymtech/am/handle-table-allocate-more-memory
2 parents e916576 + 7313857 commit 8f6f696

File tree

1 file changed

+41
-28
lines changed

1 file changed

+41
-28
lines changed

sqlx-pool-guard/src/windows.rs

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,40 +31,16 @@ const SYSTEM_HANDLE_INFORMATION_CLASS: SYSTEM_INFORMATION_CLASS = SYSTEM_INFORMA
3131
/// The number is based on what I observe on a pretty standard Windows 11
3232
const SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE: usize = 2_500_000;
3333

34+
/// Max retry attempts for querying system handle information before giving up
35+
const MAX_RETRY_ATTEMPTS: u32 = 5;
36+
3437
/// Check if there are no open handles to the given files.
3538
///
3639
/// Uses undocumented NT API to obtain open handles on the system.
3740
/// See: https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/get-all-open-handles-and-kernel-object-address-from-userland
3841
pub async fn check_files_closed(file_paths: &[&Path]) -> io::Result<bool> {
3942
let current_pid = unsafe { GetCurrentProcessId() };
40-
41-
// Allocate info struct on heap with some initial value
42-
let mut reserved_memory = SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE;
43-
let mut handle_table_info = HeapGuard::<SystemHandleInformation>::new(reserved_memory)?;
44-
45-
// Request system handle information
46-
let mut status: NTSTATUS = NTSTATUS::default();
47-
let mut return_len = reserved_memory as u32;
48-
for _ in 0..2 {
49-
status = unsafe {
50-
NtQuerySystemInformation(
51-
SYSTEM_HANDLE_INFORMATION_CLASS,
52-
handle_table_info.as_mut_ptr() as _,
53-
return_len,
54-
&mut return_len,
55-
)
56-
};
57-
58-
// Buffer is too small, resize memory and retry again.
59-
if status == STATUS_INFO_LENGTH_MISMATCH {
60-
tracing::trace!("Buffer is too small ({reserved_memory}), resizing to {return_len}");
61-
reserved_memory = return_len as usize;
62-
handle_table_info.reallocate(reserved_memory)?;
63-
} else {
64-
break;
65-
}
66-
}
67-
status.ok()?;
43+
let handle_table_info = query_system_handle_table()?;
6844

6945
// Convert returned data into slice
7046
let num_handles = unsafe { (*handle_table_info.inner).number_of_handles };
@@ -106,6 +82,43 @@ pub async fn check_files_closed(file_paths: &[&Path]) -> io::Result<bool> {
10682
Ok(true)
10783
}
10884

85+
fn query_system_handle_table() -> io::Result<HeapGuard<SystemHandleInformation>> {
86+
// Allocate info struct on heap with some initial value
87+
let mut reserved_memory = SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE;
88+
let mut handle_table_info = HeapGuard::<SystemHandleInformation>::new(reserved_memory)?;
89+
90+
// Request system handle information
91+
let mut status: NTSTATUS = NTSTATUS::default();
92+
for _ in 0..MAX_RETRY_ATTEMPTS {
93+
let mut return_len = reserved_memory as u32;
94+
status = unsafe {
95+
NtQuerySystemInformation(
96+
SYSTEM_HANDLE_INFORMATION_CLASS,
97+
handle_table_info.as_mut_ptr() as _,
98+
return_len,
99+
&mut return_len,
100+
)
101+
};
102+
103+
// Buffer is too small, resize memory and retry again.
104+
if status == STATUS_INFO_LENGTH_MISMATCH {
105+
// Allocate a bit more memory since the size of table can change between calls
106+
let resize_to = (return_len as usize) * 3 / 2;
107+
108+
tracing::trace!(
109+
"Buffer is too small ({reserved_memory}), returned length: {return_len}, resizing buffer to: {resize_to}"
110+
);
111+
112+
reserved_memory = resize_to;
113+
handle_table_info.reallocate(reserved_memory)?;
114+
} else {
115+
break;
116+
}
117+
}
118+
119+
Ok(status.ok().map(|_| handle_table_info)?)
120+
}
121+
109122
#[repr(C)]
110123
#[derive(Copy, Clone)]
111124
struct SystemHandleInformation {

0 commit comments

Comments
 (0)