Skip to content

Commit

Permalink
Updated driver to use a global buffer instead of device context, remo…
Browse files Browse the repository at this point in the history
…ved wdf_object_context.rs, updated project name and README
  • Loading branch information
svasista-ms committed Sep 25, 2024
1 parent 9f169c8 commit d5e8ec5
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 146 deletions.
23 changes: 11 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
members = [
"general/echo/kmdf/driver/*",
"general/echo/kmdf/exe",
"tools/dv/samples/kmdf/pool_leak",
"tools/dv/kmdf/fail_driver_pool_leak",
]
resolver = "2"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "pool_leak"
name = "fail_driver_pool_leak"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
Expand All @@ -16,7 +16,6 @@ wdk.workspace = true
wdk-alloc.workspace = true
wdk-panic.workspace = true
wdk-sys.workspace = true
paste = ">=1.0.14, <2.0.0"

[build-dependencies]
wdk-build.workspace = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Driver Verifier Pool Leak Sample
# Driver Verifier (Fail Driver) Pool Leak Sample

This KMDF sample contains an intentional error that demonstrates the capabilities and features of Driver Verifier and the Device Fundamentals tests.

The driver uses WDM's ExAllocatePool2 API to allocate memory in its Device Context buffer when a device is added by the PnP manager. However, this buffer is not freed anywhere in the driver, including the driver unload function.
This sample KMDF Fail Driver demonstrates the capabilities and features of Driver Verifier and the Device Fundamentals tests. It allocates a pool of memory to a global buffer when a supported device is added by the PnP Manager and intentionally does not free it before the driver is unloaded. This memory leak fault is a system vulnerability that could lead to security and performance issues and bad user experience.

By enabling Driver Verifier on this driver, this pool leak violation can be caught before the driver is unloaded and with an active KDNET session, the bug can be analyzed further.

NOTE: The driver uses WDM's ExAllocatePool2 API directly to allocate memory for its buffer. Ideally, such allocations should be freed by using ExFreePool API. A cleaner way to manage memory in a WDF Driver is to use (wdfmemory)[https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdfmemory/]

By enabling Driver Verifier on this driver, the pool leak violation can be caught when the driver is unloaded and with an active KDNET session, the bug can be analyzed further.

## Steps to Reproduce the issue

1. Clone the repository and navigate to the project directory.
1. Clone the repository and navigate to the project root.

