ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

kill.c
Go to the documentation of this file.
00001 /*
00002  * PROJECT:         ReactOS Kernel
00003  * LICENSE:         GPL - See COPYING in the top level directory
00004  * FILE:            ntoskrnl/ps/kill.c
00005  * PURPOSE:         Process Manager: Process and Thread Termination
00006  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
00007  *                  Filip Navara (xnavara@reactos.org)
00008  *                  Thomas Weidenmueller (w3seek@reactos.org
00009  */
00010 
00011 /* INCLUDES *****************************************************************/
00012 
00013 #include <ntoskrnl.h>
00014 #define NDEBUG
00015 #include <debug.h>
00016 
00017 /* GLOBALS *******************************************************************/
00018 
00019 LIST_ENTRY PspReaperListHead = { NULL, NULL };
00020 WORK_QUEUE_ITEM PspReaperWorkItem;
00021 LARGE_INTEGER ShortTime = {{-10 * 100 * 1000, -1}};
00022 
00023 /* PRIVATE FUNCTIONS *********************************************************/
00024 
00025 VOID
00026 NTAPI
00027 PspCatchCriticalBreak(IN PCHAR Message,
00028                       IN PVOID ProcessOrThread,
00029                       IN PCHAR ImageName)
00030 {
00031     CHAR Action[2];
00032     BOOLEAN Handled = FALSE;
00033     PAGED_CODE();
00034 
00035     /* Check if a debugger is enabled */
00036     if (KdDebuggerEnabled)
00037     {
00038         /* Print out the message */
00039         DbgPrint(Message, ProcessOrThread, ImageName);
00040         do
00041         {
00042             /* If a debugger isn't present, don't prompt */
00043             if (KdDebuggerNotPresent) break;
00044 
00045             /* A debuger is active, prompt for action */
00046             DbgPrompt("Break, or Ignore (bi)?", Action, sizeof(Action));
00047             switch (Action[0])
00048             {
00049                 /* Break */
00050                 case 'B': case 'b':
00051 
00052                     /* Do a breakpoint */
00053                     DbgBreakPoint();
00054 
00055                 /* Ignore */
00056                 case 'I': case 'i':
00057 
00058                     /* Handle it */
00059                     Handled = TRUE;
00060 
00061                 /* Unrecognized */
00062                 default:
00063                     break;
00064             }
00065         } while (!Handled);
00066     }
00067 
00068     /* Did we ultimately handle this? */
00069     if (!Handled)
00070     {
00071         /* We didn't, bugcheck */
00072         KeBugCheckEx(CRITICAL_OBJECT_TERMINATION,
00073                      ((PKPROCESS)ProcessOrThread)->Header.Type,
00074                      (ULONG_PTR)ProcessOrThread,
00075                      (ULONG_PTR)ImageName,
00076                      (ULONG_PTR)Message);
00077     }
00078 }
00079 
00080 NTSTATUS
00081 NTAPI
00082 PspTerminateProcess(IN PEPROCESS Process,
00083                     IN NTSTATUS ExitStatus)
00084 {
00085     PETHREAD Thread;
00086     NTSTATUS Status = STATUS_NOTHING_TO_TERMINATE;
00087     PAGED_CODE();
00088     PSTRACE(PS_KILL_DEBUG,
00089             "Process: %p ExitStatus: %p\n", Process, ExitStatus);
00090     PSREFTRACE(Process);
00091 
00092     /* Check if this is a Critical Process */
00093     if (Process->BreakOnTermination)
00094     {
00095         /* Break to debugger */
00096         PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
00097                               Process,
00098                               Process->ImageFileName);
00099     }
00100 
00101     /* Set the delete flag */
00102     InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_DELETE_BIT);
00103 
00104     /* Get the first thread */
00105     Thread = PsGetNextProcessThread(Process, NULL);
00106     while (Thread)
00107     {
00108         /* Kill it */
00109         PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
00110         Thread = PsGetNextProcessThread(Process, Thread);
00111 
00112         /* We had at least one thread, so termination is OK */
00113         Status = STATUS_SUCCESS;
00114     }
00115 
00116     /* Check if there was nothing to terminate or if we have a debug port */
00117     if ((Status == STATUS_NOTHING_TO_TERMINATE) || (Process->DebugPort))
00118     {
00119         /* Clear the handle table anyway */
00120         ObClearProcessHandleTable(Process);
00121     }
00122 
00123     /* Return status */
00124     return Status;
00125 }
00126 
00127 NTSTATUS
00128 NTAPI
00129 PsTerminateProcess(IN PEPROCESS Process,
00130                    IN NTSTATUS ExitStatus)
00131 {
00132     /* Call the internal API */
00133     return PspTerminateProcess(Process, ExitStatus);
00134 }
00135 
00136 VOID
00137 NTAPI
00138 PspShutdownProcessManager(VOID)
00139 {
00140     PEPROCESS Process = NULL;
00141 
00142     /* Loop every process */
00143     Process = PsGetNextProcess(Process);
00144     while (Process)
00145     {
00146         /* Make sure this isn't the idle or initial process */
00147         if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess))
00148         {
00149             /* Kill it */
00150             PspTerminateProcess(Process, STATUS_SYSTEM_SHUTDOWN);
00151         }
00152 
00153         /* Get the next process */
00154         Process = PsGetNextProcess(Process);
00155     }
00156 }
00157 
00158 VOID
00159 NTAPI
00160 PspExitApcRundown(IN PKAPC Apc)
00161 {
00162     PAGED_CODE();
00163 
00164     /* Free the APC */
00165     ExFreePool(Apc);
00166 }
00167 
00168 VOID
00169 NTAPI
00170 PspReapRoutine(IN PVOID Context)
00171 {
00172     PSINGLE_LIST_ENTRY NextEntry;
00173     PETHREAD Thread;
00174     PSTRACE(PS_KILL_DEBUG, "Context: %p\n", Context);
00175 
00176     /* Start main loop */
00177     do
00178     {
00179         /* Write magic value and return the next entry to process */
00180         NextEntry = InterlockedExchangePointer(&PspReaperListHead.Flink,
00181                                                (PVOID)1);
00182         ASSERT((NextEntry != NULL) && (NextEntry != (PVOID)1));
00183 
00184         /* Start inner loop */
00185         do
00186         {
00187             /* Get the first Thread Entry */
00188             Thread = CONTAINING_RECORD(NextEntry, ETHREAD, ReaperLink);
00189 
00190             /* Delete this entry's kernel stack */
00191             MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
00192                                 Thread->Tcb.LargeStack);
00193             Thread->Tcb.InitialStack = NULL;
00194 
00195             /* Move to the next entry */
00196             NextEntry = NextEntry->Next;
00197 
00198             /* Dereference this thread */
00199             ObDereferenceObject(Thread);
00200         } while ((NextEntry != NULL) && (NextEntry != (PVOID)1));
00201 
00202         /* Remove magic value, keep looping if it got changed */
00203     } while (InterlockedCompareExchangePointer(&PspReaperListHead.Flink,
00204                                                0,
00205                                                (PVOID)1) != (PVOID)1);
00206 }
00207 
00208 VOID
00209 NTAPI
00210 PspDeleteProcess(IN PVOID ObjectBody)
00211 {
00212     PEPROCESS Process = (PEPROCESS)ObjectBody;
00213     KAPC_STATE ApcState;
00214     PAGED_CODE();
00215     PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
00216     PSREFTRACE(Process);
00217 
00218     /* Check if it has an Active Process Link */
00219     if (Process->ActiveProcessLinks.Flink)
00220     {
00221         /* Remove it from the Active List */
00222         KeAcquireGuardedMutex(&PspActiveProcessMutex);
00223         RemoveEntryList(&Process->ActiveProcessLinks);
00224         KeReleaseGuardedMutex(&PspActiveProcessMutex);
00225     }
00226 
00227     /* Check for Auditing information */
00228     if (Process->SeAuditProcessCreationInfo.ImageFileName)
00229     {
00230         /* Free it */
00231         ExFreePool(Process->SeAuditProcessCreationInfo.ImageFileName);
00232         Process->SeAuditProcessCreationInfo.ImageFileName = NULL;
00233     }
00234 
00235     /* Check if we have a job */
00236     if (Process->Job)
00237     {
00238         /* Remove the process from the job */
00239         PspRemoveProcessFromJob(Process, Process->Job);
00240 
00241         /* Dereference it */
00242         ObDereferenceObject(Process->Job);
00243         Process->Job = NULL;
00244     }
00245 
00246     /* Increase the stack count */
00247     Process->Pcb.StackCount++;
00248 
00249     /* Check if we have a debug port */
00250     if (Process->DebugPort)
00251     {
00252         /* Deference the Debug Port */
00253         ObDereferenceObject(Process->DebugPort);
00254         Process->DebugPort = NULL;
00255     }
00256 
00257     /* Check if we have an exception port */
00258     if (Process->ExceptionPort)
00259     {
00260         /* Deference the Exception Port */
00261         ObDereferenceObject(Process->ExceptionPort);
00262         Process->ExceptionPort = NULL;
00263     }
00264 
00265     /* Check if we have a section object */
00266     if (Process->SectionObject)
00267     {
00268         /* Deference the Section Object */
00269         ObDereferenceObject(Process->SectionObject);
00270         Process->SectionObject = NULL;
00271     }
00272 
00273 #if defined(_X86_)
00274     /* Clean Ldt and Vdm objects */
00275     PspDeleteLdt(Process);
00276     PspDeleteVdmObjects(Process);
00277 #endif
00278 
00279     /* Delete the Object Table */
00280     if (Process->ObjectTable)
00281     {
00282         /* Attach to the process */
00283         KeStackAttachProcess(&Process->Pcb, &ApcState);
00284 
00285         /* Kill the Object Info */
00286         ObKillProcess(Process);
00287 
00288         /* Detach */
00289         KeUnstackDetachProcess(&ApcState);
00290     }
00291 
00292     /* Check if we have an address space, and clean it */
00293     if (Process->HasAddressSpace)
00294     {
00295         /* Attach to the process */
00296         KeStackAttachProcess(&Process->Pcb, &ApcState);
00297 
00298         /* Clean the Address Space */
00299         PspExitProcess(FALSE, Process);
00300 
00301         /* Detach */
00302         KeUnstackDetachProcess(&ApcState);
00303 
00304         /* Completely delete the Address Space */
00305         MmDeleteProcessAddressSpace(Process);
00306     }
00307 
00308     /* See if we have a PID */
00309     if (Process->UniqueProcessId)
00310     {
00311         /* Delete the PID */
00312         if (!(ExDestroyHandle(PspCidTable, Process->UniqueProcessId, NULL)))
00313         {
00314             /* Something wrong happened, bugcheck */
00315             KeBugCheck(CID_HANDLE_DELETION);
00316         }
00317     }
00318 
00319     /* Cleanup security information */
00320     PspDeleteProcessSecurity(Process);
00321 
00322     /* Check if we have kept information on the Working Set */
00323     if (Process->WorkingSetWatch)
00324     {
00325         /* Free it */
00326         ExFreePool(Process->WorkingSetWatch);
00327 
00328         /* And return the quota it was taking up */
00329         PsReturnProcessNonPagedPoolQuota(Process, 0x2000);
00330     }
00331 
00332     /* Dereference the Device Map */
00333     ObDereferenceDeviceMap(Process);
00334 
00335     /* Destroy the Quota Block */
00336     PspDestroyQuotaBlock(Process);
00337 }
00338 
00339 VOID
00340 NTAPI
00341 PspDeleteThread(IN PVOID ObjectBody)
00342 {
00343     PETHREAD Thread = (PETHREAD)ObjectBody;
00344     PEPROCESS Process = Thread->ThreadsProcess;
00345     PAGED_CODE();
00346     PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
00347     PSREFTRACE(Thread);
00348     ASSERT(Thread->Tcb.Win32Thread == NULL);
00349 
00350     /* Check if we have a stack */
00351     if (Thread->Tcb.InitialStack)
00352     {
00353         /* Release it */
00354         MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
00355                             Thread->Tcb.LargeStack);
00356     }
00357 
00358     /* Check if we have a CID Handle */
00359     if (Thread->Cid.UniqueThread)
00360     {
00361         /* Delete the CID Handle */
00362         if (!(ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread, NULL)))
00363         {
00364             /* Something wrong happened, bugcheck */
00365             KeBugCheck(CID_HANDLE_DELETION);
00366         }
00367     }
00368 
00369     /* Cleanup impersionation information */
00370     PspDeleteThreadSecurity(Thread);
00371 
00372     /* Make sure the thread was inserted, before continuing */
00373     if (!Process) return;
00374 
00375     /* Check if the thread list is valid */
00376     if (Thread->ThreadListEntry.Flink)
00377     {
00378         /* Lock the thread's process */
00379         KeEnterCriticalRegion();
00380         ExAcquirePushLockExclusive(&Process->ProcessLock);
00381 
00382         /* Remove us from the list */
00383         RemoveEntryList(&Thread->ThreadListEntry);
00384 
00385         /* Release the lock */
00386         ExReleasePushLockExclusive(&Process->ProcessLock);
00387         KeLeaveCriticalRegion();
00388     }
00389 
00390     /* Dereference the Process */
00391     ObDereferenceObject(Process);
00392 }
00393 
00394 /*
00395  * FUNCTION: Terminates the current thread
00396  * See "Windows Internals" - Chapter 13, Page 50-53
00397  */
00398 VOID
00399 NTAPI
00400 PspExitThread(IN NTSTATUS ExitStatus)
00401 {
00402     CLIENT_DIED_MSG TerminationMsg;
00403     NTSTATUS Status;
00404     PTEB Teb;
00405     PEPROCESS CurrentProcess;
00406     PETHREAD Thread, OtherThread, PreviousThread = NULL;
00407     PVOID DeallocationStack;
00408     SIZE_T Dummy;
00409     BOOLEAN Last = FALSE;
00410     PTERMINATION_PORT TerminationPort, NextPort;
00411     PLIST_ENTRY FirstEntry, CurrentEntry;
00412     PKAPC Apc;
00413     PTOKEN PrimaryToken;
00414     PAGED_CODE();
00415     PSTRACE(PS_KILL_DEBUG, "ExitStatus: %p\n", ExitStatus);
00416 
00417     /* Get the Current Thread and Process */
00418     Thread = PsGetCurrentThread();
00419     CurrentProcess = Thread->ThreadsProcess;
00420     ASSERT((Thread) == PsGetCurrentThread());
00421 
00422     /* Can't terminate a thread if it attached another process */
00423     if (KeIsAttachedProcess())
00424     {
00425         /* Bugcheck */
00426         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
00427                      (ULONG_PTR)CurrentProcess,
00428                      (ULONG_PTR)Thread->Tcb.ApcState.Process,
00429                      (ULONG_PTR)Thread->Tcb.ApcStateIndex,
00430                      (ULONG_PTR)Thread);
00431     }
00432 
00433     /* Lower to Passive Level */
00434     KeLowerIrql(PASSIVE_LEVEL);
00435 
00436     /* Can't be a worker thread */
00437     if (Thread->ActiveExWorker)
00438     {
00439         /* Bugcheck */
00440         KeBugCheckEx(ACTIVE_EX_WORKER_THREAD_TERMINATION,
00441                      (ULONG_PTR)Thread,
00442                      0,
00443                      0,
00444                      0);
00445     }
00446 
00447     /* Can't have pending APCs */
00448     if (Thread->Tcb.CombinedApcDisable != 0)
00449     {
00450         /* Bugcheck */
00451         KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
00452                      0,
00453                      Thread->Tcb.CombinedApcDisable,
00454                      0,
00455                      1);
00456     }
00457 
00458     /* Lock the thread */
00459     ExWaitForRundownProtectionRelease(&Thread->RundownProtect);
00460 
00461     /* Cleanup the power state */
00462     PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState);
00463 
00464     /* Call the WMI Callback for Threads */
00465     //WmiTraceThread(Thread, NULL, FALSE);
00466 
00467     /* Run Thread Notify Routines before we desintegrate the thread */
00468     PspRunCreateThreadNotifyRoutines(Thread, FALSE);
00469 
00470     /* Lock the Process before we modify its thread entries */
00471     KeEnterCriticalRegion();
00472     ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
00473 
00474     /* Decrease the active thread count, and check if it's 0 */
00475     if (!(--CurrentProcess->ActiveThreads))
00476     {
00477         /* Set the delete flag */
00478         InterlockedOr((PLONG)&CurrentProcess->Flags, PSF_PROCESS_DELETE_BIT);
00479 
00480         /* Remember we are last */
00481         Last = TRUE;
00482 
00483         /* Check if this termination is due to the thread dying */
00484         if (ExitStatus == STATUS_THREAD_IS_TERMINATING)
00485         {
00486             /* Check if the last thread was pending */
00487             if (CurrentProcess->ExitStatus == STATUS_PENDING)
00488             {
00489                 /* Use the last exit status */
00490                 CurrentProcess->ExitStatus = CurrentProcess->
00491                                              LastThreadExitStatus;
00492             }
00493         }
00494         else
00495         {
00496             /* Just a normal exit, write the code */
00497             CurrentProcess->ExitStatus = ExitStatus;
00498         }
00499 
00500         /* Loop all the current threads */
00501         FirstEntry = &CurrentProcess->ThreadListHead;
00502         CurrentEntry = FirstEntry->Flink;
00503         while (FirstEntry != CurrentEntry)
00504         {
00505             /* Get the thread on the list */
00506             OtherThread = CONTAINING_RECORD(CurrentEntry,
00507                                             ETHREAD,
00508                                             ThreadListEntry);
00509 
00510             /* Check if it's a thread that's still alive */
00511             if ((OtherThread != Thread) &&
00512                 !(KeReadStateThread(&OtherThread->Tcb)) &&
00513                 (ObReferenceObjectSafe(OtherThread)))
00514             {
00515                 /* It's a live thread and we referenced it, unlock process */
00516                 ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
00517                 KeLeaveCriticalRegion();
00518 
00519                 /* Wait on the thread */
00520                 KeWaitForSingleObject(OtherThread,
00521                                       Executive,
00522                                       KernelMode,
00523                                       FALSE,
00524                                       NULL);
00525 
00526                 /* Check if we had a previous thread to dereference */
00527                 if (PreviousThread) ObDereferenceObject(PreviousThread);
00528 
00529                 /* Remember the thread and re-lock the process */
00530                 PreviousThread = OtherThread;
00531                 KeEnterCriticalRegion();
00532                 ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
00533             }
00534 
00535             /* Go to the next thread */
00536             CurrentEntry = CurrentEntry->Flink;
00537         }
00538     }
00539     else if (ExitStatus != STATUS_THREAD_IS_TERMINATING)
00540     {
00541         /* Write down the exit status of the last thread to get killed */
00542         CurrentProcess->LastThreadExitStatus = ExitStatus;
00543     }
00544 
00545     /* Unlock the Process */
00546     ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
00547     KeLeaveCriticalRegion();
00548 
00549     /* Check if we had a previous thread to dereference */
00550     if (PreviousThread) ObDereferenceObject(PreviousThread);
00551 
00552     /* Check if the process has a debug port and if this is a user thread */
00553     if ((CurrentProcess->DebugPort) && !(Thread->SystemThread))
00554     {
00555         /* Notify the Debug API. */
00556         Last ? DbgkExitProcess(CurrentProcess->ExitStatus) :
00557                DbgkExitThread(ExitStatus);
00558     }
00559 
00560     /* Check if this is a Critical Thread */
00561     if ((KdDebuggerEnabled) && (Thread->BreakOnTermination))
00562     {
00563         /* Break to debugger */
00564         PspCatchCriticalBreak("Critical thread 0x%p (in %s) exited\n",
00565                               Thread,
00566                               CurrentProcess->ImageFileName);
00567     }
00568 
00569     /* Check if it's the last thread and this is a Critical Process */
00570     if ((Last) && (CurrentProcess->BreakOnTermination))
00571     {
00572         /* Check if a debugger is here to handle this */
00573         if (KdDebuggerEnabled)
00574         {
00575             /* Break to debugger */
00576             PspCatchCriticalBreak("Critical  process 0x%p (in %s) exited\n",
00577                                   CurrentProcess,
00578                                   CurrentProcess->ImageFileName);
00579         }
00580         else
00581         {
00582             /* Bugcheck, we can't allow this */
00583             KeBugCheckEx(CRITICAL_PROCESS_DIED,
00584                          (ULONG_PTR)CurrentProcess,
00585                          0,
00586                          0,
00587                          0);
00588         }
00589     }
00590 
00591     /* Sanity check */
00592     ASSERT(Thread->Tcb.CombinedApcDisable == 0);
00593 
00594     /* Process the Termination Ports */
00595     TerminationPort = Thread->TerminationPort;
00596     if (TerminationPort)
00597     {
00598         /* Setup the message header */
00599         TerminationMsg.h.u2.ZeroInit = 0;
00600         TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
00601         TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
00602         TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
00603                                             sizeof(PORT_MESSAGE);
00604 
00605         /* Loop each port */
00606         do
00607         {
00608             /* Save the Create Time */
00609             TerminationMsg.CreateTime = Thread->CreateTime;
00610 
00611             /* Loop trying to send message */
00612             while (TRUE)
00613             {
00614                 /* Send the LPC Message */
00615                 Status = LpcRequestPort(TerminationPort->Port,
00616                                         &TerminationMsg.h);
00617                 if ((Status == STATUS_NO_MEMORY) ||
00618                     (Status == STATUS_INSUFFICIENT_RESOURCES))
00619                 {
00620                     /* Wait a bit and try again */
00621                     KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
00622                     continue;
00623                 }
00624                 break;
00625             }
00626 
00627             /* Dereference this LPC Port */
00628             ObDereferenceObject(TerminationPort->Port);
00629 
00630             /* Move to the next one */
00631             NextPort = TerminationPort->Next;
00632 
00633             /* Free the Termination Port Object */
00634             ExFreePool(TerminationPort);
00635 
00636             /* Keep looping as long as there is a port */
00637             TerminationPort = NextPort;
00638         } while (TerminationPort);
00639     }
00640     else if (((ExitStatus == STATUS_THREAD_IS_TERMINATING) &&
00641               (Thread->DeadThread)) ||
00642              !(Thread->DeadThread))
00643     {
00644         /*
00645          * This case is special and deserves some extra comments. What
00646          * basically happens here is that this thread doesn't have a termination
00647          * port, which means that it died before being fully created. Since we
00648          * still have to notify an LPC Server, we'll use the exception port,
00649          * which we know exists. However, we need to know how far the thread
00650          * actually got created. We have three possibilites:
00651          *
00652          *  - NtCreateThread returned an error really early: DeadThread is set.
00653          *  - NtCreateThread managed to create the thread: DeadThread is off.
00654          *  - NtCreateThread was creating the thread (with Deadthread set,
00655          *    but the thread got killed prematurely: STATUS_THREAD_IS_TERMINATING
00656          *    is our exit code.)
00657          *
00658          * For the 2 & 3rd scenarios, the thread has been created far enough to
00659          * warrant notification to the LPC Server.
00660          */
00661 
00662         /* Setup the message header */
00663         TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
00664         TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
00665         TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
00666                                             sizeof(PORT_MESSAGE);
00667 
00668         /* Make sure the process has an exception port */
00669         if (CurrentProcess->ExceptionPort)
00670         {
00671             /* Save the Create Time */
00672             TerminationMsg.CreateTime = Thread->CreateTime;
00673 
00674             /* Loop trying to send message */
00675             while (TRUE)
00676             {
00677                 /* Send the LPC Message */
00678                 Status = LpcRequestPort(CurrentProcess->ExceptionPort,
00679                                         &TerminationMsg.h);
00680                 if ((Status == STATUS_NO_MEMORY) ||
00681                     (Status == STATUS_INSUFFICIENT_RESOURCES))
00682                 {
00683                     /* Wait a bit and try again */
00684                     KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
00685                     continue;
00686                 }
00687                 break;
00688             }
00689         }
00690     }
00691 
00692     /* Rundown Win32 Thread if there is one */
00693     if (Thread->Tcb.Win32Thread) PspW32ThreadCallout(Thread,
00694                                                      PsW32ThreadCalloutExit);
00695 
00696     /* If we are the last thread and have a W32 Process */
00697     if ((Last) && (CurrentProcess->Win32Process))
00698     {
00699         /* Run it down too */
00700         PspW32ProcessCallout(CurrentProcess, FALSE);
00701     }
00702 
00703     /* Make sure Stack Swap is enabled */
00704     if (!Thread->Tcb.EnableStackSwap)
00705     {
00706         /* Stack swap really shouldn't be disabled during exit! */
00707         KeBugCheckEx(KERNEL_STACK_LOCKED_AT_EXIT, 0, 0, 0, 0);
00708     }
00709 
00710     /* Cancel I/O for the thread. */
00711     IoCancelThreadIo(Thread);
00712 
00713     /* Rundown Timers */
00714     ExTimerRundown();
00715 
00716     /* FIXME: Rundown Registry Notifications (NtChangeNotify)
00717     CmNotifyRunDown(Thread); */
00718 
00719     /* Rundown Mutexes */
00720     KeRundownThread();
00721 
00722     /* Check if we have a TEB */
00723     Teb = Thread->Tcb.Teb;
00724     if (Teb)
00725     {
00726         /* Check if the thread is still alive */
00727         if (!Thread->DeadThread)
00728         {
00729             /* Check if we need to free its stack */
00730             if (Teb->FreeStackOnTermination)
00731             {
00732                 /* Set the TEB's Deallocation Stack as the Base Address */
00733                 Dummy = 0;
00734                 DeallocationStack = Teb->DeallocationStack;
00735 
00736                 /* Free the Thread's Stack */
00737                 ZwFreeVirtualMemory(NtCurrentProcess(),
00738                                     &DeallocationStack,
00739                                     &Dummy,
00740                                     MEM_RELEASE);
00741             }
00742 
00743             /* Free the debug handle */
00744             if (Teb->DbgSsReserved[1]) ObCloseHandle(Teb->DbgSsReserved[1],
00745                                                      UserMode);
00746         }
00747 
00748         /* Decommit the TEB */
00749         MmDeleteTeb(CurrentProcess, Teb);
00750         Thread->Tcb.Teb = NULL;
00751     }
00752 
00753     /* Free LPC Data */
00754     LpcExitThread(Thread);
00755 
00756     /* Save the exit status and exit time */
00757     Thread->ExitStatus = ExitStatus;
00758     KeQuerySystemTime(&Thread->ExitTime);
00759 
00760     /* Sanity check */
00761     ASSERT(Thread->Tcb.CombinedApcDisable == 0);
00762 
00763     /* Check if this is the final thread or not */
00764     if (Last)
00765     {
00766         /* Set the process exit time */
00767         CurrentProcess->ExitTime = Thread->ExitTime;
00768 
00769         /* Exit the process */
00770         PspExitProcess(TRUE, CurrentProcess);
00771 
00772         /* Get the process token and check if we need to audit */
00773         PrimaryToken = PsReferencePrimaryToken(CurrentProcess);
00774         if (SeDetailedAuditingWithToken(PrimaryToken))
00775         {
00776             /* Audit the exit */
00777             SeAuditProcessExit(CurrentProcess);
00778         }
00779 
00780         /* Dereference the process token */
00781         ObFastDereferenceObject(&CurrentProcess->Token, PrimaryToken);
00782 
00783         /* Check if this is a VDM Process and rundown the VDM DPCs if so */
00784         if (CurrentProcess->VdmObjects) { /* VdmRundownDpcs(CurrentProcess); */ }
00785 
00786         /* Kill the process in the Object Manager */
00787         ObKillProcess(CurrentProcess);
00788 
00789         /* Check if we have a section object */
00790         if (CurrentProcess->SectionObject)
00791         {
00792             /* Dereference and clear the Section Object */
00793             ObDereferenceObject(CurrentProcess->SectionObject);
00794             CurrentProcess->SectionObject = NULL;
00795         }
00796 
00797         /* Check if the process is part of a job */
00798         if (CurrentProcess->Job)
00799         {
00800             /* Remove the process from the job */
00801             PspExitProcessFromJob(CurrentProcess->Job, CurrentProcess);
00802         }
00803     }
00804 
00805     /* Disable APCs */
00806     KeEnterCriticalRegion();
00807 
00808     /* Disable APC queueing, force a resumption */
00809     Thread->Tcb.ApcQueueable = FALSE;
00810     KeForceResumeThread(&Thread->Tcb);
00811 
00812     /* Re-enable APCs */
00813     KeLeaveCriticalRegion();
00814 
00815     /* Flush the User APCs */
00816     FirstEntry = KeFlushQueueApc(&Thread->Tcb, UserMode);
00817     if (FirstEntry)
00818     {
00819         /* Start with the first entry */
00820         CurrentEntry = FirstEntry;
00821         do
00822         {
00823            /* Get the APC */
00824            Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
00825 
00826            /* Move to the next one */
00827            CurrentEntry = CurrentEntry->Flink;
00828 
00829            /* Rundown the APC or de-allocate it */
00830            if (Apc->RundownRoutine)
00831            {
00832               /* Call its own routine */
00833               Apc->RundownRoutine(Apc);
00834            }
00835            else
00836            {
00837               /* Do it ourselves */
00838               ExFreePool(Apc);
00839            }
00840         }
00841         while (CurrentEntry != FirstEntry);
00842     }
00843 
00844     /* Clean address space if this was the last thread */
00845     if (Last) MmCleanProcessAddressSpace(CurrentProcess);
00846 
00847     /* Call the Lego routine */
00848     if (Thread->Tcb.LegoData) PspRunLegoRoutine(&Thread->Tcb);
00849 
00850     /* Flush the APC queue, which should be empty */
00851     FirstEntry = KeFlushQueueApc(&Thread->Tcb, KernelMode);
00852     if ((FirstEntry) || (Thread->Tcb.CombinedApcDisable != 0))
00853     {
00854         /* Bugcheck time */
00855         KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
00856                      (ULONG_PTR)FirstEntry,
00857                      Thread->Tcb.CombinedApcDisable,
00858                      KeGetCurrentIrql(),
00859                      0);
00860     }
00861 
00862     /* Signal the process if this was the last thread */
00863     if (Last) KeSetProcess(&CurrentProcess->Pcb, 0, FALSE);
00864 
00865     /* Terminate the Thread from the Scheduler */
00866     KeTerminateThread(0);
00867 }
00868 
00869 VOID
00870 NTAPI
00871 PsExitSpecialApc(IN PKAPC Apc,
00872                  IN OUT PKNORMAL_ROUTINE* NormalRoutine,
00873                  IN OUT PVOID* NormalContext,
00874                  IN OUT PVOID* SystemArgument1,
00875                  IN OUT PVOID* SystemArgument2)
00876 {
00877     NTSTATUS Status;
00878     PAGED_CODE();
00879     PSTRACE(PS_KILL_DEBUG,
00880             "Apc: %p SystemArgument2: %p \n", Apc, SystemArgument2);
00881 
00882     /* Don't do anything unless we are in User-Mode */
00883     if (Apc->SystemArgument2)
00884     {
00885         /* Free the APC */
00886         Status = (NTSTATUS)Apc->NormalContext;
00887         PspExitApcRundown(Apc);
00888 
00889         /* Terminate the Thread */
00890         PspExitThread(Status);
00891     }
00892 }
00893 
00894 VOID
00895 NTAPI
00896 PspExitNormalApc(IN PVOID NormalContext,
00897                  IN PVOID SystemArgument1,
00898                  IN PVOID SystemArgument2)
00899 {
00900     PKAPC Apc = (PKAPC)SystemArgument1;
00901     PETHREAD Thread = PsGetCurrentThread();
00902     PAGED_CODE();
00903     PSTRACE(PS_KILL_DEBUG, "SystemArgument2: %p \n", SystemArgument2);
00904 
00905     /* This should never happen */
00906     ASSERT(!(((ULONG_PTR)SystemArgument2) & 1));
00907 
00908     /* If we're here, this is not a System Thread, so kill it from User-Mode */
00909     KeInitializeApc(Apc,
00910                     &Thread->Tcb,
00911                     OriginalApcEnvironment,
00912                     PsExitSpecialApc,
00913                     PspExitApcRundown,
00914                     PspExitNormalApc,
00915                     UserMode,
00916                     NormalContext);
00917 
00918     /* Now insert the APC with the User-Mode Flag */
00919     if (!(KeInsertQueueApc(Apc,
00920                            Apc,
00921                            (PVOID)((ULONG_PTR)SystemArgument2 | 1),
00922                            2)))
00923     {
00924         /* Failed to insert, free the APC */
00925         PspExitApcRundown(Apc);
00926     }
00927 
00928     /* Set the APC Pending flag */
00929     Thread->Tcb.ApcState.UserApcPending = TRUE;
00930 }
00931 
00932 /*
00933  * See "Windows Internals" - Chapter 13, Page 49
00934  */
00935 NTSTATUS
00936 NTAPI
00937 PspTerminateThreadByPointer(IN PETHREAD Thread,
00938                             IN NTSTATUS ExitStatus,
00939                             IN BOOLEAN bSelf)
00940 {
00941     PKAPC Apc;
00942     NTSTATUS Status = STATUS_SUCCESS;
00943     ULONG Flags;
00944     PAGED_CODE();
00945     PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %p\n", Thread, ExitStatus);
00946     PSREFTRACE(Thread);
00947 
00948     /* Check if this is a Critical Thread, and Bugcheck */
00949     if (Thread->BreakOnTermination)
00950     {
00951         /* Break to debugger */
00952         PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n",
00953                               Thread,
00954                               Thread->ThreadsProcess->ImageFileName);
00955     }
00956 
00957     /* Check if we are already inside the thread */
00958     if ((bSelf) || (PsGetCurrentThread() == Thread))
00959     {
00960         /* This should only happen at passive */
00961         ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
00962 
00963         /* Mark it as terminated */
00964         PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT);
00965 
00966         /* Directly terminate the thread */
00967         PspExitThread(ExitStatus);
00968     }
00969 
00970     /* This shouldn't be a system thread */
00971     if (Thread->SystemThread) return STATUS_ACCESS_DENIED;
00972 
00973     /* Allocate the APC */
00974     Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
00975     if (!Apc) return STATUS_INSUFFICIENT_RESOURCES;
00976 
00977     /* Set the Terminated Flag */
00978     Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT;
00979 
00980     /* Set it, and check if it was already set while we were running */
00981     if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) &
00982           CT_TERMINATED_BIT))
00983     {
00984         /* Initialize a Kernel Mode APC to Kill the Thread */
00985         KeInitializeApc(Apc,
00986                         &Thread->Tcb,
00987                         OriginalApcEnvironment,
00988                         PsExitSpecialApc,
00989                         PspExitApcRundown,
00990                         PspExitNormalApc,
00991                         KernelMode,
00992                         (PVOID)ExitStatus);
00993 
00994         /* Insert it into the APC Queue */
00995         if (!KeInsertQueueApc(Apc, Apc, NULL, 2))
00996         {
00997             /* The APC was already in the queue, fail */
00998             Status = STATUS_UNSUCCESSFUL;
00999         }
01000         else
01001         {
01002             /* Forcefully resume the thread and return */
01003             KeForceResumeThread(&Thread->Tcb);
01004             return Status;
01005         }
01006     }
01007 
01008     /* We failed, free the APC */
01009     ExFreePool(Apc);
01010 
01011     /* Return Status */
01012     return Status;
01013 }
01014 
01015 BOOLEAN
01016 NTAPI
01017 PspIsProcessExiting(IN PEPROCESS Process)
01018 {
01019     return Process->Flags & PSF_PROCESS_EXITING_BIT;
01020 }
01021 
01022 VOID
01023 NTAPI
01024 PspExitProcess(IN BOOLEAN LastThread,
01025                IN PEPROCESS Process)
01026 {
01027     ULONG Actual;
01028     PAGED_CODE();
01029     PSTRACE(PS_KILL_DEBUG,
01030             "LastThread: %p Process: %p\n", LastThread, Process);
01031     PSREFTRACE(Process);
01032 
01033     /* Set Process Exit flag */
01034     InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_EXITING_BIT);
01035 
01036     /* Check if we are the last thread */
01037     if (LastThread)
01038     {
01039         /* Notify the WMI Process Callback */
01040         //WmiTraceProcess(Process, FALSE);
01041 
01042         /* Run the Notification Routines */
01043         PspRunCreateProcessNotifyRoutines(Process, FALSE);
01044     }
01045 
01046     /* Cleanup the power state */
01047     PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState);
01048 
01049     /* Clear the security port */
01050     if (!Process->SecurityPort)
01051     {
01052         /* So we don't double-dereference */
01053         Process->SecurityPort = (PVOID)1;
01054     }
01055     else if (Process->SecurityPort != (PVOID)1)
01056     {
01057         /* Dereference it */
01058         ObDereferenceObject(Process->SecurityPort);
01059         Process->SecurityPort = (PVOID)1;
01060     }
01061 
01062     /* Check if we are the last thread */
01063     if (LastThread)
01064     {
01065         /* Check if we have to set the Timer Resolution */
01066         if (Process->SetTimerResolution)
01067         {
01068             /* Set it to default */
01069             ZwSetTimerResolution(KeMaximumIncrement, 0, &Actual);
01070         }
01071 
01072         /* Check if we are part of a Job that has a completion port */
01073         if ((Process->Job) && (Process->Job->CompletionPort))
01074         {
01075             /* FIXME: Check job status code and do I/O completion if needed */
01076         }
01077 
01078         /* FIXME: Notify the Prefetcher */
01079     }
01080     else
01081     {
01082         /* Clear process' address space here */
01083         MmCleanProcessAddressSpace(Process);
01084     }
01085 }
01086 
01087 /* PUBLIC FUNCTIONS **********************************************************/
01088 
01089 /*
01090  * @implemented
01091  */
01092 NTSTATUS
01093 NTAPI
01094 PsTerminateSystemThread(IN NTSTATUS ExitStatus)
01095 {
01096     PETHREAD Thread = PsGetCurrentThread();
01097 
01098     /* Make sure this is a system thread */
01099     if (!Thread->SystemThread) return STATUS_INVALID_PARAMETER;
01100 
01101     /* Terminate it for real */
01102     return PspTerminateThreadByPointer(Thread, ExitStatus, TRUE);
01103 }
01104 
01105 /*
01106  * @implemented
01107  */
01108 NTSTATUS
01109 NTAPI
01110 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
01111                    IN NTSTATUS ExitStatus)
01112 {
01113     NTSTATUS Status;
01114     PEPROCESS Process, CurrentProcess = PsGetCurrentProcess();
01115     PETHREAD Thread, CurrentThread = PsGetCurrentThread();
01116     BOOLEAN KillByHandle;
01117     PAGED_CODE();
01118     PSTRACE(PS_KILL_DEBUG,
01119             "ProcessHandle: %p ExitStatus: %p\n", ProcessHandle, ExitStatus);
01120 
01121     /* Were we passed a process handle? */
01122     if (ProcessHandle)
01123     {
01124         /* Yes we were, use it */
01125         KillByHandle = TRUE;
01126     }
01127     else
01128     {
01129         /* We weren't... we assume this is suicide */
01130         KillByHandle = FALSE;
01131         ProcessHandle = NtCurrentProcess();
01132     }
01133 
01134     /* Get the Process Object */
01135     Status = ObReferenceObjectByHandle(ProcessHandle,
01136                                        PROCESS_TERMINATE,
01137                                        PsProcessType,
01138                                        KeGetPreviousMode(),
01139                                        (PVOID*)&Process,
01140                                        NULL);
01141     if (!NT_SUCCESS(Status)) return(Status);
01142 
01143     /* Check if this is a Critical Process, and Bugcheck */
01144     if (Process->BreakOnTermination)
01145     {
01146         /* Break to debugger */
01147         PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
01148                               Process,
01149                               Process->ImageFileName);
01150     }
01151 
01152     /* Lock the Process */
01153     if (!ExAcquireRundownProtection(&Process->RundownProtect))
01154     {
01155         /* Failed to lock, fail */
01156         ObDereferenceObject (Process);
01157         return STATUS_PROCESS_IS_TERMINATING;
01158     }
01159 
01160     /* Set the delete flag, unless the process is comitting suicide */
01161     if (KillByHandle) PspSetProcessFlag(Process, PSF_PROCESS_DELETE_BIT);
01162 
01163     /* Get the first thread */
01164     Status = STATUS_NOTHING_TO_TERMINATE;
01165     Thread = PsGetNextProcessThread(Process, NULL);
01166     if (Thread)
01167     {
01168         /* We know we have at least a thread */
01169         Status = STATUS_SUCCESS;
01170 
01171         /* Loop and kill the others */
01172         do
01173         {
01174             /* Ensure it's not ours*/
01175             if (Thread != CurrentThread)
01176             {
01177                 /* Kill it */
01178                 PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
01179             }
01180 
01181             /* Move to the next thread */
01182             Thread = PsGetNextProcessThread(Process, Thread);
01183         } while (Thread);
01184     }
01185 
01186     /* Unlock the process */
01187     ExReleaseRundownProtection(&Process->RundownProtect);
01188 
01189     /* Check if we are killing ourselves */
01190     if (Process == CurrentProcess)
01191     {
01192         /* Also make sure the caller gave us our handle */
01193         if (KillByHandle)
01194         {
01195             /* Dereference the process */
01196             ObDereferenceObject(Process);
01197 
01198             /* Terminate ourselves */
01199             PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE);
01200         }
01201     }
01202     else if (ExitStatus == DBG_TERMINATE_PROCESS)
01203     {
01204         /* Disable debugging on this process */
01205         DbgkClearProcessDebugObject(Process, NULL);
01206     }
01207 
01208     /* Check if there was nothing to terminate, or if we have a Debug Port */
01209     if ((Status == STATUS_NOTHING_TO_TERMINATE) ||
01210         ((Process->DebugPort) && (KillByHandle)))
01211     {
01212         /* Clear the handle table */
01213         ObClearProcessHandleTable(Process);
01214 
01215         /* Return status now */
01216         Status = STATUS_SUCCESS;
01217     }
01218 
01219     /* Decrease the reference count we added */
01220     ObDereferenceObject(Process);
01221 
01222     /* Return status */
01223     return Status;
01224 }
01225 
01226 NTSTATUS
01227 NTAPI
01228 NtTerminateThread(IN HANDLE ThreadHandle,
01229                   IN NTSTATUS ExitStatus)
01230 {
01231     PETHREAD Thread;
01232     PETHREAD CurrentThread = PsGetCurrentThread();
01233     NTSTATUS Status;
01234     PAGED_CODE();
01235     PSTRACE(PS_KILL_DEBUG,
01236             "ThreadHandle: %p ExitStatus: %p\n", ThreadHandle, ExitStatus);
01237 
01238     /* Handle the special NULL case */
01239     if (!ThreadHandle)
01240     {
01241         /* Check if we're the only thread left */
01242         if (PsGetCurrentProcess()->ActiveThreads == 1)
01243         {
01244             /* This is invalid */
01245             return STATUS_CANT_TERMINATE_SELF;
01246         }
01247 
01248         /* Terminate us directly */
01249         goto TerminateSelf;
01250     }
01251     else if (ThreadHandle == NtCurrentThread())
01252     {
01253 TerminateSelf:
01254         /* Terminate this thread */
01255         return PspTerminateThreadByPointer(CurrentThread,
01256                                            ExitStatus,
01257                                            TRUE);
01258     }
01259 
01260     /* We are terminating another thread, get the Thread Object */
01261     Status = ObReferenceObjectByHandle(ThreadHandle,
01262                                        THREAD_TERMINATE,
01263                                        PsThreadType,
01264                                        KeGetPreviousMode(),
01265                                        (PVOID*)&Thread,
01266                                        NULL);
01267     if (!NT_SUCCESS(Status)) return Status;
01268 
01269     /* Check to see if we're running in the same thread */
01270     if (Thread != CurrentThread)
01271     {
01272         /* Terminate it */
01273         Status = PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
01274 
01275         /* Dereference the Thread and return */
01276         ObDereferenceObject(Thread);
01277     }
01278     else
01279     {
01280         /* Dereference the thread and terminate ourselves */
01281         ObDereferenceObject(Thread);
01282         goto TerminateSelf;
01283     }
01284 
01285     /* Return status */
01286     return Status;
01287 }
01288 
01289 NTSTATUS
01290 NTAPI
01291 NtRegisterThreadTerminatePort(IN HANDLE PortHandle)
01292 {
01293     NTSTATUS Status;
01294     PTERMINATION_PORT TerminationPort;
01295     PVOID TerminationLpcPort;
01296     PETHREAD Thread;
01297     PAGED_CODE();
01298     PSTRACE(PS_KILL_DEBUG, "PortHandle: %p\n", PortHandle);
01299 
01300     /* Get the Port */
01301     Status = ObReferenceObjectByHandle(PortHandle,
01302                                        PORT_ALL_ACCESS,
01303                                        LpcPortObjectType,
01304                                        KeGetPreviousMode(),
01305                                        &TerminationLpcPort,
01306                                        NULL);
01307     if (!NT_SUCCESS(Status)) return(Status);
01308 
01309     /* Allocate the Port and make sure it suceeded */
01310     TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
01311                                             sizeof(TERMINATION_PORT),
01312                                             '=TsP');
01313     if(TerminationPort)
01314     {
01315         /* Associate the Port */
01316         Thread = PsGetCurrentThread();
01317         TerminationPort->Port = TerminationLpcPort;
01318         TerminationPort->Next = Thread->TerminationPort;
01319         Thread->TerminationPort = TerminationPort;
01320 
01321         /* Return success */
01322         return STATUS_SUCCESS;
01323     }
01324 
01325     /* Dereference and Fail */
01326     ObDereferenceObject(TerminationPort);
01327     return STATUS_INSUFFICIENT_RESOURCES;
01328 }

Generated on Fri May 25 2012 04:36:06 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.