Leaked source code of windows server 2003
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.

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