Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3439 lines
110 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. psjob.c
  5. Abstract:
  6. This module implements bulk of the job object support
  7. Author:
  8. Mark Lucovsky (markl) 22-May-1997
  9. Revision History:
  10. --*/
  11. #include "psp.h"
  12. #include "winerror.h"
  13. #pragma alloc_text(INIT, PspInitializeJobStructures)
  14. #pragma alloc_text(INIT, PspInitializeJobStructuresPhase1)
  15. #pragma alloc_text(PAGE, NtCreateJobObject)
  16. #pragma alloc_text(PAGE, NtOpenJobObject)
  17. #pragma alloc_text(PAGE, NtAssignProcessToJobObject)
  18. #pragma alloc_text(PAGE, NtQueryInformationJobObject)
  19. #pragma alloc_text(PAGE, NtSetInformationJobObject)
  20. #pragma alloc_text(PAGE, NtTerminateJobObject)
  21. #pragma alloc_text(PAGE, NtIsProcessInJob)
  22. #pragma alloc_text(PAGE, NtCreateJobSet)
  23. #pragma alloc_text(PAGE, PspJobDelete)
  24. #pragma alloc_text(PAGE, PspJobClose)
  25. #pragma alloc_text(PAGE, PspAddProcessToJob)
  26. #pragma alloc_text(PAGE, PspRemoveProcessFromJob)
  27. #pragma alloc_text(PAGE, PspExitProcessFromJob)
  28. #pragma alloc_text(PAGE, PspApplyJobLimitsToProcessSet)
  29. #pragma alloc_text(PAGE, PspApplyJobLimitsToProcess)
  30. #pragma alloc_text(PAGE, PspTerminateAllProcessesInJob)
  31. #pragma alloc_text(PAGE, PspFoldProcessAccountingIntoJob)
  32. #pragma alloc_text(PAGE, PspCaptureTokenFilter)
  33. #pragma alloc_text(PAGE, PsReportProcessMemoryLimitViolation)
  34. #pragma alloc_text(PAGE, PspJobTimeLimitsWork)
  35. #pragma alloc_text(PAGE, PsEnforceExecutionTimeLimits)
  36. #pragma alloc_text(PAGE, PspShutdownJobLimits)
  37. #pragma alloc_text(PAGE, PspGetJobFromSet)
  38. #pragma alloc_text(PAGE, PsChangeJobMemoryUsage)
  39. #pragma alloc_text(PAGE, PspWin32SessionCallout)
  40. //
  41. // move to io.h
  42. extern POBJECT_TYPE IoCompletionObjectType;
  43. KDPC PspJobTimeLimitsDpc;
  44. KTIMER PspJobTimeLimitsTimer;
  45. WORK_QUEUE_ITEM PspJobTimeLimitsWorkItem;
  46. FAST_MUTEX PspJobTimeLimitsLock;
  47. BOOLEAN PspJobTimeLimitsShuttingDown;
  48. #define PSP_ONE_SECOND (10 * (1000*1000))
  49. #define PSP_JOB_TIME_LIMITS_TIME -7
  50. #ifdef ALLOC_DATA_PRAGMA
  51. #pragma data_seg("PAGEDATA")
  52. #endif
  53. LARGE_INTEGER PspJobTimeLimitsInterval = {0};
  54. #ifdef ALLOC_DATA_PRAGMA
  55. #pragma data_seg()
  56. #endif
  57. NTSTATUS
  58. NTAPI
  59. NtCreateJobObject (
  60. OUT PHANDLE JobHandle,
  61. IN ACCESS_MASK DesiredAccess,
  62. IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
  63. )
  64. {
  65. PEJOB Job;
  66. HANDLE Handle;
  67. KPROCESSOR_MODE PreviousMode;
  68. NTSTATUS Status;
  69. PETHREAD CurrentThread;
  70. PAGED_CODE();
  71. //
  72. // Establish an exception handler, probe the output handle address, and
  73. // attempt to create a job object. If the probe fails, then return the
  74. // exception code as the service status. Otherwise return the status value
  75. // returned by the object insertion routine.
  76. //
  77. CurrentThread = PsGetCurrentThread ();
  78. PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  79. try {
  80. //
  81. // Probe output handle address if
  82. // necessary.
  83. //
  84. if (PreviousMode != KernelMode) {
  85. ProbeForWriteHandle (JobHandle);
  86. }
  87. *JobHandle = NULL;
  88. } except (ExSystemExceptionFilter ()) {
  89. return GetExceptionCode();
  90. }
  91. //
  92. // Allocate job object.
  93. //
  94. Status = ObCreateObject (PreviousMode,
  95. PsJobType,
  96. ObjectAttributes,
  97. PreviousMode,
  98. NULL,
  99. sizeof (EJOB),
  100. 0,
  101. 0,
  102. &Job);
  103. //
  104. // If the job object was successfully allocated, then initialize it
  105. // and attempt to insert the job object in the current
  106. // process' handle table.
  107. //
  108. if (NT_SUCCESS(Status)) {
  109. RtlZeroMemory (Job, sizeof (EJOB));
  110. InitializeListHead (&Job->ProcessListHead);
  111. InitializeListHead (&Job->JobSetLinks);
  112. KeInitializeEvent (&Job->Event, NotificationEvent, FALSE);
  113. ExInitializeFastMutex (&Job->MemoryLimitsLock);
  114. //
  115. // Job Object gets the SessionId of the Process creating the Job
  116. // We will use this sessionid to restrict the processes that can
  117. // be added to a job.
  118. //
  119. Job->SessionId = MmGetSessionId (PsGetCurrentProcessByThread (CurrentThread));
  120. //
  121. // Initialize the scheduling class for the Job
  122. //
  123. Job->SchedulingClass = PSP_DEFAULT_SCHEDULING_CLASSES;
  124. ExInitializeResourceLite (&Job->JobLock);
  125. ExAcquireFastMutex (&PspJobListLock);
  126. InsertTailList (&PspJobList, &Job->JobLinks);
  127. ExReleaseFastMutex (&PspJobListLock);
  128. Status = ObInsertObject (Job,
  129. NULL,
  130. DesiredAccess,
  131. 0,
  132. NULL,
  133. &Handle);
  134. //
  135. // If the job object was successfully inserted in the current
  136. // process' handle table, then attempt to write the job object
  137. // handle value.
  138. //
  139. if (NT_SUCCESS (Status)) {
  140. try {
  141. *JobHandle = Handle;
  142. } except (ExSystemExceptionFilter ()) {
  143. Status = GetExceptionCode ();
  144. }
  145. }
  146. }
  147. //
  148. // Return service status.
  149. //
  150. return Status;
  151. }
  152. NTSTATUS
  153. NTAPI
  154. NtOpenJobObject(
  155. OUT PHANDLE JobHandle,
  156. IN ACCESS_MASK DesiredAccess,
  157. IN POBJECT_ATTRIBUTES ObjectAttributes
  158. )
  159. {
  160. HANDLE Handle;
  161. KPROCESSOR_MODE PreviousMode;
  162. NTSTATUS Status;
  163. PAGED_CODE();
  164. //
  165. // Establish an exception handler, probe the output handle address, and
  166. // attempt to open the job object. If the probe fails, then return the
  167. // exception code as the service status. Otherwise return the status value
  168. // returned by the object open routine.
  169. //
  170. PreviousMode = KeGetPreviousMode ();
  171. if (PreviousMode != KernelMode) {
  172. try {
  173. //
  174. // Probe output handle address
  175. // if necessary.
  176. //
  177. ProbeForWriteHandle (JobHandle);
  178. } except (EXCEPTION_EXECUTE_HANDLER) {
  179. //
  180. // If an exception occurs during the probe of the output job handle,
  181. // then always handle the exception and return the exception code as the
  182. // status value.
  183. //
  184. return GetExceptionCode ();
  185. }
  186. }
  187. //
  188. // Open handle to the event object with the specified desired access.
  189. //
  190. Status = ObOpenObjectByName (ObjectAttributes,
  191. PsJobType,
  192. PreviousMode,
  193. NULL,
  194. DesiredAccess,
  195. NULL,
  196. &Handle);
  197. //
  198. // If the open was successful, then attempt to write the job object
  199. // handle value. If the write attempt fails then just report an error.
  200. // When the caller attempts to access the handle value, an
  201. // access violation will occur.
  202. //
  203. if (NT_SUCCESS (Status)) {
  204. try {
  205. *JobHandle = Handle;
  206. } except(ExSystemExceptionFilter ()) {
  207. return GetExceptionCode ();
  208. }
  209. }
  210. return Status;
  211. }
  212. NTSTATUS
  213. NTAPI
  214. NtAssignProcessToJobObject(
  215. IN HANDLE JobHandle,
  216. IN HANDLE ProcessHandle
  217. )
  218. {
  219. PEJOB Job;
  220. PEPROCESS Process;
  221. PETHREAD CurrentThread;
  222. NTSTATUS Status, Status1;
  223. KPROCESSOR_MODE PreviousMode;
  224. BOOLEAN IsAdmin;
  225. PACCESS_TOKEN JobToken, NewToken = NULL;
  226. PAGED_CODE();
  227. CurrentThread = PsGetCurrentThread ();
  228. PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  229. //
  230. // Now reference the job object. Then we need to lock the process and check again
  231. //
  232. Status = ObReferenceObjectByHandle (JobHandle,
  233. JOB_OBJECT_ASSIGN_PROCESS,
  234. PsJobType,
  235. PreviousMode,
  236. &Job,
  237. NULL);
  238. if (!NT_SUCCESS (Status)) {
  239. return Status;
  240. }
  241. JobToken = Job->Token;
  242. //
  243. // Reference the process object, lock the process, test for already been assigned
  244. //
  245. Status = ObReferenceObjectByHandle (ProcessHandle,
  246. PROCESS_SET_QUOTA | PROCESS_TERMINATE |
  247. ((JobToken != NULL)?PROCESS_SET_INFORMATION:0),
  248. PsProcessType,
  249. PreviousMode,
  250. &Process,
  251. NULL);
  252. if (!NT_SUCCESS (Status)) {
  253. ObDereferenceObject (Job);
  254. return Status;
  255. }
  256. //
  257. // Quick Check for prior assignment
  258. //
  259. if (Process->Job) {
  260. Status = STATUS_ACCESS_DENIED;
  261. goto deref_and_return_status;
  262. }
  263. //
  264. // We only allow a process that is running in the Job creator's hydra session
  265. // to be assigned to the job.
  266. //
  267. if (MmGetSessionId (Process) != Job->SessionId) {
  268. Status = STATUS_ACCESS_DENIED;
  269. goto deref_and_return_status;
  270. }
  271. //
  272. // Security Rules: If the job has no-admin set, and it is running
  273. // as admin, that's not allowed
  274. //
  275. if (Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_NO_ADMIN) {
  276. PACCESS_TOKEN Token;
  277. Token = PsReferencePrimaryToken (Process);
  278. IsAdmin = SeTokenIsAdmin (Token);
  279. PsDereferencePrimaryTokenEx (Process, Token);
  280. if (IsAdmin) {
  281. Status = STATUS_ACCESS_DENIED;
  282. goto deref_and_return_status;
  283. }
  284. }
  285. //
  286. // Duplicate the primary token so we can assign it to the process.
  287. //
  288. if (JobToken != NULL) {
  289. Status = SeSubProcessToken (JobToken,
  290. &NewToken,
  291. FALSE);
  292. if (!NT_SUCCESS (Status)) {
  293. goto deref_and_return_status;
  294. }
  295. }
  296. if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
  297. Status = STATUS_PROCESS_IS_TERMINATING;
  298. if (JobToken != NULL) {
  299. ObDereferenceObject (NewToken);
  300. }
  301. goto deref_and_return_status;
  302. }
  303. //
  304. // ref the job for the process
  305. //
  306. ObReferenceObject (Job);
  307. if (InterlockedCompareExchangePointer (&Process->Job, Job, NULL) != NULL) {
  308. ExReleaseRundownProtection (&Process->RundownProtect);
  309. ObDereferenceObject (Process);
  310. ObDereferenceObjectEx (Job, 2);
  311. if (JobToken != NULL) {
  312. ObDereferenceObject (NewToken);
  313. }
  314. return STATUS_ACCESS_DENIED;
  315. }
  316. //
  317. // If the job has a token filter established,
  318. // use it to filter the
  319. //
  320. ExReleaseRundownProtection (&Process->RundownProtect);
  321. Status = PspAddProcessToJob (Job, Process);
  322. if (!NT_SUCCESS (Status)) {
  323. Status1 = PspTerminateProcess (Process, ERROR_NOT_ENOUGH_QUOTA);
  324. if (NT_SUCCESS (Status1)) {
  325. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  326. ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);
  327. Job->TotalTerminatedProcesses++;
  328. ExReleaseResourceLite (&Job->JobLock);
  329. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  330. }
  331. }
  332. //
  333. // If the job has UI restrictions and this is a GUI process, call ntuser
  334. //
  335. if ((Job->UIRestrictionsClass != JOB_OBJECT_UILIMIT_NONE) &&
  336. (Process->Win32Process != NULL)) {
  337. WIN32_JOBCALLOUT_PARAMETERS Parms;
  338. Parms.Job = Job;
  339. Parms.CalloutType = PsW32JobCalloutAddProcess;
  340. Parms.Data = Process->Win32Process;
  341. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  342. ExAcquireResourceExclusiveLite(&Job->JobLock, TRUE);
  343. PspWin32SessionCallout(PspW32JobCallout, &Parms, Job->SessionId);
  344. ExReleaseResourceLite (&Job->JobLock);
  345. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  346. }
  347. if (JobToken != NULL) {
  348. Status1 = PspSetPrimaryToken (NULL, Process, NULL, NewToken, TRUE);
  349. ObDereferenceObject (NewToken);
  350. //
  351. // Only bad callers should fail here.
  352. //
  353. ASSERT (NT_SUCCESS (Status1));
  354. }
  355. deref_and_return_status:
  356. ObDereferenceObject (Process);
  357. ObDereferenceObject (Job);
  358. return Status;
  359. }
  360. NTSTATUS
  361. PspAddProcessToJob(
  362. PEJOB Job,
  363. PEPROCESS Process
  364. )
  365. {
  366. NTSTATUS Status;
  367. PETHREAD CurrentThread;
  368. SIZE_T MinWs,MaxWs;
  369. PAGED_CODE();
  370. CurrentThread = PsGetCurrentThread ();
  371. Status = STATUS_SUCCESS;
  372. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  373. ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);
  374. InsertTailList (&Job->ProcessListHead, &Process->JobLinks);
  375. //
  376. // Update relevant ADD accounting info.
  377. //
  378. Job->TotalProcesses++;
  379. Job->ActiveProcesses++;
  380. //
  381. // Test for active process count exceeding limit
  382. //
  383. if ((Job->LimitFlags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS) &&
  384. Job->ActiveProcesses > Job->ActiveProcessLimit) {
  385. PS_SET_CLEAR_BITS (&Process->JobStatus,
  386. PS_JOB_STATUS_NOT_REALLY_ACTIVE | PS_JOB_STATUS_ACCOUNTING_FOLDED,
  387. PS_JOB_STATUS_LAST_REPORT_MEMORY);
  388. Job->ActiveProcesses--;
  389. if (Job->CompletionPort != NULL) {
  390. IoSetIoCompletion (Job->CompletionPort,
  391. Job->CompletionKey,
  392. NULL,
  393. STATUS_SUCCESS,
  394. JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT,
  395. TRUE);
  396. }
  397. Status = STATUS_QUOTA_EXCEEDED;
  398. }
  399. if ((Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME) && KeReadStateEvent (&Job->Event)) {
  400. PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE | PS_JOB_STATUS_ACCOUNTING_FOLDED);
  401. Job->ActiveProcesses--;
  402. Status = STATUS_QUOTA_EXCEEDED;
  403. }
  404. //
  405. // If the last handle to the job has been closed and the kill on close option is set
  406. // we don't let new processes enter the job. This is to make cleanup solid.
  407. //
  408. if (PS_TEST_ALL_BITS_SET (Job->JobFlags, PS_JOB_FLAGS_CLOSE_DONE|JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)) {
  409. Job->ActiveProcesses--;
  410. Status = STATUS_INVALID_PARAMETER;
  411. }
  412. if (Status == STATUS_SUCCESS) {
  413. PspApplyJobLimitsToProcess (Job, Process);
  414. if (Job->CompletionPort != NULL &&
  415. Process->UniqueProcessId &&
  416. !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) &&
  417. !(Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED)) {
  418. PS_SET_CLEAR_BITS (&Process->JobStatus,
  419. PS_JOB_STATUS_NEW_PROCESS_REPORTED,
  420. PS_JOB_STATUS_LAST_REPORT_MEMORY);
  421. IoSetIoCompletion (Job->CompletionPort,
  422. Job->CompletionKey,
  423. (PVOID)Process->UniqueProcessId,
  424. STATUS_SUCCESS,
  425. JOB_OBJECT_MSG_NEW_PROCESS,
  426. FALSE);
  427. }
  428. }
  429. if (Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) {
  430. MinWs = Job->MinimumWorkingSetSize;
  431. MaxWs = Job->MaximumWorkingSetSize;
  432. } else {
  433. MinWs = 0;
  434. MaxWs = 0;
  435. }
  436. ExReleaseResourceLite (&Job->JobLock);
  437. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  438. if (Status == STATUS_SUCCESS) {
  439. if (MinWs != 0 && MaxWs != 0) {
  440. KAPC_STATE ApcState;
  441. KeStackAttachProcess (&Process->Pcb, &ApcState);
  442. ExAcquireFastMutex (&PspWorkingSetChangeHead.Lock);
  443. MmAdjustWorkingSetSize (MinWs,MaxWs,FALSE,TRUE);
  444. //
  445. // call MM to Enable hard workingset
  446. //
  447. MmEnforceWorkingSetLimit (&Process->Vm, TRUE);
  448. ExReleaseFastMutex (&PspWorkingSetChangeHead.Lock);
  449. KeUnstackDetachProcess (&ApcState);
  450. } else {
  451. MmEnforceWorkingSetLimit (&Process->Vm, FALSE);
  452. }
  453. if (!MmAssignProcessToJob (Process)) {
  454. Status = STATUS_QUOTA_EXCEEDED;
  455. }
  456. }
  457. return Status;
  458. }
  459. //
  460. // Only callable from process delete routine !
  461. // This means that if the above fails, failure is termination of the process !
  462. //
  463. VOID
  464. PspRemoveProcessFromJob(
  465. PEJOB Job,
  466. PEPROCESS Process
  467. )
  468. {
  469. PETHREAD CurrentThread;
  470. PAGED_CODE();
  471. CurrentThread = PsGetCurrentThread ();
  472. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  473. ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);
  474. RemoveEntryList (&Process->JobLinks);
  475. //
  476. // Update REMOVE accounting info
  477. //
  478. if (!(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE)) {
  479. Job->ActiveProcesses--;
  480. PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE);
  481. }
  482. PspFoldProcessAccountingIntoJob (Job, Process);
  483. ExReleaseResourceLite (&Job->JobLock);
  484. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  485. }
  486. VOID
  487. PspExitProcessFromJob(
  488. PEJOB Job,
  489. PEPROCESS Process
  490. )
  491. {
  492. PETHREAD CurrentThread;
  493. PAGED_CODE();
  494. CurrentThread = PsGetCurrentThread ();
  495. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  496. ExAcquireResourceExclusiveLite(&Job->JobLock, TRUE);
  497. //
  498. // Update REMOVE accounting info
  499. //
  500. if (!(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE)) {
  501. Job->ActiveProcesses--;
  502. PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE);
  503. }
  504. PspFoldProcessAccountingIntoJob(Job,Process);
  505. ExReleaseResourceLite(&Job->JobLock);
  506. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  507. }
  508. VOID
  509. PspJobDelete(
  510. IN PVOID Object
  511. )
  512. {
  513. PEJOB Job, tJob;
  514. WIN32_JOBCALLOUT_PARAMETERS Parms;
  515. PPS_JOB_TOKEN_FILTER Filter;
  516. PETHREAD CurrentThread;
  517. PAGED_CODE();
  518. Job = (PEJOB) Object;
  519. //
  520. // call ntuser to delete its job structure
  521. //
  522. Parms.Job = Job;
  523. Parms.CalloutType = PsW32JobCalloutTerminate;
  524. Parms.Data = NULL;
  525. CurrentThread = PsGetCurrentThread ();
  526. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  527. ExAcquireResourceExclusiveLite(&Job->JobLock, TRUE);
  528. PspWin32SessionCallout(PspW32JobCallout, &Parms, Job->SessionId);
  529. ExReleaseResourceLite(&Job->JobLock);
  530. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  531. Job->LimitFlags = 0;
  532. if (Job->CompletionPort != NULL) {
  533. ObDereferenceObject(Job->CompletionPort);
  534. Job->CompletionPort = NULL;
  535. }
  536. //
  537. // Remove Job on Job List and job set
  538. //
  539. tJob = NULL;
  540. ExAcquireFastMutex (&PspJobListLock);
  541. RemoveEntryList (&Job->JobLinks);
  542. //
  543. // If we are part of a jobset then we must be the pinning job. We must pass on the pin to the next
  544. // job in the chain.
  545. //
  546. if (!IsListEmpty (&Job->JobSetLinks)) {
  547. tJob = CONTAINING_RECORD (Job->JobSetLinks.Flink, EJOB, JobSetLinks);
  548. RemoveEntryList (&Job->JobSetLinks);
  549. }
  550. ExReleaseFastMutex (&PspJobListLock);
  551. //
  552. // Removing the pin from the job set can cause a cascade of deletes that would cause a stack overflow
  553. // as we recursed at this point. We break recursion by forcing the defered delete path here.
  554. //
  555. if (tJob != NULL) {
  556. ObDereferenceObjectDeferDelete (tJob);
  557. }
  558. //
  559. // Free Security clutter:
  560. //
  561. if (Job->Token != NULL) {
  562. ObDereferenceObject (Job->Token);
  563. Job->Token = NULL;
  564. }
  565. Filter = Job->Filter;
  566. if (Filter != NULL) {
  567. if (Filter->CapturedSids != NULL) {
  568. ExFreePool (Filter->CapturedSids);
  569. }
  570. if (Filter->CapturedPrivileges != NULL) {
  571. ExFreePool (Filter->CapturedPrivileges);
  572. }
  573. if (Filter->CapturedGroups != NULL) {
  574. ExFreePool (Filter->CapturedGroups);
  575. }
  576. ExFreePool (Filter);
  577. }
  578. ExDeleteResourceLite (&Job->JobLock);
  579. }
  580. VOID
  581. PspJobClose (
  582. IN PEPROCESS Process,
  583. IN PVOID Object,
  584. IN ACCESS_MASK GrantedAccess,
  585. IN ULONG ProcessHandleCount,
  586. IN ULONG SystemHandleCount
  587. )
  588. /*++
  589. Routine Description:
  590. Called by the object manager when a handle is closed to the object.
  591. Arguments:
  592. Process - Process doing the close
  593. Object - Job object being closed
  594. GrantedAccess - Access ranted for this handle
  595. ProcessHandleCount - Unused and unmaintained by OB
  596. SystemHandleCount - Current handle count for this object
  597. Return Value:
  598. None.
  599. --*/
  600. {
  601. PEJOB Job = Object;
  602. PVOID Port;
  603. PETHREAD CurrentThread;
  604. PAGED_CODE ();
  605. UNREFERENCED_PARAMETER (Process);
  606. UNREFERENCED_PARAMETER (GrantedAccess);
  607. UNREFERENCED_PARAMETER (ProcessHandleCount);
  608. //
  609. // If this isn't the last handle then do nothing.
  610. //
  611. if (SystemHandleCount > 1) {
  612. return;
  613. }
  614. CurrentThread = PsGetCurrentThread ();
  615. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  616. ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);
  617. //
  618. // Mark the job has having its last handle closed.
  619. // This is used to prevent new processes entering a job
  620. // marked as terminate on close and also prevents a completion
  621. // port being set on a torn down job. Completion ports
  622. // are removed on last handle close.
  623. //
  624. PS_SET_BITS (&Job->JobFlags, PS_JOB_FLAGS_CLOSE_DONE);
  625. if (Job->LimitFlags&JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) {
  626. PspTerminateAllProcessesInJob (Job, STATUS_SUCCESS, FALSE);
  627. }
  628. ExAcquireFastMutex (&Job->MemoryLimitsLock);
  629. //
  630. // Release the completion port
  631. //
  632. Port = Job->CompletionPort;
  633. Job->CompletionPort = NULL;
  634. ExReleaseFastMutex (&Job->MemoryLimitsLock);
  635. ExReleaseResourceLite (&Job->JobLock);
  636. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  637. if (Port != NULL) {
  638. ObDereferenceObject (Port);
  639. }
  640. }
  641. #ifdef ALLOC_DATA_PRAGMA
  642. #pragma const_seg("PAGECONST")
  643. #endif
  644. const ULONG PspJobInfoLengths[] = {
  645. sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), // JobObjectBasicAccountingInformation
  646. sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION), // JobObjectBasicLimitInformation
  647. sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST), // JobObjectBasicProcessIdList
  648. sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS), // JobObjectBasicUIRestrictions
  649. sizeof(JOBOBJECT_SECURITY_LIMIT_INFORMATION), // JobObjectSecurityLimitInformation
  650. sizeof(JOBOBJECT_END_OF_JOB_TIME_INFORMATION), // JobObjectEndOfJobTimeInformation
  651. sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT), // JobObjectAssociateCompletionPortInformation
  652. sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION), // JobObjectBasicAndIoAccountingInformation
  653. sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), // JobObjectExtendedLimitInformation
  654. sizeof(JOBOBJECT_JOBSET_INFORMATION), // JobObjectJobSetInformation
  655. 0
  656. };
  657. const ULONG PspJobInfoAlign[] = {
  658. sizeof(ULONG), // JobObjectBasicAccountingInformation
  659. sizeof(ULONG), // JobObjectBasicLimitInformation
  660. sizeof(ULONG), // JobObjectBasicProcessIdList
  661. sizeof(ULONG), // JobObjectBasicUIRestrictions
  662. sizeof(ULONG), // JobObjectSecurityLimitInformation
  663. sizeof(ULONG), // JobObjectEndOfJobTimeInformation
  664. sizeof(PVOID), // JobObjectAssociateCompletionPortInformation
  665. sizeof(ULONG), // JobObjectBasicAndIoAccountingInformation
  666. sizeof(ULONG), // JobObjectExtendedLimitInformation
  667. TYPE_ALIGNMENT (JOBOBJECT_JOBSET_INFORMATION), // JobObjectJobSetInformation
  668. 0
  669. };
  670. NTSTATUS
  671. NtQueryInformationJobObject(
  672. IN HANDLE JobHandle,
  673. IN JOBOBJECTINFOCLASS JobObjectInformationClass,
  674. OUT PVOID JobObjectInformation,
  675. IN ULONG JobObjectInformationLength,
  676. OUT PULONG ReturnLength OPTIONAL
  677. )
  678. {
  679. PEJOB Job;
  680. KPROCESSOR_MODE PreviousMode;
  681. JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION AccountingInfo;
  682. JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimitInfo;
  683. JOBOBJECT_BASIC_UI_RESTRICTIONS BasicUIRestrictions;
  684. JOBOBJECT_SECURITY_LIMIT_INFORMATION SecurityLimitInfo;
  685. JOBOBJECT_JOBSET_INFORMATION JobSetInformation;
  686. JOBOBJECT_END_OF_JOB_TIME_INFORMATION EndOfJobInfo;
  687. NTSTATUS st=STATUS_SUCCESS;
  688. ULONG RequiredLength, RequiredAlign, ActualReturnLength;
  689. PVOID ReturnData=NULL;
  690. PEPROCESS Process;
  691. PLIST_ENTRY Next;
  692. LARGE_INTEGER UserTime, KernelTime;
  693. PULONG_PTR NextProcessIdSlot;
  694. ULONG WorkingLength;
  695. PJOBOBJECT_BASIC_PROCESS_ID_LIST IdList;
  696. PUCHAR CurrentOffset;
  697. PTOKEN_GROUPS WorkingGroup;
  698. PTOKEN_PRIVILEGES WorkingPrivs;
  699. ULONG RemainingSidBuffer;
  700. PSID TargetSidBuffer;
  701. PSID RemainingSid;
  702. BOOLEAN AlreadyCopied;
  703. PPS_JOB_TOKEN_FILTER Filter;
  704. PETHREAD CurrentThread;
  705. PAGED_CODE();
  706. CurrentThread = PsGetCurrentThread ();
  707. //
  708. // Get previous processor mode and probe output argument if necessary.
  709. //
  710. if (JobObjectInformationClass >= MaxJobObjectInfoClass || JobObjectInformationClass <= 0) {
  711. return STATUS_INVALID_INFO_CLASS;
  712. }
  713. RequiredLength = PspJobInfoLengths[JobObjectInformationClass-1];
  714. RequiredAlign = PspJobInfoAlign[JobObjectInformationClass-1];
  715. ActualReturnLength = RequiredLength;
  716. if (JobObjectInformationLength != RequiredLength) {
  717. //
  718. // BasicProcessIdList is variable length, so make sure header is
  719. // ok. Can not enforce an exact match ! Security Limits can be
  720. // as well, due to the token groups and privs
  721. //
  722. if ((JobObjectInformationClass == JobObjectBasicProcessIdList) ||
  723. (JobObjectInformationClass == JobObjectSecurityLimitInformation) ) {
  724. if (JobObjectInformationLength < RequiredLength) {
  725. return STATUS_INFO_LENGTH_MISMATCH;
  726. } else {
  727. RequiredLength = JobObjectInformationLength;
  728. }
  729. } else {
  730. return STATUS_INFO_LENGTH_MISMATCH;
  731. }
  732. }
  733. PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  734. if (PreviousMode != KernelMode) {
  735. try {
  736. ProbeForWrite(
  737. JobObjectInformation,
  738. JobObjectInformationLength,
  739. RequiredAlign
  740. );
  741. if (ARGUMENT_PRESENT (ReturnLength)) {
  742. ProbeForWriteUlong (ReturnLength);
  743. }
  744. } except (EXCEPTION_EXECUTE_HANDLER) {
  745. return GetExceptionCode ();
  746. }
  747. }
  748. //
  749. // reference the job
  750. //
  751. if (ARGUMENT_PRESENT (JobHandle)) {
  752. st = ObReferenceObjectByHandle(
  753. JobHandle,
  754. JOB_OBJECT_QUERY,
  755. PsJobType,
  756. PreviousMode,
  757. (PVOID *)&Job,
  758. NULL
  759. );
  760. if (!NT_SUCCESS (st)) {
  761. return st;
  762. }
  763. } else {
  764. //
  765. // if the current process has a job, NULL means the job of the
  766. // current process. Query is always allowed for this case
  767. //
  768. Process = PsGetCurrentProcessByThread(CurrentThread);
  769. if (Process->Job != NULL) {
  770. Job = Process->Job;
  771. ObReferenceObject(Job);
  772. } else {
  773. return STATUS_ACCESS_DENIED;
  774. }
  775. }
  776. AlreadyCopied = FALSE ;
  777. //
  778. // Check argument validity.
  779. //
  780. switch ( JobObjectInformationClass ) {
  781. case JobObjectBasicAccountingInformation:
  782. case JobObjectBasicAndIoAccountingInformation:
  783. //
  784. // These two cases are identical, EXCEPT that with AndIo, IO information
  785. // is returned as well, but the first part of the local is identical to
  786. // basic, and the shorter return'd data length chops what we return.
  787. //
  788. RtlZeroMemory (&AccountingInfo.IoInfo,sizeof(AccountingInfo.IoInfo));
  789. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  790. ExAcquireResourceSharedLite (&Job->JobLock, TRUE);
  791. AccountingInfo.BasicInfo.TotalUserTime = Job->TotalUserTime;
  792. AccountingInfo.BasicInfo.TotalKernelTime = Job->TotalKernelTime;
  793. AccountingInfo.BasicInfo.ThisPeriodTotalUserTime = Job->ThisPeriodTotalUserTime;
  794. AccountingInfo.BasicInfo.ThisPeriodTotalKernelTime = Job->ThisPeriodTotalKernelTime;
  795. AccountingInfo.BasicInfo.TotalPageFaultCount = Job->TotalPageFaultCount;
  796. AccountingInfo.BasicInfo.TotalProcesses = Job->TotalProcesses;
  797. AccountingInfo.BasicInfo.ActiveProcesses = Job->ActiveProcesses;
  798. AccountingInfo.BasicInfo.TotalTerminatedProcesses = Job->TotalTerminatedProcesses;
  799. AccountingInfo.IoInfo.ReadOperationCount = Job->ReadOperationCount;
  800. AccountingInfo.IoInfo.WriteOperationCount = Job->WriteOperationCount;
  801. AccountingInfo.IoInfo.OtherOperationCount = Job->OtherOperationCount;
  802. AccountingInfo.IoInfo.ReadTransferCount = Job->ReadTransferCount;
  803. AccountingInfo.IoInfo.WriteTransferCount = Job->WriteTransferCount;
  804. AccountingInfo.IoInfo.OtherTransferCount = Job->OtherTransferCount;
  805. //
  806. // Add in the time and page faults for each process
  807. //
  808. Next = Job->ProcessListHead.Flink;
  809. while ( Next != &Job->ProcessListHead) {
  810. Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks));
  811. if (!(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED)) {
  812. UserTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,KeMaximumIncrement);
  813. KernelTime.QuadPart = UInt32x32To64(Process->Pcb.KernelTime,KeMaximumIncrement);
  814. AccountingInfo.BasicInfo.TotalUserTime.QuadPart += UserTime.QuadPart;
  815. AccountingInfo.BasicInfo.TotalKernelTime.QuadPart += KernelTime.QuadPart;
  816. AccountingInfo.BasicInfo.ThisPeriodTotalUserTime.QuadPart += UserTime.QuadPart;
  817. AccountingInfo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart += KernelTime.QuadPart;
  818. AccountingInfo.BasicInfo.TotalPageFaultCount += Process->Vm.PageFaultCount;
  819. AccountingInfo.IoInfo.ReadOperationCount += Process->ReadOperationCount.QuadPart;
  820. AccountingInfo.IoInfo.WriteOperationCount += Process->WriteOperationCount.QuadPart;
  821. AccountingInfo.IoInfo.OtherOperationCount += Process->OtherOperationCount.QuadPart;
  822. AccountingInfo.IoInfo.ReadTransferCount += Process->ReadTransferCount.QuadPart;
  823. AccountingInfo.IoInfo.WriteTransferCount += Process->WriteTransferCount.QuadPart;
  824. AccountingInfo.IoInfo.OtherTransferCount += Process->OtherTransferCount.QuadPart;
  825. }
  826. Next = Next->Flink;
  827. }
  828. ExReleaseResourceLite (&Job->JobLock);
  829. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  830. ReturnData = &AccountingInfo;
  831. st = STATUS_SUCCESS;
  832. break;
  833. case JobObjectExtendedLimitInformation:
  834. case JobObjectBasicLimitInformation:
  835. //
  836. // Get the Basic Information
  837. //
  838. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  839. ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
  840. ExtendedLimitInfo.BasicLimitInformation.LimitFlags = Job->LimitFlags;
  841. ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize = Job->MinimumWorkingSetSize;
  842. ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize = Job->MaximumWorkingSetSize;
  843. ExtendedLimitInfo.BasicLimitInformation.ActiveProcessLimit = Job->ActiveProcessLimit;
  844. ExtendedLimitInfo.BasicLimitInformation.PriorityClass = (ULONG)Job->PriorityClass;
  845. ExtendedLimitInfo.BasicLimitInformation.SchedulingClass = Job->SchedulingClass;
  846. ExtendedLimitInfo.BasicLimitInformation.Affinity = (ULONG_PTR)Job->Affinity;
  847. ExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = Job->PerProcessUserTimeLimit.QuadPart;
  848. ExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit.QuadPart = Job->PerJobUserTimeLimit.QuadPart;
  849. if ( JobObjectInformationClass == JobObjectExtendedLimitInformation ) {
  850. //
  851. // Get Extended Information
  852. //
  853. ExAcquireFastMutex (&Job->MemoryLimitsLock);
  854. ExtendedLimitInfo.ProcessMemoryLimit = Job->ProcessMemoryLimit << PAGE_SHIFT;
  855. ExtendedLimitInfo.JobMemoryLimit = Job->JobMemoryLimit << PAGE_SHIFT;
  856. ExtendedLimitInfo.PeakJobMemoryUsed = Job->PeakJobMemoryUsed << PAGE_SHIFT;
  857. ExtendedLimitInfo.PeakProcessMemoryUsed = Job->PeakProcessMemoryUsed << PAGE_SHIFT;
  858. ExReleaseFastMutex (&Job->MemoryLimitsLock);
  859. ExReleaseResourceLite(&Job->JobLock);
  860. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  861. //
  862. // Zero un-used I/O counters
  863. //
  864. RtlZeroMemory(&ExtendedLimitInfo.IoInfo,sizeof(ExtendedLimitInfo.IoInfo));
  865. ReturnData = &ExtendedLimitInfo;
  866. } else {
  867. ExReleaseResourceLite(&Job->JobLock);
  868. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  869. ReturnData = &ExtendedLimitInfo.BasicLimitInformation;
  870. }
  871. st = STATUS_SUCCESS;
  872. break;
  873. case JobObjectBasicUIRestrictions:
  874. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  875. ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
  876. BasicUIRestrictions.UIRestrictionsClass = Job->UIRestrictionsClass;
  877. ExReleaseResourceLite(&Job->JobLock);
  878. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  879. ReturnData = &BasicUIRestrictions;
  880. st = STATUS_SUCCESS;
  881. break;
  882. case JobObjectBasicProcessIdList:
  883. IdList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)JobObjectInformation;
  884. NextProcessIdSlot = &IdList->ProcessIdList[0];
  885. WorkingLength = FIELD_OFFSET(JOBOBJECT_BASIC_PROCESS_ID_LIST,ProcessIdList);
  886. AlreadyCopied = TRUE;
  887. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  888. ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
  889. try {
  890. //
  891. // Acounted for in the workinglength = 2*sizeof(ULONG)
  892. //
  893. IdList->NumberOfAssignedProcesses = Job->ActiveProcesses;
  894. IdList->NumberOfProcessIdsInList = 0;
  895. Next = Job->ProcessListHead.Flink;
  896. while ( Next != &Job->ProcessListHead) {
  897. Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks));
  898. if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) ) {
  899. if ( !Process->UniqueProcessId ) {
  900. IdList->NumberOfAssignedProcesses--;
  901. } else {
  902. if ( (RequiredLength - WorkingLength) >= sizeof(ULONG_PTR) ) {
  903. *NextProcessIdSlot++ = (ULONG_PTR)Process->UniqueProcessId;
  904. WorkingLength += sizeof(ULONG_PTR);
  905. IdList->NumberOfProcessIdsInList++;
  906. } else {
  907. st = STATUS_BUFFER_OVERFLOW;
  908. ActualReturnLength = WorkingLength;
  909. break;
  910. }
  911. }
  912. }
  913. Next = Next->Flink;
  914. }
  915. ActualReturnLength = WorkingLength;
  916. } except ( ExSystemExceptionFilter() ) {
  917. st = GetExceptionCode();
  918. ActualReturnLength = 0;
  919. }
  920. ExReleaseResourceLite(&Job->JobLock);
  921. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  922. break;
  923. case JobObjectSecurityLimitInformation:
  924. RtlZeroMemory (&SecurityLimitInfo, sizeof (SecurityLimitInfo));
  925. ReturnData = &SecurityLimitInfo;
  926. st = STATUS_SUCCESS;
  927. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  928. ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
  929. SecurityLimitInfo.SecurityLimitFlags = Job->SecurityLimitFlags;
  930. //
  931. // If a filter is present, then we have an ugly marshalling to do.
  932. //
  933. Filter = Job->Filter;
  934. if (Filter != NULL) {
  935. WorkingLength = 0;
  936. //
  937. // For each field, if it is present, include the extra stuff
  938. //
  939. if (Filter->CapturedSidsLength > 0) {
  940. WorkingLength += Filter->CapturedSidsLength + sizeof (ULONG);
  941. }
  942. if (Filter->CapturedGroupsLength > 0) {
  943. WorkingLength += Filter->CapturedGroupsLength + sizeof (ULONG);
  944. }
  945. if (Filter->CapturedPrivilegesLength > 0) {
  946. WorkingLength += Filter->CapturedPrivilegesLength + sizeof (ULONG);
  947. }
  948. RequiredLength -= sizeof (SecurityLimitInfo);
  949. if (WorkingLength > RequiredLength) {
  950. st = STATUS_BUFFER_OVERFLOW ;
  951. ActualReturnLength = WorkingLength + sizeof (SecurityLimitInfo);
  952. goto unlock;
  953. }
  954. CurrentOffset = (PUCHAR) (JobObjectInformation) + sizeof (SecurityLimitInfo);
  955. try {
  956. if (Filter->CapturedSidsLength > 0) {
  957. WorkingGroup = (PTOKEN_GROUPS) CurrentOffset;
  958. CurrentOffset += sizeof (ULONG);
  959. SecurityLimitInfo.RestrictedSids = WorkingGroup;
  960. WorkingGroup->GroupCount = Filter->CapturedSidCount;
  961. TargetSidBuffer = (PSID) (CurrentOffset +
  962. sizeof (SID_AND_ATTRIBUTES) *
  963. Filter->CapturedSidCount);
  964. st = RtlCopySidAndAttributesArray (Filter->CapturedSidCount,
  965. Filter->CapturedSids,
  966. WorkingLength,
  967. WorkingGroup->Groups,
  968. TargetSidBuffer,
  969. &RemainingSid,
  970. &RemainingSidBuffer);
  971. CurrentOffset += Filter->CapturedSidsLength;
  972. }
  973. if (!NT_SUCCESS (st)) {
  974. leave;
  975. }
  976. if (Filter->CapturedGroupsLength > 0) {
  977. WorkingGroup = (PTOKEN_GROUPS) CurrentOffset;
  978. CurrentOffset += sizeof (ULONG);
  979. SecurityLimitInfo.SidsToDisable = WorkingGroup;
  980. WorkingGroup->GroupCount = Filter->CapturedGroupCount;
  981. TargetSidBuffer = (PSID) (CurrentOffset +
  982. sizeof (SID_AND_ATTRIBUTES) *
  983. Filter->CapturedGroupCount);
  984. st = RtlCopySidAndAttributesArray (Filter->CapturedGroupCount,
  985. Filter->CapturedGroups,
  986. WorkingLength,
  987. WorkingGroup->Groups,
  988. TargetSidBuffer,
  989. &RemainingSid,
  990. &RemainingSidBuffer);
  991. CurrentOffset += Filter->CapturedGroupsLength;
  992. }
  993. if (!NT_SUCCESS (st)) {
  994. leave;
  995. }
  996. if (Filter->CapturedPrivilegesLength > 0) {
  997. WorkingPrivs = (PTOKEN_PRIVILEGES) CurrentOffset;
  998. CurrentOffset += sizeof (ULONG);
  999. SecurityLimitInfo.PrivilegesToDelete = WorkingPrivs;
  1000. WorkingPrivs->PrivilegeCount = Filter->CapturedPrivilegeCount;
  1001. RtlCopyMemory (WorkingPrivs->Privileges,
  1002. Filter->CapturedPrivileges,
  1003. Filter->CapturedPrivilegesLength);
  1004. }
  1005. } except (EXCEPTION_EXECUTE_HANDLER) {
  1006. st = GetExceptionCode ();
  1007. ActualReturnLength = 0 ;
  1008. }
  1009. }
  1010. unlock:
  1011. ExReleaseResourceLite (&Job->JobLock);
  1012. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1013. AlreadyCopied = TRUE ;
  1014. if (NT_SUCCESS (st)) {
  1015. try {
  1016. RtlCopyMemory (JobObjectInformation,
  1017. &SecurityLimitInfo,
  1018. sizeof (SecurityLimitInfo));
  1019. } except (EXCEPTION_EXECUTE_HANDLER) {
  1020. st = GetExceptionCode ();
  1021. ActualReturnLength = 0 ;
  1022. break;
  1023. }
  1024. }
  1025. break;
  1026. case JobObjectJobSetInformation:
  1027. ExAcquireFastMutex (&PspJobListLock);
  1028. JobSetInformation.MemberLevel = Job->MemberLevel;
  1029. ExReleaseFastMutex (&PspJobListLock);
  1030. ReturnData = &JobSetInformation;
  1031. st = STATUS_SUCCESS;
  1032. break;
  1033. case JobObjectEndOfJobTimeInformation:
  1034. EndOfJobInfo.EndOfJobTimeAction = Job->EndOfJobTimeAction;
  1035. ReturnData = &EndOfJobInfo;
  1036. st = STATUS_SUCCESS;
  1037. break;
  1038. default:
  1039. st = STATUS_INVALID_INFO_CLASS;
  1040. }
  1041. //
  1042. // Finish Up
  1043. //
  1044. ObDereferenceObject(Job);
  1045. if (NT_SUCCESS (st)) {
  1046. //
  1047. // Either of these may cause an access violation. The
  1048. // exception handler will return access violation as
  1049. // status code. No further cleanup needs to be done.
  1050. //
  1051. try {
  1052. if (!AlreadyCopied) {
  1053. RtlCopyMemory (JobObjectInformation, ReturnData, RequiredLength);
  1054. }
  1055. if (ARGUMENT_PRESENT (ReturnLength)) {
  1056. *ReturnLength = ActualReturnLength;
  1057. }
  1058. } except(EXCEPTION_EXECUTE_HANDLER) {
  1059. return GetExceptionCode ();
  1060. }
  1061. }
  1062. return st;
  1063. }
  1064. NTSTATUS
  1065. NtSetInformationJobObject(
  1066. IN HANDLE JobHandle,
  1067. IN JOBOBJECTINFOCLASS JobObjectInformationClass,
  1068. IN PVOID JobObjectInformation,
  1069. IN ULONG JobObjectInformationLength
  1070. )
  1071. {
  1072. PEJOB Job;
  1073. EJOB LocalJob={0};
  1074. KPROCESSOR_MODE PreviousMode;
  1075. NTSTATUS st;
  1076. JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimitInfo={0};
  1077. JOBOBJECT_BASIC_UI_RESTRICTIONS BasicUIRestrictions={0};
  1078. JOBOBJECT_SECURITY_LIMIT_INFORMATION SecurityLimitInfo={0};
  1079. JOBOBJECT_END_OF_JOB_TIME_INFORMATION EndOfJobInfo={0};
  1080. JOBOBJECT_ASSOCIATE_COMPLETION_PORT AssociateInfo={0};
  1081. ULONG RequiredAccess;
  1082. ULONG RequiredLength, RequiredAlign;
  1083. PEPROCESS Process;
  1084. PETHREAD CurrentThread;
  1085. BOOLEAN HasPrivilege;
  1086. BOOLEAN IsChild=FALSE;
  1087. PLIST_ENTRY Next;
  1088. PPS_JOB_TOKEN_FILTER Filter;
  1089. PVOID IoCompletion;
  1090. PACCESS_TOKEN LocalToken;
  1091. ULONG ValidFlags;
  1092. ULONG LimitFlags;
  1093. BOOLEAN ProcessWorkingSetHead = FALSE;
  1094. PJOB_WORKING_SET_CHANGE_RECORD WsChangeRecord;
  1095. PAGED_CODE();
  1096. //
  1097. // Get previous processor mode and probe output argument if necessary.
  1098. //
  1099. if (JobObjectInformationClass >= MaxJobObjectInfoClass || JobObjectInformationClass <= 0) {
  1100. return STATUS_INVALID_INFO_CLASS;
  1101. }
  1102. RequiredLength = PspJobInfoLengths[JobObjectInformationClass-1];
  1103. RequiredAlign = PspJobInfoAlign[JobObjectInformationClass-1];
  1104. CurrentThread = PsGetCurrentThread ();
  1105. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  1106. if (PreviousMode != KernelMode) {
  1107. try {
  1108. ProbeForRead(
  1109. JobObjectInformation,
  1110. JobObjectInformationLength,
  1111. RequiredAlign
  1112. );
  1113. } except(EXCEPTION_EXECUTE_HANDLER) {
  1114. return GetExceptionCode();
  1115. }
  1116. }
  1117. if (JobObjectInformationLength != RequiredLength) {
  1118. return STATUS_INFO_LENGTH_MISMATCH;
  1119. }
  1120. //
  1121. // reference the job
  1122. //
  1123. if (JobObjectInformationClass == JobObjectSecurityLimitInformation) {
  1124. RequiredAccess = JOB_OBJECT_SET_SECURITY_ATTRIBUTES;
  1125. } else {
  1126. RequiredAccess = JOB_OBJECT_SET_ATTRIBUTES;
  1127. }
  1128. st = ObReferenceObjectByHandle(
  1129. JobHandle,
  1130. RequiredAccess,
  1131. PsJobType,
  1132. PreviousMode,
  1133. (PVOID *)&Job,
  1134. NULL
  1135. );
  1136. if (!NT_SUCCESS (st)) {
  1137. return st;
  1138. }
  1139. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1140. //
  1141. // Check argument validity.
  1142. //
  1143. switch (JobObjectInformationClass) {
  1144. case JobObjectExtendedLimitInformation:
  1145. case JobObjectBasicLimitInformation:
  1146. try {
  1147. RtlCopyMemory (&ExtendedLimitInfo, JobObjectInformation, RequiredLength);
  1148. } except(EXCEPTION_EXECUTE_HANDLER) {
  1149. st = GetExceptionCode();
  1150. }
  1151. if (NT_SUCCESS (st)) {
  1152. //
  1153. // sanity check LimitFlags
  1154. //
  1155. if (JobObjectInformationClass == JobObjectBasicLimitInformation) {
  1156. ValidFlags = JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS;
  1157. } else {
  1158. ValidFlags = JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS;
  1159. }
  1160. if ( ExtendedLimitInfo.BasicLimitInformation.LimitFlags & ~ValidFlags ) {
  1161. st = STATUS_INVALID_PARAMETER;
  1162. } else {
  1163. LimitFlags = ExtendedLimitInfo.BasicLimitInformation.LimitFlags;
  1164. ExAcquireResourceExclusiveLite(&Job->JobLock, TRUE);
  1165. //
  1166. // Deal with each of the various limit flags
  1167. //
  1168. LocalJob.LimitFlags = Job->LimitFlags;
  1169. //
  1170. // ACTIVE PROCESS LIMIT
  1171. //
  1172. if (LimitFlags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS) {
  1173. //
  1174. // Active Process Limit is NOT retroactive. New processes are denied,
  1175. // but existing ones are not killed just because the limit is
  1176. // reduced.
  1177. //
  1178. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
  1179. LocalJob.ActiveProcessLimit = ExtendedLimitInfo.BasicLimitInformation.ActiveProcessLimit;
  1180. } else {
  1181. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
  1182. LocalJob.ActiveProcessLimit = 0;
  1183. }
  1184. //
  1185. // PRIORITY CLASS LIMIT
  1186. //
  1187. if (LimitFlags & JOB_OBJECT_LIMIT_PRIORITY_CLASS) {
  1188. if (ExtendedLimitInfo.BasicLimitInformation.PriorityClass > PROCESS_PRIORITY_CLASS_ABOVE_NORMAL) {
  1189. st = STATUS_INVALID_PARAMETER;
  1190. } else {
  1191. if (ExtendedLimitInfo.BasicLimitInformation.PriorityClass == PROCESS_PRIORITY_CLASS_HIGH ||
  1192. ExtendedLimitInfo.BasicLimitInformation.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME) {
  1193. //
  1194. // Increasing the base priority of a process is a
  1195. // privileged operation. Check for the privilege
  1196. // here.
  1197. //
  1198. HasPrivilege = SeCheckPrivilegedObject(
  1199. SeIncreaseBasePriorityPrivilege,
  1200. JobHandle,
  1201. JOB_OBJECT_SET_ATTRIBUTES,
  1202. PreviousMode
  1203. );
  1204. if (!HasPrivilege) {
  1205. st = STATUS_PRIVILEGE_NOT_HELD;
  1206. }
  1207. }
  1208. if ( NT_SUCCESS(st) ) {
  1209. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
  1210. LocalJob.PriorityClass = (UCHAR)ExtendedLimitInfo.BasicLimitInformation.PriorityClass;
  1211. }
  1212. }
  1213. } else {
  1214. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_PRIORITY_CLASS;
  1215. LocalJob.PriorityClass = 0;
  1216. }
  1217. //
  1218. // SCHEDULING CLASS LIMIT
  1219. //
  1220. if (LimitFlags & JOB_OBJECT_LIMIT_SCHEDULING_CLASS) {
  1221. if (ExtendedLimitInfo.BasicLimitInformation.SchedulingClass >= PSP_NUMBER_OF_SCHEDULING_CLASSES) {
  1222. st = STATUS_INVALID_PARAMETER;
  1223. } else {
  1224. if (ExtendedLimitInfo.BasicLimitInformation.SchedulingClass > PSP_DEFAULT_SCHEDULING_CLASSES) {
  1225. //
  1226. // Increasing above the default scheduling class
  1227. // is a
  1228. // privileged operation. Check for the privilege
  1229. // here.
  1230. //
  1231. HasPrivilege = SeCheckPrivilegedObject(
  1232. SeIncreaseBasePriorityPrivilege,
  1233. JobHandle,
  1234. JOB_OBJECT_SET_ATTRIBUTES,
  1235. PreviousMode
  1236. );
  1237. if (!HasPrivilege) {
  1238. st = STATUS_PRIVILEGE_NOT_HELD;
  1239. }
  1240. }
  1241. if (NT_SUCCESS (st)) {
  1242. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_SCHEDULING_CLASS;
  1243. LocalJob.SchedulingClass = ExtendedLimitInfo.BasicLimitInformation.SchedulingClass;
  1244. }
  1245. }
  1246. } else {
  1247. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_SCHEDULING_CLASS;
  1248. LocalJob.SchedulingClass = PSP_DEFAULT_SCHEDULING_CLASSES ;
  1249. }
  1250. //
  1251. // AFFINITY LIMIT
  1252. //
  1253. if ( LimitFlags & JOB_OBJECT_LIMIT_AFFINITY ) {
  1254. if ( !ExtendedLimitInfo.BasicLimitInformation.Affinity ||
  1255. (ExtendedLimitInfo.BasicLimitInformation.Affinity != (ExtendedLimitInfo.BasicLimitInformation.Affinity & KeActiveProcessors)) ) {
  1256. st = STATUS_INVALID_PARAMETER;
  1257. } else {
  1258. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_AFFINITY;
  1259. LocalJob.Affinity = (KAFFINITY)ExtendedLimitInfo.BasicLimitInformation.Affinity;
  1260. }
  1261. } else {
  1262. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_AFFINITY;
  1263. LocalJob.Affinity = 0;
  1264. }
  1265. //
  1266. // PROCESS TIME LIMIT
  1267. //
  1268. if ( LimitFlags & JOB_OBJECT_LIMIT_PROCESS_TIME ) {
  1269. if ( !ExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart ) {
  1270. st = STATUS_INVALID_PARAMETER;
  1271. } else {
  1272. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_TIME;
  1273. LocalJob.PerProcessUserTimeLimit.QuadPart = ExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart;
  1274. }
  1275. } else {
  1276. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_PROCESS_TIME;
  1277. LocalJob.PerProcessUserTimeLimit.QuadPart = 0;
  1278. }
  1279. //
  1280. // JOB TIME LIMIT
  1281. //
  1282. if ( LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME ) {
  1283. if ( !ExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit.QuadPart ) {
  1284. st = STATUS_INVALID_PARAMETER;
  1285. } else {
  1286. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_JOB_TIME;
  1287. LocalJob.PerJobUserTimeLimit.QuadPart = ExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit.QuadPart;
  1288. }
  1289. } else {
  1290. if ( LimitFlags & JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME ) {
  1291. //
  1292. // If we are supposed to preserve existing job time limits, then
  1293. // preserve them !
  1294. //
  1295. LocalJob.LimitFlags |= (Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME);
  1296. LocalJob.PerJobUserTimeLimit.QuadPart = Job->PerJobUserTimeLimit.QuadPart;
  1297. } else {
  1298. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_TIME;
  1299. LocalJob.PerJobUserTimeLimit.QuadPart = 0;
  1300. }
  1301. }
  1302. //
  1303. // WORKING SET LIMIT
  1304. //
  1305. if ( LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET ) {
  1306. //
  1307. // the only issue with this check is that when we enforce through the
  1308. // processes, we may find a process that can not handle the new working set
  1309. // limit because it will make the process's working set not fluid
  1310. //
  1311. if ( (ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize == 0 &&
  1312. ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize == 0) ||
  1313. (ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize == (SIZE_T)-1 &&
  1314. ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize == (SIZE_T)-1) ||
  1315. (ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize >
  1316. ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize) ) {
  1317. st = STATUS_INVALID_PARAMETER;
  1318. } else {
  1319. if (ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize <= PsMinimumWorkingSet ||
  1320. SeSinglePrivilegeCheck (SeIncreaseBasePriorityPrivilege,
  1321. PreviousMode)) {
  1322. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_WORKINGSET;
  1323. LocalJob.MinimumWorkingSetSize = ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize;
  1324. LocalJob.MaximumWorkingSetSize = ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize;
  1325. } else {
  1326. st = STATUS_PRIVILEGE_NOT_HELD;
  1327. }
  1328. }
  1329. } else {
  1330. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_WORKINGSET;
  1331. LocalJob.MinimumWorkingSetSize = 0;
  1332. LocalJob.MaximumWorkingSetSize = 0;
  1333. }
  1334. if ( JobObjectInformationClass == JobObjectExtendedLimitInformation) {
  1335. //
  1336. // PROCESS MEMORY LIMIT
  1337. //
  1338. if ( LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY ) {
  1339. if ( ExtendedLimitInfo.ProcessMemoryLimit < PAGE_SIZE ) {
  1340. st = STATUS_INVALID_PARAMETER;
  1341. } else {
  1342. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY;
  1343. LocalJob.ProcessMemoryLimit = ExtendedLimitInfo.ProcessMemoryLimit >> PAGE_SHIFT;
  1344. }
  1345. } else {
  1346. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_PROCESS_MEMORY;
  1347. LocalJob.ProcessMemoryLimit = 0;
  1348. }
  1349. //
  1350. // JOB WIDE MEMORY LIMIT
  1351. //
  1352. if ( LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY ) {
  1353. if ( ExtendedLimitInfo.JobMemoryLimit < PAGE_SIZE ) {
  1354. st = STATUS_INVALID_PARAMETER;
  1355. } else {
  1356. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY;
  1357. LocalJob.JobMemoryLimit = ExtendedLimitInfo.JobMemoryLimit >> PAGE_SHIFT;
  1358. }
  1359. } else {
  1360. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_MEMORY;
  1361. LocalJob.JobMemoryLimit = 0;
  1362. }
  1363. //
  1364. // JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
  1365. //
  1366. if ( LimitFlags & JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION ) {
  1367. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
  1368. } else {
  1369. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
  1370. }
  1371. //
  1372. // JOB_OBJECT_LIMIT_BREAKAWAY_OK
  1373. //
  1374. if ( LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK ) {
  1375. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK;
  1376. } else {
  1377. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_BREAKAWAY_OK;
  1378. }
  1379. //
  1380. // JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK
  1381. //
  1382. if ( LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK ) {
  1383. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
  1384. } else {
  1385. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
  1386. }
  1387. //
  1388. // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
  1389. //
  1390. if (LimitFlags & JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) {
  1391. LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
  1392. } else {
  1393. LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
  1394. }
  1395. }
  1396. if ( NT_SUCCESS(st) ) {
  1397. //
  1398. // Copy LocalJob to Job
  1399. //
  1400. Job->LimitFlags = LocalJob.LimitFlags;
  1401. Job->MinimumWorkingSetSize = LocalJob.MinimumWorkingSetSize;
  1402. Job->MaximumWorkingSetSize = LocalJob.MaximumWorkingSetSize;
  1403. Job->ActiveProcessLimit = LocalJob.ActiveProcessLimit;
  1404. Job->Affinity = LocalJob.Affinity;
  1405. Job->PriorityClass = LocalJob.PriorityClass;
  1406. Job->SchedulingClass = LocalJob.SchedulingClass;
  1407. Job->PerProcessUserTimeLimit.QuadPart = LocalJob.PerProcessUserTimeLimit.QuadPart;
  1408. Job->PerJobUserTimeLimit.QuadPart = LocalJob.PerJobUserTimeLimit.QuadPart;
  1409. if (JobObjectInformationClass == JobObjectExtendedLimitInformation) {
  1410. ExAcquireFastMutex (&Job->MemoryLimitsLock);
  1411. Job->ProcessMemoryLimit = LocalJob.ProcessMemoryLimit;
  1412. Job->JobMemoryLimit = LocalJob.JobMemoryLimit;
  1413. ExReleaseFastMutex (&Job->MemoryLimitsLock);
  1414. }
  1415. if ( LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME ) {
  1416. //
  1417. // Take any signalled processes and fold their accounting
  1418. // intothe job. This way a process that exited clean but still
  1419. // is open won't impact the next period
  1420. //
  1421. Next = Job->ProcessListHead.Flink;
  1422. while ( Next != &Job->ProcessListHead) {
  1423. Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks));
  1424. //
  1425. // see if process has been signalled.
  1426. // This indicates that the process has exited. We can't do
  1427. // this in the exit path becuase of the lock order problem
  1428. // between the process lock and the job lock since in exit
  1429. // we hold the process lock for a long time and can't drop
  1430. // it until thread termination
  1431. //
  1432. if ( KeReadStateProcess(&Process->Pcb) ) {
  1433. PspFoldProcessAccountingIntoJob(Job,Process);
  1434. } else {
  1435. LARGE_INTEGER ProcessTime;
  1436. //
  1437. // running processes have their current runtime
  1438. // added to the programmed limit. This way, you
  1439. // can set a limit on a job with processes in the
  1440. // job and not have previous runtimes count against
  1441. // the limit
  1442. //
  1443. if ( !(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED) ) {
  1444. ProcessTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,KeMaximumIncrement);
  1445. Job->PerJobUserTimeLimit.QuadPart += ProcessTime.QuadPart;
  1446. }
  1447. }
  1448. Next = Next->Flink;
  1449. }
  1450. //
  1451. // clear period times and reset the job
  1452. //
  1453. Job->ThisPeriodTotalUserTime.QuadPart = 0;
  1454. Job->ThisPeriodTotalKernelTime.QuadPart = 0;
  1455. KeClearEvent(&Job->Event);
  1456. }
  1457. if ( Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET ) {
  1458. ExAcquireFastMutex (&PspWorkingSetChangeHead.Lock);
  1459. PspWorkingSetChangeHead.MinimumWorkingSetSize = Job->MinimumWorkingSetSize;
  1460. PspWorkingSetChangeHead.MaximumWorkingSetSize = Job->MaximumWorkingSetSize;
  1461. ProcessWorkingSetHead = TRUE;
  1462. }
  1463. PspApplyJobLimitsToProcessSet(Job);
  1464. }
  1465. ExReleaseResourceLite(&Job->JobLock);
  1466. }
  1467. }
  1468. break;
  1469. case JobObjectBasicUIRestrictions:
  1470. try {
  1471. RtlCopyMemory(&BasicUIRestrictions, JobObjectInformation, RequiredLength);
  1472. } except(EXCEPTION_EXECUTE_HANDLER) {
  1473. st = GetExceptionCode();
  1474. }
  1475. if (NT_SUCCESS (st)) {
  1476. //
  1477. // sanity check UIRestrictionsClass
  1478. //
  1479. if ( BasicUIRestrictions.UIRestrictionsClass & ~JOB_OBJECT_UI_VALID_FLAGS ) {
  1480. st = STATUS_INVALID_PARAMETER;
  1481. } else {
  1482. ExAcquireResourceExclusiveLite(&Job->JobLock, TRUE);
  1483. //
  1484. // Check for switching between UI restrictions
  1485. //
  1486. if ( Job->UIRestrictionsClass ^ BasicUIRestrictions.UIRestrictionsClass ) {
  1487. //
  1488. // notify ntuser that the UI restrictions have changed
  1489. //
  1490. WIN32_JOBCALLOUT_PARAMETERS Parms;
  1491. Parms.Job = Job;
  1492. Parms.CalloutType = PsW32JobCalloutSetInformation;
  1493. Parms.Data = ULongToPtr(BasicUIRestrictions.UIRestrictionsClass);
  1494. PspWin32SessionCallout(PspW32JobCallout, &Parms, Job->SessionId);
  1495. }
  1496. //
  1497. // save the UI restrictions into the job object
  1498. //
  1499. Job->UIRestrictionsClass = BasicUIRestrictions.UIRestrictionsClass;
  1500. ExReleaseResourceLite(&Job->JobLock);
  1501. }
  1502. }
  1503. break;
  1504. //
  1505. // SECURITY LIMITS
  1506. //
  1507. case JobObjectSecurityLimitInformation:
  1508. try {
  1509. RtlCopyMemory( &SecurityLimitInfo,
  1510. JobObjectInformation,
  1511. RequiredLength );
  1512. } except(EXCEPTION_EXECUTE_HANDLER) {
  1513. st = GetExceptionCode();
  1514. }
  1515. if (NT_SUCCESS(st)) {
  1516. if ( SecurityLimitInfo.SecurityLimitFlags &
  1517. (~JOB_OBJECT_SECURITY_VALID_FLAGS)) {
  1518. st = STATUS_INVALID_PARAMETER ;
  1519. } else {
  1520. ExAcquireResourceExclusiveLite(&Job->JobLock, TRUE);
  1521. //
  1522. // Deal with specific options. Basic rules: Once a
  1523. // flag is on, it is always on (so even with a handle to
  1524. // the job, a process could not lift the security
  1525. // restrictions).
  1526. //
  1527. if ( SecurityLimitInfo.SecurityLimitFlags &
  1528. JOB_OBJECT_SECURITY_NO_ADMIN ) {
  1529. Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_NO_ADMIN ;
  1530. if ( Job->Token ) {
  1531. if ( SeTokenIsAdmin( Job->Token ) ) {
  1532. Job->SecurityLimitFlags &= (~JOB_OBJECT_SECURITY_NO_ADMIN);
  1533. st = STATUS_INVALID_PARAMETER ;
  1534. }
  1535. }
  1536. }
  1537. if ( SecurityLimitInfo.SecurityLimitFlags &
  1538. JOB_OBJECT_SECURITY_RESTRICTED_TOKEN ) {
  1539. if ( Job->SecurityLimitFlags &
  1540. ( JOB_OBJECT_SECURITY_ONLY_TOKEN | JOB_OBJECT_SECURITY_FILTER_TOKENS ) ) {
  1541. st = STATUS_INVALID_PARAMETER ;
  1542. } else {
  1543. Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_RESTRICTED_TOKEN ;
  1544. }
  1545. }
  1546. //
  1547. // The forcible token is a little more interesting. It
  1548. // cannot be reset, so if there is a pointer there already,
  1549. // fail the call. If a filter is already in place, this is
  1550. // not allowed, either. If no-admin is set, it is checked
  1551. // at the end, once the token has been ref'd.
  1552. //
  1553. if ( SecurityLimitInfo.SecurityLimitFlags &
  1554. JOB_OBJECT_SECURITY_ONLY_TOKEN ) {
  1555. if ( Job->Token ||
  1556. (Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_FILTER_TOKENS) ) {
  1557. st = STATUS_INVALID_PARAMETER ;
  1558. } else {
  1559. st = ObReferenceObjectByHandle(
  1560. SecurityLimitInfo.JobToken,
  1561. TOKEN_ASSIGN_PRIMARY |
  1562. TOKEN_IMPERSONATE |
  1563. TOKEN_DUPLICATE ,
  1564. SeTokenObjectType,
  1565. PreviousMode,
  1566. &LocalToken,
  1567. NULL );
  1568. if ( NT_SUCCESS( st ) ) {
  1569. if (SeTokenType (LocalToken) != TokenPrimary) {
  1570. st = STATUS_BAD_TOKEN_TYPE;
  1571. } else {
  1572. st = SeIsChildTokenByPointer (LocalToken,
  1573. &IsChild);
  1574. }
  1575. if (!NT_SUCCESS (st)) {
  1576. ObDereferenceObject (LocalToken);
  1577. }
  1578. }
  1579. if (NT_SUCCESS (st)) {
  1580. //
  1581. // If the token supplied is not a restricted token
  1582. // based on the caller's ID, then they must have
  1583. // assign primary privilege in order to associate
  1584. // the token with the job.
  1585. //
  1586. if ( !IsChild ) {
  1587. HasPrivilege = SeCheckPrivilegedObject(
  1588. SeAssignPrimaryTokenPrivilege,
  1589. JobHandle,
  1590. JOB_OBJECT_SET_SECURITY_ATTRIBUTES,
  1591. PreviousMode
  1592. );
  1593. if ( !HasPrivilege ) {
  1594. st = STATUS_PRIVILEGE_NOT_HELD;
  1595. }
  1596. }
  1597. if (NT_SUCCESS (st)) {
  1598. //
  1599. // Not surprisingly, specifying no-admin and
  1600. // supplying an admin token is a no-no.
  1601. //
  1602. if ((Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_NO_ADMIN) &&
  1603. SeTokenIsAdmin (LocalToken)) {
  1604. st = STATUS_INVALID_PARAMETER;
  1605. ObDereferenceObject (LocalToken);
  1606. } else {
  1607. //
  1608. // Grab a reference to the token into the job
  1609. // object
  1610. //
  1611. KeMemoryBarrier ();
  1612. Job->Token = LocalToken;
  1613. Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_ONLY_TOKEN;
  1614. }
  1615. } else {
  1616. //
  1617. // This is the token was a child or otherwise ok,
  1618. // but assign primary was not held, so the
  1619. // request was rejected.
  1620. //
  1621. ObDereferenceObject (LocalToken);
  1622. }
  1623. }
  1624. }
  1625. }
  1626. if ( SecurityLimitInfo.SecurityLimitFlags &
  1627. JOB_OBJECT_SECURITY_FILTER_TOKENS ) {
  1628. if ( Job->SecurityLimitFlags &
  1629. ( JOB_OBJECT_SECURITY_ONLY_TOKEN |
  1630. JOB_OBJECT_SECURITY_FILTER_TOKENS ) ) {
  1631. st = STATUS_INVALID_PARAMETER;
  1632. } else {
  1633. //
  1634. // capture the token restrictions
  1635. //
  1636. st = PspCaptureTokenFilter(
  1637. PreviousMode,
  1638. &SecurityLimitInfo,
  1639. &Filter
  1640. );
  1641. if (NT_SUCCESS (st)) {
  1642. KeMemoryBarrier ();
  1643. Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_FILTER_TOKENS;
  1644. Job->Filter = Filter;
  1645. }
  1646. }
  1647. }
  1648. ExReleaseResourceLite(&Job->JobLock);
  1649. }
  1650. }
  1651. break;
  1652. case JobObjectEndOfJobTimeInformation:
  1653. try {
  1654. RtlCopyMemory(&EndOfJobInfo,JobObjectInformation,RequiredLength);
  1655. } except(EXCEPTION_EXECUTE_HANDLER) {
  1656. st = GetExceptionCode();
  1657. }
  1658. if (NT_SUCCESS (st)) {
  1659. //
  1660. // sanity check LimitFlags
  1661. //
  1662. if (EndOfJobInfo.EndOfJobTimeAction > JOB_OBJECT_POST_AT_END_OF_JOB) {
  1663. st = STATUS_INVALID_PARAMETER;
  1664. } else {
  1665. ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);
  1666. Job->EndOfJobTimeAction = EndOfJobInfo.EndOfJobTimeAction;
  1667. ExReleaseResourceLite (&Job->JobLock);
  1668. }
  1669. }
  1670. break;
  1671. case JobObjectAssociateCompletionPortInformation:
  1672. try {
  1673. RtlCopyMemory(&AssociateInfo,JobObjectInformation,RequiredLength);
  1674. } except(EXCEPTION_EXECUTE_HANDLER) {
  1675. st = GetExceptionCode();
  1676. }
  1677. if ( NT_SUCCESS(st) ) {
  1678. if (Job->CompletionPort || AssociateInfo.CompletionPort == NULL) {
  1679. st = STATUS_INVALID_PARAMETER;
  1680. } else {
  1681. st = ObReferenceObjectByHandle (AssociateInfo.CompletionPort,
  1682. IO_COMPLETION_MODIFY_STATE,
  1683. IoCompletionObjectType,
  1684. PreviousMode,
  1685. &IoCompletion,
  1686. NULL);
  1687. if (NT_SUCCESS(st)) {
  1688. ExAcquireResourceExclusiveLite(&Job->JobLock, TRUE);
  1689. //
  1690. // If the job already has a completion port or if the job has been rundown
  1691. // then reject the request.
  1692. //
  1693. if (Job->CompletionPort != NULL || (Job->JobFlags&PS_JOB_FLAGS_CLOSE_DONE) != 0) {
  1694. ExReleaseResourceLite(&Job->JobLock);
  1695. ObDereferenceObject (IoCompletion);
  1696. st = STATUS_INVALID_PARAMETER;
  1697. } else {
  1698. Job->CompletionKey = AssociateInfo.CompletionKey;
  1699. KeMemoryBarrier ();
  1700. Job->CompletionPort = IoCompletion;
  1701. //
  1702. // Now whip through ALL existing processes in the job
  1703. // and send notification messages
  1704. //
  1705. Next = Job->ProcessListHead.Flink;
  1706. while (Next != &Job->ProcessListHead) {
  1707. Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks));
  1708. //
  1709. // If the process is really considered part of the job, has
  1710. // been assigned its id, and has not yet checked in, do it now
  1711. //
  1712. if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE)
  1713. && Process->UniqueProcessId
  1714. && !(Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED)) {
  1715. PS_SET_CLEAR_BITS (&Process->JobStatus,
  1716. PS_JOB_STATUS_NEW_PROCESS_REPORTED,
  1717. PS_JOB_STATUS_LAST_REPORT_MEMORY);
  1718. IoSetIoCompletion(
  1719. Job->CompletionPort,
  1720. Job->CompletionKey,
  1721. (PVOID)Process->UniqueProcessId,
  1722. STATUS_SUCCESS,
  1723. JOB_OBJECT_MSG_NEW_PROCESS,
  1724. FALSE
  1725. );
  1726. }
  1727. Next = Next->Flink;
  1728. }
  1729. ExReleaseResourceLite(&Job->JobLock);
  1730. }
  1731. }
  1732. }
  1733. }
  1734. break;
  1735. default:
  1736. st = STATUS_INVALID_INFO_CLASS;
  1737. }
  1738. //
  1739. // Working Set Changes are processed outside of the job lock.
  1740. //
  1741. // calling MmAdjust CAN NOT cause MM to call PsChangeJobMemoryUsage !
  1742. //
  1743. if (ProcessWorkingSetHead) {
  1744. LIST_ENTRY FreeList;
  1745. KAPC_STATE ApcState;
  1746. InitializeListHead (&FreeList);
  1747. while (!IsListEmpty (&PspWorkingSetChangeHead.Links)) {
  1748. Next = RemoveHeadList(&PspWorkingSetChangeHead.Links);
  1749. InsertTailList (&FreeList, Next);
  1750. WsChangeRecord = CONTAINING_RECORD(Next,JOB_WORKING_SET_CHANGE_RECORD,Links);
  1751. KeStackAttachProcess(&WsChangeRecord->Process->Pcb, &ApcState);
  1752. MmAdjustWorkingSetSize (PspWorkingSetChangeHead.MinimumWorkingSetSize,
  1753. PspWorkingSetChangeHead.MaximumWorkingSetSize,
  1754. FALSE,
  1755. TRUE);
  1756. //
  1757. // call MM to Enable hard workingset
  1758. //
  1759. MmEnforceWorkingSetLimit(&WsChangeRecord->Process->Vm, TRUE);
  1760. KeUnstackDetachProcess(&ApcState);
  1761. }
  1762. ExReleaseFastMutex (&PspWorkingSetChangeHead.Lock);
  1763. while (!IsListEmpty (&FreeList)) {
  1764. Next = RemoveHeadList(&FreeList);
  1765. WsChangeRecord = CONTAINING_RECORD(Next,JOB_WORKING_SET_CHANGE_RECORD,Links);
  1766. ObDereferenceObject (WsChangeRecord->Process);
  1767. ExFreePool (WsChangeRecord);
  1768. }
  1769. }
  1770. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1771. //
  1772. // Finish Up
  1773. //
  1774. ObDereferenceObject(Job);
  1775. return st;
  1776. }
  1777. VOID
  1778. PspApplyJobLimitsToProcessSet(
  1779. PEJOB Job
  1780. )
  1781. {
  1782. PEPROCESS Process;
  1783. PJOB_WORKING_SET_CHANGE_RECORD WsChangeRecord;
  1784. PAGED_CODE();
  1785. //
  1786. // The job object is held exclusive by the caller
  1787. //
  1788. for (Process = PspGetNextJobProcess (Job, NULL);
  1789. Process != NULL;
  1790. Process = PspGetNextJobProcess (Job, Process)) {
  1791. if (!(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE)) {
  1792. if (Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) {
  1793. WsChangeRecord = ExAllocatePoolWithTag (PagedPool,
  1794. sizeof(*WsChangeRecord),
  1795. 'rCsP');
  1796. if (WsChangeRecord != NULL) {
  1797. WsChangeRecord->Process = Process;
  1798. ObReferenceObject (Process);
  1799. InsertTailList(&PspWorkingSetChangeHead.Links,&WsChangeRecord->Links);
  1800. }
  1801. }
  1802. PspApplyJobLimitsToProcess(Job,Process);
  1803. }
  1804. }
  1805. }
  1806. VOID
  1807. PspApplyJobLimitsToProcess(
  1808. PEJOB Job,
  1809. PEPROCESS Process
  1810. )
  1811. {
  1812. PETHREAD CurrentThread;
  1813. PAGED_CODE();
  1814. //
  1815. // The job object is held exclusive by the caller
  1816. //
  1817. if (Job->LimitFlags & JOB_OBJECT_LIMIT_PRIORITY_CLASS) {
  1818. Process->PriorityClass = Job->PriorityClass;
  1819. PsSetProcessPriorityByClass (Process,
  1820. Process->Vm.Flags.MemoryPriority == MEMORY_PRIORITY_FOREGROUND ?
  1821. PsProcessPriorityForeground : PsProcessPriorityBackground);
  1822. }
  1823. if ( Job->LimitFlags & JOB_OBJECT_LIMIT_AFFINITY ) {
  1824. CurrentThread = PsGetCurrentThread ();
  1825. PspLockProcessExclusive (Process, CurrentThread);
  1826. KeSetAffinityProcess (&Process->Pcb, Job->Affinity);
  1827. PspUnlockProcessExclusive (Process, CurrentThread);
  1828. }
  1829. if ( !(Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) ) {
  1830. //
  1831. // call MM to disable hard workingset
  1832. //
  1833. MmEnforceWorkingSetLimit(&Process->Vm, FALSE);
  1834. }
  1835. ExAcquireFastMutex (&Job->MemoryLimitsLock);
  1836. if ( Job->LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY ) {
  1837. Process->CommitChargeLimit = Job->ProcessMemoryLimit;
  1838. } else {
  1839. Process->CommitChargeLimit = 0;
  1840. }
  1841. ExReleaseFastMutex (&Job->MemoryLimitsLock);
  1842. //
  1843. // If the process is NOT IDLE Priority Class, and long fixed quantums
  1844. // are in use, use the scheduling class stored in the job object for this process
  1845. //
  1846. if ( Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE ) {
  1847. if ( PspUseJobSchedulingClasses ) {
  1848. Process->Pcb.ThreadQuantum = PspJobSchedulingClasses[Job->SchedulingClass];
  1849. }
  1850. //
  1851. // if the scheduling class is PSP_NUMBER_OF_SCHEDULING_CLASSES-1, then
  1852. // give this process non-preemptive scheduling
  1853. //
  1854. if ( Job->SchedulingClass == PSP_NUMBER_OF_SCHEDULING_CLASSES-1 ) {
  1855. KeSetDisableQuantumProcess(&Process->Pcb,TRUE);
  1856. } else {
  1857. KeSetDisableQuantumProcess(&Process->Pcb,FALSE);
  1858. }
  1859. }
  1860. }
  1861. NTSTATUS
  1862. NtTerminateJobObject(
  1863. IN HANDLE JobHandle,
  1864. IN NTSTATUS ExitStatus
  1865. )
  1866. {
  1867. PEJOB Job;
  1868. NTSTATUS st;
  1869. KPROCESSOR_MODE PreviousMode;
  1870. PETHREAD CurrentThread;
  1871. PAGED_CODE();
  1872. CurrentThread = PsGetCurrentThread ();
  1873. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  1874. st = ObReferenceObjectByHandle (JobHandle,
  1875. JOB_OBJECT_TERMINATE,
  1876. PsJobType,
  1877. PreviousMode,
  1878. &Job,
  1879. NULL);
  1880. if (!NT_SUCCESS(st)) {
  1881. return st;
  1882. }
  1883. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1884. ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);
  1885. PspTerminateAllProcessesInJob (Job,ExitStatus,FALSE);
  1886. ExReleaseResourceLite (&Job->JobLock);
  1887. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1888. ObDereferenceObject(Job);
  1889. return st;
  1890. }
  1891. VOID
  1892. PsEnforceExecutionTimeLimits(
  1893. VOID
  1894. )
  1895. {
  1896. PLIST_ENTRY NextJob;
  1897. LARGE_INTEGER RunningJobTime;
  1898. LARGE_INTEGER ProcessTime;
  1899. PEJOB Job;
  1900. PEPROCESS Process;
  1901. NTSTATUS st;
  1902. PAGED_CODE();
  1903. ExAcquireFastMutex (&PspJobListLock);
  1904. //
  1905. // Look at each job. If time limits are set for the job, then enforce them
  1906. //
  1907. NextJob = PspJobList.Flink;
  1908. while (NextJob != &PspJobList) {
  1909. Job = (PEJOB)(CONTAINING_RECORD (NextJob, EJOB, JobLinks));
  1910. if ( Job->LimitFlags & (JOB_OBJECT_LIMIT_PROCESS_TIME | JOB_OBJECT_LIMIT_JOB_TIME)) {
  1911. //
  1912. // Job looks like a candidate for time enforcing. Need to get the
  1913. // job lock to be sure, but we don't want to hang waiting for the
  1914. // job lock, so skip the job until next time around if we need to
  1915. //
  1916. //
  1917. if (ExAcquireResourceExclusiveLite (&Job->JobLock, FALSE)) {
  1918. if (Job->LimitFlags & (JOB_OBJECT_LIMIT_PROCESS_TIME | JOB_OBJECT_LIMIT_JOB_TIME)) {
  1919. //
  1920. // Job is setup for time limits
  1921. //
  1922. RunningJobTime.QuadPart = Job->ThisPeriodTotalUserTime.QuadPart;
  1923. for (Process = PspGetNextJobProcess (Job, NULL);
  1924. Process != NULL;
  1925. Process = PspGetNextJobProcess (Job, Process)) {
  1926. ProcessTime.QuadPart = UInt32x32To64 (Process->Pcb.UserTime,KeMaximumIncrement);
  1927. if (!(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED)) {
  1928. RunningJobTime.QuadPart += ProcessTime.QuadPart;
  1929. }
  1930. if (Job->LimitFlags & JOB_OBJECT_LIMIT_PROCESS_TIME ) {
  1931. if (ProcessTime.QuadPart > Job->PerProcessUserTimeLimit.QuadPart) {
  1932. //
  1933. // Process Time Limit has been exceeded.
  1934. //
  1935. // Reference the process. Assert that it is not in its
  1936. // delete routine. If all is OK, then nuke and dereferece
  1937. // the process
  1938. //
  1939. if (!(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE)) {
  1940. if (NT_SUCCESS (PspTerminateProcess (Process,ERROR_NOT_ENOUGH_QUOTA))) {
  1941. Job->TotalTerminatedProcesses++;
  1942. PS_SET_CLEAR_BITS (&Process->JobStatus,
  1943. PS_JOB_STATUS_NOT_REALLY_ACTIVE,
  1944. PS_JOB_STATUS_LAST_REPORT_MEMORY);
  1945. Job->ActiveProcesses--;
  1946. if (Job->CompletionPort != NULL) {
  1947. IoSetIoCompletion (Job->CompletionPort,
  1948. Job->CompletionKey,
  1949. (PVOID)Process->UniqueProcessId,
  1950. STATUS_SUCCESS,
  1951. JOB_OBJECT_MSG_END_OF_PROCESS_TIME,
  1952. FALSE);
  1953. }
  1954. PspFoldProcessAccountingIntoJob(Job,Process);
  1955. }
  1956. }
  1957. }
  1958. }
  1959. }
  1960. if (Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME) {
  1961. if (RunningJobTime.QuadPart > Job->PerJobUserTimeLimit.QuadPart ) {
  1962. //
  1963. // Job Time Limit has been exceeded.
  1964. //
  1965. // Perform the appropriate action
  1966. //
  1967. switch ( Job->EndOfJobTimeAction ) {
  1968. case JOB_OBJECT_TERMINATE_AT_END_OF_JOB:
  1969. if (PspTerminateAllProcessesInJob (Job, ERROR_NOT_ENOUGH_QUOTA, TRUE) ) {
  1970. if (Job->ActiveProcesses == 0) {
  1971. KeSetEvent (&Job->Event,0,FALSE);
  1972. if (Job->CompletionPort) {
  1973. IoSetIoCompletion(
  1974. Job->CompletionPort,
  1975. Job->CompletionKey,
  1976. NULL,
  1977. STATUS_SUCCESS,
  1978. JOB_OBJECT_MSG_END_OF_JOB_TIME,
  1979. FALSE
  1980. );
  1981. }
  1982. }
  1983. }
  1984. break;
  1985. case JOB_OBJECT_POST_AT_END_OF_JOB:
  1986. if (Job->CompletionPort) {
  1987. st = IoSetIoCompletion(
  1988. Job->CompletionPort,
  1989. Job->CompletionKey,
  1990. NULL,
  1991. STATUS_SUCCESS,
  1992. JOB_OBJECT_MSG_END_OF_JOB_TIME,
  1993. FALSE
  1994. );
  1995. if (NT_SUCCESS(st)) {
  1996. //
  1997. // Clear job level time limit
  1998. //
  1999. Job->LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_TIME;
  2000. Job->PerJobUserTimeLimit.QuadPart = 0;
  2001. }
  2002. } else {
  2003. if (PspTerminateAllProcessesInJob (Job,ERROR_NOT_ENOUGH_QUOTA,TRUE) ) {
  2004. if (Job->ActiveProcesses == 0) {
  2005. KeSetEvent(&Job->Event,0,FALSE);
  2006. }
  2007. }
  2008. }
  2009. break;
  2010. }
  2011. }
  2012. }
  2013. }
  2014. ExReleaseResourceLite(&Job->JobLock);
  2015. }
  2016. }
  2017. NextJob = NextJob->Flink;
  2018. }
  2019. ExReleaseFastMutex (&PspJobListLock);
  2020. }
  2021. BOOLEAN
  2022. PspTerminateAllProcessesInJob(
  2023. PEJOB Job,
  2024. NTSTATUS Status,
  2025. BOOLEAN IncCounter
  2026. )
  2027. {
  2028. PEPROCESS Process;
  2029. BOOLEAN TerminatedAProcess;
  2030. PAGED_CODE();
  2031. TerminatedAProcess = FALSE;
  2032. for (Process = PspGetNextJobProcess (Job, NULL);
  2033. Process != NULL;
  2034. Process = PspGetNextJobProcess (Job, Process)) {
  2035. if (!(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE)) {
  2036. if (NT_SUCCESS (PspTerminateProcess(Process,Status))) {
  2037. if (IncCounter) {
  2038. Job->TotalTerminatedProcesses++;
  2039. }
  2040. PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE);
  2041. Job->ActiveProcesses--;
  2042. PspFoldProcessAccountingIntoJob(Job,Process);
  2043. TerminatedAProcess = TRUE;
  2044. }
  2045. }
  2046. }
  2047. return TerminatedAProcess;
  2048. }
  2049. VOID
  2050. PspFoldProcessAccountingIntoJob(
  2051. PEJOB Job,
  2052. PEPROCESS Process
  2053. )
  2054. {
  2055. LARGE_INTEGER UserTime, KernelTime;
  2056. if ( !(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED) ) {
  2057. UserTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,KeMaximumIncrement);
  2058. KernelTime.QuadPart = UInt32x32To64(Process->Pcb.KernelTime,KeMaximumIncrement);
  2059. Job->TotalUserTime.QuadPart += UserTime.QuadPart;
  2060. Job->TotalKernelTime.QuadPart += KernelTime.QuadPart;
  2061. Job->ThisPeriodTotalUserTime.QuadPart += UserTime.QuadPart;
  2062. Job->ThisPeriodTotalKernelTime.QuadPart += KernelTime.QuadPart;
  2063. Job->ReadOperationCount += Process->ReadOperationCount.QuadPart;
  2064. Job->WriteOperationCount += Process->WriteOperationCount.QuadPart;
  2065. Job->OtherOperationCount += Process->OtherOperationCount.QuadPart;
  2066. Job->ReadTransferCount += Process->ReadTransferCount.QuadPart;
  2067. Job->WriteTransferCount += Process->WriteTransferCount.QuadPart;
  2068. Job->OtherTransferCount += Process->OtherTransferCount.QuadPart;
  2069. Job->TotalPageFaultCount += Process->Vm.PageFaultCount;
  2070. if ( Process->CommitChargePeak > Job->PeakProcessMemoryUsed ) {
  2071. Job->PeakProcessMemoryUsed = Process->CommitChargePeak;
  2072. }
  2073. PS_SET_CLEAR_BITS (&Process->JobStatus,
  2074. PS_JOB_STATUS_ACCOUNTING_FOLDED,
  2075. PS_JOB_STATUS_LAST_REPORT_MEMORY);
  2076. if ( Job->CompletionPort && Job->ActiveProcesses == 0) {
  2077. IoSetIoCompletion(
  2078. Job->CompletionPort,
  2079. Job->CompletionKey,
  2080. NULL,
  2081. STATUS_SUCCESS,
  2082. JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO,
  2083. FALSE
  2084. );
  2085. }
  2086. }
  2087. }
  2088. NTSTATUS
  2089. PspCaptureTokenFilter(
  2090. KPROCESSOR_MODE PreviousMode,
  2091. PJOBOBJECT_SECURITY_LIMIT_INFORMATION SecurityLimitInfo,
  2092. PPS_JOB_TOKEN_FILTER * TokenFilter
  2093. )
  2094. {
  2095. NTSTATUS Status ;
  2096. PPS_JOB_TOKEN_FILTER Filter ;
  2097. Filter = ExAllocatePoolWithTag( NonPagedPool,
  2098. sizeof( PS_JOB_TOKEN_FILTER ),
  2099. 'fTsP' );
  2100. if ( !Filter )
  2101. {
  2102. *TokenFilter = NULL ;
  2103. return STATUS_INSUFFICIENT_RESOURCES ;
  2104. }
  2105. RtlZeroMemory( Filter, sizeof( PS_JOB_TOKEN_FILTER ) );
  2106. try {
  2107. Status = STATUS_SUCCESS ;
  2108. //
  2109. // Capture Sids to remove
  2110. //
  2111. if (ARGUMENT_PRESENT (SecurityLimitInfo->SidsToDisable)) {
  2112. ProbeForReadSmallStructure (SecurityLimitInfo->SidsToDisable,
  2113. sizeof (TOKEN_GROUPS),
  2114. sizeof (ULONG));
  2115. Filter->CapturedGroupCount = SecurityLimitInfo->SidsToDisable->GroupCount;
  2116. Status = SeCaptureSidAndAttributesArray(
  2117. SecurityLimitInfo->SidsToDisable->Groups,
  2118. Filter->CapturedGroupCount,
  2119. PreviousMode,
  2120. NULL, 0,
  2121. NonPagedPool,
  2122. TRUE,
  2123. &Filter->CapturedGroups,
  2124. &Filter->CapturedGroupsLength
  2125. );
  2126. }
  2127. //
  2128. // Capture PrivilegesToDelete
  2129. //
  2130. if (NT_SUCCESS(Status) &&
  2131. ARGUMENT_PRESENT (SecurityLimitInfo->PrivilegesToDelete)) {
  2132. ProbeForReadSmallStructure (SecurityLimitInfo->PrivilegesToDelete,
  2133. sizeof (TOKEN_PRIVILEGES),
  2134. sizeof (ULONG));
  2135. Filter->CapturedPrivilegeCount = SecurityLimitInfo->PrivilegesToDelete->PrivilegeCount;
  2136. Status = SeCaptureLuidAndAttributesArray(
  2137. SecurityLimitInfo->PrivilegesToDelete->Privileges,
  2138. Filter->CapturedPrivilegeCount,
  2139. PreviousMode,
  2140. NULL, 0,
  2141. NonPagedPool,
  2142. TRUE,
  2143. &Filter->CapturedPrivileges,
  2144. &Filter->CapturedPrivilegesLength
  2145. );
  2146. }
  2147. //
  2148. // Capture Restricted Sids
  2149. //
  2150. if (NT_SUCCESS(Status) &&
  2151. ARGUMENT_PRESENT(SecurityLimitInfo->RestrictedSids)) {
  2152. ProbeForReadSmallStructure (SecurityLimitInfo->RestrictedSids,
  2153. sizeof (TOKEN_GROUPS),
  2154. sizeof (ULONG));
  2155. Filter->CapturedSidCount = SecurityLimitInfo->RestrictedSids->GroupCount;
  2156. Status = SeCaptureSidAndAttributesArray(
  2157. SecurityLimitInfo->RestrictedSids->Groups,
  2158. Filter->CapturedSidCount,
  2159. PreviousMode,
  2160. NULL, 0,
  2161. NonPagedPool,
  2162. TRUE,
  2163. &Filter->CapturedSids,
  2164. &Filter->CapturedSidsLength
  2165. );
  2166. }
  2167. } except(EXCEPTION_EXECUTE_HANDLER) {
  2168. Status = GetExceptionCode();
  2169. } // end_try
  2170. if ( !NT_SUCCESS( Status ) )
  2171. {
  2172. if ( Filter->CapturedSids )
  2173. {
  2174. ExFreePool( Filter->CapturedSids );
  2175. }
  2176. if ( Filter->CapturedPrivileges )
  2177. {
  2178. ExFreePool( Filter->CapturedPrivileges );
  2179. }
  2180. if ( Filter->CapturedGroups )
  2181. {
  2182. ExFreePool( Filter->CapturedGroups );
  2183. }
  2184. ExFreePool( Filter );
  2185. Filter = NULL ;
  2186. }
  2187. *TokenFilter = Filter ;
  2188. return Status ;
  2189. }
  2190. BOOLEAN
  2191. PsChangeJobMemoryUsage(
  2192. SSIZE_T Amount
  2193. )
  2194. {
  2195. PEPROCESS Process;
  2196. PEJOB Job;
  2197. SIZE_T CurrentJobMemoryUsed;
  2198. BOOLEAN ReturnValue;
  2199. ReturnValue = TRUE;
  2200. Process = PsGetCurrentProcess();
  2201. Job = Process->Job;
  2202. if ( Job ) {
  2203. //
  2204. // This routine can be called while holding the process lock (during
  2205. // teb deletion... So instead of using the job lock, we must use the
  2206. // memory limits lock. The lock order is always (job lock followed by
  2207. // process lock. The memory limits lock never nests or calls other
  2208. // code while held. It can be grapped while holding the job lock, or
  2209. // the process lock.
  2210. //
  2211. ExAcquireFastMutex (&Job->MemoryLimitsLock);
  2212. CurrentJobMemoryUsed = Job->CurrentJobMemoryUsed + Amount;
  2213. if ( Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY &&
  2214. CurrentJobMemoryUsed > Job->JobMemoryLimit ) {
  2215. CurrentJobMemoryUsed = Job->CurrentJobMemoryUsed;
  2216. ReturnValue = FALSE;
  2217. //
  2218. // Tell the job port that commit has been exceeded, and process id x
  2219. // was the one that hit it.
  2220. //
  2221. if ( Job->CompletionPort
  2222. && Process->UniqueProcessId
  2223. && (Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED)
  2224. && (Process->JobStatus & PS_JOB_STATUS_LAST_REPORT_MEMORY) == 0) {
  2225. PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_LAST_REPORT_MEMORY);
  2226. IoSetIoCompletion(
  2227. Job->CompletionPort,
  2228. Job->CompletionKey,
  2229. (PVOID)Process->UniqueProcessId,
  2230. STATUS_SUCCESS,
  2231. JOB_OBJECT_MSG_JOB_MEMORY_LIMIT,
  2232. TRUE
  2233. );
  2234. }
  2235. }
  2236. if (ReturnValue) {
  2237. Job->CurrentJobMemoryUsed = CurrentJobMemoryUsed;
  2238. //
  2239. // Update current and peak counters if this is an addition.
  2240. //
  2241. if (Amount > 0) {
  2242. if (CurrentJobMemoryUsed > Job->PeakJobMemoryUsed) {
  2243. Job->PeakJobMemoryUsed = CurrentJobMemoryUsed;
  2244. }
  2245. if (Process->CommitCharge + Amount > Job->PeakProcessMemoryUsed) {
  2246. Job->PeakProcessMemoryUsed = Process->CommitCharge + Amount;
  2247. }
  2248. }
  2249. }
  2250. ExReleaseFastMutex (&Job->MemoryLimitsLock);
  2251. }
  2252. return ReturnValue;
  2253. }
  2254. VOID
  2255. PsReportProcessMemoryLimitViolation(
  2256. VOID
  2257. )
  2258. {
  2259. PEPROCESS Process;
  2260. PEJOB Job;
  2261. PAGED_CODE();
  2262. Process = PsGetCurrentProcess();
  2263. Job = Process->Job;
  2264. if ( Job && (Job->LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) ) {
  2265. ExAcquireFastMutex (&Job->MemoryLimitsLock);
  2266. //
  2267. // Tell the job port that commit has been exceeded, and process id x
  2268. // was the one that hit it.
  2269. //
  2270. if ( Job->CompletionPort
  2271. && Process->UniqueProcessId
  2272. && (Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED)
  2273. && (Process->JobStatus & PS_JOB_STATUS_LAST_REPORT_MEMORY) == 0) {
  2274. PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_LAST_REPORT_MEMORY);
  2275. IoSetIoCompletion(
  2276. Job->CompletionPort,
  2277. Job->CompletionKey,
  2278. (PVOID)Process->UniqueProcessId,
  2279. STATUS_SUCCESS,
  2280. JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT,
  2281. TRUE
  2282. );
  2283. }
  2284. ExReleaseFastMutex (&Job->MemoryLimitsLock);
  2285. }
  2286. }
  2287. VOID
  2288. PspJobTimeLimitsWork(
  2289. IN PVOID Context
  2290. )
  2291. {
  2292. PAGED_CODE();
  2293. UNREFERENCED_PARAMETER (Context);
  2294. PsEnforceExecutionTimeLimits();
  2295. //
  2296. // Reset timer
  2297. //
  2298. ExAcquireFastMutex (&PspJobTimeLimitsLock);
  2299. if (!PspJobTimeLimitsShuttingDown) {
  2300. KeSetTimer (&PspJobTimeLimitsTimer,
  2301. PspJobTimeLimitsInterval,
  2302. &PspJobTimeLimitsDpc);
  2303. }
  2304. ExReleaseFastMutex (&PspJobTimeLimitsLock);
  2305. }
  2306. VOID
  2307. PspJobTimeLimitsDpcRoutine(
  2308. IN PKDPC Dpc,
  2309. IN PVOID DeferredContext,
  2310. IN PVOID SystemArgument1,
  2311. IN PVOID SystemArgument2
  2312. )
  2313. {
  2314. UNREFERENCED_PARAMETER (Dpc);
  2315. UNREFERENCED_PARAMETER (DeferredContext);
  2316. UNREFERENCED_PARAMETER (SystemArgument1);
  2317. UNREFERENCED_PARAMETER (SystemArgument2);
  2318. ExQueueWorkItem(&PspJobTimeLimitsWorkItem, DelayedWorkQueue);
  2319. }
  2320. VOID
  2321. PspInitializeJobStructures(
  2322. )
  2323. {
  2324. //
  2325. // Initialize job list head and mutex
  2326. //
  2327. InitializeListHead (&PspJobList);
  2328. ExInitializeFastMutex (&PspJobListLock);
  2329. //
  2330. // Initialize job time limits timer, etc
  2331. //
  2332. ExInitializeFastMutex (&PspJobTimeLimitsLock);
  2333. PspJobTimeLimitsShuttingDown = FALSE;
  2334. KeInitializeDpc (&PspJobTimeLimitsDpc,
  2335. PspJobTimeLimitsDpcRoutine,
  2336. NULL);
  2337. ExInitializeWorkItem (&PspJobTimeLimitsWorkItem, PspJobTimeLimitsWork, NULL);
  2338. KeInitializeTimer (&PspJobTimeLimitsTimer);
  2339. PspJobTimeLimitsInterval.QuadPart = Int32x32To64(PSP_ONE_SECOND,
  2340. PSP_JOB_TIME_LIMITS_TIME);
  2341. }
  2342. VOID
  2343. PspInitializeJobStructuresPhase1(
  2344. )
  2345. {
  2346. //
  2347. // Wait until Phase1 executive initialization completes (ie: the worker
  2348. // queues must be initialized) before setting off our DPC timer (which
  2349. // queues work items!).
  2350. //
  2351. KeSetTimer (&PspJobTimeLimitsTimer,
  2352. PspJobTimeLimitsInterval,
  2353. &PspJobTimeLimitsDpc);
  2354. }
  2355. VOID
  2356. PspShutdownJobLimits(
  2357. VOID
  2358. )
  2359. {
  2360. // Cancel the job time limits enforcement worker
  2361. ExAcquireFastMutex (&PspJobTimeLimitsLock);
  2362. PspJobTimeLimitsShuttingDown = TRUE;
  2363. KeCancelTimer (&PspJobTimeLimitsTimer);
  2364. ExReleaseFastMutex (&PspJobTimeLimitsLock);
  2365. }
  2366. NTSTATUS
  2367. NtIsProcessInJob (
  2368. IN HANDLE ProcessHandle,
  2369. IN HANDLE JobHandle
  2370. )
  2371. /*++
  2372. Routine Description:
  2373. This finds out if a process is in a specific or any job
  2374. Arguments:
  2375. ProcessHandle - Handle to process to be checked
  2376. JobHandle - Handle of job to check process against, May be NULL to do general query.
  2377. Return Value:
  2378. NTSTATUS - Status of call
  2379. --*/
  2380. {
  2381. KPROCESSOR_MODE PreviousMode;
  2382. PEPROCESS Process;
  2383. PEJOB Job;
  2384. NTSTATUS Status;
  2385. PreviousMode = KeGetPreviousMode ();
  2386. Status = ObReferenceObjectByHandle (ProcessHandle,
  2387. PROCESS_QUERY_INFORMATION,
  2388. PsProcessType,
  2389. PreviousMode,
  2390. &Process,
  2391. NULL);
  2392. if (!NT_SUCCESS (Status)) {
  2393. return Status;
  2394. }
  2395. if (JobHandle == NULL) {
  2396. Job = Process->Job;
  2397. } else {
  2398. Status = ObReferenceObjectByHandle (JobHandle,
  2399. JOB_OBJECT_QUERY,
  2400. PsJobType,
  2401. PreviousMode,
  2402. &Job,
  2403. NULL);
  2404. if (!NT_SUCCESS (Status)) {
  2405. ObDereferenceObject (Process);
  2406. return Status;
  2407. }
  2408. }
  2409. if (Process->Job == NULL || Process->Job != Job) {
  2410. Status = STATUS_PROCESS_NOT_IN_JOB;
  2411. } else {
  2412. Status = STATUS_PROCESS_IN_JOB;
  2413. }
  2414. if (JobHandle != NULL) {
  2415. ObDereferenceObject (Job);
  2416. }
  2417. ObDereferenceObject (Process);
  2418. return Status;
  2419. }
  2420. NTSTATUS
  2421. PspGetJobFromSet (
  2422. IN PEJOB ParentJob,
  2423. IN ULONG JobMemberLevel,
  2424. OUT PEJOB *pJob)
  2425. /*++
  2426. Routine Description:
  2427. The function selects the job a process will run in. Either the same job as the parent or a job in the same
  2428. job set as the parent but with a JobMemberLevel >= to the parents level/
  2429. Arguments:
  2430. ParentJob - Job the parent is in.
  2431. JobMemberLevel - Member level requested for this process. Zero for use parents job.
  2432. Pjob - Returned job to place process in.
  2433. Return Value:
  2434. NTSTATUS - Status of call
  2435. --*/
  2436. {
  2437. PLIST_ENTRY Entry;
  2438. PEJOB Job;
  2439. NTSTATUS Status;
  2440. //
  2441. // This is the normal case. We are not asking to be moved jobs or we are askign for our current level
  2442. //
  2443. if (JobMemberLevel == 0) {
  2444. ObReferenceObject (ParentJob);
  2445. *pJob = ParentJob;
  2446. return STATUS_SUCCESS;
  2447. }
  2448. ExAcquireFastMutex (&PspJobListLock);
  2449. Status = STATUS_ACCESS_DENIED;
  2450. if (ParentJob->MemberLevel != 0 && ParentJob->MemberLevel <= JobMemberLevel) {
  2451. for (Entry = ParentJob->JobSetLinks.Flink;
  2452. Entry != &ParentJob->JobSetLinks;
  2453. Entry = Entry->Flink) {
  2454. Job = CONTAINING_RECORD (Entry, EJOB, JobSetLinks);
  2455. if (Job->MemberLevel == JobMemberLevel &&
  2456. ObReferenceObjectSafe (Job)) {
  2457. *pJob = Job;
  2458. Status = STATUS_SUCCESS;
  2459. break;
  2460. }
  2461. }
  2462. }
  2463. ExReleaseFastMutex (&PspJobListLock);
  2464. return Status;
  2465. }
  2466. NTSTATUS
  2467. NtCreateJobSet (
  2468. IN ULONG NumJob,
  2469. IN PJOB_SET_ARRAY UserJobSet,
  2470. IN ULONG Flags)
  2471. /*++
  2472. Routine Description:
  2473. This function creates a job set from multiple job objects.
  2474. Arguments:
  2475. NumJob - Number of jobs in JobSet
  2476. UserJobSet - Pointer to array of jobs to combine
  2477. Flags - Flags mask for future expansion
  2478. Return Value:
  2479. NTSTATUS - Status of call
  2480. --*/
  2481. {
  2482. KPROCESSOR_MODE PreviousMode;
  2483. NTSTATUS Status;
  2484. ULONG_PTR BufLen;
  2485. PJOB_SET_ARRAY JobSet;
  2486. ULONG JobsProcessed;
  2487. PEJOB Job;
  2488. ULONG MinMemberLevel;
  2489. PEJOB HeadJob;
  2490. PLIST_ENTRY ListEntry;
  2491. //
  2492. // Flags must be zero and number of jobs >= 2 and not overflow when the length is caculated
  2493. //
  2494. if (Flags != 0) {
  2495. return STATUS_INVALID_PARAMETER;
  2496. }
  2497. if (NumJob <= 1 || NumJob > MAXULONG_PTR / sizeof (JobSet[0])) {
  2498. return STATUS_INVALID_PARAMETER;
  2499. }
  2500. BufLen = NumJob * sizeof (JobSet[0]);
  2501. JobSet = ExAllocatePoolWithQuotaTag (PagedPool|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE, BufLen, 'bjsP');
  2502. if (JobSet == NULL) {
  2503. return STATUS_INSUFFICIENT_RESOURCES;
  2504. }
  2505. PreviousMode = KeGetPreviousMode ();
  2506. try {
  2507. if (PreviousMode == UserMode) {
  2508. ProbeForRead (UserJobSet, BufLen, TYPE_ALIGNMENT (JOB_SET_ARRAY));
  2509. }
  2510. RtlCopyMemory (JobSet, UserJobSet, BufLen);
  2511. } except (ExSystemExceptionFilter ()) {
  2512. ExFreePool (JobSet);
  2513. return GetExceptionCode ();
  2514. }
  2515. MinMemberLevel = 0;
  2516. Status = STATUS_SUCCESS;
  2517. for (JobsProcessed = 0; JobsProcessed < NumJob; JobsProcessed++) {
  2518. if (JobSet[JobsProcessed].MemberLevel <= MinMemberLevel || JobSet[JobsProcessed].Flags != 0) {
  2519. Status = STATUS_INVALID_PARAMETER;
  2520. break;
  2521. }
  2522. MinMemberLevel = JobSet[JobsProcessed].MemberLevel;
  2523. Status = ObReferenceObjectByHandle (JobSet[JobsProcessed].JobHandle,
  2524. JOB_OBJECT_QUERY,
  2525. PsJobType,
  2526. PreviousMode,
  2527. &Job,
  2528. NULL);
  2529. if (!NT_SUCCESS (Status)) {
  2530. break;
  2531. }
  2532. JobSet[JobsProcessed].JobHandle = Job;
  2533. }
  2534. if (!NT_SUCCESS (Status)) {
  2535. while (JobsProcessed-- > 0) {
  2536. Job = JobSet[JobsProcessed].JobHandle;
  2537. ObDereferenceObject (Job);
  2538. }
  2539. ExFreePool (JobSet);
  2540. return Status;
  2541. }
  2542. ExAcquireFastMutex (&PspJobListLock);
  2543. HeadJob = NULL;
  2544. for (JobsProcessed = 0; JobsProcessed < NumJob; JobsProcessed++) {
  2545. Job = JobSet[JobsProcessed].JobHandle;
  2546. //
  2547. // If we are already in a job set then reject this call.
  2548. //
  2549. if (Job->MemberLevel != 0) {
  2550. Status = STATUS_INVALID_PARAMETER;
  2551. break;
  2552. }
  2553. if (HeadJob != NULL) {
  2554. if (HeadJob == Job) {
  2555. Status = STATUS_INVALID_PARAMETER;
  2556. break;
  2557. }
  2558. InsertTailList (&HeadJob->JobSetLinks, &Job->JobSetLinks);
  2559. } else {
  2560. HeadJob = Job;
  2561. }
  2562. Job->MemberLevel = JobSet[JobsProcessed].MemberLevel;
  2563. }
  2564. if (!NT_SUCCESS (Status)) {
  2565. if (HeadJob) {
  2566. while (!IsListEmpty (&HeadJob->JobSetLinks)) {
  2567. ListEntry = RemoveHeadList (&HeadJob->JobSetLinks);
  2568. Job = CONTAINING_RECORD (ListEntry, EJOB, JobSetLinks);
  2569. Job->MemberLevel = 0;
  2570. InitializeListHead (&Job->JobSetLinks);
  2571. }
  2572. HeadJob->MemberLevel = 0;
  2573. }
  2574. }
  2575. ExReleaseFastMutex (&PspJobListLock);
  2576. //
  2577. // Dereference all the objects in the error path. If we suceeded then pin all but the first object by
  2578. // leaving the reference there.
  2579. //
  2580. if (!NT_SUCCESS (Status)) {
  2581. for (JobsProcessed = 0; JobsProcessed < NumJob; JobsProcessed++) {
  2582. Job = JobSet[JobsProcessed].JobHandle;
  2583. ObDereferenceObject (Job);
  2584. }
  2585. } else {
  2586. Job = JobSet[0].JobHandle;
  2587. ObDereferenceObject (Job);
  2588. }
  2589. ExFreePool (JobSet);
  2590. return Status;
  2591. }
  2592. NTSTATUS
  2593. PspWin32SessionCallout(
  2594. IN PKWIN32_JOB_CALLOUT CalloutRoutine,
  2595. IN PKWIN32_JOBCALLOUT_PARAMETERS Parameters,
  2596. IN ULONG SessionId
  2597. )
  2598. /*++
  2599. Routine Description:
  2600. This routine calls the specified callout routine in session space, for the
  2601. specified session.
  2602. Parameters:
  2603. CalloutRoutine - Callout routine in session space.
  2604. Parameters - Parameters to pass the callout routine.
  2605. SessionId - Specifies the ID of the session in which the specified
  2606. callout routine is to be called.
  2607. Return Value:
  2608. Status code that indicates whether or not the function was successful.
  2609. Notes:
  2610. Returns STATUS_NOT_FOUND if the specified session was not found.
  2611. --*/
  2612. {
  2613. NTSTATUS Status;
  2614. PVOID OpaqueSession;
  2615. KAPC_STATE ApcState;
  2616. PEPROCESS Process;
  2617. PAGED_CODE();
  2618. //
  2619. // Make sure we have all the information we need to deliver notification.
  2620. //
  2621. if (CalloutRoutine == NULL) {
  2622. return STATUS_INVALID_PARAMETER;
  2623. }
  2624. //
  2625. // Make sure the callout routine in session space.
  2626. //
  2627. ASSERT(MmIsSessionAddress((PVOID)CalloutRoutine));
  2628. Process = PsGetCurrentProcess();
  2629. if ((Process->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
  2630. (SessionId == MmGetSessionId (Process))) {
  2631. //
  2632. // If the call is from a user mode process, and we are asked to call the
  2633. // current session, call directly.
  2634. //
  2635. (CalloutRoutine)(Parameters);
  2636. Status = STATUS_SUCCESS;
  2637. } else {
  2638. //
  2639. // Reference the session object for the specified session.
  2640. //
  2641. OpaqueSession = MmGetSessionById (SessionId);
  2642. if (OpaqueSession == NULL) {
  2643. return STATUS_NOT_FOUND;
  2644. }
  2645. //
  2646. // Attach to the specified session.
  2647. //
  2648. Status = MmAttachSession(OpaqueSession, &ApcState);
  2649. if (!NT_SUCCESS(Status)) {
  2650. KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_WARNING_LEVEL,
  2651. "PspWin32SessionCallout: "
  2652. "could not attach to 0x%p, session %d for registered notification callout @ 0x%p\n",
  2653. OpaqueSession,
  2654. SessionId,
  2655. CalloutRoutine));
  2656. MmQuitNextSession(OpaqueSession);
  2657. return Status;
  2658. }
  2659. //
  2660. // Dispatch notification to the callout routine.
  2661. //
  2662. (CalloutRoutine)(Parameters);
  2663. //
  2664. // Detach from the session.
  2665. //
  2666. Status = MmDetachSession (OpaqueSession, &ApcState);
  2667. ASSERT(NT_SUCCESS(Status));
  2668. //
  2669. // Dereference the session object.
  2670. //
  2671. Status = MmQuitNextSession (OpaqueSession);
  2672. ASSERT(NT_SUCCESS(Status));
  2673. }
  2674. return Status;
  2675. }