1
0
mirror of https://github.com/veracrypt/VeraCrypt.git synced 2025-11-11 02:58:02 -06:00

Windows driver: add safe MapIrpDataBuffer function to prevent rare BSOD when irp->MdlAddress is NULL

Introduce MapIrpDataBuffer to handle Direct/Buffered/Neither I/O, probing & locking pages and allocating a temp MDL when needed.
Replace blind MmGetSystemAddressForMdlSafe usage. clean up TempUserMdl in OnItemCompleted to avoid crashes when MdlAddress is NULL.

Issue reported at https://sourceforge.net/p/veracrypt/discussion/technical/thread/e43bde8d86/
This commit is contained in:
Mounir IDRASSI
2025-09-15 14:34:22 +09:00
parent f257d7b4a5
commit e4e6b167e2
2 changed files with 107 additions and 8 deletions

View File

@@ -19,6 +19,86 @@
#include "Volumes.h"
#include <IntSafe.h>
// Returns STATUS_SUCCESS on success and sets *outVa.
// On failure, returns STATUS_INVALID_USER_BUFFER or STATUS_INSUFFICIENT_RESOURCES
// and leaves *outVa as NULL. If *outTempMdl not NULL, the caller must unlock/free it at completion.
__drv_maxIRQL(APC_LEVEL) static NTSTATUS
MapIrpDataBuffer(
_In_ PIRP irp,
_In_ BOOL isWriteIRP, // TRUE for IRP_MJ_WRITE (we READ from caller buffer)
_In_ ULONG length,
_Outptr_result_bytebuffer_(length) PUCHAR *outVa,
_Outptr_result_maybenull_ PMDL *outTempMdl)
{
ULONG mapFlags = HighPagePriority | MdlMappingNoExecute;
PUCHAR va = NULL;
ASSERT(outVa && outTempMdl);
*outVa = NULL;
*outTempMdl = NULL;
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
if (length == 0)
return STATUS_INVALID_PARAMETER;
// If this is a WRITE IRP we only read from callers buffer: ask for a no-write mapping.
if (isWriteIRP)
mapFlags |= MdlMappingNoWrite;
// --- Direct I/O ---
if (irp->MdlAddress)
{
if (MmGetMdlByteCount(irp->MdlAddress) < length)
return STATUS_INVALID_USER_BUFFER; // caller asked for more than mapped
va = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, mapFlags);
if (!va)
return STATUS_INSUFFICIENT_RESOURCES; // low PTEs, etc.
*outVa = va;
return STATUS_SUCCESS;
}
// --- Buffered I/O ---
if (irp->AssociatedIrp.SystemBuffer)
{
*outVa = (PUCHAR)irp->AssociatedIrp.SystemBuffer;
return STATUS_SUCCESS;
}
// --- Neither I/O ---
if (!irp->UserBuffer)
return STATUS_INVALID_USER_BUFFER;
PMDL mdl = IoAllocateMdl(irp->UserBuffer, length, FALSE, FALSE, NULL);
if (!mdl)
return STATUS_INSUFFICIENT_RESOURCES;
__try
{
// For WRITE IRPs we read from user => IoReadAccess.
// For READ IRPs we write to user => IoWriteAccess.
MmProbeAndLockPages(mdl, irp->RequestorMode, isWriteIRP ? IoReadAccess : IoWriteAccess);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
IoFreeMdl(mdl);
return STATUS_INVALID_USER_BUFFER; // bad pointer/range/rights
}
va = (PUCHAR)MmGetSystemAddressForMdlSafe(mdl, mapFlags);
if (!va)
{
MmUnlockPages(mdl);
IoFreeMdl(mdl);
return STATUS_INSUFFICIENT_RESOURCES;
}
*outTempMdl = mdl;
*outVa = va;
return STATUS_SUCCESS;
}
static void AcquireBufferPoolMutex (EncryptedIoQueue *queue)
{
@@ -160,6 +240,12 @@ static void DecrementOutstandingIoCount (EncryptedIoQueue *queue)
static void OnItemCompleted (EncryptedIoQueueItem *item, BOOL freeItem)
{
if (item->TempUserMdl) {
MmUnlockPages(item->TempUserMdl);
IoFreeMdl(item->TempUserMdl);
item->TempUserMdl = NULL;
}
DecrementOutstandingIoCount (item->Queue);
IoReleaseRemoveLock (&item->Queue->RemoveLock, item->OriginalIrp);
@@ -700,6 +786,7 @@ static VOID MainThreadProc (PVOID threadArg)
item->Queue = queue;
item->OriginalIrp = irp;
item->TempUserMdl = NULL;
item->Status = STATUS_SUCCESS;
IoSetCancelRoutine (irp, NULL);
@@ -764,11 +851,17 @@ static VOID MainThreadProc (PVOID threadArg)
{
UINT64_STRUCT dataUnit;
dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, (HighPagePriority | MdlMappingNoExecute));
if (!dataBuffer)
dataBuffer = NULL;
NTSTATUS mapStatus = MapIrpDataBuffer(
irp,
FALSE,
item->OriginalLength,
&dataBuffer,
&item->TempUserMdl);
if (!NT_SUCCESS(mapStatus))
{
TCfree (buffer);
CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0);
CompleteOriginalIrp (item, mapStatus, 0);
continue;
}
@@ -884,11 +977,16 @@ static VOID MainThreadProc (PVOID threadArg)
continue;
}
dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, (HighPagePriority | MdlMappingNoExecute));
if (dataBuffer == NULL)
dataBuffer = NULL;
NTSTATUS mapStatus = MapIrpDataBuffer(
irp,
item->Write,
item->OriginalLength,
&dataBuffer,
&item->TempUserMdl);
if (!NT_SUCCESS(mapStatus))
{
CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0);
CompleteOriginalIrp (item, mapStatus, 0);
continue;
}

View File

@@ -156,6 +156,7 @@ typedef struct
ULONG OriginalLength;
LARGE_INTEGER OriginalOffset;
NTSTATUS Status;
PMDL TempUserMdl; // NULL if none. Used in MapIrpDataBuffer logic
#ifdef TC_TRACE_IO_QUEUE
LARGE_INTEGER OriginalIrpOffset;