Ensure that buffers are unmapped on process exit and adapter deletion

Before duplicating a handle elsewhere and closing the original process
would result in disaster. Also, it turns out that TunHaltEx can be
called before the handles are all closed, so we need to unregister prior
to freeing the ctx, lest disaster occurs. Finally, now that we have a
few different things happening with registration and deregistration, we
serialize all accesses with an eresource, a bit heavy-weight but
sufficient.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2019-10-06 09:38:44 +00:00
parent 53332cd078
commit fd52a3a429

View File

@ -122,7 +122,10 @@ typedef struct _TUN_CTX
struct struct
{ {
FILE_OBJECT *volatile Owner; LIST_ENTRY Entry;
ERESOURCE RegistrationLock;
FILE_OBJECT *OwningFileObject;
HANDLE OwningProcessId;
KEVENT Disconnected; KEVENT Disconnected;
struct struct
@ -161,7 +164,8 @@ typedef struct _TUN_CTX
static UINT NdisVersion; static UINT NdisVersion;
static NDIS_HANDLE NdisMiniportDriverHandle; static NDIS_HANDLE NdisMiniportDriverHandle;
static DRIVER_DISPATCH *NdisDispatchDeviceControl, *NdisDispatchClose; static DRIVER_DISPATCH *NdisDispatchDeviceControl, *NdisDispatchClose;
static ERESOURCE TunDispatchCtxGuard; static ERESOURCE TunDispatchCtxGuard, TunDispatchDeviceListLock;
static RTL_STATIC_LIST_HEAD(TunDispatchDeviceList);
static SECURITY_DESCRIPTOR *TunDispatchSecurityDescriptor; static SECURITY_DESCRIPTOR *TunDispatchSecurityDescriptor;
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
@ -542,11 +546,15 @@ _Must_inspect_result_
static NTSTATUS static NTSTATUS
TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp) TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
{ {
NTSTATUS Status; NTSTATUS Status = STATUS_ALREADY_INITIALIZED;
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp); IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
if (InterlockedCompareExchangePointer(&Ctx->Device.Owner, Stack->FileObject, NULL) != NULL) if (!ExAcquireResourceExclusiveLite(&Ctx->Device.RegistrationLock, FALSE))
return STATUS_ALREADY_INITIALIZED; return Status;
if (Ctx->Device.OwningFileObject)
goto cleanupMutex;
Ctx->Device.OwningFileObject = Stack->FileObject;
ASSERT(InterlockedGet(&Ctx->Running)); ASSERT(InterlockedGet(&Ctx->Running));
@ -631,6 +639,13 @@ TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
&Ctx->Device.Receive.Thread, THREAD_ALL_ACCESS, &ObjectAttributes, NULL, NULL, TunProcessReceiveData, Ctx))) &Ctx->Device.Receive.Thread, THREAD_ALL_ACCESS, &ObjectAttributes, NULL, NULL, TunProcessReceiveData, Ctx)))
goto cleanupFlagsConnected; goto cleanupFlagsConnected;
Ctx->Device.OwningProcessId = PsGetCurrentProcessId();
InitializeListHead(&Ctx->Device.Entry);
ExAcquireResourceExclusiveLite(&TunDispatchDeviceListLock, TRUE);
InsertTailList(&TunDispatchDeviceList, &Ctx->Device.Entry);
ExReleaseResourceLite(&TunDispatchDeviceListLock);
ExReleaseResourceLite(&Ctx->Device.RegistrationLock);
TunIndicateStatus(Ctx->MiniportAdapterHandle, MediaConnectStateConnected); TunIndicateStatus(Ctx->MiniportAdapterHandle, MediaConnectStateConnected);
return STATUS_SUCCESS; return STATUS_SUCCESS;
@ -652,16 +667,30 @@ cleanupSendMdl:
cleanupSendTailMoved: cleanupSendTailMoved:
ObDereferenceObject(Ctx->Device.Send.TailMoved); ObDereferenceObject(Ctx->Device.Send.TailMoved);
cleanupResetOwner: cleanupResetOwner:
InterlockedSetPointer(&Ctx->Device.Owner, NULL); Ctx->Device.OwningFileObject = NULL;
cleanupMutex:
ExReleaseResourceLite(&Ctx->Device.RegistrationLock);
return Status; return Status;
} }
#define TUN_FORCE_UNREGISTRATION ((FILE_OBJECT *)-1)
_IRQL_requires_max_(PASSIVE_LEVEL) _IRQL_requires_max_(PASSIVE_LEVEL)
static VOID static VOID
TunUnregisterBuffers(_Inout_ TUN_CTX *Ctx, _In_ FILE_OBJECT *Owner) TunUnregisterBuffers(_Inout_ TUN_CTX *Ctx, _In_ FILE_OBJECT *Owner)
{ {
if (InterlockedCompareExchangePointer(&Ctx->Device.Owner, NULL, Owner) != Owner) if (!Owner)
return; return;
ExAcquireResourceExclusiveLite(&Ctx->Device.RegistrationLock, TRUE);
if (!Ctx->Device.OwningFileObject || (Owner != TUN_FORCE_UNREGISTRATION && Ctx->Device.OwningFileObject != Owner))
{
ExReleaseResourceLite(&Ctx->Device.RegistrationLock);
return;
}
Ctx->Device.OwningFileObject = NULL;
ExAcquireResourceExclusiveLite(&TunDispatchDeviceListLock, TRUE);
RemoveEntryList(&Ctx->Device.Entry);
ExReleaseResourceLite(&TunDispatchDeviceListLock);
TunIndicateStatus(Ctx->MiniportAdapterHandle, MediaConnectStateDisconnected); TunIndicateStatus(Ctx->MiniportAdapterHandle, MediaConnectStateDisconnected);
@ -688,10 +717,12 @@ TunUnregisterBuffers(_Inout_ TUN_CTX *Ctx, _In_ FILE_OBJECT *Owner)
MmUnlockPages(Ctx->Device.Send.Mdl); MmUnlockPages(Ctx->Device.Send.Mdl);
IoFreeMdl(Ctx->Device.Send.Mdl); IoFreeMdl(Ctx->Device.Send.Mdl);
ObDereferenceObject(Ctx->Device.Send.TailMoved); ObDereferenceObject(Ctx->Device.Send.TailMoved);
ExReleaseResourceLite(&Ctx->Device.RegistrationLock);
} }
_IRQL_requires_max_(PASSIVE_LEVEL) _IRQL_requires_max_(PASSIVE_LEVEL)
static void static VOID
TunForceHandlesClosed(_Inout_ DEVICE_OBJECT *DeviceObject) TunForceHandlesClosed(_Inout_ DEVICE_OBJECT *DeviceObject)
{ {
NTSTATUS Status; NTSTATUS Status;
@ -788,6 +819,30 @@ static NTSTATUS TunInitializeDispatchSecurityDescriptor(VOID)
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
_IRQL_requires_max_(PASSIVE_LEVEL)
static VOID
TunProcessNotification(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
{
if (Create)
return;
ExAcquireSharedStarveExclusive(&TunDispatchDeviceListLock, TRUE);
TUN_CTX *Ctx = NULL;
for (LIST_ENTRY *Entry = TunDispatchDeviceList.Flink; Entry != &TunDispatchDeviceList; Entry = Entry->Flink)
{
TUN_CTX *Candidate = CONTAINING_RECORD(Entry, TUN_CTX, Device.Entry);
if (Candidate->Device.OwningProcessId == ProcessId)
{
Ctx = Candidate;
break;
}
}
ExReleaseResourceLite(&TunDispatchDeviceListLock);
if (!Ctx)
return;
TunUnregisterBuffers(Ctx, TUN_FORCE_UNREGISTRATION);
}
_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
static DRIVER_DISPATCH_PAGED TunDispatchDeviceControl; static DRIVER_DISPATCH_PAGED TunDispatchDeviceControl;
_Use_decl_annotations_ _Use_decl_annotations_
@ -947,6 +1002,7 @@ TunInitializeEx(
KeInitializeSpinLock(&Ctx->Device.Send.Lock); KeInitializeSpinLock(&Ctx->Device.Send.Lock);
KeInitializeSpinLock(&Ctx->Device.Receive.Lock); KeInitializeSpinLock(&Ctx->Device.Receive.Lock);
KeInitializeEvent(&Ctx->Device.Receive.ActiveNbls.Empty, NotificationEvent, TRUE); KeInitializeEvent(&Ctx->Device.Receive.ActiveNbls.Empty, NotificationEvent, TRUE);
ExInitializeResourceLite(&Ctx->Device.RegistrationLock);
NET_BUFFER_LIST_POOL_PARAMETERS NblPoolParameters = { NET_BUFFER_LIST_POOL_PARAMETERS NblPoolParameters = {
.Header = { .Type = NDIS_OBJECT_TYPE_DEFAULT, .Header = { .Type = NDIS_OBJECT_TYPE_DEFAULT,
@ -1063,6 +1119,8 @@ TunHaltEx(NDIS_HANDLE MiniportAdapterContext, NDIS_HALT_ACTION HaltAction)
{ {
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext; TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
TunUnregisterBuffers(Ctx, TUN_FORCE_UNREGISTRATION);
ExReleaseSpinLockExclusive( ExReleaseSpinLockExclusive(
&Ctx->TransitionLock, &Ctx->TransitionLock,
ExAcquireSpinLockExclusive(&Ctx->TransitionLock)); /* Ensure above change is visible to all readers. */ ExAcquireSpinLockExclusive(&Ctx->TransitionLock)); /* Ensure above change is visible to all readers. */
@ -1075,6 +1133,7 @@ TunHaltEx(NDIS_HANDLE MiniportAdapterContext, NDIS_HALT_ACTION HaltAction)
ExAcquireResourceExclusiveLite(&TunDispatchCtxGuard, TRUE); /* Ensure above change is visible to all readers. */ ExAcquireResourceExclusiveLite(&TunDispatchCtxGuard, TRUE); /* Ensure above change is visible to all readers. */
ExReleaseResourceLite(&TunDispatchCtxGuard); ExReleaseResourceLite(&TunDispatchCtxGuard);
KeLeaveCriticalRegion(); KeLeaveCriticalRegion();
ExDeleteResourceLite(&Ctx->Device.RegistrationLock);
ExFreePoolWithTag(Ctx, TUN_MEMORY_TAG); ExFreePoolWithTag(Ctx, TUN_MEMORY_TAG);
} }
@ -1323,8 +1382,10 @@ _Use_decl_annotations_
static VOID static VOID
TunUnload(PDRIVER_OBJECT DriverObject) TunUnload(PDRIVER_OBJECT DriverObject)
{ {
PsSetCreateProcessNotifyRoutine(TunProcessNotification, TRUE);
NdisMDeregisterMiniportDriver(NdisMiniportDriverHandle); NdisMDeregisterMiniportDriver(NdisMiniportDriverHandle);
ExDeleteResourceLite(&TunDispatchCtxGuard); ExDeleteResourceLite(&TunDispatchCtxGuard);
ExDeleteResourceLite(&TunDispatchDeviceListLock);
ExFreePoolWithTag(TunDispatchSecurityDescriptor, TUN_MEMORY_TAG); ExFreePoolWithTag(TunDispatchSecurityDescriptor, TUN_MEMORY_TAG);
} }
@ -1345,6 +1406,7 @@ DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
NdisVersion = NDIS_MINIPORT_VERSION_MAX; NdisVersion = NDIS_MINIPORT_VERSION_MAX;
ExInitializeResourceLite(&TunDispatchCtxGuard); ExInitializeResourceLite(&TunDispatchCtxGuard);
ExInitializeResourceLite(&TunDispatchDeviceListLock);
NDIS_MINIPORT_DRIVER_CHARACTERISTICS miniport = { NDIS_MINIPORT_DRIVER_CHARACTERISTICS miniport = {
.Header = { .Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS, .Header = { .Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS,
@ -1377,13 +1439,14 @@ DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
.CancelDirectOidRequestHandler = TunCancelDirectOidRequest, .CancelDirectOidRequestHandler = TunCancelDirectOidRequest,
.SynchronousOidRequestHandler = TunSynchronousOidRequest .SynchronousOidRequestHandler = TunSynchronousOidRequest
}; };
Status = PsSetCreateProcessNotifyRoutine(TunProcessNotification, FALSE);
if (!NT_SUCCESS(Status))
goto cleanupResources;
Status = NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, &miniport, &NdisMiniportDriverHandle); Status = NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, &miniport, &NdisMiniportDriverHandle);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ goto cleanupNotifier;
ExDeleteResourceLite(&TunDispatchCtxGuard);
ExFreePoolWithTag(TunDispatchSecurityDescriptor, TUN_MEMORY_TAG);
return Status;
}
NdisDispatchDeviceControl = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]; NdisDispatchDeviceControl = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL];
NdisDispatchClose = DriverObject->MajorFunction[IRP_MJ_CLOSE]; NdisDispatchClose = DriverObject->MajorFunction[IRP_MJ_CLOSE];
@ -1391,4 +1454,12 @@ DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
DriverObject->MajorFunction[IRP_MJ_CLOSE] = TunDispatchClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = TunDispatchClose;
return STATUS_SUCCESS; return STATUS_SUCCESS;
cleanupNotifier:
PsSetCreateProcessNotifyRoutine(TunProcessNotification, TRUE);
cleanupResources:
ExDeleteResourceLite(&TunDispatchCtxGuard);
ExDeleteResourceLite(&TunDispatchDeviceListLock);
ExFreePoolWithTag(TunDispatchSecurityDescriptor, TUN_MEMORY_TAG);
return Status;
} }