2. Build the driver project using the following command in a WDK environment (or EWDK prompt) -
```
Expand All @@ -34,25 +35,25 @@ By enabling Driver Verifier on this driver, the pool leak violation can be caugh
shutdown -r -t 0
```
4. Copy the driver package, available under ".\target\debug\pool_leak_package" to the target system.
4. Copy the driver package, available under ".\target\debug\fail_driver_pool_leak_package" to the target system.
5. Copy "devgen.exe" from host to the target system. Alternatively you may install WDK on the target system and add the directory that contains "devgen.exe" to PATH variable.
6. Install the driver package and create the device in the target system using the below commands -
```
cd "pool_leak_package"
devgen.exe /add /bus ROOT /hardwareid "pool_leak"
cd "fail_driver_pool_leak_package"
devgen.exe /add /bus ROOT /hardwareid "fail_driver_pool_leak"
## Copy the Device ID. This will be used later to run the tests
pnputil.exe /add-driver .\pool_leak.inf /install
pnputil.exe /add-driver .\fail_driver_pool_leak.inf /install
```
7. Enable Driver Verifier for 'pool_leak.sys' driver package
7. Enable Driver Verifier for 'fail_driver_pool_leak.sys' driver package
1. Open run command prompt (Start + R) or cmd as administator and run "verifier"
2. In the verifier manager,
- Create Standard Settings
- Select driver names from list
- Select 'pool_leak.sys'
- Select 'fail_driver_pool_leak.sys'
- Finish
- Restart the system
Expand All @@ -70,7 +71,7 @@ By enabling Driver Verifier on this driver, the pool leak violation can be caugh
```
The logs will be available in WinDbg
run ```!analyze -v``` for detailed bugcheck report
run ```!verifier 3 pool_leak.sys``` for info on the allocations that were leaked that caused the bugcheck.
run ```!verifier 3 fail_driver_pool_leak.sys``` for info on the allocations that were leaked that caused the bugcheck.
11. (Alternatively), the bugcheck can be observed when all the devices managed by this driver are removed.
You may use pnputil/devcon to enumerate and remove the devices -
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
; Copyright (c)2023, Microsoft Corporation
;
;Module Name:
; pool_leak.INF
; fail_driver_pool_leak.INF
;===================================================================

[Version]
Expand All @@ -19,38 +19,38 @@ DefaultDestDir = 13
1 = %DiskId1%,,,""

[SourceDisksFiles]
pool_leak.sys = 1,,
fail_driver_pool_leak.sys = 1,,

; ================= Install section =================

[Manufacturer]
%StdMfg%=Standard,NT$ARCH$.10.0...16299

[Standard.NT$ARCH$.10.0...16299]
%POOL_LEAK.DeviceDesc%=POOL_LEAK_Device, pool_leak
%fail_driver_pool_leak.DeviceDesc%=fail_driver_pool_leak_Device, fail_driver_pool_leak

[POOL_LEAK_Device.NT$ARCH$]
[fail_driver_pool_leak_Device.NT$ARCH$]
CopyFiles=Drivers_Dir

[Drivers_Dir]
pool_leak.sys
fail_driver_pool_leak.sys

; ================= Service installation =================
[POOL_LEAK_Device.NT$ARCH$.Services]
AddService = pool_leak, %SPSVCINST_ASSOCSERVICE%, pool_leak_svc_ins
[fail_driver_pool_leak_Device.NT$ARCH$.Services]
AddService = fail_driver_pool_leak, %SPSVCINST_ASSOCSERVICE%, fail_driver_pool_leak_svc_ins

[pool_leak_svc_ins]
DisplayName = %POOL_LEAK.SVCDESC%
[fail_driver_pool_leak_svc_ins]
DisplayName = %fail_driver_pool_leak.SVCDESC%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %13%\pool_leak.sys
ServiceBinary = %13%\fail_driver_pool_leak.sys

; ================= Strings =================
[Strings]
SPSVCINST_ASSOCSERVICE = 0x00000002
ProviderString = "Rust-DVFail-Sample"
StdMfg = "(Standard system devices)"
DiskId1 = "WDF DVFail Sample POOL_LEAK Installation Disk #1"
POOL_LEAK.DeviceDesc = "DVFail Sample WDF POOL_LEAK Driver"
POOL_LEAK.SVCDESC = "DVFail Sample WDF POOL_LEAK Service"
DiskId1 = "WDF DVFail Sample fail_driver_pool_leak Installation Disk #1"
fail_driver_pool_leak.DeviceDesc = "DVFail Sample WDF fail_driver_pool_leak Driver"
fail_driver_pool_leak.SVCDESC = "DVFail Sample WDF fail_driver_pool_leak Service"
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use wdk_sys::{
WDFDEVICE,
WDFDEVICE_INIT,
WDFDRIVER,
WDFOBJECT,
WDF_DRIVER_CONFIG,
WDF_NO_HANDLE,
WDF_NO_OBJECT_ATTRIBUTES,
Expand All @@ -25,12 +24,7 @@ use wdk_sys::{
_WDF_SYNCHRONIZATION_SCOPE,
};

use crate::{
wdf_object_context::wdf_get_context_type_info,
wdf_object_get_device_context,
GUID_DEVINTERFACE,
WDF_DEVICE_CONTEXT_TYPE_INFO,
};
use crate::{GLOBAL_BUFFER, GUID_DEVINTERFACE};

/// `DriverEntry` initializes the driver and is the first routine called by the
/// system after the driver is loaded. `DriverEntry` specifies the other entry
Expand All @@ -57,6 +51,8 @@ extern "system" fn driver_entry(
driver: &mut DRIVER_OBJECT,
registry_path: PCUNICODE_STRING,
) -> NTSTATUS {
println!("Enter: driver_entry");

let mut driver_config = {
let wdf_driver_config_size: ULONG;

Expand Down Expand Up @@ -98,6 +94,8 @@ extern "system" fn driver_entry(
return nt_status;
}

println!("Exit: driver_entry");

nt_status
}

Expand All @@ -121,14 +119,13 @@ extern "C" fn evt_driver_device_add(
) -> NTSTATUS {
paged_code!();

println!("Enter: EchoEvtDeviceAdd");
println!("Enter: evt_driver_device_add");

#[allow(clippy::cast_possible_truncation)]
let mut attributes = WDF_OBJECT_ATTRIBUTES {
Size: core::mem::size_of::<WDF_OBJECT_ATTRIBUTES>() as ULONG,
ExecutionLevel: _WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent,
SynchronizationScope: _WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent,
ContextTypeInfo: wdf_get_context_type_info!(DeviceContext),
..WDF_OBJECT_ATTRIBUTES::default()
};

Expand All @@ -147,19 +144,12 @@ extern "C" fn evt_driver_device_add(
return nt_status;
}

// Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an
// inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the
// device.h header file. This function will do the type checking and return
// the device context. If you pass a wrong object handle
// it will return NULL and assert if run under framework verifier mode.
let device_context = unsafe { wdf_object_get_device_context(device as WDFOBJECT) };
// Allocate non-paged memory pool of 64 bytes (arbitrarily chosen) for the
// Global buffer. This pool of memory is intentionally not freed by
// the driver.
unsafe {
// Allocate non-paged memory pool of 64 bytes (arbitrarily chosen) for the
// device context's buffer. This pool of memory is intentionally not freed by
// the driver.
const LENGTH: usize = 64;
(*device_context).buffer =
ExAllocatePool2(POOL_FLAG_NON_PAGED, LENGTH as SIZE_T, 's' as u32);
GLOBAL_BUFFER = ExAllocatePool2(POOL_FLAG_NON_PAGED, LENGTH as SIZE_T, 's' as u32);
}

nt_status = unsafe {
Expand All @@ -176,7 +166,7 @@ extern "C" fn evt_driver_device_add(
return nt_status;
}

println!("Exit: EchoEvtDeviceAdd");
println!("Exit: evt_driver_device_add");

nt_status
}
Expand All @@ -197,9 +187,11 @@ extern "C" fn evt_driver_device_add(
extern "C" fn evt_driver_unload(_driver: WDFDRIVER) {
println!("Enter: evt_driver_unload");

// Ideally, the memory allocated to the device context's buffer in L160 should
// Ideally, the memory allocated to the Global buffer in lib.rs L51 should
// be freed here by calling the ExFreePool API. But to demonstrate the Driver
// Verifier's ability to catch pool leaks, the buffer is deliberately not freed.

// unsafe { wdk_sys::ntddk::ExFreePool(GLOBAL_BUFFER) };

println!("Exit: evt_driver_unload");
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ use wdk_alloc::WDKAllocator;
#[global_allocator]
static GLOBAL_ALLOCATOR: WDKAllocator = WDKAllocator;

mod driver;
mod wdf_object_context;
use wdf_object_context::wdf_declare_context_type;
use wdk_sys::{macros, GUID, PVOID, ULONG, WDFOBJECT, WDF_OBJECT_CONTEXT_TYPE_INFO};
use wdk_sys::{GUID, PVOID};

// {A1B2C3D4-E5F6-7890-1234-56789ABCDEF0}
const GUID_DEVINTERFACE: GUID = GUID {
Expand All @@ -50,7 +47,7 @@ const GUID_DEVINTERFACE: GUID = GUID {
],
};

pub struct DeviceContext {
buffer: PVOID,
}
wdf_declare_context_type!(DeviceContext);
// Global Buffer for the driver
static mut GLOBAL_BUFFER: PVOID = core::ptr::null_mut();

mod driver;
75 changes: 0 additions & 75 deletions tools/dv/samples/kmdf/pool_leak/src/wdf_object_context.rs

This file was deleted.

0 comments on commit d5e8ec5

Please sign in to comment.