diff options
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp')
-rw-r--r-- | src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp new file mode 100644 index 00000000..3061d5cf --- /dev/null +++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp @@ -0,0 +1,208 @@ +/* $Id: VBoxUsbHook.cpp $ */ +/** @file + * Driver Dispatch Table Hooking API + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxUsbMon.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VBOXUSBHOOK_MEMTAG 'HUBV' + + +NTSTATUS VBoxUsbHookInstall(PVBOXUSBHOOK_ENTRY pHook) +{ + KIRQL Irql; + KeAcquireSpinLock(&pHook->Lock, &Irql); + if (pHook->fIsInstalled) + { + WARN(("hook is marked installed, returning failure")); + KeReleaseSpinLock(&pHook->Lock, Irql); + return STATUS_UNSUCCESSFUL; + } + + pHook->pfnOldHandler = (PDRIVER_DISPATCH)InterlockedExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnHook); + Assert(pHook->pfnOldHandler); + Assert(pHook->pfnHook != pHook->pfnOldHandler); + pHook->fIsInstalled = TRUE; + KeReleaseSpinLock(&pHook->Lock, Irql); + return STATUS_SUCCESS; + +} +NTSTATUS VBoxUsbHookUninstall(PVBOXUSBHOOK_ENTRY pHook) +{ + KIRQL Irql; + KeAcquireSpinLock(&pHook->Lock, &Irql); + if (!pHook->fIsInstalled) + { + KeReleaseSpinLock(&pHook->Lock, Irql); + return STATUS_SUCCESS; + } + + PDRIVER_DISPATCH pfnOldVal = (PDRIVER_DISPATCH)InterlockedCompareExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnOldHandler, pHook->pfnHook); + Assert(pfnOldVal == pHook->pfnHook); + if (pfnOldVal != pHook->pfnHook) + { + AssertMsgFailed(("unhook failed!!!\n")); + /* this is bad! this could happen if someone else has chained another hook, + * or (which is even worse) restored the "initial" entry value it saved when doing a hooking before us + * return the failure and don't do anything else + * the best thing to do if this happens is to leave everything as is + * and to prevent the driver from being unloaded to ensure no one references our unloaded hook routine */ + KeReleaseSpinLock(&pHook->Lock, Irql); + return STATUS_UNSUCCESSFUL; + } + + pHook->fIsInstalled = FALSE; + KeReleaseSpinLock(&pHook->Lock, Irql); + + /* wait for the current handlers to exit */ + VBoxDrvToolRefWaitEqual(&pHook->HookRef, 1); + + return STATUS_SUCCESS; +} + +BOOLEAN VBoxUsbHookIsInstalled(PVBOXUSBHOOK_ENTRY pHook) +{ + KIRQL Irql; + BOOLEAN fIsInstalled; + KeAcquireSpinLock(&pHook->Lock, &Irql); + fIsInstalled = pHook->fIsInstalled; + KeReleaseSpinLock(&pHook->Lock, Irql); + return fIsInstalled; +} + +VOID VBoxUsbHookInit(PVBOXUSBHOOK_ENTRY pHook, PDRIVER_OBJECT pDrvObj, UCHAR iMjFunction, PDRIVER_DISPATCH pfnHook) +{ + Assert(pDrvObj); + Assert(iMjFunction <= IRP_MJ_MAXIMUM_FUNCTION); + Assert(pfnHook); + memset(pHook, 0, sizeof (*pHook)); + InitializeListHead(&pHook->RequestList); + KeInitializeSpinLock(&pHook->Lock); + VBoxDrvToolRefInit(&pHook->HookRef); + pHook->pDrvObj = pDrvObj; + pHook->iMjFunction = iMjFunction; + pHook->pfnHook = pfnHook; + Assert(!pHook->pfnOldHandler); + Assert(!pHook->fIsInstalled); + +} + +static void vboxUsbHookRequestRegisterCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest) +{ + Assert(pfnCompletion); + Assert(pRequest); + Assert(pDevObj); + Assert(pIrp); + PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp); + memset(pRequest, 0, sizeof (*pRequest)); + pRequest->pHook = pHook; + pRequest->OldLocation = *pSl; + pRequest->pDevObj = pDevObj; + pRequest->pIrp = pIrp; + pRequest->bCompletionStopped = FALSE; + pSl->CompletionRoutine = pfnCompletion; + pSl->Context = pRequest; + pSl->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + + KIRQL oldIrql; + KeAcquireSpinLock(&pHook->Lock, &oldIrql); + InsertTailList(&pHook->RequestList, &pRequest->ListEntry); + KeReleaseSpinLock(&pHook->Lock, oldIrql); +} + +NTSTATUS VBoxUsbHookRequestPassDownHookCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest) +{ + Assert(pfnCompletion); + vboxUsbHookRequestRegisterCompletion(pHook, pDevObj, pIrp, pfnCompletion, pRequest); + return pHook->pfnOldHandler(pDevObj, pIrp); +} + +NTSTATUS VBoxUsbHookRequestPassDownHookSkip(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp) +{ + return pHook->pfnOldHandler(pDevObj, pIrp); +} + +NTSTATUS VBoxUsbHookRequestMoreProcessingRequired(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, + PVBOXUSBHOOK_REQUEST pRequest) +{ + RT_NOREF3(pHook, pDevObj, pIrp); + Assert(!pRequest->bCompletionStopped); + pRequest->bCompletionStopped = TRUE; + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS VBoxUsbHookRequestComplete(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (pRequest->OldLocation.CompletionRoutine && pRequest->OldLocation.Control) + { + Status = pRequest->OldLocation.CompletionRoutine(pDevObj, pIrp, pRequest->OldLocation.Context); + } + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) + { + if (pRequest->bCompletionStopped) + { + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + } + /* + * else - in case driver returned STATUS_MORE_PROCESSING_REQUIRED, + * it will call IoCompleteRequest itself + */ + + KIRQL oldIrql; + KeAcquireSpinLock(&pHook->Lock, &oldIrql); + RemoveEntryList(&pRequest->ListEntry); + KeReleaseSpinLock(&pHook->Lock, oldIrql); + return Status; +} + +#define PVBOXUSBHOOK_REQUEST_FROM_LE(_pLe) ( (PVBOXUSBHOOK_REQUEST)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBHOOK_REQUEST, ListEntry) ) ) + +VOID VBoxUsbHookVerifyCompletion(PVBOXUSBHOOK_ENTRY pHook, PVBOXUSBHOOK_REQUEST pRequest, PIRP pIrp) +{ + KIRQL oldIrql; + KeAcquireSpinLock(&pHook->Lock, &oldIrql); + for (PLIST_ENTRY pLe = pHook->RequestList.Flink; pLe != &pHook->RequestList; pLe = pLe->Flink) + { + PVBOXUSBHOOK_REQUEST pCur = PVBOXUSBHOOK_REQUEST_FROM_LE(pLe); + if (pCur != pRequest) + continue; + if (pCur->pIrp != pIrp) + continue; + WARN(("found pending IRP(0x%p) when it should not be", pIrp)); + } + KeReleaseSpinLock(&pHook->Lock, oldIrql); + +} |