static NTSTATUS V4vInitializeEventChannel(PDEVICE_OBJECT fdo) { XENV4V_EXTENSION *pde = V4vGetDeviceExtension(fdo); KLOCK_QUEUE_HANDLE lqh; KeAcquireInStackQueuedSpinLock(&pde->virqLock, &lqh); if (!is_null_EVTCHN_PORT(pde->virqPort)) { KeReleaseInStackQueuedSpinLock(&lqh); TraceWarning(("V4V VIRQ already bound?\n")); return STATUS_SUCCESS; } pde->virqPort = EvtchnBindVirq(VIRQ_V4V, V4vVirqNotifyIsr, fdo); if (is_null_EVTCHN_PORT(pde->virqPort)) { KeReleaseInStackQueuedSpinLock(&lqh); TraceError(("failed to bind V4V VIRQ\n")); return STATUS_INSUFFICIENT_RESOURCES; } KeReleaseInStackQueuedSpinLock(&lqh); TraceNotice(("V4V VIRQ connected.\n")); return STATUS_SUCCESS; }
VOID DriverUnload( IN PDRIVER_OBJECT driverObject ) { NTSTATUS status; PVOID threadObj; KLOCK_QUEUE_HANDLE connListLockHandle; KLOCK_QUEUE_HANDLE packetQueueLockHandle; UNREFERENCED_PARAMETER(driverObject); KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); gDriverUnloading = TRUE; KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); KeReleaseInStackQueuedSpinLock(&connListLockHandle); if (IsListEmpty(&gConnList) && IsListEmpty(&gPacketQueue)) { KeSetEvent( &gWorkerEvent, IO_NO_INCREMENT, FALSE ); } ASSERT(gThreadObj != NULL); KeWaitForSingleObject( gThreadObj, Executive, KernelMode, FALSE, NULL ); ObDereferenceObject(gThreadObj); TLInspectUnregisterCallouts(); FwpsInjectionHandleDestroy0(gInjectionHandle); IoDeleteDevice(gDeviceObject); ZwClose(gRegistryKey); }
static NTSTATUS V4vCtrlGetInfo(XENV4V_CONTEXT *ctx, V4V_GETINFO_VALUES *gi) { NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; LONG val; KLOCK_QUEUE_HANDLE lqh; val = InterlockedExchangeAdd(&ctx->state, 0); if (gi->type == V4vGetPeerInfo) { if (val & (XENV4V_STATE_CONNECTING|XENV4V_STATE_CONNECTED|XENV4V_STATE_ACCEPTED)) { RtlMoveMemory(&gi->ringInfo.addr, &ctx->sdst, sizeof(v4v_addr_t)); gi->ringInfo.partner = V4V_DOMID_NONE; status = STATUS_SUCCESS; } } else if (gi->type == V4vGetLocalInfo) { if (val & (XENV4V_STATE_BOUND|XENV4V_STATE_LISTENING|XENV4V_STATE_WAITING| XENV4V_STATE_CONNECTING|XENV4V_STATE_CONNECTED| XENV4V_STATE_ACCEPTED)) { KeAcquireInStackQueuedSpinLock(&ctx->ringObject->lock, &lqh); RtlMoveMemory(&gi->ringInfo, &ctx->ringObject->ring->id, sizeof(v4v_ring_id_t)); KeReleaseInStackQueuedSpinLock(&lqh); status = STATUS_SUCCESS; } } return status; }
NTSTATUS drvStreamDeletion( IN UINT16 layerId, IN UINT32 calloutId, IN UINT64 flowContext) { // We can't free the memory of the corresponding FLOW_DATA // disposition here, while thAnalyzer thread may use it. Stream // deletion is handled in the thAnalyzer thread, while processing // the "close" packet, which is sent from here KLOCK_QUEUE_HANDLE packetQueueLockHandle; PENDED_PACKET *pendedPacket; BOOLEAN signalWorkerThread; // pendedPacket gets deleted in FreePendedPacket #pragma warning( suppress : 28197 ) pendedPacket = ExAllocatePoolWithTag( NonPagedPool, sizeof(PENDED_PACKET), TAG_PENDEDPACKET); if (pendedPacket == NULL) { return STATUS_UNSUCCESSFUL; } RtlZeroMemory(pendedPacket, sizeof(PENDED_PACKET)); pendedPacket->flowContext = (FLOW_DATA *) flowContext; pendedPacket->close = TRUE; KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle); if (!gDriverUnloading) { signalWorkerThread = IsListEmpty(&gPacketQueue); InsertTailList(&gPacketQueue, &pendedPacket->listEntry); pendedPacket = NULL; // ownership transferred } else { // Driver is being unloaded, permit any connect classify. signalWorkerThread = FALSE; } KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); if (signalWorkerThread) { KeSetEvent( &gWorkerEvent, 0, FALSE); } return STATUS_SUCCESS; }
NTSTATUS MonitorCoInsertFlowContext( _Inout_ FLOW_DATA* flowContext) { KLOCK_QUEUE_HANDLE lockHandle; NTSTATUS status; KeAcquireInStackQueuedSpinLock(&flowContextListLock, &lockHandle); // Catch the case where we disabled monitoring after we had intended to // associate the context to the flow so that we don't bugcheck due to // our driver being unloaded and then receiving a call for a particular // flow or leak the memory because we unloaded without freeing it. if (monitoringEnabled) { DoTraceMessage(TRACE_FLOW_ESTABLISHED, "Creating flow for traffic.\r\n"); InsertTailList(&flowContextList, &flowContext->listEntry); status = STATUS_SUCCESS; } else { DoTraceMessage(TRACE_FLOW_ESTABLISHED, "Unable to create flow, driver shutting down.\r\n"); // Our driver is shutting down. status = STATUS_SHUTDOWN_IN_PROGRESS; } KeReleaseInStackQueuedSpinLock(&lockHandle); return status; }
// Switchs the current log buffer, saves the contents of old buffer to the log // file, and prints them out as necessary. This function does not flush the log // file, so code should call LogpWriteMessageToFile() or ZwFlushBuffersFile() // later. _Use_decl_annotations_ static NTSTATUS LogpFlushLogBuffer(LogBufferInfo *info) { NT_ASSERT(info); NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); auto status = STATUS_SUCCESS; // Enter a critical section and acquire a reader lock for info in order to // write a log file safely. ExEnterCriticalRegionAndAcquireResourceExclusive(&info->resource); // Acquire a spin lock for info.log_buffer(s) in order to switch its head // safely. KLOCK_QUEUE_HANDLE lock_handle = {}; KeAcquireInStackQueuedSpinLock(&info->spin_lock, &lock_handle); const auto old_log_buffer = const_cast<char *>(info->log_buffer_head); if (old_log_buffer[0]) { info->log_buffer_head = (old_log_buffer == info->log_buffer1) ? info->log_buffer2 : info->log_buffer1; info->log_buffer_head[0] = '\0'; info->log_buffer_tail = info->log_buffer_head; } KeReleaseInStackQueuedSpinLock(&lock_handle); // Write all log entries in old log buffer. IO_STATUS_BLOCK io_status = {}; for (auto current_log_entry = old_log_buffer; current_log_entry[0]; /**/) { // Check the printed bit and clear it const auto printed_out = LogpIsPrinted(current_log_entry); LogpSetPrintedBit(current_log_entry, false); const auto current_log_entry_length = strlen(current_log_entry); status = ZwWriteFile(info->log_file_handle, nullptr, nullptr, nullptr, &io_status, current_log_entry, static_cast<ULONG>(current_log_entry_length), nullptr, nullptr); if (!NT_SUCCESS(status)) { // It could happen when you did not register IRP_SHUTDOWN and call // LogIrpShutdownHandler() and the system tried to log to a file after // a file system was unmounted. LogpDbgBreak(); } // Print it out if requested and the message is not already printed out if (!printed_out) { LogpDoDbgPrint(current_log_entry); } current_log_entry += current_log_entry_length + 1; } old_log_buffer[0] = '\0'; ExReleaseResourceAndLeaveCriticalRegion(&info->resource); return status; }
static VOID V4vUninitializeEventChannel(PDEVICE_OBJECT fdo) { XENV4V_EXTENSION *pde = V4vGetDeviceExtension(fdo); KLOCK_QUEUE_HANDLE lqh; KeAcquireInStackQueuedSpinLock(&pde->virqLock, &lqh); if (is_null_EVTCHN_PORT(pde->virqPort)) { // This is ok, e.g. getting a stop and remove PnP call KeReleaseInStackQueuedSpinLock(&lqh); return; } EvtchnClose(pde->virqPort); pde->virqPort = null_EVTCHN_PORT(); KeReleaseInStackQueuedSpinLock(&lqh); TraceNotice(("V4V VIRQ disconnected.\n")); }
VOID DriverUnload( IN PDRIVER_OBJECT driverObject) { UNICODE_STRING dosDeviceName; UNREFERENCED_PARAMETER(driverObject); // set the unloading marker { KLOCK_QUEUE_HANDLE packetQueueLockHandle; KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); gDriverUnloading = TRUE; KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); } CleanupFlowContextList(); if (IsListEmpty(&gPacketQueue)) { KeSetEvent( &gWorkerEvent, IO_NO_INCREMENT, FALSE); } ASSERT(gThreadObj != NULL); KeWaitForSingleObject( gThreadObj, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(gThreadObj); UnregisterCallouts(); NdisFreeNetBufferListPool(gNetBufferListPool); NdisFreeGenericObject(gNdisGenericObj); FwpsInjectionHandleDestroy0(gInjectionHandle); RtlInitUnicodeString(&dosDeviceName, SYMBOLIC_LINK_NAME); IoDeleteSymbolicLink(&dosDeviceName); IoDeleteDevice(gDeviceObject); }
VOID V4vStartConnectionTimer(XENV4V_EXTENSION *pde) { KLOCK_QUEUE_HANDLE lqh; ULONG count; LARGE_INTEGER due; KeAcquireInStackQueuedSpinLock(&pde->timerLock, &lqh); count = ++pde->timerCounter; KeReleaseInStackQueuedSpinLock(&lqh); // Just transitioned from 1 if (count == 1) { due.QuadPart = XENV4V_LARGEINT_DELAY(XENV4V_TIMER_INTERVAL/2); KeSetTimerEx(&pde->timer, due, XENV4V_TIMER_INTERVAL, &pde->timerDpc); } }
void OobEditShutdown( _Out_ STREAM_EDITOR* streamEditor ) { KLOCK_QUEUE_HANDLE editLockHandle; KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); streamEditor->oobEditInfo.shuttingDown = TRUE; switch (streamEditor->oobEditInfo.editState) { case OOB_EDIT_IDLE: { streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; KeSetEvent( &gStreamEditor.oobEditInfo.editEvent, IO_NO_INCREMENT, FALSE ); break; } default: break; }; KeReleaseInStackQueuedSpinLock(&editLockHandle); NT_ASSERT(gThreadObj != NULL); KeWaitForSingleObject( gThreadObj, Executive, KernelMode, FALSE, NULL ); ObDereferenceObject(gThreadObj); }
void DDProxyRemoveFlows() { while (!IsListEmpty(&gFlowList)) { KLOCK_QUEUE_HANDLE flowListLockHandle; LIST_ENTRY* listEntry = NULL; DD_PROXY_FLOW_CONTEXT* flowContext; KeAcquireInStackQueuedSpinLock( &gFlowListLock, &flowListLockHandle ); if (!IsListEmpty(&gFlowList)) { listEntry = RemoveHeadList(&gFlowList); } // // Releasing the lock here since removing the flow context // will invoke the callout's flowDeleteFn synchronously // if there are no active classifications in progress. // KeReleaseInStackQueuedSpinLock(&flowListLockHandle); if (listEntry != NULL) { flowContext = CONTAINING_RECORD( listEntry, DD_PROXY_FLOW_CONTEXT, listEntry ); flowContext->deleted = TRUE; FwpsFlowRemoveContext0( flowContext->flowId, flowContext->layerId, flowContext->calloutId ); } } }
RBOOL task_get_new_processes ( RPU8 pArgs, RU32 argsSize, RPU8 pResult, RU32* resultSize ) { RBOOL isSuccess = FALSE; KLOCK_QUEUE_HANDLE hMutex = { 0 }; RU32 toCopy = 0; UNREFERENCED_PARAMETER( pArgs ); UNREFERENCED_PARAMETER( argsSize ); if( NULL != pResult && NULL != resultSize && 0 != *resultSize ) { KeAcquireInStackQueuedSpinLock( &g_collector_1_mutex, &hMutex ); toCopy = ( *resultSize ) / sizeof( KernelAcqProcess ); if( 0 != toCopy ) { toCopy = ( toCopy > g_nextProcess ? g_nextProcess : toCopy ); *resultSize = toCopy * sizeof( KernelAcqProcess ); memcpy( pResult, g_processes, *resultSize ); g_nextProcess -= toCopy; memmove( g_processes, g_processes + toCopy, g_nextProcess ); } KeReleaseInStackQueuedSpinLock( &hMutex ); isSuccess = TRUE; } return isSuccess; }
static NTSTATUS V4vCtrlDumpRing(XENV4V_CONTEXT *ctx) { NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; LONG val; KLOCK_QUEUE_HANDLE lqh; val = InterlockedExchangeAdd(&ctx->state, 0); if (val & (XENV4V_STATE_BOUND|XENV4V_STATE_LISTENING|XENV4V_STATE_WAITING| XENV4V_STATE_CONNECTING|XENV4V_STATE_CONNECTED| XENV4V_STATE_ACCEPTED)) { KeAcquireInStackQueuedSpinLock(&ctx->ringObject->lock, &lqh); V4vDumpRing(ctx->ringObject->ring); KeReleaseInStackQueuedSpinLock(&lqh); status = STATUS_SUCCESS; } return status; }
VOID V4vStopConnectionTimer(XENV4V_EXTENSION *pde, BOOLEAN immediate) { KLOCK_QUEUE_HANDLE lqh; ULONG count = (ULONG)-1; KeAcquireInStackQueuedSpinLock(&pde->timerLock, &lqh); if (immediate) { count = pde->timerCounter = 0; } else if (pde->timerCounter > 0) { count = --pde->timerCounter; } KeReleaseInStackQueuedSpinLock(&lqh); // Dropped back to 0, turn off the timer if (count == 0) { KeCancelTimer(&pde->timer); } }
BOOLEAN ImScsiVirtualDrivesPresent() { PLIST_ENTRY list_ptr; BOOLEAN result = FALSE; #if defined(_AMD64_) KLOCK_QUEUE_HANDLE LockHandle; KeAcquireInStackQueuedSpinLock( // Serialize the linked list of HBA. &pMPDrvInfoGlobal->DrvInfoLock, &LockHandle); #else KIRQL SaveIrql; KeAcquireSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, &SaveIrql); #endif for (list_ptr = pMPDrvInfoGlobal->ListMPHBAObj.Flink; list_ptr != &pMPDrvInfoGlobal->ListMPHBAObj; list_ptr = list_ptr->Flink ) { pHW_HBA_EXT pHBAExt; pHBAExt = CONTAINING_RECORD(list_ptr, HW_HBA_EXT, List); if (!IsListEmpty(&pHBAExt->LUList)) { result = TRUE; break; } } pMPDrvInfoGlobal->DrvInfoLock; #if defined(_AMD64_) KeReleaseInStackQueuedSpinLock(&LockHandle); #else KeReleaseSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, SaveIrql); #endif return result; }
VOID MpHwFreeAdapterResources(__in pHW_HBA_EXT pHBAExt) { PLIST_ENTRY pNextEntry; pHW_HBA_EXT pLclHBAExt; #if defined(_AMD64_) KLOCK_QUEUE_HANDLE LockHandle; #else KIRQL SaveIrql; #endif KdPrint2(("PhDskMnt::MpHwFreeAdapterResources: pHBAExt = 0x%p\n", pHBAExt)); #if defined(_AMD64_) KeAcquireInStackQueuedSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, &LockHandle); #else KeAcquireSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, &SaveIrql); #endif for ( // Go through linked list of HBA extensions. pNextEntry = pMPDrvInfoGlobal->ListMPHBAObj.Flink; pNextEntry != &pMPDrvInfoGlobal->ListMPHBAObj; pNextEntry = pNextEntry->Flink ) { pLclHBAExt = CONTAINING_RECORD(pNextEntry, HW_HBA_EXT, List); if (pLclHBAExt==pHBAExt) { // Is this entry the same as pHBAExt? RemoveEntryList(pNextEntry); pMPDrvInfoGlobal->DrvInfoNbrMPHBAObj--; break; } } #if defined(_AMD64_) KeReleaseInStackQueuedSpinLock(&LockHandle); #else KeReleaseSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, SaveIrql); #endif } // End MpHwFreeAdapterResources().
// Buffer the log entry to the log buffer. _Use_decl_annotations_ static NTSTATUS LogpBufferMessage(const char *message, LogBufferInfo *info) { NT_ASSERT(info); // Acquire a spin lock to add the log safely. KLOCK_QUEUE_HANDLE lock_handle = {}; const auto old_irql = KeGetCurrentIrql(); if (old_irql < DISPATCH_LEVEL) { KeAcquireInStackQueuedSpinLock(&info->spin_lock, &lock_handle); } else { KeAcquireInStackQueuedSpinLockAtDpcLevel(&info->spin_lock, &lock_handle); } NT_ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); // Copy the current log to the buffer. SIZE_T used_buffer_size = info->log_buffer_tail - info->log_buffer_head; auto status = RtlStringCchCopyA(const_cast<char *>(info->log_buffer_tail), kLogpBufferUsableSize - used_buffer_size, message); // Update info.log_max_usage if necessary. if (NT_SUCCESS(status)) { const auto message_length = strlen(message) + 1; info->log_buffer_tail += message_length; used_buffer_size += message_length; if (used_buffer_size > info->log_max_usage) { info->log_max_usage = used_buffer_size; // Update } } else { info->log_max_usage = kLogpBufferSize; // Indicates overflow } *info->log_buffer_tail = '\0'; if (old_irql < DISPATCH_LEVEL) { KeReleaseInStackQueuedSpinLock(&lock_handle); } else { KeReleaseInStackQueuedSpinLockFromDpcLevel(&lock_handle); } return status; }
static VOID CreateProcessNotifyEx ( PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo ) { KLOCK_QUEUE_HANDLE hMutex = { 0 }; UNREFERENCED_PARAMETER( Process ); KeAcquireInStackQueuedSpinLock( &g_collector_1_mutex, &hMutex ); // We're only interested in starts for now, a non-NULL CreateInfo indicates this. if( NULL != CreateInfo ) { g_processes[ g_nextProcess ].pid = (RU32)ProcessId; g_processes[ g_nextProcess ].ppid = (RU32)CreateInfo->ParentProcessId; g_processes[ g_nextProcess ].ts = rpal_time_getLocal(); g_processes[ g_nextProcess ].uid = KERNEL_ACQ_NO_USER_ID; copyUnicodeStringToBuffer( CreateInfo->ImageFileName, g_processes[ g_nextProcess ].path ); copyUnicodeStringToBuffer( CreateInfo->CommandLine, g_processes[ g_nextProcess ].cmdline ); g_nextProcess++; if( g_nextProcess == _NUM_BUFFERED_PROCESSES ) { g_nextProcess = 0; } } KeReleaseInStackQueuedSpinLock( &hMutex ); }
ULONG MpHwFindAdapter( __in PVOID DeviceExtension, __in PVOID pReservedArg1, __in PVOID pReservedArg2, #ifdef USE_STORPORT __in PVOID pReservedArg3, #endif __in PCHAR ArgumentString, __in __out PPORT_CONFIGURATION_INFORMATION pConfigInfo, __out PBOOLEAN pBAgain ) { ULONG i, len, status = SP_RETURN_FOUND; PCHAR pChar; pHW_HBA_EXT pHBAExt = (pHW_HBA_EXT)DeviceExtension; NTSTATUS ntstatus; #if defined(_AMD64_) KLOCK_QUEUE_HANDLE LockHandle; #else KIRQL SaveIrql; #endif UNREFERENCED_PARAMETER(pReservedArg1); UNREFERENCED_PARAMETER(pReservedArg2); #ifdef USE_STORPORT UNREFERENCED_PARAMETER(pReservedArg3); #endif UNREFERENCED_PARAMETER(ArgumentString); KdPrint2(("PhDskMnt::MpHwFindAdapter: Arg=%s%s%s, pHBAExt = 0x%p, pConfigInfo = 0x%p, IRQL=%i\n", ArgumentString != NULL ? "\"" : "(", ArgumentString != NULL ? ArgumentString : "null", ArgumentString != NULL ? "\"" : ")", pHBAExt, pConfigInfo, KeGetCurrentIrql())); if (pMPDrvInfoGlobal->GlobalsInitialized) { LARGE_INTEGER wait_time; DbgPrint("PhDskMnt::MpHwFindAdapter: Already initialized.\n"); wait_time.QuadPart = -1000000; KeDelayExecutionThread(KernelMode, FALSE, &wait_time); } KeInitializeSpinLock(&pHBAExt->LUListLock); InitializeListHead(&pHBAExt->LUList); pHBAExt->HostTargetId = (UCHAR)pMPDrvInfoGlobal->MPRegInfo.InitiatorID; #ifdef USE_STORPORT pConfigInfo->VirtualDevice = TRUE; // Inidicate no real hardware. pConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex; #endif pConfigInfo->WmiDataProvider = FALSE; // Indicate WMI provider. pConfigInfo->MaximumTransferLength = SP_UNINITIALIZED_VALUE; // Indicate unlimited. pConfigInfo->AlignmentMask = 0x3; // Indicate DWORD alignment. pConfigInfo->CachesData = FALSE; // Indicate miniport wants flush and shutdown notification. pConfigInfo->MaximumNumberOfTargets = SCSI_MAXIMUM_TARGETS; // Indicate maximum targets. pConfigInfo->NumberOfBuses = (UCHAR) pMPDrvInfoGlobal->MPRegInfo.NumberOfBuses; // Indicate number of busses. pConfigInfo->ScatterGather = TRUE; // Indicate scatter-gather (explicit setting needed for Win2003 at least). pConfigInfo->AutoRequestSense = TRUE; pConfigInfo->TaggedQueuing = TRUE; pConfigInfo->MultipleRequestPerLu = TRUE; // Save Vendor Id, Product Id, Revision in device extension. pChar = (PCHAR)pMPDrvInfoGlobal->MPRegInfo.VendorId.Buffer; len = min(8, (pMPDrvInfoGlobal->MPRegInfo.VendorId.Length/2)); for ( i = 0; i < len; i++, pChar+=2) pHBAExt->VendorId[i] = *pChar; pChar = (PCHAR)pMPDrvInfoGlobal->MPRegInfo.ProductId.Buffer; len = min(16, (pMPDrvInfoGlobal->MPRegInfo.ProductId.Length/2)); for ( i = 0; i < len; i++, pChar+=2) pHBAExt->ProductId[i] = *pChar; pChar = (PCHAR)pMPDrvInfoGlobal->MPRegInfo.ProductRevision.Buffer; len = min(4, (pMPDrvInfoGlobal->MPRegInfo.ProductRevision.Length/2)); for ( i = 0; i < len; i++, pChar+=2) pHBAExt->ProductRevision[i] = *pChar; // Add HBA extension to master driver object's linked list. #if defined(_AMD64_) KeAcquireInStackQueuedSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, &LockHandle); #else KeAcquireSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, &SaveIrql); #endif InsertTailList(&pMPDrvInfoGlobal->ListMPHBAObj, &pHBAExt->List); pMPDrvInfoGlobal->DrvInfoNbrMPHBAObj++; #if defined(_AMD64_) KeReleaseInStackQueuedSpinLock(&LockHandle); #else KeReleaseSpinLock(&pMPDrvInfoGlobal->DrvInfoLock, SaveIrql); #endif if (!pMPDrvInfoGlobal->GlobalsInitialized) { HANDLE thread_handle; OBJECT_ATTRIBUTES object_attributes; KeInitializeSpinLock(&pMPDrvInfoGlobal->RequestListLock); InitializeListHead(&pMPDrvInfoGlobal->RequestList); KeInitializeEvent(&pMPDrvInfoGlobal->RequestEvent, SynchronizationEvent, FALSE); #ifdef USE_SCSIPORT KeInitializeSpinLock(&pMPDrvInfoGlobal->ResponseListLock); KeInitializeEvent(&pMPDrvInfoGlobal->ResponseEvent, SynchronizationEvent, FALSE); InitializeListHead(&pMPDrvInfoGlobal->ResponseList); #endif KeInitializeEvent(&pMPDrvInfoGlobal->StopWorker, NotificationEvent, FALSE); pMPDrvInfoGlobal->GlobalsInitialized = TRUE; InitializeObjectAttributes(&object_attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); ntstatus = PsCreateSystemThread( &thread_handle, (ACCESS_MASK) 0L, &object_attributes, NULL, NULL, ImScsiWorkerThread, NULL); if (!NT_SUCCESS(ntstatus)) { DbgPrint("PhDskMnt::ScsiGetLUExtension: Cannot create worker thread. (%#x)\n", ntstatus); status = SP_RETURN_ERROR; } else { ObReferenceObjectByHandle( thread_handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, *PsThreadType, KernelMode, (PVOID*)&pMPDrvInfoGlobal->WorkerThread, NULL ); ZwClose(thread_handle); //for (i = 0; i < pHBAExt->NbrLUNsperHBA; i++) // ImScsiCreateLU(pHBAExt, 0, (UCHAR)i, 0); } } //Done: *pBAgain = FALSE; KdPrint2(("PhDskMnt::MpHwFindAdapter: End, status = 0x%x\n", status)); return status; } // End MpHwFindAdapter().
NTSTATUS StreamOobFlushOutgoingData( _Inout_ STREAM_EDITOR* streamEditor ) { NTSTATUS status = STATUS_SUCCESS; KLOCK_QUEUE_HANDLE editLockHandle; OUTGOING_STREAM_DATA* outgoingStreamData = NULL; for(;;) { KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); if (!IsListEmpty(&streamEditor->oobEditInfo.outgoingDataQueue)) { LIST_ENTRY* listEntry = RemoveHeadList(&streamEditor->oobEditInfo.outgoingDataQueue); outgoingStreamData = CONTAINING_RECORD( listEntry, OUTGOING_STREAM_DATA, listEntry ); } KeReleaseInStackQueuedSpinLock(&editLockHandle); if (outgoingStreamData == NULL) { break; } status = FwpsStreamInjectAsync( gInjectionHandle, NULL, 0, streamEditor->oobEditInfo.flowId, streamEditor->oobEditInfo.calloutId, streamEditor->oobEditInfo.layerId, outgoingStreamData->streamFlags, outgoingStreamData->netBufferList, outgoingStreamData->dataLength, outgoingStreamData->isClone ? StreamOobInjectCloneCompletionFn : StreamOobInjectCompletionFn, outgoingStreamData->mdl ); if (!NT_SUCCESS(status)) { goto Exit; } ExFreePoolWithTag( outgoingStreamData, STREAM_EDITOR_OUTGOING_DATA_TAG ); outgoingStreamData = NULL; } Exit: if (outgoingStreamData != NULL) { NT_ASSERT(!NT_SUCCESS(status)); if (outgoingStreamData->isClone) { FwpsDiscardClonedStreamData( outgoingStreamData->netBufferList, 0, FALSE ); } else { FwpsFreeNetBufferList(outgoingStreamData->netBufferList); if (outgoingStreamData->mdl != NULL) { IoFreeMdl(outgoingStreamData->mdl); ExFreePoolWithTag( outgoingStreamData->mdl->MappedSystemVa, STREAM_EDITOR_MDL_DATA_TAG ); } } ExFreePoolWithTag( outgoingStreamData, STREAM_EDITOR_OUTGOING_DATA_TAG ); } return status; }
NTSTATUS StreamOobQueueUpOutgoingData( _Inout_ STREAM_EDITOR* streamEditor, _Inout_ NET_BUFFER_LIST* netBufferList, BOOLEAN isClone, size_t dataLength, DWORD streamFlags, _In_opt_ MDL* mdl ) /* ++ This function queues up processed data (either sections of the indicated data or newly created data) such that they can be (re-)injected back to the data stream during the following context. 1. Before FWP_ACTION_BLOCK is returned from the ClassifyFn, or 2. After EOF is indicated. Under the conditions above, the incoming data (which we pend) and the outgoing data (which we (re-)inject) can be synchronized properly). -- */ { NTSTATUS status = STATUS_SUCCESS; KLOCK_QUEUE_HANDLE editLockHandle; OUTGOING_STREAM_DATA* outgoingStreamData; outgoingStreamData = (OUTGOING_STREAM_DATA*) ExAllocatePoolWithTag( NonPagedPool, sizeof(OUTGOING_STREAM_DATA), STREAM_EDITOR_OUTGOING_DATA_TAG ); if (outgoingStreamData == NULL) { status = STATUS_NO_MEMORY; return status; } RtlZeroMemory(outgoingStreamData, sizeof(OUTGOING_STREAM_DATA)); outgoingStreamData->netBufferList = netBufferList; outgoingStreamData->isClone = isClone; outgoingStreamData->dataLength = dataLength; outgoingStreamData->streamFlags = streamFlags; outgoingStreamData->mdl = mdl; KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); InsertTailList( &streamEditor->oobEditInfo.outgoingDataQueue, &outgoingStreamData->listEntry ); KeReleaseInStackQueuedSpinLock(&editLockHandle); return status; }
void StreamOobEdit( _Inout_ STREAM_EDITOR* streamEditor, const FWPS_INCOMING_VALUES* inFixedValues, const FWPS_INCOMING_METADATA_VALUES* inMetaValues, const FWPS_FILTER* filter, _Inout_ FWPS_STREAM_DATA* streamData, _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket, _Inout_ FWPS_CLASSIFY_OUT* classifyOut ) /* ++ This function queues up incoming data and notifies the worker thread to process them. The incoming data is blocked and removed from the stream while data is pending. If the editor is shutdown (e.g. during driverUnload) as indicated by OOB_EDIT_SHUT_DOWN state, it permits the indicated data inline after flushing all pended data (to be carried out by the caller). -- */ { NTSTATUS status; KLOCK_QUEUE_HANDLE editLockHandle; KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); if (streamEditor->oobEditInfo.nblEof != NULL) { // // A new flow arrives before we finish processing an earlier flow. Production // code should create 1:1 between streamEditor and flow to handle this // condition. See the "MSN Monitor sample" for how that can be implemented. // ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; goto Exit; } if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) { NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL); streamEditor->oobEditInfo.noMoreData = TRUE; } // // Record needed flow information etc for data (re-)injection. // streamEditor->oobEditInfo.calloutId = filter->action.calloutId; streamEditor->oobEditInfo.flowId = inMetaValues->flowHandle; streamEditor->oobEditInfo.layerId = inFixedValues->layerId; switch (streamEditor->oobEditInfo.editState) { case OOB_EDIT_PROCESSING: { if ((streamEditor->oobEditInfo.totalDataLength + streamData->dataLength) > streamEditor->oobEditInfo.busyThreshold) { ioPacket->streamAction = FWPS_STREAM_ACTION_DEFER; classifyOut->actionType = FWP_ACTION_NONE; streamEditor->oobEditInfo.editState = OOB_EDIT_BUSY; } else { status = StreamOobQueueUpIncomingData( streamEditor, streamData ); if (!NT_SUCCESS(status)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; } else { // // State remains at OOB_EDIT_PROCESSING state. Since the worker thread // is active there is no need to set the event (to wake it up) // ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; ioPacket->countBytesEnforced = 0; classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } } break; } case OOB_EDIT_IDLE: { status = StreamOobQueueUpIncomingData( streamEditor, streamData ); if (!NT_SUCCESS(status)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; } else { streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; // // The worker thread is idle waiting for more work, now wake it up. // KeSetEvent( &streamEditor->oobEditInfo.editEvent, 0, FALSE ); ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; ioPacket->countBytesEnforced = 0; classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } break; } case OOB_EDIT_SHUT_DOWN: { ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; ioPacket->countBytesEnforced = 0; classifyOut->actionType = FWP_ACTION_PERMIT; if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) { classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } break; } case OOB_EDIT_ERROR: { ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; break; } default: NT_ASSERT(FALSE); }; Exit: KeReleaseInStackQueuedSpinLock(&editLockHandle); }
void StreamOobEditWorker( _In_ void* StartContext ) /* ++ This function waits for an event which gets signalled when there is data waiting to be inspected. Once awaken, the worker thread edits the stream until all stream data is processed (and then it waits for more work again). When requested to shutdown, it will finish the editing task and enters "shutdown" state. -- */ { NTSTATUS status = STATUS_SUCCESS; NET_BUFFER_LIST* netBufferListChain = NULL; size_t totalDataLength; STREAM_EDITOR* streamEditor = (STREAM_EDITOR*)StartContext; DWORD streamFlags; for(;;) { KLOCK_QUEUE_HANDLE editLockHandle; KeWaitForSingleObject( &streamEditor->oobEditInfo.editEvent, Executive, KernelMode, FALSE, NULL ); if (streamEditor->oobEditInfo.editState == OOB_EDIT_ERROR || streamEditor->oobEditInfo.editState == OOB_EDIT_SHUT_DOWN) { break; } KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); NT_ASSERT(streamEditor->oobEditInfo.editState == OOB_EDIT_PROCESSING || streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY); netBufferListChain = streamEditor->oobEditInfo.nblHead; totalDataLength = streamEditor->oobEditInfo.totalDataLength; streamFlags = streamEditor->oobEditInfo.streamFlags; streamEditor->oobEditInfo.nblHead = NULL; streamEditor->oobEditInfo.nblTail = NULL; streamEditor->oobEditInfo.totalDataLength = 0; KeReleaseInStackQueuedSpinLock(&editLockHandle); _Analysis_assume_(netBufferListChain != NULL); status = StreamOobEditData( streamEditor, netBufferListChain, totalDataLength, streamFlags ); if (!NT_SUCCESS(status)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; break; } if (streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY) { NTSTATUS streamContinueStatus; streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; streamContinueStatus = FwpsStreamContinue( streamEditor->oobEditInfo.flowId, streamEditor->oobEditInfo.calloutId, streamEditor->oobEditInfo.layerId, streamEditor->oobEditInfo.streamFlags ); if (!NT_SUCCESS(streamContinueStatus)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; break; } } KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); if (streamEditor->oobEditInfo.nblHead == NULL) { if (!streamEditor->oobEditInfo.shuttingDown) { streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE; KeClearEvent(&streamEditor->oobEditInfo.editEvent); } else { streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; } } KeReleaseInStackQueuedSpinLock(&editLockHandle); } PsTerminateSystemThread(status); }
void TLInspectALERecvAcceptClassify( IN const FWPS_INCOMING_VALUES0* inFixedValues, IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, IN OUT void* layerData, IN const FWPS_FILTER0* filter, IN UINT64 flowContext, OUT FWPS_CLASSIFY_OUT0* classifyOut ) /* ++ This is the classifyFn function for the ALE Recv-Accept (v4 and v6) callout. For an initial classify (where the FWP_CONDITION_FLAG_IS_REAUTHORIZE flag is not set), it is queued to the connection list for inspection by the worker thread. For re-auth, it is queued to the packet queue to be process by the worker thread like any other regular packets. -- */ { NTSTATUS status; KLOCK_QUEUE_HANDLE connListLockHandle; KLOCK_QUEUE_HANDLE packetQueueLockHandle; TL_INSPECT_PENDED_PACKET* pendedRecvAccept = NULL; TL_INSPECT_PENDED_PACKET* pendedPacket = NULL; ADDRESS_FAMILY addressFamily; FWPS_PACKET_INJECTION_STATE packetState; BOOLEAN signalWorkerThread; UNREFERENCED_PARAMETER(flowContext); UNREFERENCED_PARAMETER(filter); // // We don't have the necessary right to alter the classify, exit. // if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) { goto Exit; } ASSERT(layerData != NULL); // // We don't re-inspect packets that we've inspected earlier. // packetState = FwpsQueryPacketInjectionState0( gInjectionHandle, layerData, NULL ); if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)) { classifyOut->actionType = FWP_ACTION_PERMIT; goto Exit; } addressFamily = GetAddressFamilyForLayer(inFixedValues->layerId); if (!IsAleReauthorize(inFixedValues)) { // // If the classify is the initial authorization for a connection, we // queue it to the pended connection list and notify the worker thread // for out-of-band processing. // pendedRecvAccept = AllocateAndInitializePendedPacket( inFixedValues, inMetaValues, addressFamily, layerData, TL_INSPECT_CONNECT_PACKET, FWP_DIRECTION_INBOUND ); if (pendedRecvAccept == NULL) { classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; goto Exit; } ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_COMPLETION_HANDLE)); // // Pend the ALE_AUTH_RECV_ACCEPT classify. // status = FwpsPendOperation0( inMetaValues->completionHandle, &pendedRecvAccept->completionContext ); if (!NT_SUCCESS(status)) { classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; goto Exit; } KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); signalWorkerThread = IsListEmpty(&gConnList) && IsListEmpty(&gPacketQueue); InsertTailList(&gConnList, &pendedRecvAccept->listEntry); pendedRecvAccept = NULL; // ownership transferred KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); KeReleaseInStackQueuedSpinLock(&connListLockHandle); classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; if (signalWorkerThread) { KeSetEvent( &gWorkerEvent, 0, FALSE ); } } else // re-auth @ ALE_AUTH_RECV_ACCEPT { FWP_DIRECTION packetDirection; // // The classify is the re-authorization for a existing connection, it // could have been triggered for one of the two cases -- // // 1) The re-auth is triggered by an outbound packet sent immediately // after a policy change at ALE_AUTH_RECV_ACCEPT layer. // 2) The re-auth is triggered by an inbound packet received // immediately after a policy change at ALE_AUTH_RECV_ACCEPT layer. // ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_PACKET_DIRECTION)); packetDirection = inMetaValues->packetDirection; pendedPacket = AllocateAndInitializePendedPacket( inFixedValues, inMetaValues, addressFamily, layerData, TL_INSPECT_REAUTH_PACKET, packetDirection ); if (pendedPacket == NULL) { classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; goto Exit; } if (packetDirection == FWP_DIRECTION_INBOUND) { pendedPacket->ipSecProtected = IsSecureConnection(inFixedValues); } KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); if (!gDriverUnloading) { signalWorkerThread = IsListEmpty(&gPacketQueue) && IsListEmpty(&gConnList); InsertTailList(&gPacketQueue, &pendedPacket->listEntry); pendedPacket = NULL; // ownership transferred classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; } else { // // Driver is being unloaded, permit any connect classify. // signalWorkerThread = FALSE; classifyOut->actionType = FWP_ACTION_PERMIT; } KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); KeReleaseInStackQueuedSpinLock(&connListLockHandle); if (signalWorkerThread) { KeSetEvent( &gWorkerEvent, 0, FALSE ); } } Exit: if (pendedPacket != NULL) { FreePendedPacket(pendedPacket); } if (pendedRecvAccept != NULL) { FreePendedPacket(pendedRecvAccept); } return; }
void TLInspectALEConnectClassify( IN const FWPS_INCOMING_VALUES0* inFixedValues, IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, IN OUT void* layerData, IN const FWPS_FILTER0* filter, IN UINT64 flowContext, OUT FWPS_CLASSIFY_OUT0* classifyOut ) /* ++ This is the classifyFn function for the ALE connect (v4 and v6) callout. For an initial classify (where the FWP_CONDITION_FLAG_IS_REAUTHORIZE flag is not set), it is queued to the connection list for inspection by the worker thread. For re-auth, we first check if it is triggered by an ealier FwpsCompleteOperation0 call by looking for an pended connect that has been inspected. If found, we remove it from the connect list and return the inspection result; otherwise we can conclude that the re-auth is triggered by policy change so we queue it to the packet queue to be process by the worker thread like any other regular packets. -- */ { NTSTATUS status; KLOCK_QUEUE_HANDLE connListLockHandle; KLOCK_QUEUE_HANDLE packetQueueLockHandle; TL_INSPECT_PENDED_PACKET* pendedConnect = NULL; TL_INSPECT_PENDED_PACKET* pendedPacket = NULL; ADDRESS_FAMILY addressFamily; FWPS_PACKET_INJECTION_STATE packetState; BOOLEAN signalWorkerThread; UNREFERENCED_PARAMETER(flowContext); UNREFERENCED_PARAMETER(filter); // // We don't have the necessary right to alter the classify, exit. // if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) { goto Exit; } if (layerData != NULL) { // // We don't re-inspect packets that we've inspected earlier. // packetState = FwpsQueryPacketInjectionState0( gInjectionHandle, layerData, NULL ); if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)) { classifyOut->actionType = FWP_ACTION_PERMIT; goto Exit; } } addressFamily = GetAddressFamilyForLayer(inFixedValues->layerId); if (!IsAleReauthorize(inFixedValues)) { // // If the classify is the initial authorization for a connection, we // queue it to the pended connection list and notify the worker thread // for out-of-band processing. // pendedConnect = AllocateAndInitializePendedPacket( inFixedValues, inMetaValues, addressFamily, layerData, TL_INSPECT_CONNECT_PACKET, FWP_DIRECTION_OUTBOUND ); if (pendedConnect == NULL) { classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; goto Exit; } ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_COMPLETION_HANDLE)); // // Pend the ALE_AUTH_CONNECT classify. // status = FwpsPendOperation0( inMetaValues->completionHandle, &pendedConnect->completionContext ); if (!NT_SUCCESS(status)) { classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; goto Exit; } KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); signalWorkerThread = IsListEmpty(&gConnList) && IsListEmpty(&gPacketQueue); InsertTailList(&gConnList, &pendedConnect->listEntry); pendedConnect = NULL; // ownership transferred KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); KeReleaseInStackQueuedSpinLock(&connListLockHandle); classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; if (signalWorkerThread) { KeSetEvent( &gWorkerEvent, 0, FALSE ); } } else // re-auth @ ALE_AUTH_CONNECT { FWP_DIRECTION packetDirection; // // The classify is the re-authorization for an existing connection, it // could have been triggered for one of the three cases -- // // 1) The re-auth is triggered by a FwpsCompleteOperation0 call to // complete a ALE_AUTH_CONNECT classify pended earlier. // 2) The re-auth is triggered by an outbound packet sent immediately // after a policy change at ALE_AUTH_CONNECT layer. // 3) The re-auth is triggered by an inbound packet received // immediately after a policy change at ALE_AUTH_CONNECT layer. // ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_PACKET_DIRECTION)); packetDirection = inMetaValues->packetDirection; if (packetDirection == FWP_DIRECTION_OUTBOUND) { LIST_ENTRY* listEntry; BOOLEAN authComplete = FALSE; // // We first check whether this is a FwpsCompleteOperation0- triggered // reauth by looking for a pended connect that has the inspection // decision recorded. If found, we return that decision and remove // the pended connect from the list. // KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); for (listEntry = gConnList.Flink; listEntry != &gConnList; listEntry = listEntry->Flink) { pendedConnect = CONTAINING_RECORD( listEntry, TL_INSPECT_PENDED_PACKET, listEntry ); if (IsMatchingConnectPacket( inFixedValues, addressFamily, packetDirection, pendedConnect ) && (pendedConnect->authConnectDecision != 0)) { ASSERT((pendedConnect->authConnectDecision == FWP_ACTION_PERMIT) || (pendedConnect->authConnectDecision == FWP_ACTION_BLOCK)); classifyOut->actionType = pendedConnect->authConnectDecision; RemoveEntryList(&pendedConnect->listEntry); if (!gDriverUnloading && (pendedConnect->netBufferList != NULL) && (pendedConnect->authConnectDecision == FWP_ACTION_PERMIT)) { // // Now the outbound connection has been authorized. If the // pended connect has a net buffer list in it, we need it // morph it into a data packet and queue it to the packet // queue for send injecition. // pendedConnect->type = TL_INSPECT_DATA_PACKET; KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); signalWorkerThread = IsListEmpty(&gPacketQueue) && IsListEmpty(&gConnList); InsertTailList(&gPacketQueue, &pendedConnect->listEntry); pendedConnect = NULL; // ownership transferred KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); if (signalWorkerThread) { KeSetEvent( &gWorkerEvent, 0, FALSE ); } } authComplete = TRUE; break; } } KeReleaseInStackQueuedSpinLock(&connListLockHandle); if (authComplete) { goto Exit; } } // // If we reach here it means this is a policy change triggered re-auth // for an pre-existing connection. For such a packet (inbound or // outbound) we queue it to the packet queue and inspect it just like // other regular data packets from TRANSPORT layers. // ASSERT(layerData != NULL); pendedPacket = AllocateAndInitializePendedPacket( inFixedValues, inMetaValues, addressFamily, layerData, TL_INSPECT_REAUTH_PACKET, packetDirection ); if (pendedPacket == NULL) { classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; goto Exit; } if (packetDirection == FWP_DIRECTION_INBOUND) { pendedPacket->ipSecProtected = IsSecureConnection(inFixedValues); } KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); if (!gDriverUnloading) { signalWorkerThread = IsListEmpty(&gPacketQueue) && IsListEmpty(&gConnList); InsertTailList(&gPacketQueue, &pendedPacket->listEntry); pendedPacket = NULL; // ownership transferred classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; } else { // // Driver is being unloaded, permit any connect classify. // signalWorkerThread = FALSE; classifyOut->actionType = FWP_ACTION_PERMIT; } KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); KeReleaseInStackQueuedSpinLock(&connListLockHandle); if (signalWorkerThread) { KeSetEvent( &gWorkerEvent, 0, FALSE ); } } Exit: if (pendedPacket != NULL) { FreePendedPacket(pendedPacket); } if (pendedConnect != NULL) { FreePendedPacket(pendedConnect); } return; }
void TLInspectWorker( IN PVOID StartContext ) /* ++ This worker thread waits for the connect and packet queue event when the queues are empty; and it will be woken up when there are connects/packets queued needing to be inspected. Once awaking, It will run in a loop to complete the pended ALE classifies and/or clone-reinject packets back until both queues are exhausted (and it will go to sleep waiting for more work). The worker thread will end once it detected the driver is unloading. -- */ { NTSTATUS status; TL_INSPECT_PENDED_PACKET* packet = NULL; LIST_ENTRY* listEntry; KLOCK_QUEUE_HANDLE packetQueueLockHandle; KLOCK_QUEUE_HANDLE connListLockHandle; UNREFERENCED_PARAMETER(StartContext); for(;;) { KeWaitForSingleObject( &gWorkerEvent, Executive, KernelMode, FALSE, NULL ); if (gDriverUnloading) { break; } configPermitTraffic = IsTrafficPermitted(); listEntry = NULL; KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); if (!IsListEmpty(&gConnList)) { listEntry = gConnList.Flink; packet = CONTAINING_RECORD( listEntry, TL_INSPECT_PENDED_PACKET, listEntry ); if (packet->direction == FWP_DIRECTION_INBOUND) { RemoveEntryList(&packet->listEntry); } // // Leave the pended ALE_AUTH_CONNECT in the connection list, it will // be processed and removed from the list during re-auth. // } KeReleaseInStackQueuedSpinLock(&connListLockHandle); if (listEntry == NULL) { ASSERT(!IsListEmpty(&gPacketQueue)); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); listEntry = RemoveHeadList(&gPacketQueue); packet = CONTAINING_RECORD( listEntry, TL_INSPECT_PENDED_PACKET, listEntry ); KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); } if (packet->type == TL_INSPECT_CONNECT_PACKET) { TlInspectCompletePendedConnection( &packet, configPermitTraffic); } if ((packet != NULL) && configPermitTraffic) { if (packet->direction == FWP_DIRECTION_OUTBOUND) { status = TLInspectCloneReinjectOutbound(packet); } else { status = TLInspectCloneReinjectInbound(packet); } if (NT_SUCCESS(status)) { packet = NULL; // ownership transferred. } } if (packet != NULL) { FreePendedPacket(packet); } KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); if (IsListEmpty(&gConnList) && IsListEmpty(&gPacketQueue) && !gDriverUnloading) { KeClearEvent(&gWorkerEvent); } KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); KeReleaseInStackQueuedSpinLock(&connListLockHandle); } ASSERT(gDriverUnloading); while (!IsListEmpty(&gConnList)) { packet = NULL; KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); if (!IsListEmpty(&gConnList)) { listEntry = gConnList.Flink; packet = CONTAINING_RECORD( listEntry, TL_INSPECT_PENDED_PACKET, listEntry ); } KeReleaseInStackQueuedSpinLock(&connListLockHandle); if (packet != NULL) { TlInspectCompletePendedConnection(&packet, FALSE); ASSERT(packet == NULL); } } // // Discard all the pended packets if driver is being unloaded. // while (!IsListEmpty(&gPacketQueue)) { packet = NULL; KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); if (!IsListEmpty(&gPacketQueue)) { listEntry = RemoveHeadList(&gPacketQueue); packet = CONTAINING_RECORD( listEntry, TL_INSPECT_PENDED_PACKET, listEntry ); } KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); if (packet != NULL) { FreePendedPacket(packet); } } PsTerminateSystemThread(STATUS_SUCCESS); }
BOOLEAN KeRemoveEntryDeviceQueue ( __inout PKDEVICE_QUEUE DeviceQueue, __inout PKDEVICE_QUEUE_ENTRY DeviceQueueEntry ) /*++ Routine Description: This function removes a specified entry from the the specified device queue. If the device queue entry is not in the device queue, then no operation is performed. Otherwise the specified device queue entry is removed from the device queue and its inserted status is set to FALSE. Arguments: DeviceQueue - Supplies a pointer to a control object of type device queue. DeviceQueueEntry - Supplies a pointer to a device queue entry which is to be removed from its device queue. Return Value: A value of TRUE is returned if the device queue entry is removed from its device queue. Otherwise a value of FALSE is returned. --*/ { KLOCK_QUEUE_HANDLE LockHandle; BOOLEAN Removed; ASSERT_DEVICE_QUEUE(DeviceQueue); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); // // Raise IRQL to dispatcher level and lock specified device queue. // KeAcquireInStackQueuedSpinLock(&DeviceQueue->Lock, &LockHandle); // // If the device queue entry is not in a device queue, then no operation // is performed. Otherwise remove the specified device queue entry from its // device queue. // Removed = DeviceQueueEntry->Inserted; if (Removed == TRUE) { DeviceQueueEntry->Inserted = FALSE; RemoveEntryList(&DeviceQueueEntry->DeviceListEntry); KiInvalidateDeviceQueueKeyHint(DeviceQueue,DeviceQueueEntry); } // // Unlock specified device queue, lower IRQL to its previous level, and // return whether the device queue entry was removed from its queue. // KeReleaseInStackQueuedSpinLock(&LockHandle); return Removed; }
VOID DriverUnload( IN PDRIVER_OBJECT driverObject ) { NTSTATUS status; PVOID threadObj; KLOCK_QUEUE_HANDLE packetQueueLockHandle; KLOCK_QUEUE_HANDLE flowListLockHandle; UNREFERENCED_PARAMETER(driverObject); UNREFERENCED_PARAMETER(status); UNREFERENCED_PARAMETER(threadObj); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); KeAcquireInStackQueuedSpinLock( &gFlowListLock, &flowListLockHandle ); gDriverUnloading = TRUE; KeReleaseInStackQueuedSpinLock(&flowListLockHandle); // // Any associated flow contexts must be removed before // a callout can be successfully unregistered. // DDProxyRemoveFlows(); if (IsListEmpty(&gPacketQueue)) { KeSetEvent( &gPacketQueueEvent, IO_NO_INCREMENT, FALSE ); } KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); ASSERT(gThreadObj != NULL); KeWaitForSingleObject( gThreadObj, Executive, KernelMode, FALSE, NULL ); ObDereferenceObject(gThreadObj); DDProxyUnregisterCallouts(); FwpsInjectionHandleDestroy0(gInjectionHandle); IoDeleteDevice(gDeviceObject); }
static NTSTATUS NTAPI PortAddDevice( _In_ PDRIVER_OBJECT DriverObject, _In_ PDEVICE_OBJECT PhysicalDeviceObject) { PDRIVER_OBJECT_EXTENSION DriverObjectExtension; PFDO_DEVICE_EXTENSION DeviceExtension = NULL; WCHAR NameBuffer[80]; UNICODE_STRING DeviceName; PDEVICE_OBJECT Fdo = NULL; KLOCK_QUEUE_HANDLE LockHandle; NTSTATUS Status; DPRINT1("PortAddDevice(%p %p)\n", DriverObject, PhysicalDeviceObject); ASSERT(DriverObject); ASSERT(PhysicalDeviceObject); swprintf(NameBuffer, L"\\Device\\RaidPort%lu", PortNumber); RtlInitUnicodeString(&DeviceName, NameBuffer); PortNumber++; DPRINT1("Creating device: %wZ\n", &DeviceName); /* Create the port device */ Status = IoCreateDevice(DriverObject, sizeof(FDO_DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_CONTROLLER, FILE_DEVICE_SECURE_OPEN, FALSE, &Fdo); if (!NT_SUCCESS(Status)) { DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status); return Status; } DPRINT1("Created device: %wZ (%p)\n", &DeviceName, Fdo); /* Initialize the device */ Fdo->Flags |= DO_DIRECT_IO; Fdo->Flags |= DO_POWER_PAGABLE; /* Initialize the device extension */ DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension; RtlZeroMemory(DeviceExtension, sizeof(FDO_DEVICE_EXTENSION)); DeviceExtension->ExtensionType = FdoExtension; DeviceExtension->Device = Fdo; DeviceExtension->PhysicalDevice = PhysicalDeviceObject; DeviceExtension->PnpState = dsStopped; /* Attach the FDO to the device stack */ Status = IoAttachDeviceToDeviceStackSafe(Fdo, PhysicalDeviceObject, &DeviceExtension->LowerDevice); if (!NT_SUCCESS(Status)) { DPRINT1("IoAttachDeviceToDeviceStackSafe() failed (Status 0x%08lx)\n", Status); IoDeleteDevice(Fdo); return Status; } /* Insert the FDO to the drivers FDO list */ DriverObjectExtension = IoGetDriverObjectExtension(DriverObject, (PVOID)DriverEntry); ASSERT(DriverObjectExtension->ExtensionType == DriverExtension); DeviceExtension->DriverExtension = DriverObjectExtension; KeAcquireInStackQueuedSpinLock(&DriverObjectExtension->AdapterListLock, &LockHandle); InsertHeadList(&DriverObjectExtension->AdapterListHead, &DeviceExtension->AdapterListEntry); DriverObjectExtension->AdapterCount++; KeReleaseInStackQueuedSpinLock(&LockHandle); /* The device has been initialized */ Fdo->Flags &= ~DO_DEVICE_INITIALIZING; DPRINT1("PortAddDevice() done (Status 0x%08lx)\n", Status); return Status; }
void TLInspectTransportClassify( IN const FWPS_INCOMING_VALUES0* inFixedValues, IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, IN OUT void* layerData, IN const FWPS_FILTER0* filter, IN UINT64 flowContext, OUT FWPS_CLASSIFY_OUT0* classifyOut ) /* ++ This is the classifyFn function for the Transport (v4 and v6) callout. packets (inbound or outbound) are ueued to the packet queue to be processed by the worker thread. -- */ { KLOCK_QUEUE_HANDLE connListLockHandle; KLOCK_QUEUE_HANDLE packetQueueLockHandle; TL_INSPECT_PENDED_PACKET* pendedPacket = NULL; FWP_DIRECTION packetDirection; ADDRESS_FAMILY addressFamily; FWPS_PACKET_INJECTION_STATE packetState; BOOLEAN signalWorkerThread; UNREFERENCED_PARAMETER(flowContext); UNREFERENCED_PARAMETER(filter); // // We don't have the necessary right to alter the classify, exit. // if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) { goto Exit; } ASSERT(layerData != NULL); // // We don't re-inspect packets that we've inspected earlier. // packetState = FwpsQueryPacketInjectionState0( gInjectionHandle, layerData, NULL ); if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)) { classifyOut->actionType = FWP_ACTION_PERMIT; goto Exit; } addressFamily = GetAddressFamilyForLayer(inFixedValues->layerId); packetDirection = GetPacketDirectionForLayer(inFixedValues->layerId); if (packetDirection == FWP_DIRECTION_INBOUND) { if (IsAleClassifyRequired(inFixedValues, inMetaValues)) { // // Inbound transport packets that are destined to ALE Recv-Accept // layers, for initial authorization or reauth, should be inspected // at the ALE layer. We permit it from Tranport here. // classifyOut->actionType = FWP_ACTION_PERMIT; goto Exit; } else { // // To be compatible with Vista's IpSec implementation, we must not // intercept not-yet-detunneled IpSec traffic. // FWPS_PACKET_LIST_INFORMATION0 packetInfo = {0}; FwpsGetPacketListSecurityInformation0( layerData, FWPS_PACKET_LIST_INFORMATION_QUERY_IPSEC | FWPS_PACKET_LIST_INFORMATION_QUERY_INBOUND, &packetInfo ); if (packetInfo.ipsecInformation.inbound.isTunnelMode && !packetInfo.ipsecInformation.inbound.isDeTunneled) { classifyOut->actionType = FWP_ACTION_PERMIT; goto Exit; } } } pendedPacket = AllocateAndInitializePendedPacket( inFixedValues, inMetaValues, addressFamily, layerData, TL_INSPECT_DATA_PACKET, packetDirection ); if (pendedPacket == NULL) { classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; goto Exit; } KeAcquireInStackQueuedSpinLock( &gConnListLock, &connListLockHandle ); KeAcquireInStackQueuedSpinLock( &gPacketQueueLock, &packetQueueLockHandle ); if (!gDriverUnloading) { signalWorkerThread = IsListEmpty(&gPacketQueue) && IsListEmpty(&gConnList); InsertTailList(&gPacketQueue, &pendedPacket->listEntry); pendedPacket = NULL; // ownership transferred classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; } else { // // Driver is being unloaded, permit any connect classify. // signalWorkerThread = FALSE; classifyOut->actionType = FWP_ACTION_PERMIT; } KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); KeReleaseInStackQueuedSpinLock(&connListLockHandle); if (signalWorkerThread) { KeSetEvent( &gWorkerEvent, 0, FALSE ); } Exit: if (pendedPacket != NULL) { FreePendedPacket(pendedPacket); } return; }