NTKERNELAPI VOID ExAcquireCacheAwarePushLockExclusive ( __inout PEX_PUSH_LOCK_CACHE_AWARE PushLock ) /*++ Routine Description: Acquire a cache aware push lock exclusive. Arguments: PushLock - Cache aware push lock to be acquired Return Value: None --*/ { PEX_PUSH_LOCK *Start, *End; ULONG MaxLine; // // Exclusive acquires must obtain all the slots exclusive. // Take the first slot exclusive and then we can take the // rest of the slots in any order we want. // There is no deadlock here. A->B->C does not deadlock with A->C->B. // Start = &PushLock->Locks[1]; MaxLine = KeNumberProcessors; if (MaxLine > EX_PUSH_LOCK_FANNED_COUNT) { MaxLine = EX_PUSH_LOCK_FANNED_COUNT; } End = &PushLock->Locks[MaxLine - 1]; ExAcquirePushLockExclusive (PushLock->Locks[0]); while (Start <= End) { if (ExTryAcquirePushLockExclusive (*Start)) { Start++; } else { ExAcquirePushLockExclusive (*End); End--; } } }
NTSTATUS NTAPI CmpDestroyHive(IN PCMHIVE CmHive) { /* Remove the hive from the list */ ExAcquirePushLockExclusive(&CmpHiveListHeadLock); RemoveEntryList(&CmHive->HiveList); ExReleasePushLock(&CmpHiveListHeadLock); /* Delete the flusher lock */ ExDeleteResourceLite(CmHive->FlusherLock); ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE); /* Delete the view lock */ ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE); /* Destroy the security descriptor cache */ CmpDestroySecurityCache(CmHive); /* Destroy the view list */ CmpDestroyHiveViewList(CmHive); /* Free the hive storage */ HvFree(&CmHive->Hive); /* Free the hive */ CmpFree(CmHive, TAG_CM); return STATUS_SUCCESS; }
VOID KphpTestPushLockThreadStart( __in PVOID Context ) { ULONG i, j; for (i = 0; i < 400000; i++) { ExAcquirePushLockShared(&TestLock); for (j = 0; j < 1000; j++) YieldProcessor(); ExReleasePushLock(&TestLock); ExAcquirePushLockExclusive(&TestLock); for (j = 0; j < 9000; j++) YieldProcessor(); ExReleasePushLock(&TestLock); } PsTerminateSystemThread(STATUS_SUCCESS); }
FORCEINLINE VOID ObpSdAcquireLock(IN POB_SD_CACHE_LIST CacheEntry) { /* Acquire the lock */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&CacheEntry->PushLock); }
VOID NTAPI PspDeleteThread(IN PVOID ObjectBody) { PETHREAD Thread = (PETHREAD)ObjectBody; PEPROCESS Process = Thread->ThreadsProcess; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody); PSREFTRACE(Thread); ASSERT(Thread->Tcb.Win32Thread == NULL); /* Check if we have a stack */ if (Thread->Tcb.InitialStack) { /* Release it */ MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase, Thread->Tcb.LargeStack); } /* Check if we have a CID Handle */ if (Thread->Cid.UniqueThread) { /* Delete the CID Handle */ if (!(ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread, NULL))) { /* Something wrong happened, bugcheck */ KeBugCheck(CID_HANDLE_DELETION); } } /* Cleanup impersionation information */ PspDeleteThreadSecurity(Thread); /* Make sure the thread was inserted, before continuing */ if (!Process) return; /* Check if the thread list is valid */ if (Thread->ThreadListEntry.Flink) { /* Lock the thread's process */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&Process->ProcessLock); /* Remove us from the list */ RemoveEntryList(&Thread->ThreadListEntry); /* Release the lock */ ExReleasePushLockExclusive(&Process->ProcessLock); KeLeaveCriticalRegion(); } /* Dereference the Process */ ObDereferenceObject(Process); }
BOOLEAN RtlpLockAtomTable( IN PRTL_ATOM_TABLE AtomTable ) { if (AtomTable == NULL || AtomTable->Signature != RTL_ATOM_TABLE_SIGNATURE) { return FALSE; } KeEnterCriticalRegion (); ExAcquirePushLockExclusive( &AtomTable->PushLock ); return TRUE; }
NTSTATUS NTAPI CmpInitializeHive(OUT PCMHIVE *RegistryHive, IN ULONG OperationType, IN ULONG HiveFlags, IN ULONG FileType, IN PVOID HiveData OPTIONAL, IN HANDLE Primary, IN HANDLE Log, IN HANDLE External, IN PCUNICODE_STRING FileName OPTIONAL, IN ULONG CheckFlags) { PCMHIVE Hive; FILE_STANDARD_INFORMATION FileInformation; IO_STATUS_BLOCK IoStatusBlock; FILE_FS_SIZE_INFORMATION FileSizeInformation; NTSTATUS Status; ULONG Cluster; /* Assume failure */ *RegistryHive = NULL; /* * The following are invalid: * An external hive that is also internal. * A log hive that's not a primary hive too. * A volatile hive that's linked to permanent storage. * An in-memory initialization without hive data. * A log hive that's not linked to a correct file type. */ if (((External) && ((Primary) || (Log))) || ((Log) && !(Primary)) || ((HiveFlags & HIVE_VOLATILE) && ((Primary) || (External) || (Log))) || ((OperationType == HINIT_MEMORY) && (!HiveData)) || ((Log) && (FileType != HFILE_TYPE_LOG))) { /* Fail the request */ return STATUS_INVALID_PARAMETER; } /* Check if this is a primary hive */ if (Primary) { /* Get the cluster size */ Status = ZwQueryVolumeInformationFile(Primary, &IoStatusBlock, &FileSizeInformation, sizeof(FILE_FS_SIZE_INFORMATION), FileFsSizeInformation); if (!NT_SUCCESS(Status)) return Status; /* Make sure it's not larger then the block size */ if (FileSizeInformation.BytesPerSector > HBLOCK_SIZE) { /* Fail */ return STATUS_REGISTRY_IO_FAILED; } /* Otherwise, calculate the cluster */ Cluster = FileSizeInformation.BytesPerSector / HSECTOR_SIZE; Cluster = max(1, Cluster); } else { /* Otherwise use cluster 1 */ Cluster = 1; } /* Allocate the hive */ Hive = ExAllocatePoolWithTag(NonPagedPool, sizeof(CMHIVE), TAG_CMHIVE); if (!Hive) return STATUS_INSUFFICIENT_RESOURCES; /* Setup null fields */ Hive->UnloadEvent = NULL; Hive->RootKcb = NULL; Hive->Frozen = FALSE; Hive->UnloadWorkItem = NULL; Hive->GrowOnlyMode = FALSE; Hive->GrowOffset = 0; Hive->CellRemapArray = NULL; Hive->UseCountLog.Next = 0; Hive->LockHiveLog.Next = 0; Hive->FileObject = NULL; Hive->NotifyList.Flink = NULL; Hive->NotifyList.Blink = NULL; /* Set loading flag */ Hive->HiveIsLoading = TRUE; /* Set the current thread as creator */ Hive->CreatorOwner = KeGetCurrentThread(); /* Initialize lists */ InitializeListHead(&Hive->KcbConvertListHead); InitializeListHead(&Hive->KnodeConvertListHead); InitializeListHead(&Hive->TrustClassEntry); /* Allocate the view log */ Hive->ViewLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(KGUARDED_MUTEX), TAG_CMHIVE); if (!Hive->ViewLock) { /* Cleanup allocation and fail */ ExFreePoolWithTag(Hive, TAG_CMHIVE); return STATUS_INSUFFICIENT_RESOURCES; } /* Allocate the flush lock */ Hive->FlusherLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(ERESOURCE), TAG_CMHIVE); if (!Hive->FlusherLock) { /* Cleanup allocations and fail */ ExFreePoolWithTag(Hive->ViewLock, TAG_CMHIVE); ExFreePoolWithTag(Hive, TAG_CMHIVE); return STATUS_INSUFFICIENT_RESOURCES; } /* Setup the handles */ Hive->FileHandles[HFILE_TYPE_PRIMARY] = Primary; Hive->FileHandles[HFILE_TYPE_LOG] = Log; Hive->FileHandles[HFILE_TYPE_EXTERNAL] = External; /* Initailize the guarded mutex */ KeInitializeGuardedMutex(Hive->ViewLock); Hive->ViewLockOwner = NULL; /* Initialize the flush lock */ ExInitializeResourceLite(Hive->FlusherLock); /* Setup hive locks */ ExInitializePushLock(&Hive->HiveLock); Hive->HiveLockOwner = NULL; ExInitializePushLock(&Hive->WriterLock); Hive->WriterLockOwner = NULL; ExInitializePushLock(&Hive->SecurityLock); Hive->HiveSecurityLockOwner = NULL; /* Clear file names */ RtlInitEmptyUnicodeString(&Hive->FileUserName, NULL, 0); RtlInitEmptyUnicodeString(&Hive->FileFullPath, NULL, 0); /* Initialize the view list */ CmpInitHiveViewList(Hive); /* Initailize the security cache */ CmpInitSecurityCache(Hive); /* Setup flags */ Hive->Flags = 0; Hive->FlushCount = 0; /* Set flags */ Hive->Flags = HiveFlags; /* Check if this is a primary */ if (Primary) { /* Check how large the file is */ ZwQueryInformationFile(Primary, &IoStatusBlock, &FileInformation, sizeof(FileInformation), FileStandardInformation); Cluster = FileInformation.EndOfFile.LowPart; } /* Initialize it */ Status = HvInitialize(&Hive->Hive, OperationType, FileType, HiveFlags, HiveData, CmpAllocate, CmpFree, CmpFileSetSize, CmpFileWrite, CmpFileRead, CmpFileFlush, Cluster, FileName); if (!NT_SUCCESS(Status)) { /* Cleanup allocations and fail */ ExDeleteResourceLite(Hive->FlusherLock); ExFreePoolWithTag(Hive->FlusherLock, TAG_CMHIVE); ExFreePoolWithTag(Hive->ViewLock, TAG_CMHIVE); ExFreePoolWithTag(Hive, TAG_CMHIVE); return Status; } /* Check if we should verify the registry */ if ((OperationType == HINIT_FILE) || (OperationType == HINIT_MEMORY) || (OperationType == HINIT_MEMORY_INPLACE) || (OperationType == HINIT_MAPFILE)) { /* Verify integrity */ ULONG CheckStatus = CmCheckRegistry(Hive, CheckFlags); if (CheckStatus != 0) { /* Cleanup allocations and fail */ ExDeleteResourceLite(Hive->FlusherLock); ExFreePoolWithTag(Hive->FlusherLock, TAG_CMHIVE); ExFreePoolWithTag(Hive->ViewLock, TAG_CMHIVE); ExFreePoolWithTag(Hive, TAG_CMHIVE); return STATUS_REGISTRY_CORRUPT; } } /* Lock the hive list */ ExAcquirePushLockExclusive(&CmpHiveListHeadLock); /* Insert this hive */ InsertHeadList(&CmpHiveListHead, &Hive->HiveList); /* Release the lock */ ExReleasePushLock(&CmpHiveListHeadLock); /* Return the hive and success */ *RegistryHive = (PCMHIVE)Hive; return STATUS_SUCCESS; }
/* * FUNCTION: Terminates the current thread * See "Windows Internals" - Chapter 13, Page 50-53 */ VOID NTAPI PspExitThread(IN NTSTATUS ExitStatus) { CLIENT_DIED_MSG TerminationMsg; NTSTATUS Status; PTEB Teb; PEPROCESS CurrentProcess; PETHREAD Thread, OtherThread, PreviousThread = NULL; PVOID DeallocationStack; SIZE_T Dummy; BOOLEAN Last = FALSE; PTERMINATION_PORT TerminationPort, NextPort; PLIST_ENTRY FirstEntry, CurrentEntry; PKAPC Apc; PTOKEN PrimaryToken; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "ExitStatus: %d\n", ExitStatus); /* Get the Current Thread and Process */ Thread = PsGetCurrentThread(); CurrentProcess = Thread->ThreadsProcess; ASSERT((Thread) == PsGetCurrentThread()); /* Can't terminate a thread if it attached another process */ if (KeIsAttachedProcess()) { /* Bugcheck */ KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG_PTR)CurrentProcess, (ULONG_PTR)Thread->Tcb.ApcState.Process, (ULONG_PTR)Thread->Tcb.ApcStateIndex, (ULONG_PTR)Thread); } /* Lower to Passive Level */ KeLowerIrql(PASSIVE_LEVEL); /* Can't be a worker thread */ if (Thread->ActiveExWorker) { /* Bugcheck */ KeBugCheckEx(ACTIVE_EX_WORKER_THREAD_TERMINATION, (ULONG_PTR)Thread, 0, 0, 0); } /* Can't have pending APCs */ if (Thread->Tcb.CombinedApcDisable != 0) { /* Bugcheck */ KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT, 0, Thread->Tcb.CombinedApcDisable, 0, 1); } /* Lock the thread */ ExWaitForRundownProtectionRelease(&Thread->RundownProtect); /* Cleanup the power state */ PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState); /* Call the WMI Callback for Threads */ //WmiTraceThread(Thread, NULL, FALSE); /* Run Thread Notify Routines before we desintegrate the thread */ PspRunCreateThreadNotifyRoutines(Thread, FALSE); /* Lock the Process before we modify its thread entries */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock); /* Decrease the active thread count, and check if it's 0 */ if (!(--CurrentProcess->ActiveThreads)) { /* Set the delete flag */ InterlockedOr((PLONG)&CurrentProcess->Flags, PSF_PROCESS_DELETE_BIT); /* Remember we are last */ Last = TRUE; /* Check if this termination is due to the thread dying */ if (ExitStatus == STATUS_THREAD_IS_TERMINATING) { /* Check if the last thread was pending */ if (CurrentProcess->ExitStatus == STATUS_PENDING) { /* Use the last exit status */ CurrentProcess->ExitStatus = CurrentProcess-> LastThreadExitStatus; } } else { /* Just a normal exit, write the code */ CurrentProcess->ExitStatus = ExitStatus; } /* Loop all the current threads */ FirstEntry = &CurrentProcess->ThreadListHead; CurrentEntry = FirstEntry->Flink; while (FirstEntry != CurrentEntry) { /* Get the thread on the list */ OtherThread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry); /* Check if it's a thread that's still alive */ if ((OtherThread != Thread) && !(KeReadStateThread(&OtherThread->Tcb)) && (ObReferenceObjectSafe(OtherThread))) { /* It's a live thread and we referenced it, unlock process */ ExReleasePushLockExclusive(&CurrentProcess->ProcessLock); KeLeaveCriticalRegion(); /* Wait on the thread */ KeWaitForSingleObject(OtherThread, Executive, KernelMode, FALSE, NULL); /* Check if we had a previous thread to dereference */ if (PreviousThread) ObDereferenceObject(PreviousThread); /* Remember the thread and re-lock the process */ PreviousThread = OtherThread; KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock); } /* Go to the next thread */ CurrentEntry = CurrentEntry->Flink; } } else if (ExitStatus != STATUS_THREAD_IS_TERMINATING) { /* Write down the exit status of the last thread to get killed */ CurrentProcess->LastThreadExitStatus = ExitStatus; } /* Unlock the Process */ ExReleasePushLockExclusive(&CurrentProcess->ProcessLock); KeLeaveCriticalRegion(); /* Check if we had a previous thread to dereference */ if (PreviousThread) ObDereferenceObject(PreviousThread); /* Check if the process has a debug port and if this is a user thread */ if ((CurrentProcess->DebugPort) && !(Thread->SystemThread)) { /* Notify the Debug API. */ Last ? DbgkExitProcess(CurrentProcess->ExitStatus) : DbgkExitThread(ExitStatus); } /* Check if this is a Critical Thread */ if ((KdDebuggerEnabled) && (Thread->BreakOnTermination)) { /* Break to debugger */ PspCatchCriticalBreak("Critical thread 0x%p (in %s) exited\n", Thread, CurrentProcess->ImageFileName); } /* Check if it's the last thread and this is a Critical Process */ if ((Last) && (CurrentProcess->BreakOnTermination)) { /* Check if a debugger is here to handle this */ if (KdDebuggerEnabled) { /* Break to debugger */ PspCatchCriticalBreak("Critical process 0x%p (in %s) exited\n", CurrentProcess, CurrentProcess->ImageFileName); } else { /* Bugcheck, we can't allow this */ KeBugCheckEx(CRITICAL_PROCESS_DIED, (ULONG_PTR)CurrentProcess, 0, 0, 0); } } /* Sanity check */ ASSERT(Thread->Tcb.CombinedApcDisable == 0); /* Process the Termination Ports */ TerminationPort = Thread->TerminationPort; if (TerminationPort) { /* Setup the message header */ TerminationMsg.h.u2.ZeroInit = 0; TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED; TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg); TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) - sizeof(PORT_MESSAGE); /* Loop each port */ do { /* Save the Create Time */ TerminationMsg.CreateTime = Thread->CreateTime; /* Loop trying to send message */ while (TRUE) { /* Send the LPC Message */ Status = LpcRequestPort(TerminationPort->Port, &TerminationMsg.h); if ((Status == STATUS_NO_MEMORY) || (Status == STATUS_INSUFFICIENT_RESOURCES)) { /* Wait a bit and try again */ KeDelayExecutionThread(KernelMode, FALSE, &ShortTime); continue; } break; } /* Dereference this LPC Port */ ObDereferenceObject(TerminationPort->Port); /* Move to the next one */ NextPort = TerminationPort->Next; /* Free the Termination Port Object */ ExFreePoolWithTag(TerminationPort, '=TsP'); /* Keep looping as long as there is a port */ TerminationPort = NextPort; } while (TerminationPort); } else if (((ExitStatus == STATUS_THREAD_IS_TERMINATING) && (Thread->DeadThread)) || !(Thread->DeadThread)) { /* * This case is special and deserves some extra comments. What * basically happens here is that this thread doesn't have a termination * port, which means that it died before being fully created. Since we * still have to notify an LPC Server, we'll use the exception port, * which we know exists. However, we need to know how far the thread * actually got created. We have three possibilities: * * - NtCreateThread returned an error really early: DeadThread is set. * - NtCreateThread managed to create the thread: DeadThread is off. * - NtCreateThread was creating the thread (with DeadThread set, * but the thread got killed prematurely: STATUS_THREAD_IS_TERMINATING * is our exit code.) * * For the 2 & 3rd scenarios, the thread has been created far enough to * warrant notification to the LPC Server. */ /* Setup the message header */ TerminationMsg.h.u2.ZeroInit = 0; TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED; TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg); TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) - sizeof(PORT_MESSAGE); /* Make sure the process has an exception port */ if (CurrentProcess->ExceptionPort) { /* Save the Create Time */ TerminationMsg.CreateTime = Thread->CreateTime; /* Loop trying to send message */ while (TRUE) { /* Send the LPC Message */ Status = LpcRequestPort(CurrentProcess->ExceptionPort, &TerminationMsg.h); if ((Status == STATUS_NO_MEMORY) || (Status == STATUS_INSUFFICIENT_RESOURCES)) { /* Wait a bit and try again */ KeDelayExecutionThread(KernelMode, FALSE, &ShortTime); continue; } break; } } } /* Rundown Win32 Thread if there is one */ if (Thread->Tcb.Win32Thread) PspW32ThreadCallout(Thread, PsW32ThreadCalloutExit); /* If we are the last thread and have a W32 Process */ if ((Last) && (CurrentProcess->Win32Process)) { /* Run it down too */ PspW32ProcessCallout(CurrentProcess, FALSE); } /* Make sure Stack Swap is enabled */ if (!Thread->Tcb.EnableStackSwap) { /* Stack swap really shouldn't be disabled during exit! */ KeBugCheckEx(KERNEL_STACK_LOCKED_AT_EXIT, 0, 0, 0, 0); } /* Cancel I/O for the thread. */ IoCancelThreadIo(Thread); /* Rundown Timers */ ExTimerRundown(); /* FIXME: Rundown Registry Notifications (NtChangeNotify) CmNotifyRunDown(Thread); */ /* Rundown Mutexes */ KeRundownThread(); /* Check if we have a TEB */ Teb = Thread->Tcb.Teb; if (Teb) { /* Check if the thread is still alive */ if (!Thread->DeadThread) { /* Check if we need to free its stack */ if (Teb->FreeStackOnTermination) { /* Set the TEB's Deallocation Stack as the Base Address */ Dummy = 0; DeallocationStack = Teb->DeallocationStack; /* Free the Thread's Stack */ ZwFreeVirtualMemory(NtCurrentProcess(), &DeallocationStack, &Dummy, MEM_RELEASE); } /* Free the debug handle */ if (Teb->DbgSsReserved[1]) ObCloseHandle(Teb->DbgSsReserved[1], UserMode); } /* Decommit the TEB */ MmDeleteTeb(CurrentProcess, Teb); Thread->Tcb.Teb = NULL; } /* Free LPC Data */ LpcExitThread(Thread); /* Save the exit status and exit time */ Thread->ExitStatus = ExitStatus; KeQuerySystemTime(&Thread->ExitTime); /* Sanity check */ ASSERT(Thread->Tcb.CombinedApcDisable == 0); /* Check if this is the final thread or not */ if (Last) { /* Set the process exit time */ CurrentProcess->ExitTime = Thread->ExitTime; /* Exit the process */ PspExitProcess(TRUE, CurrentProcess); /* Get the process token and check if we need to audit */ PrimaryToken = PsReferencePrimaryToken(CurrentProcess); if (SeDetailedAuditingWithToken(PrimaryToken)) { /* Audit the exit */ SeAuditProcessExit(CurrentProcess); } /* Dereference the process token */ ObFastDereferenceObject(&CurrentProcess->Token, PrimaryToken); /* Check if this is a VDM Process and rundown the VDM DPCs if so */ if (CurrentProcess->VdmObjects) { /* VdmRundownDpcs(CurrentProcess); */ } /* Kill the process in the Object Manager */ ObKillProcess(CurrentProcess); /* Check if we have a section object */ if (CurrentProcess->SectionObject) { /* Dereference and clear the Section Object */ ObDereferenceObject(CurrentProcess->SectionObject); CurrentProcess->SectionObject = NULL; } /* Check if the process is part of a job */ if (CurrentProcess->Job) { /* Remove the process from the job */ PspExitProcessFromJob(CurrentProcess->Job, CurrentProcess); } } /* Disable APCs */ KeEnterCriticalRegion(); /* Disable APC queueing, force a resumption */ Thread->Tcb.ApcQueueable = FALSE; KeForceResumeThread(&Thread->Tcb); /* Re-enable APCs */ KeLeaveCriticalRegion(); /* Flush the User APCs */ FirstEntry = KeFlushQueueApc(&Thread->Tcb, UserMode); if (FirstEntry) { /* Start with the first entry */ CurrentEntry = FirstEntry; do { /* Get the APC */ Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry); /* Move to the next one */ CurrentEntry = CurrentEntry->Flink; /* Rundown the APC or de-allocate it */ if (Apc->RundownRoutine) { /* Call its own routine */ Apc->RundownRoutine(Apc); } else { /* Do it ourselves */ ExFreePool(Apc); } } while (CurrentEntry != FirstEntry); } /* Clean address space if this was the last thread */ if (Last) MmCleanProcessAddressSpace(CurrentProcess); /* Call the Lego routine */ if (Thread->Tcb.LegoData) PspRunLegoRoutine(&Thread->Tcb); /* Flush the APC queue, which should be empty */ FirstEntry = KeFlushQueueApc(&Thread->Tcb, KernelMode); if ((FirstEntry) || (Thread->Tcb.CombinedApcDisable != 0)) { /* Bugcheck time */ KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT, (ULONG_PTR)FirstEntry, Thread->Tcb.CombinedApcDisable, KeGetCurrentIrql(), 0); } /* Signal the process if this was the last thread */ if (Last) KeSetProcess(&CurrentProcess->Pcb, 0, FALSE); /* Terminate the Thread from the Scheduler */ KeTerminateThread(0); }
VOID NTAPI GdiPoolFree( PGDI_POOL pPool, PVOID pvAlloc) { PLIST_ENTRY ple; PGDI_POOL_SECTION pSection = NULL; ULONG_PTR cjOffset; ULONG ulIndex; DPRINT("GdiPoolFree: %p\n", pvAlloc); /* Disable APCs and acquire the pool lock */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&pPool->pushlock); /* Loop all used sections */ for (ple = pPool->leInUseList.Flink; ple != &pPool->leInUseList; ple = ple->Flink) { /* Get the pointer to the section */ pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink); /* Calculate offset */ cjOffset = (ULONG_PTR)pvAlloc - (ULONG_PTR)pSection->pvBaseAddress; /* Check if the allocation is from this section */ if (cjOffset < pPool->cjSectionSize) { /* Calculate the index of the allocation */ ulIndex = cjOffset / pPool->cjAllocSize; /* Mark it as free */ ASSERT(RtlTestBit(&pSection->bitmap, ulIndex) == TRUE); RtlClearBit(&pSection->bitmap, ulIndex); /* Decrease allocation count */ pSection->cAllocCount--; ASSERT(RtlNumberOfSetBits(&pSection->bitmap) == pSection->cAllocCount); DBG_LOGEVENT(&pPool->slhLog, EVENT_FREE, pvAlloc); /* Check if the section got valid now */ if (pSection->cAllocCount == pPool->cSlotsPerSection - 1) { /* Insert it into the ready list */ InsertTailList(&pPool->leReadyList, &pSection->leReadyLink); } /* Check if it got empty now */ else if (pSection->cAllocCount == 0) { /* Remove the section from the lists */ RemoveEntryList(&pSection->leInUseLink); RemoveEntryList(&pSection->leReadyLink); if (pPool->cEmptySections >= 1) { /* Delete the section */ GdiPoolDeleteSection(pPool, pSection); } else { /* Insert it into the empty list */ InsertHeadList(&pPool->leEmptyList, &pSection->leInUseLink); pPool->cEmptySections++; } } goto done; } } DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n", pvAlloc, pSection ? pSection->pvBaseAddress : NULL, pPool->cjSectionSize); ASSERT(FALSE); // KeBugCheck() done: /* Release the pool lock and enable APCs */ ExReleasePushLockExclusive(&pPool->pushlock); KeLeaveCriticalRegion(); }
PVOID NTAPI GdiPoolAllocate( PGDI_POOL pPool) { PGDI_POOL_SECTION pSection; ULONG ulIndex, cjOffset, ulPageBit; PLIST_ENTRY ple; PVOID pvAlloc, pvBaseAddress; SIZE_T cjSize; NTSTATUS status; /* Disable APCs and acquire the pool lock */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&pPool->pushlock); /* Check if we have a ready section */ if (!IsListEmpty(&pPool->leReadyList)) { /* Get a free section */ ple = pPool->leReadyList.Flink; pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leReadyLink); if (pSection->cAllocCount >= pPool->cSlotsPerSection) { DPRINT1("pSection->cAllocCount=%lu, pPool->cSlotsPerSection=%lu\n", pSection->cAllocCount, pPool->cSlotsPerSection); DBG_DUMP_EVENT_LIST(&pPool->slhLog); ASSERT(FALSE); } ASSERT(pSection->cAllocCount < pPool->cSlotsPerSection); } else { /* No, check if we have something on the empty list */ if (!IsListEmpty(&pPool->leEmptyList)) { /* Yes, remove it from the empty list */ ple = RemoveHeadList(&pPool->leEmptyList); pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink); pPool->cEmptySections--; ASSERT(pSection->cAllocCount == 0); } else { /* No, allocate a new section */ pSection = GdiPoolAllocateSection(pPool); if (!pSection) { DPRINT1("Couldn't allocate a section\n"); pvAlloc = NULL; goto done; } } /* Insert it into the in-use and ready list */ InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink); InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink); } /* Find and set a single bit */ ulIndex = RtlFindClearBitsAndSet(&pSection->bitmap, 1, 0); ASSERT(ulIndex != MAXULONG); /* Calculate the allocation address */ cjOffset = ulIndex * pPool->cjAllocSize; pvAlloc = (PVOID)((ULONG_PTR)pSection->pvBaseAddress + cjOffset); /* Check if memory is comitted */ ulPageBit = 1 << (cjOffset / PAGE_SIZE); ulPageBit |= 1 << ((cjOffset + pPool->cjAllocSize - 1) / PAGE_SIZE); if ((pSection->ulCommitBitmap & ulPageBit) != ulPageBit) { /* Commit the pages */ pvBaseAddress = PAGE_ALIGN(pvAlloc); cjSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc, pPool->cjAllocSize) * PAGE_SIZE; status = ZwAllocateVirtualMemory(NtCurrentProcess(), &pvBaseAddress, 0, &cjSize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(status)) { pvAlloc = NULL; goto done; } pSection->ulCommitBitmap |= ulPageBit; } /* Increase alloc count */ pSection->cAllocCount++; ASSERT(RtlNumberOfSetBits(&pSection->bitmap) == pSection->cAllocCount); DBG_LOGEVENT(&pPool->slhLog, EVENT_ALLOCATE, pvAlloc); /* Check if section is now busy */ if (pSection->cAllocCount == pPool->cSlotsPerSection) { /* Remove the section from the ready list */ RemoveEntryList(&pSection->leReadyLink); } done: /* Release the pool lock and enable APCs */ ExReleasePushLockExclusive(&pPool->pushlock); KeLeaveCriticalRegion(); DPRINT("GdiPoolallocate: %p\n", pvAlloc); return pvAlloc; }
PEX_CALLBACK_ROUTINE_BLOCK ExReferenceCallBackBlock ( IN OUT PEX_CALLBACK CallBack ) /*++ Routine Description: This function takes a reference on the call back block inside the callback structure. Arguments: CallBack - Call back to obtain the call back block from Return Value: PEX_CALLBACK_ROUTINE_BLOCK - Referenced structure or NULL if these wasn't one --*/ { EX_FAST_REF OldRef; PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock; // // Get a reference to the callback block if we can. // OldRef = ExFastReference (&CallBack->RoutineBlock); // // If there is no callback then return // if (ExFastRefObjectNull (OldRef)) { return NULL; } // // If we didn't get a reference then use a lock to get one. // if (!ExFastRefCanBeReferenced (OldRef)) { PKTHREAD CurrentThread; CurrentThread = KeGetCurrentThread (); KeEnterCriticalRegionThread (CurrentThread); ExAcquirePushLockExclusive (&ExpCallBackFlush); CallBackBlock = ExFastRefGetObject (CallBack->RoutineBlock); if (CallBackBlock && !ExAcquireRundownProtection (&CallBackBlock->RundownProtect)) { CallBackBlock = NULL; } ExReleasePushLockExclusive (&ExpCallBackFlush); KeLeaveCriticalRegionThread (CurrentThread); if (CallBackBlock == NULL) { return NULL; } } else { CallBackBlock = ExFastRefGetObject (OldRef); // // If we just removed the last reference then attempt fix it up. // if (ExFastRefIsLastReference (OldRef) && !ExpCallBackReturnRefs) { ULONG RefsToAdd; RefsToAdd = ExFastRefGetAdditionalReferenceCount (); // // If we can't add the references then just give up // if (ExAcquireRundownProtectionEx (&CallBackBlock->RundownProtect, RefsToAdd)) { // // Repopulate the cached refs. If this fails we just give them back. // if (!ExFastRefAddAdditionalReferenceCounts (&CallBack->RoutineBlock, CallBackBlock, RefsToAdd)) { ExReleaseRundownProtectionEx (&CallBackBlock->RundownProtect, RefsToAdd); } } } } return CallBackBlock; }
NTSTATUS NTAPI PspCreateThread(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, IN PEPROCESS TargetProcess, OUT PCLIENT_ID ClientId, IN PCONTEXT ThreadContext, IN PINITIAL_TEB InitialTeb, IN BOOLEAN CreateSuspended, IN PKSTART_ROUTINE StartRoutine OPTIONAL, IN PVOID StartContext OPTIONAL) { HANDLE hThread; PEPROCESS Process; PETHREAD Thread; PTEB TebBase = NULL; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status, AccessStatus; HANDLE_TABLE_ENTRY CidEntry; ACCESS_STATE LocalAccessState; PACCESS_STATE AccessState = &LocalAccessState; AUX_ACCESS_DATA AuxData; BOOLEAN Result, SdAllocated; PSECURITY_DESCRIPTOR SecurityDescriptor; SECURITY_SUBJECT_CONTEXT SubjectContext; PAGED_CODE(); PSTRACE(PS_THREAD_DEBUG, "ThreadContext: %p TargetProcess: %p ProcessHandle: %p\n", ThreadContext, TargetProcess, ProcessHandle); /* If we were called from PsCreateSystemThread, then we're kernel mode */ if (StartRoutine) PreviousMode = KernelMode; /* Reference the Process by handle or pointer, depending on what we got */ if (ProcessHandle) { /* Normal thread or System Thread */ Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_CREATE_THREAD, PsProcessType, PreviousMode, (PVOID*)&Process, NULL); PSREFTRACE(Process); } else { /* System thread inside System Process, or Normal Thread with a bug */ if (StartRoutine) { /* Reference the Process by Pointer */ ObReferenceObject(TargetProcess); Process = TargetProcess; Status = STATUS_SUCCESS; } else { /* Fake ObReference returning this */ Status = STATUS_INVALID_HANDLE; } } /* Check for success */ if (!NT_SUCCESS(Status)) return Status; /* Also make sure that User-Mode isn't trying to create a system thread */ if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) { /* Fail */ ObDereferenceObject(Process); return STATUS_INVALID_HANDLE; } /* Create Thread Object */ Status = ObCreateObject(PreviousMode, PsThreadType, ObjectAttributes, PreviousMode, NULL, sizeof(ETHREAD), 0, 0, (PVOID*)&Thread); if (!NT_SUCCESS(Status)) { /* We failed; dereference the process and exit */ ObDereferenceObject(Process); return Status; } /* Zero the Object entirely */ RtlZeroMemory(Thread, sizeof(ETHREAD)); /* Initialize rundown protection */ ExInitializeRundownProtection(&Thread->RundownProtect); /* Initialize exit code */ Thread->ExitStatus = STATUS_PENDING; /* Set the Process CID */ Thread->ThreadsProcess = Process; Thread->Cid.UniqueProcess = Process->UniqueProcessId; /* Create Cid Handle */ CidEntry.Object = Thread; CidEntry.GrantedAccess = 0; Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry); if (!Thread->Cid.UniqueThread) { /* We couldn't create the CID, dereference the thread and fail */ ObDereferenceObject(Thread); return STATUS_INSUFFICIENT_RESOURCES; } /* Save the read cluster size */ Thread->ReadClusterSize = MmReadClusterSize; /* Initialize the LPC Reply Semaphore */ KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, 1); /* Initialize the list heads and locks */ InitializeListHead(&Thread->LpcReplyChain); InitializeListHead(&Thread->IrpList); InitializeListHead(&Thread->PostBlockList); InitializeListHead(&Thread->ActiveTimerListHead); KeInitializeSpinLock(&Thread->ActiveTimerListLock); /* Acquire rundown protection */ if (!ExAcquireRundownProtection (&Process->RundownProtect)) { /* Fail */ ObDereferenceObject(Thread); return STATUS_PROCESS_IS_TERMINATING; } /* Now let the kernel initialize the context */ if (ThreadContext) { /* User-mode Thread, create Teb */ Status = MmCreateTeb(Process, &Thread->Cid, InitialTeb, &TebBase); if (!NT_SUCCESS(Status)) { /* Failed to create the TEB. Release rundown and dereference */ ExReleaseRundownProtection(&Process->RundownProtect); ObDereferenceObject(Thread); return Status; } /* Set the Start Addresses */ Thread->StartAddress = (PVOID)KeGetContextPc(ThreadContext); Thread->Win32StartAddress = (PVOID)KeGetContextReturnRegister(ThreadContext); /* Let the kernel intialize the Thread */ Status = KeInitThread(&Thread->Tcb, NULL, PspUserThreadStartup, NULL, Thread->StartAddress, ThreadContext, TebBase, &Process->Pcb); } else { /* System Thread */ Thread->StartAddress = StartRoutine; PspSetCrossThreadFlag(Thread, CT_SYSTEM_THREAD_BIT); /* Let the kernel intialize the Thread */ Status = KeInitThread(&Thread->Tcb, NULL, PspSystemThreadStartup, StartRoutine, StartContext, NULL, NULL, &Process->Pcb); } /* Check if we failed */ if (!NT_SUCCESS(Status)) { /* Delete the TEB if we had done */ if (TebBase) MmDeleteTeb(Process, TebBase); /* Release rundown and dereference */ ExReleaseRundownProtection(&Process->RundownProtect); ObDereferenceObject(Thread); return Status; } /* Lock the process */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&Process->ProcessLock); /* Make sure the proces didn't just die on us */ if (Process->ProcessDelete) goto Quickie; /* Check if the thread was ours, terminated and it was user mode */ if ((Thread->Terminated) && (ThreadContext) && (Thread->ThreadsProcess == Process)) { /* Cleanup, we don't want to start it up and context switch */ goto Quickie; } /* * Insert the Thread into the Process's Thread List * Note, this is the ETHREAD Thread List. It is removed in * ps/kill.c!PspExitThread. */ InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry); Process->ActiveThreads++; /* Start the thread */ KeStartThread(&Thread->Tcb); /* Release the process lock */ ExReleasePushLockExclusive(&Process->ProcessLock); KeLeaveCriticalRegion(); /* Release rundown */ ExReleaseRundownProtection(&Process->RundownProtect); /* Notify WMI */ //WmiTraceProcess(Process, TRUE); //WmiTraceThread(Thread, InitialTeb, TRUE); /* Notify Thread Creation */ PspRunCreateThreadNotifyRoutines(Thread, TRUE); /* Reference ourselves as a keep-alive */ ObReferenceObjectEx(Thread, 2); /* Suspend the Thread if we have to */ if (CreateSuspended) KeSuspendThread(&Thread->Tcb); /* Check if we were already terminated */ if (Thread->Terminated) KeForceResumeThread(&Thread->Tcb); /* Create an access state */ Status = SeCreateAccessStateEx(NULL, ThreadContext ? PsGetCurrentProcess() : Process, &LocalAccessState, &AuxData, DesiredAccess, &PsThreadType->TypeInfo.GenericMapping); if (!NT_SUCCESS(Status)) { /* Access state failed, thread is dead */ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT); /* If we were suspended, wake it up */ if (CreateSuspended) KeResumeThread(&Thread->Tcb); /* Dispatch thread */ KeReadyThread(&Thread->Tcb); /* Dereference completely to kill it */ ObDereferenceObjectEx(Thread, 2); return Status; } /* Insert the Thread into the Object Manager */ Status = ObInsertObject(Thread, AccessState, DesiredAccess, 0, NULL, &hThread); /* Delete the access state if we had one */ if (AccessState) SeDeleteAccessState(AccessState); /* Check for success */ if (NT_SUCCESS(Status)) { /* Wrap in SEH to protect against bad user-mode pointers */ _SEH2_TRY { /* Return Cid and Handle */ if (ClientId) *ClientId = Thread->Cid; *ThreadHandle = hThread; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Thread insertion failed, thread is dead */ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT); /* If we were suspended, wake it up */ if (CreateSuspended) KeResumeThread(&Thread->Tcb); /* Dispatch thread */ KeReadyThread(&Thread->Tcb); /* Dereference it, leaving only the keep-alive */ ObDereferenceObject(Thread); /* Close its handle, killing it */ ObCloseHandle(ThreadHandle, PreviousMode); /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else {