pgroups.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199
  1. /*++
  2. Copyright (c) 2013 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. pgroups.c
  9. Abstract:
  10. This module implements support for process groups and sessions.
  11. Author:
  12. Evan Green 14-May-2013
  13. Environment:
  14. Kernel
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include "psp.h"
  21. //
  22. // --------------------------------------------------------------------- Macros
  23. //
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. #define PROCESS_GROUP_MAX_REFERENCE_COUNT 0x10000000
  28. //
  29. // ------------------------------------------------------ Data Type Definitions
  30. //
  31. //
  32. // ----------------------------------------------- Internal Function Prototypes
  33. //
  34. VOID
  35. PspProcessGroupAddReference (
  36. PPROCESS_GROUP ProcessGroup
  37. );
  38. VOID
  39. PspProcessGroupReleaseReference (
  40. PPROCESS_GROUP ProcessGroup
  41. );
  42. PPROCESS_GROUP
  43. PspLookupProcessGroup (
  44. PROCESS_GROUP_ID ProcessGroupId
  45. );
  46. VOID
  47. PspSignalProcessGroup (
  48. PPROCESS_GROUP ProcessGroup,
  49. ULONG SignalNumber
  50. );
  51. VOID
  52. PspProcessGroupHandleLeavingProcess (
  53. PKPROCESS Process,
  54. PPROCESS_GROUP OldGroup,
  55. PPROCESS_GROUP NewGroup
  56. );
  57. BOOL
  58. PspIsOrphanedProcessGroupStopped (
  59. PPROCESS_GROUP ProcessGroup
  60. );
  61. //
  62. // -------------------------------------------------------------------- Globals
  63. //
  64. //
  65. // Store the list of process groups.
  66. //
  67. LIST_ENTRY PsProcessGroupList;
  68. //
  69. // Store the lock protecting the session and process group lists. The lock
  70. // ordering requires that this lock be acquired before any process lock.
  71. //
  72. PQUEUED_LOCK PsProcessGroupListLock;
  73. //
  74. // ------------------------------------------------------------------ Functions
  75. //
  76. VOID
  77. PsGetProcessGroup (
  78. PKPROCESS Process,
  79. PPROCESS_GROUP_ID ProcessGroupId,
  80. PSESSION_ID SessionId
  81. )
  82. /*++
  83. Routine Description:
  84. This routine returns the process group and session ID for the given process.
  85. Arguments:
  86. Process - Supplies a pointer to the process whose process group and session
  87. IDs are desired. Supply NULL and the current process will be used.
  88. ProcessGroupId - Supplies an optional pointer where the process group ID of
  89. the requested process will be returned.
  90. SessionId - Supplies an optional pointer where the session ID of the
  91. requested process will be returned.
  92. Return Value:
  93. None.
  94. --*/
  95. {
  96. if (Process == NULL) {
  97. Process = PsGetCurrentProcess();
  98. }
  99. //
  100. // Take a snap of the process' session ID and process group ID.
  101. //
  102. if (ProcessGroupId != NULL) {
  103. *ProcessGroupId = Process->Identifiers.ProcessGroupId;
  104. }
  105. if (SessionId != NULL) {
  106. *SessionId = Process->Identifiers.SessionId;
  107. }
  108. return;
  109. }
  110. BOOL
  111. PsIsProcessGroupOrphaned (
  112. PROCESS_GROUP_ID ProcessGroupId
  113. )
  114. /*++
  115. Routine Description:
  116. This routine determines if a process group is orphaned.
  117. Arguments:
  118. ProcessGroupId - Supplies the ID of the process group to query.
  119. Return Value:
  120. TRUE if the process group is orphaned or does not exist.
  121. FALSE if the process group has at least one parent within the session but
  122. outside the process group.
  123. --*/
  124. {
  125. PPROCESS_GROUP ProcessGroup;
  126. BOOL Result;
  127. Result = TRUE;
  128. KeAcquireQueuedLock(PsProcessGroupListLock);
  129. ProcessGroup = PspLookupProcessGroup(ProcessGroupId);
  130. if ((ProcessGroup != NULL) && (ProcessGroup->OutsideParents != 0)) {
  131. Result = FALSE;
  132. }
  133. KeReleaseQueuedLock(PsProcessGroupListLock);
  134. return Result;
  135. }
  136. BOOL
  137. PsIsProcessGroupInSession (
  138. PROCESS_GROUP_ID ProcessGroupId,
  139. SESSION_ID SessionId
  140. )
  141. /*++
  142. Routine Description:
  143. This routine determines whether or not the given process group belongs to
  144. the given session.
  145. Arguments:
  146. ProcessGroupId - Supplies the ID of the process group to be tested.
  147. SessionId - Supplies the ID of the session to be searched for the given
  148. process group.
  149. Return Value:
  150. Returns TRUE if the process group is in the given session.
  151. --*/
  152. {
  153. PPROCESS_GROUP ProcessGroup;
  154. BOOL Result;
  155. Result = FALSE;
  156. KeAcquireQueuedLock(PsProcessGroupListLock);
  157. ProcessGroup = PspLookupProcessGroup(ProcessGroupId);
  158. if ((ProcessGroup != NULL) &&
  159. (ProcessGroup->SessionId == SessionId)) {
  160. Result = TRUE;
  161. }
  162. KeReleaseQueuedLock(PsProcessGroupListLock);
  163. return Result;
  164. }
  165. KSTATUS
  166. PsSignalProcessGroup (
  167. PROCESS_GROUP_ID ProcessGroupId,
  168. ULONG SignalNumber
  169. )
  170. /*++
  171. Routine Description:
  172. This routine sends a signal to every process in the given process group.
  173. Arguments:
  174. ProcessGroupId - Supplies the ID of the process group to send the signal to.
  175. SignalNumber - Supplies the signal to send to each process in the group.
  176. Return Value:
  177. STATUS_SUCCESS on success.
  178. STATUS_NOT_FOUND if there was no such process group.
  179. --*/
  180. {
  181. PPROCESS_GROUP ProcessGroup;
  182. KSTATUS Status;
  183. Status = STATUS_NOT_FOUND;
  184. KeAcquireQueuedLock(PsProcessGroupListLock);
  185. ProcessGroup = PspLookupProcessGroup(ProcessGroupId);
  186. if (ProcessGroup != NULL) {
  187. PspSignalProcessGroup(ProcessGroup, SignalNumber);
  188. Status = STATUS_SUCCESS;
  189. }
  190. KeReleaseQueuedLock(PsProcessGroupListLock);
  191. return Status;
  192. }
  193. KSTATUS
  194. PspInitializeProcessGroupSupport (
  195. VOID
  196. )
  197. /*++
  198. Routine Description:
  199. This routine initializes support for process groups.
  200. Arguments:
  201. None.
  202. Return Value:
  203. Status code.
  204. --*/
  205. {
  206. INITIALIZE_LIST_HEAD(&PsProcessGroupList);
  207. ASSERT(PsProcessGroupListLock == NULL);
  208. PsProcessGroupListLock = KeCreateQueuedLock();
  209. if (PsProcessGroupListLock == NULL) {
  210. return STATUS_INSUFFICIENT_RESOURCES;
  211. }
  212. return STATUS_SUCCESS;
  213. }
  214. KSTATUS
  215. PspJoinProcessGroup (
  216. PKPROCESS Process,
  217. PROCESS_GROUP_ID ProcessGroupId,
  218. BOOL NewSession
  219. )
  220. /*++
  221. Routine Description:
  222. This routine creates a new process group and places the given process in it.
  223. Arguments:
  224. Process - Supplies a pointer to the process to move to the new process
  225. group. Again, a process only gets to play this trick once; to play it
  226. again it needs to fork.
  227. ProcessGroupId - Supplies the identifier of the process group to join or
  228. create.
  229. NewSession - Supplies a boolean indicating whether to also join a new
  230. session or not.
  231. Return Value:
  232. Status code.
  233. --*/
  234. {
  235. PKPROCESS CurrentProcess;
  236. PPROCESS_GROUP ExistingGroup;
  237. BOOL GroupLockHeld;
  238. PPROCESS_GROUP NewGroup;
  239. PPROCESS_GROUP OriginalGroup;
  240. PPROCESS_GROUP ProcessGroup;
  241. PROCESS_ID ProcessId;
  242. BOOL ProcessLockHeld;
  243. KSTATUS Status;
  244. CurrentProcess = PsGetCurrentProcess();
  245. GroupLockHeld = FALSE;
  246. NewGroup = NULL;
  247. OriginalGroup = NULL;
  248. ProcessGroup = NULL;
  249. ProcessId = Process->Identifiers.ProcessId;
  250. ProcessLockHeld = FALSE;
  251. //
  252. // If joining a new session, the process group ID better be the process ID.
  253. //
  254. ASSERT((NewSession == FALSE) || (ProcessGroupId == ProcessId));
  255. //
  256. // Fail if the process is not in the same session.
  257. //
  258. if (Process->Identifiers.SessionId !=
  259. CurrentProcess->Identifiers.SessionId) {
  260. return STATUS_PERMISSION_DENIED;
  261. }
  262. //
  263. // Do a quick exit check for success.
  264. //
  265. if (ProcessGroupId == Process->Identifiers.ProcessGroupId) {
  266. return STATUS_SUCCESS;
  267. }
  268. //
  269. // Create a new process group if this process is off to its own group.
  270. //
  271. if (ProcessGroupId == ProcessId) {
  272. NewGroup = MmAllocatePagedPool(sizeof(PROCESS_GROUP),
  273. PS_ALLOCATION_TAG);
  274. if (NewGroup == NULL) {
  275. Status = STATUS_INSUFFICIENT_RESOURCES;
  276. goto JoinProcessGroupEnd;
  277. }
  278. RtlZeroMemory(NewGroup, sizeof(PROCESS_GROUP));
  279. NewGroup->Identifier = ProcessGroupId;
  280. NewGroup->ReferenceCount = 1;
  281. NewGroup->SessionId = Process->Identifiers.SessionId;
  282. INITIALIZE_LIST_HEAD(&(NewGroup->ProcessListHead));
  283. }
  284. //
  285. // Acquire the process group list lock to synchronize with other
  286. // process group and session changes.
  287. //
  288. KeAcquireQueuedLock(PsProcessGroupListLock);
  289. GroupLockHeld = TRUE;
  290. //
  291. // If the process is already a session leader, then do not allow it to
  292. // join a new process group.
  293. //
  294. if (PsIsSessionLeader(Process)) {
  295. Status = STATUS_PERMISSION_DENIED;
  296. goto JoinProcessGroupEnd;
  297. }
  298. //
  299. // Fail if the process is exiting. Exempt new session creation because
  300. // 1) Kernel-initiated processes won't have any threads yet.
  301. // 2) Session creation can only happen by the process itself, so there's
  302. // no way the process can exit.
  303. //
  304. if ((Process->ThreadCount == 0) && (NewSession == FALSE)) {
  305. Status = STATUS_NO_SUCH_PROCESS;
  306. goto JoinProcessGroupEnd;
  307. }
  308. //
  309. // See if it's already done.
  310. //
  311. if (ProcessGroupId == Process->Identifiers.ProcessGroupId) {
  312. Status = STATUS_SUCCESS;
  313. goto JoinProcessGroupEnd;
  314. }
  315. //
  316. // See if this process group exists already.
  317. //
  318. ExistingGroup = PspLookupProcessGroup(ProcessGroupId);
  319. if (ExistingGroup != NULL) {
  320. //
  321. // It's not possible to create a new session from an existing group.
  322. // This is the case that fails process group leaders trying to jump
  323. // sessions, as there will already be a pre-existing group for their
  324. // ID.
  325. //
  326. if (NewSession != FALSE) {
  327. Status = STATUS_PERMISSION_DENIED;
  328. goto JoinProcessGroupEnd;
  329. }
  330. //
  331. // The process group better have something in it if its still
  332. // hanging around.
  333. //
  334. ASSERT(LIST_EMPTY(&(ExistingGroup->ProcessListHead)) == FALSE);
  335. //
  336. // The process can only join the group if its calling process is in
  337. // the same session as a process with the given group ID.
  338. //
  339. if (CurrentProcess->Identifiers.SessionId != ExistingGroup->SessionId) {
  340. ExistingGroup = NULL;
  341. Status = STATUS_PERMISSION_DENIED;
  342. goto JoinProcessGroupEnd;
  343. }
  344. PspProcessGroupAddReference(ExistingGroup);
  345. ProcessGroup = ExistingGroup;
  346. //
  347. // There is no process group by that ID. If it's not trying to change to
  348. // the same ID as itself, then fail.
  349. //
  350. } else {
  351. if (NewGroup == NULL) {
  352. Status = STATUS_PERMISSION_DENIED;
  353. goto JoinProcessGroupEnd;
  354. }
  355. ProcessGroup = NewGroup;
  356. NewGroup = NULL;
  357. }
  358. //
  359. // Move to a new session if desired.
  360. //
  361. if (NewSession != FALSE) {
  362. ProcessGroup->SessionId = ProcessId;
  363. Process->Identifiers.SessionId = ProcessId;
  364. //
  365. // Clear the controlling terminal. The child process' controlling
  366. // terminals would only need to be cleared if this process was a
  367. // session leader. It is not, otherwise it could not become one now.
  368. //
  369. Process->ControllingTerminal = NULL;
  370. } else {
  371. //
  372. // If not creating a new session, moving process groups shouldn't
  373. // jump sessions.
  374. //
  375. ASSERT(ProcessGroup->SessionId == Process->Identifiers.SessionId);
  376. }
  377. //
  378. // Acquire the process lock and make sure that it has not executed an image
  379. // if there is no new session and it is not the current process.
  380. //
  381. KeAcquireQueuedLock(Process->QueuedLock);
  382. ProcessLockHeld = TRUE;
  383. if ((Process != CurrentProcess) &&
  384. ((Process->Flags & PROCESS_FLAG_EXECUTED_IMAGE) != 0)) {
  385. Status = STATUS_ACCESS_DENIED;
  386. goto JoinProcessGroupEnd;
  387. }
  388. //
  389. // If the process joining the new group brings with it a parent not
  390. // in the group but in the session, then the process group has a new tie to
  391. // the outside.
  392. //
  393. // Note that the parent's identifiers are only still valid if its
  394. // process group pointer is not NULL. It may be that the parent is on
  395. // its way out, having left its process group, but not quite orphaned
  396. // its children.
  397. //
  398. if ((Process->Parent != NULL) &&
  399. (Process->Parent->ProcessGroup != NULL) &&
  400. (Process->Parent->Identifiers.SessionId == ProcessGroup->SessionId) &&
  401. (Process->Parent->Identifiers.ProcessGroupId !=
  402. ProcessGroup->Identifier)) {
  403. ASSERT(NewSession == FALSE);
  404. ProcessGroup->OutsideParents += 1;
  405. }
  406. //
  407. // Pull the process off the old process group list.
  408. //
  409. OriginalGroup = Process->ProcessGroup;
  410. if (OriginalGroup != NULL) {
  411. LIST_REMOVE(&(Process->ProcessGroupListEntry));
  412. }
  413. //
  414. // If this is a new process group, add it to the global list and the
  415. // session.
  416. //
  417. if (ProcessGroup->ListEntry.Next == NULL) {
  418. INSERT_BEFORE(&(ProcessGroup->ListEntry), &PsProcessGroupList);
  419. }
  420. //
  421. // Add the process to its new process group's list and set the identifiers.
  422. //
  423. INSERT_BEFORE(&(Process->ProcessGroupListEntry),
  424. &(ProcessGroup->ProcessListHead));
  425. Process->ProcessGroup = ProcessGroup;
  426. Process->Identifiers.ProcessGroupId = ProcessGroup->Identifier;
  427. //
  428. // Now that the process has officially switched process groups, release the
  429. // lock and let any execute image attempts proceed.
  430. //
  431. KeReleaseQueuedLock(Process->QueuedLock);
  432. ProcessLockHeld = FALSE;
  433. //
  434. // If the process has left behind a process group, handle that.
  435. //
  436. if (OriginalGroup != NULL) {
  437. PspProcessGroupHandleLeavingProcess(Process,
  438. OriginalGroup,
  439. ProcessGroup);
  440. }
  441. ProcessGroup = NULL;
  442. Status = STATUS_SUCCESS;
  443. JoinProcessGroupEnd:
  444. if (ProcessLockHeld != FALSE) {
  445. KeReleaseQueuedLock(Process->QueuedLock);
  446. }
  447. if (GroupLockHeld != FALSE) {
  448. KeReleaseQueuedLock(PsProcessGroupListLock);
  449. }
  450. if (NewGroup != NULL) {
  451. PspProcessGroupReleaseReference(NewGroup);
  452. }
  453. if (ProcessGroup != NULL) {
  454. ASSERT(ProcessGroup != NewGroup);
  455. PspProcessGroupReleaseReference(ProcessGroup);
  456. }
  457. if (OriginalGroup != NULL) {
  458. PspProcessGroupReleaseReference(OriginalGroup);
  459. }
  460. return Status;
  461. }
  462. VOID
  463. PspAddProcessToParentProcessGroup (
  464. PKPROCESS Process
  465. )
  466. /*++
  467. Routine Description:
  468. This routine adds the given new process to its parent's process group. The
  469. caller cannot have any of the process locks held.
  470. Arguments:
  471. Process - Supplies a pointer to the new process.
  472. Return Value:
  473. None.
  474. --*/
  475. {
  476. PPROCESS_GROUP ProcessGroup;
  477. ASSERT(Process->ProcessGroup == NULL);
  478. ASSERT(Process->Parent != NULL);
  479. //
  480. // The process groups outside parent count does not need changing because
  481. // the new process's parent will always be an inside parent. The child
  482. // inherits the parent's process group and session.
  483. //
  484. KeAcquireQueuedLock(PsProcessGroupListLock);
  485. ProcessGroup = Process->Parent->ProcessGroup;
  486. ASSERT((ProcessGroup != NULL) &&
  487. (Process->Identifiers.ProcessGroupId == ProcessGroup->Identifier) &&
  488. (Process->Identifiers.SessionId == ProcessGroup->SessionId));
  489. INSERT_BEFORE(&(Process->ProcessGroupListEntry),
  490. &(ProcessGroup->ProcessListHead));
  491. Process->ProcessGroup = ProcessGroup;
  492. Process->Identifiers.ProcessGroupId = ProcessGroup->Identifier;
  493. Process->Identifiers.SessionId = ProcessGroup->SessionId;
  494. PspProcessGroupAddReference(ProcessGroup);
  495. KeReleaseQueuedLock(PsProcessGroupListLock);
  496. return;
  497. }
  498. VOID
  499. PspRemoveProcessFromProcessGroup (
  500. PKPROCESS Process
  501. )
  502. /*++
  503. Routine Description:
  504. This routine removes a dying process from its process group, potentially
  505. orphaning its childrens' process groups. The process lock should not be
  506. held by the caller.
  507. Arguments:
  508. Process - Supplies a pointer to the process that's being destroyed.
  509. Return Value:
  510. None.
  511. --*/
  512. {
  513. PPROCESS_GROUP ProcessGroup;
  514. //
  515. // Acquire the process group list lock to prevent the parent or children
  516. // from changing process groups while the process leaves.
  517. //
  518. KeAcquireQueuedLock(PsProcessGroupListLock);
  519. ProcessGroup = Process->ProcessGroup;
  520. ASSERT(ProcessGroup != NULL);
  521. //
  522. // Remove the process from the group list.
  523. //
  524. if (Process->ProcessGroupListEntry.Next != NULL) {
  525. LIST_REMOVE(&(Process->ProcessGroupListEntry));
  526. Process->ProcessGroupListEntry.Next = NULL;
  527. }
  528. //
  529. // Fix up the process group as its process has left.
  530. //
  531. PspProcessGroupHandleLeavingProcess(Process, ProcessGroup, NULL);
  532. Process->ProcessGroup = NULL;
  533. KeReleaseQueuedLock(PsProcessGroupListLock);
  534. PspProcessGroupReleaseReference(ProcessGroup);
  535. return;
  536. }
  537. //
  538. // --------------------------------------------------------- Internal Functions
  539. //
  540. VOID
  541. PspProcessGroupAddReference (
  542. PPROCESS_GROUP ProcessGroup
  543. )
  544. /*++
  545. Routine Description:
  546. This routine increments the reference count on a process group.
  547. Arguments:
  548. ProcessGroup - Supplies a pointer to the process group whose reference
  549. count shall be incremented.
  550. Return Value:
  551. None.
  552. --*/
  553. {
  554. ASSERT((ProcessGroup->ReferenceCount != 0) &&
  555. (ProcessGroup->ReferenceCount < PROCESS_GROUP_MAX_REFERENCE_COUNT));
  556. RtlAtomicAdd32(&(ProcessGroup->ReferenceCount), 1);
  557. return;
  558. }
  559. VOID
  560. PspProcessGroupReleaseReference (
  561. PPROCESS_GROUP ProcessGroup
  562. )
  563. /*++
  564. Routine Description:
  565. This routine decrements the reference count on a process group. If it hits
  566. zero, the process group is destroyed.
  567. Arguments:
  568. ProcessGroup - Supplies a pointer to the process group whose reference
  569. count shall be decremented.
  570. Return Value:
  571. None.
  572. --*/
  573. {
  574. PKPROCESS KernelProcess;
  575. ULONG PreviousValue;
  576. ASSERT((ProcessGroup->ReferenceCount != 0) &&
  577. (ProcessGroup->ReferenceCount < PROCESS_GROUP_MAX_REFERENCE_COUNT));
  578. PreviousValue = RtlAtomicAdd32(&(ProcessGroup->ReferenceCount), -1);
  579. if (PreviousValue == 1) {
  580. ASSERT(LIST_EMPTY(&(ProcessGroup->ProcessListHead)) != FALSE);
  581. if (ProcessGroup->ListEntry.Next != NULL) {
  582. KeAcquireQueuedLock(PsProcessGroupListLock);
  583. if (ProcessGroup->ListEntry.Next != NULL) {
  584. LIST_REMOVE(&(ProcessGroup->ListEntry));
  585. }
  586. KeReleaseQueuedLock(PsProcessGroupListLock);
  587. }
  588. KernelProcess = PsGetKernelProcess();
  589. if (ProcessGroup->Identifier == KernelProcess->Identifiers.ProcessId) {
  590. MmFreeNonPagedPool(ProcessGroup);
  591. } else {
  592. MmFreePagedPool(ProcessGroup);
  593. }
  594. }
  595. return;
  596. }
  597. PPROCESS_GROUP
  598. PspLookupProcessGroup (
  599. PROCESS_GROUP_ID ProcessGroupId
  600. )
  601. /*++
  602. Routine Description:
  603. This routine attempts to find the process group with the given identifier.
  604. This routine assumes the process list lock is already held.
  605. Arguments:
  606. ProcessGroupId - Supplies the identifier of the process group.
  607. Return Value:
  608. Returns a pointer to the process group on success.
  609. NULL if no such process group exists.
  610. --*/
  611. {
  612. PLIST_ENTRY CurrentEntry;
  613. PPROCESS_GROUP ProcessGroup;
  614. ASSERT(KeIsQueuedLockHeld(PsProcessGroupListLock) != FALSE);
  615. CurrentEntry = PsProcessGroupList.Next;
  616. while (CurrentEntry != &PsProcessGroupList) {
  617. ProcessGroup = LIST_VALUE(CurrentEntry, PROCESS_GROUP, ListEntry);
  618. //
  619. // Ignore any process groups that no longer contain any processes. A
  620. // process group is only valid if it contains a process, but process
  621. // groups do not get removed from the global list until after the
  622. // reference count has gone to zero.
  623. //
  624. if ((ProcessGroup->Identifier == ProcessGroupId) &&
  625. (LIST_EMPTY(&(ProcessGroup->ProcessListHead)) == FALSE)) {
  626. break;
  627. }
  628. CurrentEntry = CurrentEntry->Next;
  629. }
  630. if (CurrentEntry == &PsProcessGroupList) {
  631. ProcessGroup = NULL;
  632. }
  633. return ProcessGroup;
  634. }
  635. VOID
  636. PspSignalProcessGroup (
  637. PPROCESS_GROUP ProcessGroup,
  638. ULONG SignalNumber
  639. )
  640. /*++
  641. Routine Description:
  642. This routine sends a signal to every process in the given process group.
  643. This routine assumes that the process group list lock is already held.
  644. Arguments:
  645. ProcessGroup - Supplies a pointer to the process group to signal.
  646. SignalNumber - Supplies the signal to send to each process in the group.
  647. Return Value:
  648. None.
  649. --*/
  650. {
  651. PLIST_ENTRY CurrentEntry;
  652. PKPROCESS Process;
  653. ASSERT(KeIsQueuedLockHeld(PsProcessGroupListLock) != FALSE);
  654. //
  655. // Loop through every process in the list.
  656. //
  657. CurrentEntry = ProcessGroup->ProcessListHead.Next;
  658. while (CurrentEntry != &(ProcessGroup->ProcessListHead)) {
  659. Process = LIST_VALUE(CurrentEntry, KPROCESS, ProcessGroupListEntry);
  660. PsSignalProcess(Process, SignalNumber, NULL);
  661. CurrentEntry = CurrentEntry->Next;
  662. }
  663. return;
  664. }
  665. VOID
  666. PspProcessGroupHandleLeavingProcess (
  667. PKPROCESS Process,
  668. PPROCESS_GROUP OldGroup,
  669. PPROCESS_GROUP NewGroup
  670. )
  671. /*++
  672. Routine Description:
  673. This routine handles a process leaving the given old process group for the
  674. given (optional) new process group. It looks at all of the processes
  675. children and its parent to see if either the new or old group's outside
  676. ties have changed. The caller must not hold any process locks. This routine
  677. assumes the process group list lock is already head.
  678. Arguments:
  679. Process - Supplies a pointer to the process that is leaving the old process
  680. group.
  681. OldGroup - Supplies a pointer to the process group the process is leaving.
  682. NewGroup - Supplies an optional pointer to the process group the process is
  683. joining.
  684. Return Value:
  685. None.
  686. --*/
  687. {
  688. PLIST_ENTRY ChildEntry;
  689. PPROCESS_GROUP ChildGroup;
  690. PKPROCESS ChildProcess;
  691. PLIST_ENTRY CurrentEntry;
  692. BOOL DecrementOutsideParents;
  693. ASSERT(OldGroup != NewGroup);
  694. ASSERT(OldGroup != NULL);
  695. ASSERT(Process->ProcessGroup != NULL);
  696. ASSERT(KeIsQueuedLockHeld(PsProcessGroupListLock) != FALSE);
  697. //
  698. // If the process has no child and no parent, then there is nothing to do.
  699. //
  700. if ((LIST_EMPTY(&(Process->ChildListHead)) != FALSE) &&
  701. (Process->Parent == NULL)) {
  702. return;
  703. }
  704. //
  705. // Acquire the process' queued lock to safely iterate over the children.
  706. //
  707. KeAcquireQueuedLock(Process->QueuedLock);
  708. ChildEntry = Process->ChildListHead.Next;
  709. while (ChildEntry != &(Process->ChildListHead)) {
  710. ChildProcess = LIST_VALUE(ChildEntry, KPROCESS, SiblingListEntry);
  711. ChildEntry = ChildEntry->Next;
  712. //
  713. // A child may not have a process group. It may have already exited and
  714. // cleaned up its process group, but be awaiting destruction and
  715. // removal from its parent's child list. Or it may be a new child and
  716. // is waiting for the parent's process group to transition before
  717. // joining.
  718. //
  719. ChildGroup = ChildProcess->ProcessGroup;
  720. if (ChildGroup == NULL) {
  721. continue;
  722. }
  723. //
  724. // If the old parent group was the same as the child's and the new
  725. // parent group is not NULL, then the child's process group has a new
  726. // outside parent, as long as the new group is in the same session.
  727. //
  728. if (ChildGroup == OldGroup) {
  729. if ((NewGroup != NULL) &&
  730. (NewGroup->SessionId == ChildGroup->SessionId)) {
  731. ChildGroup->OutsideParents += 1;
  732. }
  733. //
  734. // Otherwise if the old parent group was in the same session, then it
  735. // was an outside parent. If the new parent is NULL or has the same
  736. // group as the child or is in a new session, then an outside parent
  737. // was lost.
  738. //
  739. } else if (ChildGroup->SessionId == OldGroup->SessionId) {
  740. if ((NewGroup == NULL) ||
  741. (ChildGroup == NewGroup) ||
  742. (ChildGroup->SessionId != NewGroup->SessionId)) {
  743. ChildGroup->OutsideParents -= 1;
  744. ASSERT(ChildGroup->OutsideParents <= 0x10000000);
  745. //
  746. // If the decremented outside parent count reached 0, then the
  747. // entire process group needs to be signaled if at least one
  748. // process is stopped. It is OK to temporarily release the
  749. // parent lock here and pick up from where the loop left off.
  750. // The child cannot go anywhere: a process does not remove
  751. // itself from it's sibling list until it is destroyed and a
  752. // process cannot be destroyed if it belongs to a process
  753. // group. And a parent does not remove its children until after
  754. // destroying its process group. So, as long as the global
  755. // process group lock is held, all processes in play here are
  756. // stuck.
  757. //
  758. if ((ChildGroup->OutsideParents == 0) &&
  759. (PspIsOrphanedProcessGroupStopped(ChildGroup) != FALSE)) {
  760. //
  761. // The child is still in the group. The process list should
  762. // not be empty.
  763. //
  764. ASSERT(LIST_EMPTY(&(ChildGroup->ProcessListHead)) == FALSE);
  765. KeReleaseQueuedLock(Process->QueuedLock);
  766. PspSignalProcessGroup(ChildGroup,
  767. SIGNAL_CONTROLLING_TERMINAL_CLOSED);
  768. PspSignalProcessGroup(ChildGroup, SIGNAL_CONTINUE);
  769. KeAcquireQueuedLock(Process->QueuedLock);
  770. ASSERT(ChildProcess->ProcessGroup == ChildGroup);
  771. CurrentEntry = ChildProcess->SiblingListEntry.Next;
  772. ASSERT(CurrentEntry != NULL);
  773. }
  774. }
  775. }
  776. }
  777. //
  778. // If the process' parent belonged to a different group in the same
  779. // session, then the old process group has lost an outside tie.
  780. //
  781. // Note that the parent's identifiers are only still valid if its process
  782. // group pointer is not NULL. It may be that the parent is on its way out,
  783. // having left its process group, but not quite orphaned its children.
  784. //
  785. DecrementOutsideParents = FALSE;
  786. if ((Process->Parent != NULL) &&
  787. (Process->Parent->ProcessGroup != NULL) &&
  788. (Process->Parent->Identifiers.SessionId == OldGroup->SessionId) &&
  789. (Process->Parent->Identifiers.ProcessGroupId !=
  790. OldGroup->Identifier)) {
  791. ASSERT(Process->Parent->ProcessGroup->SessionId ==
  792. OldGroup->SessionId);
  793. ASSERT(Process->Parent->ProcessGroup != OldGroup);
  794. DecrementOutsideParents = TRUE;
  795. }
  796. KeReleaseQueuedLock(Process->QueuedLock);
  797. //
  798. // If an outside parent left the old group, decrement the count. If it goes
  799. // to zero and there is a stopped process in the group, signal the group.
  800. //
  801. if (DecrementOutsideParents != FALSE) {
  802. OldGroup->OutsideParents -= 1;
  803. ASSERT(OldGroup->OutsideParents <= 0x10000000);
  804. if ((OldGroup->OutsideParents == 0) &&
  805. (PspIsOrphanedProcessGroupStopped(OldGroup) != FALSE)) {
  806. PspSignalProcessGroup(OldGroup, SIGNAL_CONTROLLING_TERMINAL_CLOSED);
  807. PspSignalProcessGroup(OldGroup, SIGNAL_CONTINUE);
  808. }
  809. }
  810. return;
  811. }
  812. BOOL
  813. PspIsOrphanedProcessGroupStopped (
  814. PPROCESS_GROUP ProcessGroup
  815. )
  816. /*++
  817. Routine Description:
  818. This routine determines if the given process group contains a stopped
  819. process. It assumes the process group list lock is held.
  820. Arguments:
  821. ProcessGroup - Supplies a pointer to a process group.
  822. Return Value:
  823. Returns TRUE if there is a process in the group that has stopped. Returns
  824. FALSE otherwise.
  825. --*/
  826. {
  827. PLIST_ENTRY CurrentEntry;
  828. PKPROCESS Process;
  829. BOOL Stopped;
  830. ASSERT(KeIsQueuedLockHeld(PsProcessGroupListLock) != FALSE);
  831. ASSERT(ProcessGroup->OutsideParents == 0);
  832. //
  833. // Make sure that one of the processes is stopped.
  834. //
  835. Stopped = FALSE;
  836. CurrentEntry = ProcessGroup->ProcessListHead.Next;
  837. while (CurrentEntry != &(ProcessGroup->ProcessListHead)) {
  838. Process = LIST_VALUE(CurrentEntry, KPROCESS, ProcessGroupListEntry);
  839. if (Process->StoppedThreadCount != 0) {
  840. Stopped = TRUE;
  841. break;
  842. }
  843. CurrentEntry = CurrentEntry->Next;
  844. }
  845. return Stopped;
  846. }