windows-nt/Source/XPSP1/NT/base/ntos/io/iomgr/loadunld.c

532 lines
16 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
loadunld.c
Abstract:
This module contains the code to implement the NtLoadDriver and
NtUnLoadDriver system services for the NT I/O system.
Author:
Darryl E. Havens (darrylh) 5-Apr-1992
Environment:
Kernel mode only
Revision History:
--*/
#include "iomgr.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtLoadDriver)
#pragma alloc_text(PAGE, NtUnloadDriver)
#endif
NTSTATUS
NtLoadDriver(
IN PUNICODE_STRING DriverServiceName
)
/*++
Routine Description:
This service dynamically loads a device or file system driver into
the currently running system. It requires that the caller have the
appropriate privilege to execute this service.
Arguments:
DriverServiceName - Specifies the name of the node in the registry
associated with the driver to be loaded.
Return Value:
The status returned is the final completion status of the load operation.
--*/
{
KPROCESSOR_MODE requestorMode;
UNICODE_STRING driverServiceName;
PWCHAR nameBuffer = (PWCHAR) NULL;
LOAD_PACKET loadPacket;
PETHREAD CurrentThread;
PAGED_CODE();
//
// Get the previous mode; i.e., the mode of the caller.
//
CurrentThread = PsGetCurrentThread ();
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
if (requestorMode != KernelMode) {
//
// The caller's access mode is not kernel so check to ensure that
// the caller has the privilege to load a driver and probe and
// capture the name of the driver service entry.
//
if (!SeSinglePrivilegeCheck( SeLoadDriverPrivilege, requestorMode )) {
return STATUS_PRIVILEGE_NOT_HELD;
}
//
// The caller has the appropriate privilege to load and unload
// drivers, so capture the driver service name string so that it
// can be used to locate the driver from the registry node.
//
try {
driverServiceName = ProbeAndReadUnicodeString( DriverServiceName );
if (!driverServiceName.Length) {
return STATUS_INVALID_PARAMETER;
}
ProbeForRead( driverServiceName.Buffer,
driverServiceName.Length,
sizeof( WCHAR ) );
nameBuffer = ExAllocatePoolWithQuota( PagedPool,
driverServiceName.Length );
RtlCopyMemory( nameBuffer,
driverServiceName.Buffer,
driverServiceName.Length );
driverServiceName.Buffer = nameBuffer;
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred while attempting to capture the
// input name string or while attempting to allocate the name
// string buffer. Simply clean everything up and return an
// appropriate error status code.
//
if (nameBuffer) {
ExFreePool( nameBuffer );
}
return GetExceptionCode();
}
} else {
driverServiceName = *DriverServiceName;
}
//
// Because drivers may wish to create a system thread and execute in
// its context, the remainder of this service must be executed in the
// context of the primary system process. This is accomplished by
// queueing a request to one of the EX worker threads and having it
// invoke the I/O system routine to complete this work.
//
// Fill in a request packet and queue it to the worker thread then, so
// that it can actually do the load.
//
KeInitializeEvent( &loadPacket.Event, NotificationEvent, FALSE );
loadPacket.DriverObject = (PDRIVER_OBJECT) NULL;
loadPacket.DriverServiceName = &driverServiceName;
if (PsGetCurrentProcessByThread(CurrentThread) == PsInitialSystemProcess) {
//
// If we are already in the system process, just use this thread.
//
IopLoadUnloadDriver(&loadPacket);
} else {
ExInitializeWorkItem( &loadPacket.WorkQueueItem,
IopLoadUnloadDriver,
&loadPacket );
ExQueueWorkItem( &loadPacket.WorkQueueItem, DelayedWorkQueue );
KeWaitForSingleObject( &loadPacket.Event,
UserRequest,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
}
//
// The load operation is now complete. If a name buffer was allocated,
// deallocate it now, and return the final status of the load operation.
//
if (nameBuffer) {
ExFreePool( nameBuffer );
}
return loadPacket.FinalStatus;
}
NTSTATUS
IopCheckUnloadDriver(
IN PDRIVER_OBJECT driverObject,
OUT PBOOLEAN unloadDriver
)
{
PDEVICE_OBJECT deviceObject;
KIRQL irql;
//
// Check to see whether the driver has already been marked for an unload
// operation by anyone in the past.
//
irql = KeAcquireQueuedSpinLock( LockQueueIoDatabaseLock );
if ((driverObject->DeviceObject == NULL &&
(driverObject->Flags & DRVO_UNLOAD_INVOKED)) ||
(!(driverObject->Flags & DRVO_BASE_FILESYSTEM_DRIVER) && driverObject->DeviceObject &&
driverObject->DeviceObject->DeviceObjectExtension->ExtensionFlags
& DOE_UNLOAD_PENDING)) {
//
// The driver has already been marked for unload or is being
// unloaded. Simply return a successful completion status since
// the driver is on its way out and therefore has been "marked for
// unload".
//
KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, irql );
ObDereferenceObject( driverObject );
return STATUS_SUCCESS;
}
//
// The driver exists, and it implements unload, and it has not, so far,
// been marked for an unload operation. Simply mark all of the devices
// that the driver owns as being marked for unload. While this is going
// on, count the references for each of the devices. If all of the
// devices have a zero reference count, then tell the driver that it
// should unload itself.
//
deviceObject = driverObject->DeviceObject;
*unloadDriver = TRUE;
while (deviceObject) {
deviceObject->DeviceObjectExtension->ExtensionFlags |= DOE_UNLOAD_PENDING;
if (deviceObject->ReferenceCount || deviceObject->AttachedDevice) {
*unloadDriver = FALSE;
}
deviceObject = deviceObject->NextDevice;
}
//
// If this is a base filesystem driver then delay the unload until all its device objects
// are deleted.
//
if (driverObject->Flags & DRVO_BASE_FILESYSTEM_DRIVER && driverObject->DeviceObject) {
*unloadDriver = FALSE;
}
if (*unloadDriver) {
driverObject->Flags |= DRVO_UNLOAD_INVOKED;
}
KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, irql );
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NtUnloadDriver(
IN PUNICODE_STRING DriverServiceName
)
{
return (IopUnloadDriver(DriverServiceName, FALSE));
}
NTSTATUS
IopUnloadDriver(
IN PUNICODE_STRING DriverServiceName,
IN BOOLEAN InvokedByPnpMgr
)
/*++
Routine Description:
This service dynamically unloads a device or file system driver from
the currently running system. It requires that the caller have the
appropriate privilege to execute this service.
Arguments:
DriverServiceName - Specifies the name of the node in the registry
associated with the driver to be unloaded.
Return Value:
The status returned is the final completion status of the operation.
--*/
{
KPROCESSOR_MODE requestorMode;
UNICODE_STRING driverServiceName;
PWCHAR nameBuffer = (PWCHAR) NULL;
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE keyHandle;
UNICODE_STRING driverName;
HANDLE driverHandle;
PDRIVER_OBJECT driverObject;
BOOLEAN unloadDriver;
PAGED_CODE();
//
// Get the previous mode; i.e., the mode of the caller.
//
requestorMode = KeGetPreviousMode();
if ((requestorMode != KernelMode) && (InvokedByPnpMgr == FALSE)) {
//
// The caller's access mode is not kernel so check to ensure that
// the caller has the privilege to unload a driver and probe and
// capture the name of the driver service entry.
//
if (!SeSinglePrivilegeCheck( SeLoadDriverPrivilege, requestorMode )) {
return STATUS_PRIVILEGE_NOT_HELD;
}
//
// The caller has the appropriate privilege to load and unload
// drivers, so capture the driver service name string so that it
// can be used to locate the driver from the registry node.
//
try {
driverServiceName = ProbeAndReadUnicodeString( DriverServiceName );
if (!driverServiceName.Length) {
return STATUS_INVALID_PARAMETER;
}
ProbeForRead( driverServiceName.Buffer,
driverServiceName.Length,
sizeof( WCHAR ) );
nameBuffer = ExAllocatePoolWithQuota( PagedPool,
driverServiceName.Length );
RtlCopyMemory( nameBuffer,
driverServiceName.Buffer,
driverServiceName.Length );
driverServiceName.Buffer = nameBuffer;
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred while attempting to capture the
// input name string or while attempting to allocate the name
// string buffer. Simply clean everything up and return an
// appropriate error status code.
//
if (nameBuffer) {
ExFreePool( nameBuffer );
}
return GetExceptionCode();
}
//
// Now that the caller's parameters have been captured and everything
// appears to have checked out, actually attempt to unload the driver.
// This is done with a previous mode of kernel so that drivers will
// not fail to unload because the caller didn't happen to have access
// to some resource that the driver needs in order to complete its
// unload operation.
//
status = ZwUnloadDriver( &driverServiceName );
ExFreePool( nameBuffer );
return status;
}
//
// The caller's mode is now kernel mode. Attempt to actually unload the
// driver specified by the indicated registry node. Begin by opening
// the registry node for this driver.
//
status = IopOpenRegistryKey( &keyHandle,
(HANDLE) NULL,
DriverServiceName,
KEY_READ,
FALSE );
if (!NT_SUCCESS( status )) {
return status;
}
//
// Get the optional object name for this driver from the value for this
// key. If one exists, then its name overrides the default name of the
// driver.
//
status = IopGetDriverNameFromKeyNode( keyHandle,
&driverName );
NtClose( keyHandle );
if (!NT_SUCCESS( status )) {
return status;
}
//
// Now attempt to open the driver object for the specified driver.
//
InitializeObjectAttributes( &objectAttributes,
&driverName,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
status = ObOpenObjectByName( &objectAttributes,
IoDriverObjectType,
KernelMode,
NULL,
FILE_READ_DATA,
(PVOID) NULL,
&driverHandle );
//
// Perform some common cleanup by getting rid of buffers that have been
// allocated up to this point so that error conditions do not have as
// much work to do on each exit path.
//
ExFreePool( driverName.Buffer );
//
// If the driver object could not be located in the first place, then
// return now before attempting to do anything else.
//
if (!NT_SUCCESS( status )) {
return status;
}
//
// The driver object was located, so convert the handle into a pointer
// so that the driver object itself can be examined.
//
status = ObReferenceObjectByHandle( driverHandle,
0,
IoDriverObjectType,
KernelMode,
(PVOID *) &driverObject,
NULL );
NtClose( driverHandle );
if (!NT_SUCCESS( status )) {
return status;
}
//
// Check to see whether or not this driver implements unload. Also,
// if the driver has no section associated with it, then it was loaded
// be the OS loader and therefore cannot be unloaded. If either is true,
// return an appropriate error status code.
//
if (driverObject->DriverUnload == (PDRIVER_UNLOAD) NULL ||
!driverObject->DriverSection) {
ObDereferenceObject( driverObject );
return STATUS_INVALID_DEVICE_REQUEST;
}
if (!InvokedByPnpMgr && !IopIsLegacyDriver(driverObject)) {
ObDereferenceObject( driverObject );
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Check to see whether the driver has already been marked for an unload
// operation by anyone in the past.
//
status = IopCheckUnloadDriver(driverObject,&unloadDriver);
if ( NT_SUCCESS(status) ) {
return status;
}
if (unloadDriver) {
if (PsGetCurrentProcess() == PsInitialSystemProcess) {
//
// The current thread is alrady executing in the context of the
// system process, so simply invoke the driver's unload routine.
//
driverObject->DriverUnload( driverObject );
} else {
//
// The current thread is not executing in the context of the system
// process, which is required in order to invoke the driver's unload
// routine. Queue a worker item to one of the worker threads to
// get into the appropriate process context and then invoke the
// routine.
//
LOAD_PACKET loadPacket;
KeInitializeEvent( &loadPacket.Event, NotificationEvent, FALSE );
loadPacket.DriverObject = driverObject;
ExInitializeWorkItem( &loadPacket.WorkQueueItem,
IopLoadUnloadDriver,
&loadPacket );
ExQueueWorkItem( &loadPacket.WorkQueueItem, DelayedWorkQueue );
(VOID) KeWaitForSingleObject( &loadPacket.Event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
}
ObMakeTemporaryObject( driverObject );
ObDereferenceObject( driverObject );
}
//
// The driver has either been unloaded, or it has successfully been
// marked for an unload operation. Simply dereference the pointer to
// the object and return success.
//
ObDereferenceObject( driverObject );
return STATUS_SUCCESS;
}