Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

psjob.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1997 Microsoft Corporation 00004 00005 Module Name: 00006 00007 psjob.c 00008 00009 Abstract: 00010 00011 This module implements bulk of the job object support 00012 00013 Author: 00014 00015 Mark Lucovsky (markl) 22-May-1997 00016 00017 Revision History: 00018 00019 --*/ 00020 00021 #include "psp.h" 00022 #include "winerror.h" 00023 00024 #pragma alloc_text(PAGE, PspJobDelete) 00025 #pragma alloc_text(PAGE, PspJobClose) 00026 #pragma alloc_text(PAGE, NtCreateJobObject) 00027 #pragma alloc_text(PAGE, NtOpenJobObject) 00028 #pragma alloc_text(PAGE, NtAssignProcessToJobObject) 00029 #pragma alloc_text(PAGE, PspAddProcessToJob) 00030 #pragma alloc_text(PAGE, PspRemoveProcessFromJob) 00031 #pragma alloc_text(PAGE, PspExitProcessFromJob) 00032 #pragma alloc_text(PAGE, NtQueryInformationJobObject) 00033 #pragma alloc_text(PAGE, NtSetInformationJobObject) 00034 #pragma alloc_text(PAGE, PspApplyJobLimitsToProcessSet) 00035 #pragma alloc_text(PAGE, PspApplyJobLimitsToProcess) 00036 #pragma alloc_text(PAGE, NtTerminateJobObject) 00037 #pragma alloc_text(PAGE, PspTerminateAllProcessesInJob) 00038 #pragma alloc_text(PAGE, PspFoldProcessAccountingIntoJob) 00039 #pragma alloc_text(PAGE, PspCaptureTokenFilter) 00040 00041 // 00042 // move to io.h 00043 extern POBJECT_TYPE IoCompletionObjectType; 00044 00045 00046 NTSTATUS 00047 NTAPI 00048 NtCreateJobObject ( 00049 OUT PHANDLE JobHandle, 00050 IN ACCESS_MASK DesiredAccess, 00051 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL 00052 ) 00053 { 00054 00055 PEJOB Job; 00056 HANDLE Handle; 00057 KPROCESSOR_MODE PreviousMode; 00058 NTSTATUS Status; 00059 00060 PAGED_CODE(); 00061 00062 // 00063 // Establish an exception handler, probe the output handle address, and 00064 // attempt to create a job object. If the probe fails, then return the 00065 // exception code as the service status. Otherwise return the status value 00066 // returned by the object insertion routine. 00067 // 00068 00069 try { 00070 00071 // 00072 // Get previous processor mode and probe output handle address if 00073 // necessary. 00074 // 00075 00076 PreviousMode = KeGetPreviousMode(); 00077 if (PreviousMode != KernelMode) { 00078 ProbeForWriteHandle(JobHandle); 00079 } 00080 00081 // 00082 // Allocate job object. 00083 // 00084 00085 Status = ObCreateObject( 00086 PreviousMode, 00087 PsJobType, 00088 ObjectAttributes, 00089 PreviousMode, 00090 NULL, 00091 sizeof(EJOB), 00092 0, 00093 0, 00094 (PVOID *)&Job 00095 ); 00096 00097 // 00098 // If the job object was successfully allocated, then initialize it 00099 // and attempt to insert the job object in the current 00100 // process' handle table. 00101 // 00102 00103 if (NT_SUCCESS(Status)) { 00104 00105 RtlZeroMemory(Job,sizeof(EJOB)); 00106 InitializeListHead(&Job->ProcessListHead); 00107 KeInitializeEvent(&Job->Event,NotificationEvent,FALSE); 00108 00109 // 00110 // Job Object gets the SessionId of the Process creating the Job 00111 // We will use this sessionid to restrict the processes that can 00112 // be added to a job. 00113 // 00114 Job->SessionId = PsGetCurrentProcess()->SessionId; 00115 00116 // 00117 // Initialize the scheduling class for the Job 00118 // 00119 Job->SchedulingClass = PSP_DEFAULT_SCHEDULING_CLASSES; 00120 00121 // 00122 // Insert Job on Job List 00123 // 00124 00125 ExAcquireFastMutex(&PspJobListLock); 00126 InsertTailList(&PspJobList,&Job->JobLinks); 00127 ExReleaseFastMutex(&PspJobListLock); 00128 00129 Status = ExInitializeResource(&Job->JobLock); 00130 if ( !NT_SUCCESS(Status) ) { 00131 00132 // 00133 // Note that ExInitializeResource really can't fail and 00134 // is hard coded to return success. 00135 // 00136 ObDereferenceObject(Job); 00137 00138 } 00139 else { 00140 ExInitializeFastMutex(&Job->MemoryLimitsLock); 00141 Status = ObInsertObject( 00142 (PVOID)Job, 00143 NULL, 00144 DesiredAccess, 00145 0, 00146 (PVOID *)NULL, 00147 &Handle 00148 ); 00149 } 00150 00151 // 00152 // If the job object was successfully inserted in the current 00153 // process' handle table, then attempt to write the job object 00154 // handle value. If the write attempt fails, then do not report 00155 // an error. When the caller attempts to access the handle value, 00156 // an access violation will occur. 00157 // 00158 00159 if (NT_SUCCESS(Status)) { 00160 try { 00161 *JobHandle = Handle; 00162 } 00163 except(ExSystemExceptionFilter()) { 00164 } 00165 } 00166 } 00167 00168 // 00169 // If an exception occurs during the probe of the output handle address, 00170 // then always handle the exception and return the exception code as the 00171 // status value. 00172 // 00173 00174 } 00175 except(ExSystemExceptionFilter()) { 00176 return GetExceptionCode(); 00177 } 00178 00179 // 00180 // Return service status. 00181 // 00182 00183 return Status; 00184 } 00185 00186 NTSTATUS 00187 NTAPI 00188 NtOpenJobObject( 00189 OUT PHANDLE JobHandle, 00190 IN ACCESS_MASK DesiredAccess, 00191 IN POBJECT_ATTRIBUTES ObjectAttributes 00192 ) 00193 { 00194 HANDLE Handle; 00195 KPROCESSOR_MODE PreviousMode; 00196 NTSTATUS Status; 00197 00198 PAGED_CODE(); 00199 00200 // 00201 // Establish an exception handler, probe the output handle address, and 00202 // attempt to open the job object. If the probe fails, then return the 00203 // exception code as the service status. Otherwise return the status value 00204 // returned by the object open routine. 00205 // 00206 00207 try { 00208 00209 // 00210 // Get previous processor mode and probe output handle address 00211 // if necessary. 00212 // 00213 00214 PreviousMode = KeGetPreviousMode(); 00215 if (PreviousMode != KernelMode) { 00216 ProbeForWriteHandle(JobHandle); 00217 } 00218 00219 // 00220 // Open handle to the event object with the specified desired access. 00221 // 00222 00223 Status = ObOpenObjectByName( 00224 ObjectAttributes, 00225 PsJobType, 00226 PreviousMode, 00227 NULL, 00228 DesiredAccess, 00229 NULL, 00230 &Handle 00231 ); 00232 00233 // 00234 // If the open was successful, then attempt to write the job object 00235 // handle value. If the write attempt fails, then do not report an 00236 // error. When the caller attempts to access the handle value, an 00237 // access violation will occur. 00238 // 00239 00240 if (NT_SUCCESS(Status)) { 00241 try { 00242 *JobHandle = Handle; 00243 } 00244 except(ExSystemExceptionFilter()) { 00245 } 00246 } 00247 00248 } 00249 00250 except(ExSystemExceptionFilter()) { 00251 00252 // 00253 // If an exception occurs during the probe of the output job handle, 00254 // then always handle the exception and return the exception code as the 00255 // status value. 00256 // 00257 00258 Status = GetExceptionCode(); 00259 } 00260 00261 return Status; 00262 } 00263 00264 NTSTATUS 00265 NTAPI 00266 NtAssignProcessToJobObject( 00267 IN HANDLE JobHandle, 00268 IN HANDLE ProcessHandle 00269 ) 00270 { 00271 PEJOB Job; 00272 PEPROCESS Process; 00273 NTSTATUS Status; 00274 KPROCESSOR_MODE PreviousMode; 00275 BOOLEAN IsAdmin ; 00276 00277 PAGED_CODE(); 00278 00279 PreviousMode = KeGetPreviousMode(); 00280 00281 // 00282 // Reference the process object, lock the process, test for already been assigned 00283 // 00284 00285 Status = ObReferenceObjectByHandle( 00286 ProcessHandle, 00287 PROCESS_SET_QUOTA | PROCESS_TERMINATE, 00288 PsProcessType, 00289 PreviousMode, 00290 (PVOID *)&Process, 00291 NULL 00292 ); 00293 if ( !NT_SUCCESS(Status) ) { 00294 return Status; 00295 } 00296 00297 // 00298 // Quick Check for prior assignment 00299 // 00300 00301 if ( Process->Job ) { 00302 ObDereferenceObject(Process); 00303 return STATUS_ACCESS_DENIED; 00304 } 00305 00306 00307 // 00308 // Now reference the job object. Then we need to lock the process and check again 00309 // 00310 00311 Status = ObReferenceObjectByHandle( 00312 JobHandle, 00313 JOB_OBJECT_ASSIGN_PROCESS, 00314 PsJobType, 00315 PreviousMode, 00316 (PVOID *)&Job, 00317 NULL 00318 ); 00319 if ( !NT_SUCCESS(Status) ) { 00320 ObDereferenceObject(Process); 00321 return Status; 00322 } 00323 00324 // 00325 // We only allow a process that is running in the Job creator's hydra session 00326 // to be assigned to the job. 00327 // 00328 00329 if ( Process->SessionId != Job->SessionId ) { 00330 00331 ObDereferenceObject(Process); 00332 ObDereferenceObject(Job); 00333 return STATUS_ACCESS_DENIED; 00334 00335 } 00336 00337 00338 Status = PsLockProcess(Process,PreviousMode,PsLockPollOnTimeout); 00339 if ( !NT_SUCCESS(Status) || Process->Job ) { 00340 if ( !NT_SUCCESS(Status) ) { 00341 Status = STATUS_PROCESS_IS_TERMINATING; 00342 } 00343 else { 00344 Status = STATUS_ACCESS_DENIED; 00345 PsUnlockProcess(Process); 00346 } 00347 ObDereferenceObject(Process); 00348 ObDereferenceObject(Job); 00349 return Status ; 00350 } 00351 00352 // 00353 // Security Rules: If the job has no-admin set, and it is running 00354 // as admin, that's not allowed 00355 // 00356 00357 if ( Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_NO_ADMIN ) 00358 { 00359 PsLockProcessSecurityFields(); 00360 00361 IsAdmin = SeTokenIsAdmin( Process->Token ); 00362 00363 PsFreeProcessSecurityFields(); 00364 00365 if ( IsAdmin ) 00366 { 00367 Status = STATUS_ACCESS_DENIED ; 00368 00369 PsUnlockProcess( Process ); 00370 00371 ObDereferenceObject( Process ); 00372 00373 ObDereferenceObject( Job ); 00374 00375 return Status ; 00376 } 00377 00378 } 00379 00380 // 00381 // If the job has a token filter established, 00382 // use it to filter the 00383 00384 // 00385 // ref the job for the process 00386 // 00387 00388 ObReferenceObject(Job); 00389 00390 Process->Job = Job; 00391 00392 PsUnlockProcess(Process); 00393 00394 Status = PspAddProcessToJob(Job,Process); 00395 if ( !NT_SUCCESS(Status) ) { 00396 Job->TotalTerminatedProcesses++; 00397 PspTerminateProcess(Process,ERROR_NOT_ENOUGH_QUOTA,PsLockPollOnTimeout); 00398 } 00399 00400 // 00401 // If the job has UI restrictions and this is a GUI process, call ntuser 00402 // 00403 if ( ( Job->UIRestrictionsClass != JOB_OBJECT_UILIMIT_NONE ) && 00404 ( Process->Win32Process != NULL ) ) { 00405 WIN32_JOBCALLOUT_PARAMETERS Parms; 00406 00407 KeEnterCriticalRegion(); 00408 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 00409 00410 Parms.Job = Job; 00411 Parms.CalloutType = PsW32JobCalloutAddProcess; 00412 Parms.Data = Process->Win32Process; 00413 MmDispatchWin32Callout( PspW32JobCallout,NULL, (PVOID)&Parms, &(Job->SessionId)); 00414 00415 ExReleaseResource(&Job->JobLock); 00416 KeLeaveCriticalRegion(); 00417 00418 } 00419 00420 if ( Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_ONLY_TOKEN ) 00421 { 00422 Status = PspSetPrimaryToken( ProcessHandle, NULL, Job->Token ); 00423 00424 if ( !NT_SUCCESS( Status ) ) 00425 { 00426 // 00427 // What? 00428 // 00429 } 00430 } 00431 00432 ObDereferenceObject(Process); 00433 ObDereferenceObject(Job); 00434 00435 return Status; 00436 } 00437 00438 NTSTATUS 00439 PspAddProcessToJob( 00440 PEJOB Job, 00441 PEPROCESS Process 00442 ) 00443 { 00444 00445 NTSTATUS Status; 00446 SIZE_T MinWs,MaxWs; 00447 00448 PAGED_CODE(); 00449 00450 00451 KeEnterCriticalRegion(); 00452 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 00453 00454 InsertTailList(&Job->ProcessListHead,&Process->JobLinks); 00455 00456 // 00457 // Update relevant ADD accounting info. 00458 // 00459 00460 Job->TotalProcesses++; 00461 Job->ActiveProcesses++; 00462 00463 00464 00465 // 00466 // Test for active process count exceeding limit 00467 // 00468 00469 Status = STATUS_SUCCESS; 00470 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS && 00471 Job->ActiveProcesses > Job->ActiveProcessLimit ) { 00472 00473 PS_SET_CLEAR_BITS (&Process->JobStatus, 00474 PS_JOB_STATUS_NOT_REALLY_ACTIVE | PS_JOB_STATUS_ACCOUNTING_FOLDED, 00475 PS_JOB_STATUS_LAST_REPORT_MEMORY); 00476 00477 Job->ActiveProcesses--; 00478 00479 if ( Job->CompletionPort ) { 00480 IoSetIoCompletion( 00481 Job->CompletionPort, 00482 Job->CompletionKey, 00483 NULL, 00484 STATUS_SUCCESS, 00485 JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT, 00486 TRUE 00487 ); 00488 } 00489 00490 Status = STATUS_QUOTA_EXCEEDED; 00491 } 00492 00493 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME && KeReadStateEvent(&Job->Event) ) { 00494 PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE | PS_JOB_STATUS_ACCOUNTING_FOLDED); 00495 00496 Job->ActiveProcesses--; 00497 00498 Status = STATUS_QUOTA_EXCEEDED; 00499 } 00500 00501 if ( Status == STATUS_SUCCESS ) { 00502 00503 PspApplyJobLimitsToProcess(Job,Process); 00504 00505 if ( Process->Job->CompletionPort 00506 && Process->UniqueProcessId 00507 && !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) 00508 && !(Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED)) { 00509 00510 PS_SET_CLEAR_BITS (&Process->JobStatus, 00511 PS_JOB_STATUS_NEW_PROCESS_REPORTED, 00512 PS_JOB_STATUS_LAST_REPORT_MEMORY); 00513 00514 IoSetIoCompletion( 00515 Job->CompletionPort, 00516 Job->CompletionKey, 00517 (PVOID)Process->UniqueProcessId, 00518 STATUS_SUCCESS, 00519 JOB_OBJECT_MSG_NEW_PROCESS, 00520 FALSE 00521 ); 00522 } 00523 00524 } 00525 00526 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET ) { 00527 MinWs = Job->MinimumWorkingSetSize; 00528 MaxWs = Job->MaximumWorkingSetSize; 00529 } 00530 else { 00531 MinWs = 0; 00532 MaxWs = 0; 00533 } 00534 00535 ExReleaseResource(&Job->JobLock); 00536 00537 KeLeaveCriticalRegion(); 00538 00539 if ( Status == STATUS_SUCCESS ) { 00540 00541 if ( MinWs != 0 && MaxWs != 0 ) { 00542 00543 KeEnterCriticalRegion(); 00544 ExAcquireFastMutexUnsafe(&PspWorkingSetChangeHead.Lock); 00545 00546 KeAttachProcess (&Process->Pcb); 00547 MmAdjustWorkingSetSize(MinWs,MaxWs,FALSE); 00548 00549 // 00550 // call MM to Enable hard workingset 00551 // 00552 00553 MmEnforceWorkingSetLimit(&Process->Vm, TRUE); 00554 00555 KeDetachProcess(); 00556 00557 ExReleaseFastMutexUnsafe(&PspWorkingSetChangeHead.Lock); 00558 KeLeaveCriticalRegion(); 00559 00560 } 00561 else { 00562 MmEnforceWorkingSetLimit(&Process->Vm, FALSE); 00563 } 00564 00565 if ( !MmAssignProcessToJob(Process) ) { 00566 Status = STATUS_QUOTA_EXCEEDED; 00567 } 00568 00569 } 00570 00571 return Status; 00572 } 00573 00574 // 00575 // Only callable from process delete routine ! 00576 // This means that if the above fails, failure is termination of the process ! 00577 // 00578 VOID 00579 PspRemoveProcessFromJob( 00580 PEJOB Job, 00581 PEPROCESS Process 00582 ) 00583 { 00584 PAGED_CODE(); 00585 00586 KeEnterCriticalRegion(); 00587 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 00588 00589 RemoveEntryList(&Process->JobLinks); 00590 00591 // 00592 // Update REMOVE accounting info 00593 // 00594 00595 00596 if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) ) { 00597 Job->ActiveProcesses--; 00598 PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE); 00599 } 00600 00601 PspFoldProcessAccountingIntoJob(Job,Process); 00602 00603 ExReleaseResource(&Job->JobLock); 00604 KeLeaveCriticalRegion(); 00605 } 00606 00607 VOID 00608 PspExitProcessFromJob( 00609 PEJOB Job, 00610 PEPROCESS Process 00611 ) 00612 { 00613 00614 PAGED_CODE(); 00615 00616 KeEnterCriticalRegion(); 00617 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 00618 00619 // 00620 // Update REMOVE accounting info 00621 // 00622 00623 00624 if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) ) { 00625 Job->ActiveProcesses--; 00626 PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE); 00627 } 00628 00629 PspFoldProcessAccountingIntoJob(Job,Process); 00630 00631 ExReleaseResource(&Job->JobLock); 00632 KeLeaveCriticalRegion(); 00633 } 00634 00635 VOID 00636 PspJobDelete( 00637 IN PVOID Object 00638 ) 00639 { 00640 PEJOB Job; 00641 WIN32_JOBCALLOUT_PARAMETERS Parms; 00642 00643 PAGED_CODE(); 00644 00645 Job = (PEJOB) Object; 00646 00647 // 00648 // call ntuser to delete its job structure 00649 // 00650 00651 KeEnterCriticalRegion(); 00652 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 00653 00654 00655 Parms.Job = Job; 00656 Parms.CalloutType = PsW32JobCalloutTerminate; 00657 Parms.Data = NULL; 00658 MmDispatchWin32Callout( PspW32JobCallout,NULL, (PVOID)&Parms, &(Job->SessionId)); 00659 00660 ExReleaseResource(&Job->JobLock); 00661 KeLeaveCriticalRegion(); 00662 00663 Job->LimitFlags = 0; 00664 00665 if ( Job->CompletionPort ) { 00666 ObDereferenceObject(Job->CompletionPort); 00667 Job->CompletionPort = NULL; 00668 } 00669 00670 00671 // 00672 // Remove Job on Job List 00673 // 00674 00675 ExAcquireFastMutex(&PspJobListLock); 00676 RemoveEntryList(&Job->JobLinks); 00677 ExReleaseFastMutex(&PspJobListLock); 00678 00679 // 00680 // Free Security clutter: 00681 // 00682 00683 if ( Job->Token ) { 00684 ObDereferenceObject( Job->Token ); 00685 Job->Token = NULL ; 00686 } 00687 00688 if ( Job->Filter ) { 00689 if ( Job->Filter->CapturedSids ) { 00690 ExFreePool( Job->Filter->CapturedSids ); 00691 } 00692 00693 if ( Job->Filter->CapturedPrivileges ) { 00694 ExFreePool( Job->Filter->CapturedPrivileges ); 00695 } 00696 00697 if ( Job->Filter->CapturedGroups ) { 00698 ExFreePool( Job->Filter->CapturedGroups ); 00699 } 00700 00701 ExFreePool( Job->Filter ); 00702 00703 } 00704 00705 ExDeleteResource(&Job->JobLock); 00706 } 00707 00708 VOID 00709 PspJobClose ( 00710 IN PEPROCESS Process, 00711 IN PVOID Object, 00712 IN ACCESS_MASK GrantedAccess, 00713 IN ULONG ProcessHandleCount, 00714 IN ULONG SystemHandleCount 00715 ) 00716 /*++ 00717 00718 Routine Description: 00719 00720 Called by the object manager when a handle is closed to the object. 00721 00722 Arguments: 00723 00724 Process - Process doing the close 00725 Object - Job object being closed 00726 GrantedAccess - Access ranted for this handle 00727 ProcessHandleCount - Unused and unmaintained by OB 00728 SystemHandleCount - Current handle count for this object 00729 00730 Return Value: 00731 00732 None. 00733 00734 --*/ 00735 { 00736 PEJOB Job = Object; 00737 PVOID Port; 00738 00739 PAGED_CODE (); 00740 // 00741 // If this isn't the last handle then do nothing. 00742 // 00743 if (SystemHandleCount > 1) { 00744 return; 00745 } 00746 00747 KeEnterCriticalRegion(); 00748 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 00749 ExAcquireFastMutexUnsafe(&Job->MemoryLimitsLock); 00750 00751 // 00752 // Release the completion port 00753 // 00754 Port = Job->CompletionPort; 00755 Job->CompletionPort = NULL; 00756 00757 00758 ExReleaseFastMutexUnsafe(&Job->MemoryLimitsLock); 00759 ExReleaseResource(&Job->JobLock); 00760 KeLeaveCriticalRegion(); 00761 00762 if (Port != NULL) { 00763 ObDereferenceObject(Port); 00764 } 00765 } 00766 00767 ULONG PspJobInfoLengths[] = { 00768 sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), // JobObjectBasicAccountingInformation 00769 sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION), // JobObjectBasicLimitInformation 00770 sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST), // JobObjectBasicProcessIdList 00771 sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS), // JobObjectBasicUIRestrictions 00772 sizeof(JOBOBJECT_SECURITY_LIMIT_INFORMATION), // JobObjectSecurityLimitInformation 00773 sizeof(JOBOBJECT_END_OF_JOB_TIME_INFORMATION), // JobObjectEndOfJobTimeInformation 00774 sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT), // JobObjectAssociateCompletionPortInformation 00775 sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION), // JobObjectBasicAndIoAccountingInformation 00776 sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), // JobObjectExtendedLimitInformation 00777 0 00778 }; 00779 00780 #if defined(_ALPHA_) 00781 00782 // 00783 // Alpha's run with aligned stacks, so we can require quad alignment 00784 // 00785 00786 ULONG PspJobInfoAlign[] = { 00787 sizeof(LARGE_INTEGER), // JobObjectBasicAccountingInformation 00788 sizeof(LARGE_INTEGER), // JobObjectBasicLimitInformation 00789 sizeof(ULONG), // JobObjectBasicProcessIdList 00790 sizeof(ULONG), // JobObjectBasicUIRestrictions 00791 sizeof(ULONG), // JobObjectSecurityLimitInformation 00792 sizeof(ULONG), // JobObjectEndOfJobTimeInformation 00793 sizeof(PVOID), // JobObjectAssociateCompletionPortInformation 00794 sizeof(LARGE_INTEGER), // JobObjectBasicAndIoAccountingInformation 00795 sizeof(LARGE_INTEGER), // JobObjectExtendedLimitInformation 00796 0 00797 }; 00798 #else 00799 ULONG PspJobInfoAlign[] = { 00800 sizeof(ULONG), // JobObjectBasicAccountingInformation 00801 sizeof(ULONG), // JobObjectBasicLimitInformation 00802 sizeof(ULONG), // JobObjectBasicProcessIdList 00803 sizeof(ULONG), // JobObjectBasicUIRestrictions 00804 sizeof(ULONG), // JobObjectSecurityLimitInformation 00805 sizeof(ULONG), // JobObjectEndOfJobTimeInformation 00806 sizeof(PVOID), // JobObjectAssociateCompletionPortInformation 00807 sizeof(ULONG), // JobObjectBasicAndIoAccountingInformation 00808 sizeof(ULONG), // JobObjectExtendedLimitInformation 00809 0 00810 }; 00811 #endif // _ALPHA_ 00812 00813 NTSTATUS 00814 NtQueryInformationJobObject( 00815 IN HANDLE JobHandle, 00816 IN JOBOBJECTINFOCLASS JobObjectInformationClass, 00817 OUT PVOID JobObjectInformation, 00818 IN ULONG JobObjectInformationLength, 00819 OUT PULONG ReturnLength OPTIONAL 00820 ) 00821 { 00822 PEJOB Job; 00823 KPROCESSOR_MODE PreviousMode; 00824 JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION AccountingInfo; 00825 JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInfo; 00826 JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimitInfo; 00827 JOBOBJECT_BASIC_UI_RESTRICTIONS BasicUIRestrictions; 00828 JOBOBJECT_SECURITY_LIMIT_INFORMATION SecurityLimitInfo ; 00829 NTSTATUS st; 00830 ULONG RequiredLength, RequiredAlign, ActualReturnLength; 00831 PVOID ReturnData; 00832 PEPROCESS Process; 00833 PLIST_ENTRY Next; 00834 LARGE_INTEGER UserTime, KernelTime; 00835 PULONG_PTR NextProcessIdSlot; 00836 ULONG WorkingLength; 00837 PJOBOBJECT_BASIC_PROCESS_ID_LIST IdList; 00838 PUCHAR CurrentOffset ; 00839 PTOKEN_GROUPS WorkingGroup ; 00840 PTOKEN_PRIVILEGES WorkingPrivs ; 00841 ULONG RemainingSidBuffer ; 00842 PSID TargetSidBuffer ; 00843 PSID RemainingSid ; 00844 BOOLEAN AlreadyCopied ; 00845 00846 PAGED_CODE(); 00847 00848 // 00849 // Get previous processor mode and probe output argument if necessary. 00850 // 00851 00852 if ( JobObjectInformationClass >= MaxJobObjectInfoClass || JobObjectInformationClass <= 0) { 00853 return STATUS_INVALID_INFO_CLASS; 00854 } 00855 00856 RequiredLength = PspJobInfoLengths[JobObjectInformationClass-1]; 00857 RequiredAlign = PspJobInfoAlign[JobObjectInformationClass-1]; 00858 ActualReturnLength = RequiredLength; 00859 00860 if ( JobObjectInformationLength != RequiredLength ) { 00861 00862 // 00863 // BasicProcessIdList is variable length, so make sure header is 00864 // ok. Can not enforce an exact match ! Security Limits can be 00865 // as well, due to the token groups and privs 00866 // 00867 if ( ( JobObjectInformationClass == JobObjectBasicProcessIdList ) || 00868 ( JobObjectInformationClass == JobObjectSecurityLimitInformation ) ) { 00869 if ( JobObjectInformationLength < RequiredLength ) { 00870 return STATUS_INFO_LENGTH_MISMATCH; 00871 } 00872 else { 00873 RequiredLength = JobObjectInformationLength; 00874 } 00875 } 00876 else { 00877 return STATUS_INFO_LENGTH_MISMATCH; 00878 } 00879 } 00880 00881 00882 PreviousMode = KeGetPreviousMode(); 00883 if (PreviousMode != KernelMode) { 00884 try { 00885 ProbeForWrite( 00886 JobObjectInformation, 00887 JobObjectInformationLength, 00888 RequiredAlign 00889 ); 00890 if (ARGUMENT_PRESENT(ReturnLength)) { 00891 ProbeForWriteUlong(ReturnLength); 00892 } 00893 } 00894 except(EXCEPTION_EXECUTE_HANDLER) { 00895 return GetExceptionCode(); 00896 } 00897 } 00898 00899 // 00900 // reference the job 00901 // 00902 00903 if ( ARGUMENT_PRESENT(JobHandle) ) { 00904 st = ObReferenceObjectByHandle( 00905 JobHandle, 00906 JOB_OBJECT_QUERY, 00907 PsJobType, 00908 PreviousMode, 00909 (PVOID *)&Job, 00910 NULL 00911 ); 00912 if ( !NT_SUCCESS(st) ) { 00913 return st; 00914 } 00915 } 00916 else { 00917 00918 // 00919 // if the current process has a job, NULL means the job of the 00920 // current process. Query is always allowed for this case 00921 // 00922 00923 Process = PsGetCurrentProcess(); 00924 00925 if ( Process->Job ) { 00926 Job = Process->Job; 00927 ObReferenceObject(Job); 00928 } 00929 else { 00930 return STATUS_ACCESS_DENIED; 00931 } 00932 } 00933 00934 AlreadyCopied = FALSE ; 00935 00936 KeEnterCriticalRegion(); 00937 ExAcquireResourceShared(&Job->JobLock, TRUE); 00938 00939 // 00940 // Check argument validity. 00941 // 00942 00943 switch ( JobObjectInformationClass ) { 00944 00945 case JobObjectBasicAccountingInformation: 00946 case JobObjectBasicAndIoAccountingInformation: 00947 00948 // 00949 // These two cases are identical, EXCEPT that with AndIo, IO information 00950 // is returned as well, but the first part of the local is identical to 00951 // basic, and the shorter return'd data length chops what we return. 00952 // 00953 00954 RtlZeroMemory(&AccountingInfo.IoInfo,sizeof(AccountingInfo.IoInfo)); 00955 00956 AccountingInfo.BasicInfo.TotalUserTime = Job->TotalUserTime; 00957 AccountingInfo.BasicInfo.TotalKernelTime = Job->TotalKernelTime; 00958 AccountingInfo.BasicInfo.ThisPeriodTotalUserTime = Job->ThisPeriodTotalUserTime; 00959 AccountingInfo.BasicInfo.ThisPeriodTotalKernelTime = Job->ThisPeriodTotalKernelTime; 00960 AccountingInfo.BasicInfo.TotalPageFaultCount = Job->TotalPageFaultCount; 00961 00962 AccountingInfo.BasicInfo.TotalProcesses = Job->TotalProcesses; 00963 AccountingInfo.BasicInfo.ActiveProcesses = Job->ActiveProcesses; 00964 AccountingInfo.BasicInfo.TotalTerminatedProcesses = Job->TotalTerminatedProcesses; 00965 00966 AccountingInfo.IoInfo.ReadOperationCount = Job->ReadOperationCount; 00967 AccountingInfo.IoInfo.WriteOperationCount = Job->WriteOperationCount; 00968 AccountingInfo.IoInfo.OtherOperationCount = Job->OtherOperationCount; 00969 AccountingInfo.IoInfo.ReadTransferCount = Job->ReadTransferCount; 00970 AccountingInfo.IoInfo.WriteTransferCount = Job->WriteTransferCount; 00971 AccountingInfo.IoInfo.OtherTransferCount = Job->OtherTransferCount; 00972 00973 // 00974 // Add in the time and page faults for each process 00975 // 00976 00977 Next = Job->ProcessListHead.Flink; 00978 00979 while ( Next != &Job->ProcessListHead) { 00980 00981 Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks)); 00982 if ( !(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED) ) { 00983 00984 UserTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,KeMaximumIncrement); 00985 KernelTime.QuadPart = UInt32x32To64(Process->Pcb.KernelTime,KeMaximumIncrement); 00986 00987 AccountingInfo.BasicInfo.TotalUserTime.QuadPart += UserTime.QuadPart; 00988 AccountingInfo.BasicInfo.TotalKernelTime.QuadPart += KernelTime.QuadPart; 00989 AccountingInfo.BasicInfo.ThisPeriodTotalUserTime.QuadPart += UserTime.QuadPart; 00990 AccountingInfo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart += KernelTime.QuadPart; 00991 AccountingInfo.BasicInfo.TotalPageFaultCount += Process->Vm.PageFaultCount; 00992 00993 AccountingInfo.IoInfo.ReadOperationCount += Process->ReadOperationCount.QuadPart; 00994 AccountingInfo.IoInfo.WriteOperationCount += Process->WriteOperationCount.QuadPart; 00995 AccountingInfo.IoInfo.OtherOperationCount += Process->OtherOperationCount.QuadPart; 00996 AccountingInfo.IoInfo.ReadTransferCount += Process->ReadTransferCount.QuadPart; 00997 AccountingInfo.IoInfo.WriteTransferCount += Process->WriteTransferCount.QuadPart; 00998 AccountingInfo.IoInfo.OtherTransferCount += Process->OtherTransferCount.QuadPart; 00999 } 01000 Next = Next->Flink; 01001 } 01002 01003 ReturnData = &AccountingInfo; 01004 st = STATUS_SUCCESS; 01005 01006 break; 01007 01008 case JobObjectExtendedLimitInformation: 01009 case JobObjectBasicLimitInformation: 01010 01011 // 01012 // Get the Basic Information 01013 // 01014 ExtendedLimitInfo.BasicLimitInformation.LimitFlags = Job->LimitFlags; 01015 ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize = Job->MinimumWorkingSetSize; 01016 ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize = Job->MaximumWorkingSetSize; 01017 ExtendedLimitInfo.BasicLimitInformation.ActiveProcessLimit = Job->ActiveProcessLimit; 01018 ExtendedLimitInfo.BasicLimitInformation.PriorityClass = (ULONG)Job->PriorityClass; 01019 ExtendedLimitInfo.BasicLimitInformation.SchedulingClass = Job->SchedulingClass; 01020 ExtendedLimitInfo.BasicLimitInformation.Affinity = (ULONG_PTR)Job->Affinity; 01021 ExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = Job->PerProcessUserTimeLimit.QuadPart; 01022 ExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit.QuadPart = Job->PerJobUserTimeLimit.QuadPart; 01023 ReturnData = &ExtendedLimitInfo.BasicLimitInformation; 01024 01025 if ( JobObjectInformationClass == JobObjectExtendedLimitInformation ) { 01026 01027 // 01028 // Get Extended Information 01029 // 01030 01031 ExAcquireFastMutexUnsafe(&Job->MemoryLimitsLock); 01032 01033 ExtendedLimitInfo.ProcessMemoryLimit = Job->ProcessMemoryLimit << PAGE_SHIFT; 01034 ExtendedLimitInfo.JobMemoryLimit = Job->JobMemoryLimit << PAGE_SHIFT; 01035 ExtendedLimitInfo.PeakJobMemoryUsed = Job->PeakJobMemoryUsed << PAGE_SHIFT; 01036 01037 ExtendedLimitInfo.PeakProcessMemoryUsed = Job->PeakProcessMemoryUsed << PAGE_SHIFT; 01038 01039 ExReleaseFastMutexUnsafe(&Job->MemoryLimitsLock); 01040 01041 01042 // 01043 // Zero un-used I/O counters 01044 // 01045 RtlZeroMemory(&ExtendedLimitInfo.IoInfo,sizeof(ExtendedLimitInfo.IoInfo)); 01046 01047 ReturnData = &ExtendedLimitInfo; 01048 } 01049 01050 st = STATUS_SUCCESS; 01051 01052 break; 01053 01054 case JobObjectBasicUIRestrictions: 01055 01056 BasicUIRestrictions.UIRestrictionsClass = Job->UIRestrictionsClass; 01057 01058 ReturnData = &BasicUIRestrictions; 01059 st = STATUS_SUCCESS; 01060 01061 break; 01062 01063 case JobObjectBasicProcessIdList: 01064 01065 IdList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)JobObjectInformation; 01066 NextProcessIdSlot = &IdList->ProcessIdList[0]; 01067 WorkingLength = FIELD_OFFSET(JOBOBJECT_BASIC_PROCESS_ID_LIST,ProcessIdList); 01068 01069 AlreadyCopied = TRUE ; 01070 01071 try { 01072 01073 // 01074 // Acounted for in the workinglength = 2*sizeof(ULONG) 01075 // 01076 01077 IdList->NumberOfAssignedProcesses = Job->ActiveProcesses; 01078 IdList->NumberOfProcessIdsInList = 0; 01079 01080 Next = Job->ProcessListHead.Flink; 01081 01082 while ( Next != &Job->ProcessListHead) { 01083 01084 Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks)); 01085 if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) ) { 01086 if ( !Process->UniqueProcessId ) { 01087 IdList->NumberOfAssignedProcesses--; 01088 } 01089 else { 01090 if ( (RequiredLength - WorkingLength) >= sizeof(ULONG_PTR) ) { 01091 *NextProcessIdSlot++ = (ULONG_PTR)Process->UniqueProcessId; 01092 WorkingLength += sizeof(ULONG_PTR); 01093 IdList->NumberOfProcessIdsInList++; 01094 } 01095 else { 01096 st = STATUS_BUFFER_OVERFLOW; 01097 ActualReturnLength = WorkingLength; 01098 break; 01099 } 01100 } 01101 } 01102 Next = Next->Flink; 01103 } 01104 ActualReturnLength = WorkingLength; 01105 01106 } 01107 except ( ExSystemExceptionFilter() ) { 01108 st = GetExceptionCode(); 01109 ActualReturnLength = 0; 01110 } 01111 01112 break; 01113 01114 case JobObjectSecurityLimitInformation: 01115 01116 RtlZeroMemory( &SecurityLimitInfo, sizeof( SecurityLimitInfo ) ); 01117 01118 SecurityLimitInfo.SecurityLimitFlags = Job->SecurityLimitFlags ; 01119 01120 ReturnData = &SecurityLimitInfo; 01121 01122 st = STATUS_SUCCESS; 01123 01124 // 01125 // If a filter is present, then we have an ugly marshalling to do. 01126 // 01127 01128 if ( Job->Filter ) 01129 { 01130 01131 // 01132 // Compute size needed: 01133 // 01134 01135 WorkingLength = Job->Filter->CapturedSidsLength + 01136 Job->Filter->CapturedGroupsLength + 01137 Job->Filter->CapturedPrivilegesLength ; 01138 01139 WorkingLength = 0 ; 01140 01141 // 01142 // For each field, if it is present, include the extra stuff 01143 // 01144 01145 if ( Job->Filter->CapturedSidsLength ) 01146 { 01147 WorkingLength += Job->Filter->CapturedSidsLength + 01148 sizeof( ULONG ); 01149 } 01150 01151 if ( Job->Filter->CapturedGroupsLength ) 01152 { 01153 WorkingLength += Job->Filter->CapturedGroupsLength + 01154 sizeof( ULONG ); 01155 01156 } 01157 01158 if ( Job->Filter->CapturedPrivilegesLength ) 01159 { 01160 WorkingLength += Job->Filter->CapturedPrivilegesLength + 01161 sizeof( ULONG ); 01162 } 01163 01164 RequiredLength -= sizeof( SecurityLimitInfo ); 01165 01166 if ( WorkingLength > RequiredLength ) 01167 { 01168 st = STATUS_BUFFER_OVERFLOW ; 01169 ActualReturnLength = WorkingLength + sizeof( SecurityLimitInfo ); 01170 break; 01171 } 01172 01173 CurrentOffset = (PUCHAR) (JobObjectInformation) + sizeof( SecurityLimitInfo ); 01174 01175 try { 01176 01177 // 01178 // 01179 // 01180 01181 if ( Job->Filter->CapturedSidsLength ) 01182 { 01183 WorkingGroup = (PTOKEN_GROUPS) CurrentOffset ; 01184 01185 CurrentOffset += sizeof( ULONG ); 01186 01187 SecurityLimitInfo.RestrictedSids = WorkingGroup ; 01188 01189 WorkingGroup->GroupCount = Job->Filter->CapturedSidCount ; 01190 01191 TargetSidBuffer = (PSID) (CurrentOffset + 01192 sizeof( SID_AND_ATTRIBUTES ) * 01193 Job->Filter->CapturedSidCount ); 01194 01195 st = RtlCopySidAndAttributesArray( 01196 Job->Filter->CapturedSidCount, 01197 Job->Filter->CapturedSids, 01198 WorkingLength, 01199 WorkingGroup->Groups, 01200 TargetSidBuffer, 01201 &RemainingSid, 01202 &RemainingSidBuffer ); 01203 01204 CurrentOffset += Job->Filter->CapturedSidsLength ; 01205 01206 } 01207 01208 if ( !NT_SUCCESS( st ) ) 01209 { 01210 leave ; 01211 } 01212 01213 if ( Job->Filter->CapturedGroupsLength ) 01214 { 01215 WorkingGroup = (PTOKEN_GROUPS) CurrentOffset ; 01216 01217 CurrentOffset += sizeof( ULONG ); 01218 01219 SecurityLimitInfo.SidsToDisable = WorkingGroup ; 01220 01221 WorkingGroup->GroupCount = Job->Filter->CapturedGroupCount ; 01222 01223 TargetSidBuffer = (PSID) (CurrentOffset + 01224 sizeof( SID_AND_ATTRIBUTES ) * 01225 Job->Filter->CapturedGroupCount ); 01226 01227 st = RtlCopySidAndAttributesArray( 01228 Job->Filter->CapturedGroupCount, 01229 Job->Filter->CapturedGroups, 01230 WorkingLength, 01231 WorkingGroup->Groups, 01232 TargetSidBuffer, 01233 &RemainingSid, 01234 &RemainingSidBuffer ); 01235 01236 CurrentOffset += Job->Filter->CapturedGroupsLength ; 01237 01238 } 01239 01240 if ( !NT_SUCCESS( st ) ) 01241 { 01242 leave ; 01243 } 01244 01245 if ( Job->Filter->CapturedPrivilegesLength ) 01246 { 01247 WorkingPrivs = (PTOKEN_PRIVILEGES) CurrentOffset; 01248 01249 CurrentOffset += sizeof( ULONG ); 01250 01251 SecurityLimitInfo.PrivilegesToDelete = WorkingPrivs ; 01252 01253 WorkingPrivs->PrivilegeCount = Job->Filter->CapturedPrivilegeCount ; 01254 01255 RtlCopyMemory( WorkingPrivs->Privileges, 01256 Job->Filter->CapturedPrivileges, 01257 Job->Filter->CapturedPrivilegesLength ); 01258 01259 } 01260 01261 AlreadyCopied = TRUE ; 01262 01263 RtlCopyMemory( JobObjectInformation, 01264 &SecurityLimitInfo, 01265 sizeof( SecurityLimitInfo ) ); 01266 01267 01268 } 01269 except (EXCEPTION_EXECUTE_HANDLER) { 01270 st = GetExceptionCode(); 01271 ActualReturnLength = 0 ; 01272 break; 01273 } 01274 01275 } 01276 01277 01278 break; 01279 01280 default: 01281 01282 st = STATUS_INVALID_INFO_CLASS; 01283 } 01284 01285 ExReleaseResource(&Job->JobLock); 01286 KeLeaveCriticalRegion(); 01287 01288 01289 // 01290 // Finish Up 01291 // 01292 01293 ObDereferenceObject(Job); 01294 01295 01296 if ( NT_SUCCESS(st) ) { 01297 01298 // 01299 // Either of these may cause an access violation. The 01300 // exception handler will return access violation as 01301 // status code. No further cleanup needs to be done. 01302 // 01303 01304 try { 01305 if ( !AlreadyCopied ) { 01306 RtlCopyMemory(JobObjectInformation,ReturnData,RequiredLength); 01307 } 01308 01309 if (ARGUMENT_PRESENT(ReturnLength) ) { 01310 *ReturnLength = ActualReturnLength; 01311 } 01312 } 01313 except(EXCEPTION_EXECUTE_HANDLER) { 01314 return STATUS_SUCCESS; 01315 } 01316 } 01317 01318 return st; 01319 01320 } 01321 01322 NTSTATUS 01323 NtSetInformationJobObject( 01324 IN HANDLE JobHandle, 01325 IN JOBOBJECTINFOCLASS JobObjectInformationClass, 01326 IN PVOID JobObjectInformation, 01327 IN ULONG JobObjectInformationLength 01328 ) 01329 { 01330 PEJOB Job; 01331 EJOB LocalJob; 01332 KPROCESSOR_MODE PreviousMode; 01333 NTSTATUS st; 01334 JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimitInfo; 01335 JOBOBJECT_BASIC_UI_RESTRICTIONS BasicUIRestrictions; 01336 JOBOBJECT_SECURITY_LIMIT_INFORMATION SecurityLimitInfo ; 01337 JOBOBJECT_END_OF_JOB_TIME_INFORMATION EndOfJobInfo; 01338 JOBOBJECT_ASSOCIATE_COMPLETION_PORT AssociateInfo; 01339 ULONG RequiredAccess ; 01340 ULONG RequiredLength, RequiredAlign; 01341 PEPROCESS Process; 01342 BOOLEAN HasPrivilege; 01343 BOOLEAN IsChild ; 01344 PLIST_ENTRY Next; 01345 PPS_JOB_TOKEN_FILTER Filter ; 01346 PVOID IoCompletion; 01347 PACCESS_TOKEN LocalToken ; 01348 ULONG ValidFlags; 01349 ULONG LimitFlags; 01350 BOOLEAN ProcessWorkingSetHead = FALSE; 01351 PJOB_WORKING_SET_CHANGE_RECORD WsChangeRecord; 01352 01353 01354 PAGED_CODE(); 01355 01356 // 01357 // Get previous processor mode and probe output argument if necessary. 01358 // 01359 01360 if ( JobObjectInformationClass >= MaxJobObjectInfoClass || JobObjectInformationClass <= 0) { 01361 return STATUS_INVALID_INFO_CLASS; 01362 } 01363 RequiredLength = PspJobInfoLengths[JobObjectInformationClass-1]; 01364 RequiredAlign = PspJobInfoAlign[JobObjectInformationClass-1]; 01365 01366 PreviousMode = KeGetPreviousMode(); 01367 if (PreviousMode != KernelMode) { 01368 try { 01369 ProbeForRead( 01370 JobObjectInformation, 01371 JobObjectInformationLength, 01372 RequiredAlign 01373 ); 01374 } 01375 except(EXCEPTION_EXECUTE_HANDLER) { 01376 return GetExceptionCode(); 01377 } 01378 } 01379 01380 if ( JobObjectInformationLength != RequiredLength ) { 01381 return STATUS_INFO_LENGTH_MISMATCH; 01382 } 01383 01384 // 01385 // reference the job 01386 // 01387 01388 if ( JobObjectInformationClass == JobObjectSecurityLimitInformation ) 01389 { 01390 RequiredAccess = JOB_OBJECT_SET_SECURITY_ATTRIBUTES ; 01391 } 01392 else 01393 { 01394 RequiredAccess = JOB_OBJECT_SET_ATTRIBUTES ; 01395 } 01396 01397 st = ObReferenceObjectByHandle( 01398 JobHandle, 01399 RequiredAccess, 01400 PsJobType, 01401 PreviousMode, 01402 (PVOID *)&Job, 01403 NULL 01404 ); 01405 if ( !NT_SUCCESS(st) ) { 01406 return st; 01407 } 01408 01409 KeEnterCriticalRegion(); 01410 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 01411 01412 // 01413 // Check argument validity. 01414 // 01415 01416 switch ( JobObjectInformationClass ) { 01417 01418 case JobObjectExtendedLimitInformation: 01419 case JobObjectBasicLimitInformation: 01420 try { 01421 RtlCopyMemory(&ExtendedLimitInfo,JobObjectInformation,RequiredLength); 01422 } 01423 except(EXCEPTION_EXECUTE_HANDLER) { 01424 st = GetExceptionCode(); 01425 } 01426 if ( NT_SUCCESS(st) ) { 01427 // 01428 // sanity check LimitFlags 01429 // 01430 if ( JobObjectInformationClass == JobObjectBasicLimitInformation) { 01431 ValidFlags = JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS; 01432 } 01433 else { 01434 ValidFlags = JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS; 01435 } 01436 01437 if ( ExtendedLimitInfo.BasicLimitInformation.LimitFlags & ~ValidFlags ) { 01438 st = STATUS_INVALID_PARAMETER; 01439 } 01440 else { 01441 01442 LimitFlags = ExtendedLimitInfo.BasicLimitInformation.LimitFlags; 01443 01444 // 01445 // Deal with each of the various limit flags 01446 // 01447 01448 LocalJob.LimitFlags = Job->LimitFlags; 01449 01450 01451 // 01452 // ACTIVE PROCESS LIMIT 01453 // 01454 if ( LimitFlags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS ) { 01455 01456 // 01457 // Active Process Limit is NOT retroactive. New processes are denied, 01458 // but existing ones are not killed just because the limit is 01459 // reduced. 01460 // 01461 01462 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; 01463 LocalJob.ActiveProcessLimit = ExtendedLimitInfo.BasicLimitInformation.ActiveProcessLimit; 01464 } 01465 else { 01466 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_ACTIVE_PROCESS; 01467 LocalJob.ActiveProcessLimit = 0; 01468 } 01469 01470 // 01471 // PRIORITY CLASS LIMIT 01472 // 01473 if ( LimitFlags & JOB_OBJECT_LIMIT_PRIORITY_CLASS ) { 01474 01475 if ( ExtendedLimitInfo.BasicLimitInformation.PriorityClass > PROCESS_PRIORITY_CLASS_ABOVE_NORMAL ) { 01476 st = STATUS_INVALID_PARAMETER; 01477 } 01478 else { 01479 if ( ExtendedLimitInfo.BasicLimitInformation.PriorityClass == PROCESS_PRIORITY_CLASS_HIGH || 01480 ExtendedLimitInfo.BasicLimitInformation.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME ) { 01481 01482 // 01483 // Increasing the base priority of a process is a 01484 // privileged operation. Check for the privilege 01485 // here. 01486 // 01487 01488 HasPrivilege = SeCheckPrivilegedObject( 01489 SeIncreaseBasePriorityPrivilege, 01490 JobHandle, 01491 JOB_OBJECT_SET_ATTRIBUTES, 01492 PreviousMode 01493 ); 01494 01495 if (!HasPrivilege) { 01496 st = STATUS_PRIVILEGE_NOT_HELD; 01497 } 01498 } 01499 01500 if ( NT_SUCCESS(st) ) { 01501 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS; 01502 LocalJob.PriorityClass = (UCHAR)ExtendedLimitInfo.BasicLimitInformation.PriorityClass; 01503 } 01504 } 01505 } 01506 else { 01507 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_PRIORITY_CLASS; 01508 LocalJob.PriorityClass = 0; 01509 } 01510 01511 // 01512 // SCHEDULING CLASS LIMIT 01513 // 01514 if ( LimitFlags & JOB_OBJECT_LIMIT_SCHEDULING_CLASS ) { 01515 01516 if ( ExtendedLimitInfo.BasicLimitInformation.SchedulingClass >= PSP_NUMBER_OF_SCHEDULING_CLASSES) { 01517 st = STATUS_INVALID_PARAMETER; 01518 } 01519 else { 01520 if ( ExtendedLimitInfo.BasicLimitInformation.SchedulingClass > PSP_DEFAULT_SCHEDULING_CLASSES ) { 01521 01522 // 01523 // Increasing above the default scheduling class 01524 // is a 01525 // privileged operation. Check for the privilege 01526 // here. 01527 // 01528 01529 HasPrivilege = SeCheckPrivilegedObject( 01530 SeIncreaseBasePriorityPrivilege, 01531 JobHandle, 01532 JOB_OBJECT_SET_ATTRIBUTES, 01533 PreviousMode 01534 ); 01535 01536 if (!HasPrivilege) { 01537 st = STATUS_PRIVILEGE_NOT_HELD; 01538 } 01539 } 01540 01541 if ( NT_SUCCESS(st) ) { 01542 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_SCHEDULING_CLASS; 01543 LocalJob.SchedulingClass = ExtendedLimitInfo.BasicLimitInformation.SchedulingClass; 01544 } 01545 } 01546 } 01547 else { 01548 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_SCHEDULING_CLASS; 01549 LocalJob.SchedulingClass = PSP_DEFAULT_SCHEDULING_CLASSES ; 01550 } 01551 01552 // 01553 // AFFINITY LIMIT 01554 // 01555 if ( LimitFlags & JOB_OBJECT_LIMIT_AFFINITY ) { 01556 01557 if ( !ExtendedLimitInfo.BasicLimitInformation.Affinity || 01558 (ExtendedLimitInfo.BasicLimitInformation.Affinity != (ExtendedLimitInfo.BasicLimitInformation.Affinity & KeActiveProcessors)) ) { 01559 st = STATUS_INVALID_PARAMETER; 01560 } 01561 else { 01562 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_AFFINITY; 01563 LocalJob.Affinity = (KAFFINITY)ExtendedLimitInfo.BasicLimitInformation.Affinity; 01564 } 01565 } 01566 else { 01567 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_AFFINITY; 01568 LocalJob.Affinity = 0; 01569 } 01570 01571 // 01572 // PROCESS TIME LIMIT 01573 // 01574 if ( LimitFlags & JOB_OBJECT_LIMIT_PROCESS_TIME ) { 01575 01576 if ( !ExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart ) { 01577 st = STATUS_INVALID_PARAMETER; 01578 } 01579 else { 01580 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_TIME; 01581 LocalJob.PerProcessUserTimeLimit.QuadPart = ExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart; 01582 } 01583 } 01584 else { 01585 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_PROCESS_TIME; 01586 LocalJob.PerProcessUserTimeLimit.QuadPart = 0; 01587 } 01588 01589 // 01590 // JOB TIME LIMIT 01591 // 01592 if ( LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME ) { 01593 01594 if ( !ExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit.QuadPart ) { 01595 st = STATUS_INVALID_PARAMETER; 01596 } 01597 else { 01598 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_JOB_TIME; 01599 LocalJob.PerJobUserTimeLimit.QuadPart = ExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit.QuadPart; 01600 } 01601 } 01602 else { 01603 if ( LimitFlags & JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME ) { 01604 01605 // 01606 // If we are supposed to preserve existing job time limits, then 01607 // preserve them ! 01608 // 01609 01610 LocalJob.LimitFlags |= (Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME); 01611 LocalJob.PerJobUserTimeLimit.QuadPart = Job->PerJobUserTimeLimit.QuadPart; 01612 } 01613 else { 01614 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_TIME; 01615 LocalJob.PerJobUserTimeLimit.QuadPart = 0; 01616 } 01617 } 01618 01619 // 01620 // WORKING SET LIMIT 01621 // 01622 if ( LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET ) { 01623 01624 01625 // 01626 // the only issue with this check is that when we enforce through the 01627 // processes, we may find a process that can not handle the new working set 01628 // limit because it will make the process's working set not fluid 01629 // 01630 01631 if ( (ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize == 0 && 01632 ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize == 0) || 01633 01634 (ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize == (SIZE_T)-1 && 01635 ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize == (SIZE_T)-1) || 01636 01637 (ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize > 01638 ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize) ) { 01639 01640 01641 st = STATUS_INVALID_PARAMETER; 01642 } 01643 else { 01644 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_WORKINGSET; 01645 LocalJob.MinimumWorkingSetSize = ExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize; 01646 LocalJob.MaximumWorkingSetSize = ExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize; 01647 } 01648 } 01649 else { 01650 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_WORKINGSET; 01651 LocalJob.MinimumWorkingSetSize = 0; 01652 LocalJob.MaximumWorkingSetSize = 0; 01653 } 01654 01655 if ( JobObjectInformationClass == JobObjectExtendedLimitInformation) { 01656 // 01657 // PROCESS MEMORY LIMIT 01658 // 01659 if ( LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY ) { 01660 if ( ExtendedLimitInfo.ProcessMemoryLimit < PAGE_SIZE ) { 01661 st = STATUS_INVALID_PARAMETER; 01662 } 01663 else { 01664 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY; 01665 LocalJob.ProcessMemoryLimit = ExtendedLimitInfo.ProcessMemoryLimit >> PAGE_SHIFT; 01666 } 01667 } 01668 else { 01669 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_PROCESS_MEMORY; 01670 LocalJob.ProcessMemoryLimit = 0; 01671 } 01672 01673 // 01674 // JOB WIDE MEMORY LIMIT 01675 // 01676 if ( LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY ) { 01677 if ( ExtendedLimitInfo.JobMemoryLimit < PAGE_SIZE ) { 01678 st = STATUS_INVALID_PARAMETER; 01679 } 01680 else { 01681 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY; 01682 LocalJob.JobMemoryLimit = ExtendedLimitInfo.JobMemoryLimit >> PAGE_SHIFT; 01683 } 01684 } 01685 else { 01686 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_MEMORY; 01687 LocalJob.JobMemoryLimit = 0; 01688 } 01689 01690 // 01691 // JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION 01692 // 01693 if ( LimitFlags & JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION ) { 01694 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; 01695 } 01696 else { 01697 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; 01698 } 01699 01700 // 01701 // JOB_OBJECT_LIMIT_BREAKAWAY_OK 01702 // 01703 if ( LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK ) { 01704 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK; 01705 } 01706 else { 01707 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_BREAKAWAY_OK; 01708 } 01709 01710 // 01711 // JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK 01712 // 01713 if ( LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK ) { 01714 LocalJob.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; 01715 } 01716 else { 01717 LocalJob.LimitFlags &= ~JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; 01718 } 01719 } 01720 01721 if ( NT_SUCCESS(st) ) { 01722 01723 01724 // 01725 // Copy LocalJob to Job 01726 // 01727 01728 Job->LimitFlags = LocalJob.LimitFlags; 01729 Job->MinimumWorkingSetSize = LocalJob.MinimumWorkingSetSize; 01730 Job->MaximumWorkingSetSize = LocalJob.MaximumWorkingSetSize; 01731 Job->ActiveProcessLimit = LocalJob.ActiveProcessLimit; 01732 Job->Affinity = LocalJob.Affinity; 01733 Job->PriorityClass = LocalJob.PriorityClass; 01734 Job->SchedulingClass = LocalJob.SchedulingClass; 01735 Job->PerProcessUserTimeLimit.QuadPart = LocalJob.PerProcessUserTimeLimit.QuadPart; 01736 Job->PerJobUserTimeLimit.QuadPart = LocalJob.PerJobUserTimeLimit.QuadPart; 01737 01738 if ( JobObjectInformationClass == JobObjectExtendedLimitInformation) { 01739 ExAcquireFastMutexUnsafe(&Job->MemoryLimitsLock); 01740 Job->ProcessMemoryLimit = LocalJob.ProcessMemoryLimit; 01741 Job->JobMemoryLimit = LocalJob.JobMemoryLimit; 01742 ExReleaseFastMutexUnsafe(&Job->MemoryLimitsLock); 01743 } 01744 01745 if ( LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME ) { 01746 01747 // 01748 // Take any signalled processes and fold their accounting 01749 // intothe job. This way a process that exited clean but still 01750 // is open won't impact the next period 01751 // 01752 01753 Next = Job->ProcessListHead.Flink; 01754 01755 while ( Next != &Job->ProcessListHead) { 01756 01757 Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks)); 01758 01759 // 01760 // see if process has been signalled. 01761 // This indicates that the process has exited. We can't do 01762 // this in the exit path becuase of the lock order problem 01763 // between the process lock and the job lock since in exit 01764 // we hold the process lock for a long time and can't drop 01765 // it until thread termination 01766 // 01767 01768 if ( KeReadStateProcess(&Process->Pcb) ) { 01769 PspFoldProcessAccountingIntoJob(Job,Process); 01770 } 01771 else { 01772 01773 LARGE_INTEGER ProcessTime; 01774 01775 // 01776 // running processes have their current runtime 01777 // added to the programmed limit. This way, you 01778 // can set a limit on a job with processes in the 01779 // job and not have previous runtimes count against 01780 // the limit 01781 // 01782 01783 if ( !(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED) ) { 01784 ProcessTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,KeMaximumIncrement); 01785 Job->PerJobUserTimeLimit.QuadPart += ProcessTime.QuadPart; 01786 } 01787 } 01788 01789 Next = Next->Flink; 01790 } 01791 01792 01793 // 01794 // clear period times and reset the job 01795 // 01796 01797 Job->ThisPeriodTotalUserTime.QuadPart = 0; 01798 Job->ThisPeriodTotalKernelTime.QuadPart = 0; 01799 01800 KeClearEvent(&Job->Event); 01801 01802 } 01803 01804 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET ) { 01805 ExAcquireFastMutexUnsafe(&PspWorkingSetChangeHead.Lock); 01806 PspWorkingSetChangeHead.MinimumWorkingSetSize = Job->MinimumWorkingSetSize; 01807 PspWorkingSetChangeHead.MaximumWorkingSetSize = Job->MaximumWorkingSetSize; 01808 ProcessWorkingSetHead = TRUE; 01809 } 01810 01811 PspApplyJobLimitsToProcessSet(Job); 01812 01813 } 01814 } 01815 01816 } 01817 break; 01818 01819 case JobObjectBasicUIRestrictions: 01820 try { 01821 RtlCopyMemory(&BasicUIRestrictions, JobObjectInformation, RequiredLength); 01822 } 01823 except(EXCEPTION_EXECUTE_HANDLER) { 01824 st = GetExceptionCode(); 01825 } 01826 01827 if ( NT_SUCCESS(st) ) { 01828 // 01829 // sanity check UIRestrictionsClass 01830 // 01831 if ( BasicUIRestrictions.UIRestrictionsClass & ~JOB_OBJECT_UI_VALID_FLAGS ) { 01832 st = STATUS_INVALID_PARAMETER; 01833 } 01834 else { 01835 01836 // 01837 // Check for switching between UI restrictions 01838 // 01839 01840 if ( Job->UIRestrictionsClass ^ BasicUIRestrictions.UIRestrictionsClass ) { 01841 01842 // 01843 // notify ntuser that the UI restrictions have changed 01844 // 01845 WIN32_JOBCALLOUT_PARAMETERS Parms; 01846 01847 Parms.Job = Job; 01848 Parms.CalloutType = PsW32JobCalloutSetInformation; 01849 Parms.Data = ULongToPtr(BasicUIRestrictions.UIRestrictionsClass); 01850 MmDispatchWin32Callout( PspW32JobCallout,NULL, (PVOID)&Parms, &(Job->SessionId) ); 01851 01852 } 01853 01854 01855 // 01856 // save the UI restrictions into the job object 01857 // 01858 01859 Job->UIRestrictionsClass = BasicUIRestrictions.UIRestrictionsClass; 01860 } 01861 } 01862 break; 01863 01864 // 01865 // SECURITY LIMITS 01866 // 01867 01868 case JobObjectSecurityLimitInformation: 01869 01870 try { 01871 RtlCopyMemory( &SecurityLimitInfo, 01872 JobObjectInformation, 01873 RequiredLength ); 01874 } 01875 except(EXCEPTION_EXECUTE_HANDLER) { 01876 st = GetExceptionCode(); 01877 } 01878 01879 if ( NT_SUCCESS(st) ) { 01880 01881 if ( SecurityLimitInfo.SecurityLimitFlags & 01882 (~JOB_OBJECT_SECURITY_VALID_FLAGS)) 01883 { 01884 st = STATUS_INVALID_PARAMETER ; 01885 } 01886 else 01887 { 01888 // 01889 // Deal with specific options. Basic rules: Once a 01890 // flag is on, it is always on (so even with a handle to 01891 // the job, a process could not lift the security 01892 // restrictions). 01893 // 01894 01895 if ( SecurityLimitInfo.SecurityLimitFlags & 01896 JOB_OBJECT_SECURITY_NO_ADMIN ) 01897 { 01898 Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_NO_ADMIN ; 01899 01900 if ( Job->Token ) 01901 { 01902 if ( SeTokenIsAdmin( Job->Token ) ) 01903 { 01904 Job->SecurityLimitFlags &= (~JOB_OBJECT_SECURITY_NO_ADMIN); 01905 01906 st = STATUS_INVALID_PARAMETER ; 01907 } 01908 } 01909 } 01910 01911 if ( SecurityLimitInfo.SecurityLimitFlags & 01912 JOB_OBJECT_SECURITY_RESTRICTED_TOKEN ) 01913 { 01914 if ( Job->SecurityLimitFlags & 01915 ( JOB_OBJECT_SECURITY_ONLY_TOKEN | JOB_OBJECT_SECURITY_FILTER_TOKENS ) ) 01916 { 01917 st = STATUS_INVALID_PARAMETER ; 01918 } 01919 else 01920 { 01921 Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_RESTRICTED_TOKEN ; 01922 } 01923 } 01924 01925 // 01926 // The forcible token is a little more interesting. It 01927 // cannot be reset, so if there is a pointer there already, 01928 // fail the call. If a filter is already in place, this is 01929 // not allowed, either. If no-admin is set, it is checked 01930 // at the end, once the token has been ref'd. 01931 // 01932 01933 if ( SecurityLimitInfo.SecurityLimitFlags & 01934 JOB_OBJECT_SECURITY_ONLY_TOKEN ) 01935 { 01936 if ( Job->Token || 01937 (Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_FILTER_TOKENS) ) 01938 { 01939 st = STATUS_INVALID_PARAMETER ; 01940 } 01941 else 01942 { 01943 st = ObReferenceObjectByHandle( 01944 SecurityLimitInfo.JobToken, 01945 TOKEN_ASSIGN_PRIMARY | 01946 TOKEN_IMPERSONATE | 01947 TOKEN_DUPLICATE , 01948 SeTokenObjectType(), 01949 PreviousMode, 01950 &LocalToken, 01951 NULL ); 01952 01953 if ( NT_SUCCESS( st ) ) 01954 { 01955 st = SeIsChildTokenByPointer( LocalToken, 01956 &IsChild ); 01957 01958 if ( !NT_SUCCESS( st ) ) 01959 { 01960 ObDereferenceObject( LocalToken ); 01961 } 01962 } 01963 01964 01965 if ( NT_SUCCESS( st ) ) 01966 { 01967 // 01968 // If the token supplied is not a restricted token 01969 // based on the caller's ID, then they must have 01970 // assign primary privilege in order to associate 01971 // the token with the job. 01972 // 01973 01974 if ( !IsChild ) 01975 { 01976 HasPrivilege = SeCheckPrivilegedObject( 01977 SeAssignPrimaryTokenPrivilege, 01978 JobHandle, 01979 JOB_OBJECT_SET_SECURITY_ATTRIBUTES, 01980 PreviousMode 01981 ); 01982 01983 if ( !HasPrivilege ) 01984 { 01985 st = STATUS_PRIVILEGE_NOT_HELD; 01986 } 01987 } 01988 01989 if ( NT_SUCCESS( st ) ) 01990 { 01991 // 01992 // Grab a reference to the token into the job 01993 // object 01994 // 01995 01996 Job->Token = LocalToken ; 01997 01998 // 01999 // Not surprisingly, specifying no-admin and 02000 // supplying an admin token is a no-no. 02001 // 02002 02003 if ( (Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_NO_ADMIN) && 02004 SeTokenIsAdmin( Job->Token ) ) 02005 { 02006 st = STATUS_INVALID_PARAMETER ; 02007 02008 ObDereferenceObject( Job->Token ); 02009 02010 Job->Token = NULL ; 02011 } 02012 else 02013 { 02014 Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_ONLY_TOKEN ; 02015 } 02016 02017 } 02018 else 02019 { 02020 // 02021 // This is the token was a child or otherwise ok, 02022 // but assign primary was not held, so the 02023 // request was rejected. 02024 // 02025 02026 ObDereferenceObject( LocalToken ); 02027 } 02028 02029 } 02030 02031 } 02032 } 02033 if ( SecurityLimitInfo.SecurityLimitFlags & 02034 JOB_OBJECT_SECURITY_FILTER_TOKENS ) 02035 { 02036 if ( Job->SecurityLimitFlags & 02037 ( JOB_OBJECT_SECURITY_ONLY_TOKEN | 02038 JOB_OBJECT_SECURITY_FILTER_TOKENS ) ) 02039 { 02040 st = STATUS_INVALID_PARAMETER ; 02041 } 02042 else 02043 { 02044 // 02045 // capture the token restrictions 02046 // 02047 02048 st = PspCaptureTokenFilter( 02049 PreviousMode, 02050 &SecurityLimitInfo, 02051 &Filter 02052 ); 02053 02054 if ( NT_SUCCESS( st ) ) 02055 { 02056 Job->SecurityLimitFlags |= JOB_OBJECT_SECURITY_FILTER_TOKENS ; 02057 Job->Filter = Filter ; 02058 } 02059 02060 } 02061 } 02062 02063 } 02064 } 02065 break; 02066 02067 case JobObjectEndOfJobTimeInformation: 02068 02069 try { 02070 RtlCopyMemory(&EndOfJobInfo,JobObjectInformation,RequiredLength); 02071 } 02072 except(EXCEPTION_EXECUTE_HANDLER) { 02073 st = GetExceptionCode(); 02074 } 02075 02076 if ( NT_SUCCESS(st) ) { 02077 // 02078 // sanity check LimitFlags 02079 // 02080 if ( EndOfJobInfo.EndOfJobTimeAction > JOB_OBJECT_POST_AT_END_OF_JOB ) { 02081 st = STATUS_INVALID_PARAMETER; 02082 } 02083 else { 02084 Job->EndOfJobTimeAction = EndOfJobInfo.EndOfJobTimeAction; 02085 } 02086 } 02087 break; 02088 02089 case JobObjectAssociateCompletionPortInformation: 02090 02091 try { 02092 RtlCopyMemory(&AssociateInfo,JobObjectInformation,RequiredLength); 02093 } 02094 except(EXCEPTION_EXECUTE_HANDLER) { 02095 st = GetExceptionCode(); 02096 } 02097 02098 if ( NT_SUCCESS(st) ) { 02099 if ( !Job->CompletionPort && AssociateInfo.CompletionPort ) { 02100 st = ObReferenceObjectByHandle( 02101 AssociateInfo.CompletionPort, 02102 IO_COMPLETION_MODIFY_STATE, 02103 IoCompletionObjectType, 02104 PreviousMode, 02105 &IoCompletion, 02106 NULL 02107 ); 02108 02109 if (NT_SUCCESS(st)) { 02110 Job->CompletionKey = AssociateInfo.CompletionKey; 02111 Job->CompletionPort = IoCompletion; 02112 02113 // 02114 // Now whip through ALL existing processes in the job 02115 // and send notification messages 02116 // 02117 02118 Next = Job->ProcessListHead.Flink; 02119 02120 while ( Next != &Job->ProcessListHead) { 02121 02122 Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks)); 02123 02124 02125 // 02126 // If the process is really considered part of the job, has 02127 // been assigned its id, and has not yet checked in, do it now 02128 // 02129 02130 if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) 02131 && Process->UniqueProcessId 02132 && !(Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED)) { 02133 02134 PS_SET_CLEAR_BITS (&Process->JobStatus, 02135 PS_JOB_STATUS_NEW_PROCESS_REPORTED, 02136 PS_JOB_STATUS_LAST_REPORT_MEMORY); 02137 02138 IoSetIoCompletion( 02139 Job->CompletionPort, 02140 Job->CompletionKey, 02141 (PVOID)Process->UniqueProcessId, 02142 STATUS_SUCCESS, 02143 JOB_OBJECT_MSG_NEW_PROCESS, 02144 FALSE 02145 ); 02146 02147 } 02148 Next = Next->Flink; 02149 } 02150 } 02151 } 02152 else { 02153 st = STATUS_INVALID_PARAMETER; 02154 } 02155 } 02156 break; 02157 02158 02159 default: 02160 02161 st = STATUS_INVALID_INFO_CLASS; 02162 } 02163 02164 ExReleaseResource(&Job->JobLock); 02165 02166 // 02167 // Working Set Changes are processed outside of the job lock. 02168 // 02169 // calling MmAdjust CAN NOT cause MM to call PsChangeJobMemoryUsage ! 02170 // 02171 02172 if ( ProcessWorkingSetHead ) { 02173 if ( !IsListEmpty(&PspWorkingSetChangeHead.Links) ) { 02174 while ( !IsListEmpty(&PspWorkingSetChangeHead.Links) ) { 02175 Next = RemoveHeadList(&PspWorkingSetChangeHead.Links); 02176 WsChangeRecord = CONTAINING_RECORD(Next,JOB_WORKING_SET_CHANGE_RECORD,Links); 02177 KeAttachProcess(&WsChangeRecord->Process->Pcb); 02178 02179 MmAdjustWorkingSetSize( 02180 PspWorkingSetChangeHead.MinimumWorkingSetSize, 02181 PspWorkingSetChangeHead.MaximumWorkingSetSize, 02182 FALSE 02183 ); 02184 02185 // 02186 // call MM to Enable hard workingset 02187 // 02188 02189 MmEnforceWorkingSetLimit(&WsChangeRecord->Process->Vm, TRUE); 02190 KeDetachProcess(); 02191 ObDereferenceObject(WsChangeRecord->Process); 02192 ExFreePool(WsChangeRecord); 02193 } 02194 } 02195 ExReleaseFastMutexUnsafe(&PspWorkingSetChangeHead.Lock); 02196 } 02197 KeLeaveCriticalRegion(); 02198 02199 02200 // 02201 // Finish Up 02202 // 02203 02204 ObDereferenceObject(Job); 02205 02206 return st; 02207 } 02208 02209 VOID 02210 PspApplyJobLimitsToProcessSet( 02211 PEJOB Job 02212 ) 02213 { 02214 02215 PLIST_ENTRY Next; 02216 PEPROCESS Process; 02217 PJOB_WORKING_SET_CHANGE_RECORD WsChangeRecord; 02218 02219 PAGED_CODE(); 02220 02221 // 02222 // The job object is held exclusive by the caller 02223 // 02224 02225 Next = Job->ProcessListHead.Flink; 02226 02227 while ( Next != &Job->ProcessListHead) { 02228 02229 Process = (PEPROCESS)(CONTAINING_RECORD(Next,EPROCESS,JobLinks)); 02230 if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) ) { 02231 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET ) { 02232 WsChangeRecord = ExAllocatePoolWithTag( 02233 PagedPool, 02234 sizeof( *WsChangeRecord ), 02235 'rCsP' 02236 ); 02237 if ( WsChangeRecord ) { 02238 WsChangeRecord->Process = Process; 02239 ObReferenceObject(Process); 02240 // 02241 // Avoid double delete since process could be in delete routine during the above ref 02242 // 02243 if ( ObGetObjectPointerCount(Process) > 1 ) { 02244 InsertTailList(&PspWorkingSetChangeHead.Links,&WsChangeRecord->Links); 02245 } 02246 else { 02247 // 02248 // process is possibly in delete routine waiting to come 02249 // out of job. DON'T Dereference ! 02250 // 02251 ExFreePool(WsChangeRecord); 02252 } 02253 } 02254 } 02255 PspApplyJobLimitsToProcess(Job,Process); 02256 } 02257 Next = Next->Flink; 02258 } 02259 } 02260 02261 VOID 02262 PspApplyJobLimitsToProcess( 02263 PEJOB Job, 02264 PEPROCESS Process 02265 ) 02266 { 02267 02268 NTSTATUS Status; 02269 PLIST_ENTRY Next; 02270 PETHREAD Thread; 02271 02272 PAGED_CODE(); 02273 02274 // 02275 // The job object is held exclusive by the caller 02276 // 02277 02278 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_PRIORITY_CLASS ) { 02279 Process->PriorityClass = Job->PriorityClass; 02280 02281 PsSetProcessPriorityByClass( 02282 Process, 02283 Process->Vm.MemoryPriority == MEMORY_PRIORITY_FOREGROUND ? 02284 PsProcessPriorityForeground : PsProcessPriorityBackground 02285 ); 02286 } 02287 02288 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_AFFINITY ) { 02289 02290 // 02291 // the following allows this api to properly if 02292 // called while the exiting process is blocked holding the 02293 // createdeletelock. This can happen during debugger/server 02294 // lpc transactions that occur in pspexitthread 02295 // 02296 02297 Status = PsLockProcess(Process,KeGetPreviousMode(),PsLockPollOnTimeout); 02298 02299 if ( Status == STATUS_SUCCESS ) { 02300 Process->Pcb.Affinity = Job->Affinity; 02301 02302 Next = Process->ThreadListHead.Flink; 02303 02304 while ( Next != &Process->ThreadListHead) { 02305 02306 Thread = (PETHREAD)(CONTAINING_RECORD(Next,ETHREAD,ThreadListEntry)); 02307 KeSetAffinityThread(&Thread->Tcb,Job->Affinity); 02308 Next = Next->Flink; 02309 } 02310 02311 PsUnlockProcess(Process); 02312 } 02313 } 02314 02315 if ( !(Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) ) { 02316 // 02317 // call MM to disable hard workingset 02318 // 02319 02320 MmEnforceWorkingSetLimit(&Process->Vm, FALSE); 02321 } 02322 02323 ExAcquireFastMutexUnsafe(&Job->MemoryLimitsLock); 02324 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY ) { 02325 Process->CommitChargeLimit = Job->ProcessMemoryLimit; 02326 } 02327 else { 02328 Process->CommitChargeLimit = 0; 02329 } 02330 ExReleaseFastMutexUnsafe(&Job->MemoryLimitsLock); 02331 02332 // 02333 // If the process is NOT IDLE Priority Class, and long fixed quantums 02334 // are in use, use the scheduling class stored in the job object for this process 02335 // 02336 if ( Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE ) { 02337 02338 if ( PspUseJobSchedulingClasses ) { 02339 Process->Pcb.ThreadQuantum = PspJobSchedulingClasses[Job->SchedulingClass]; 02340 } 02341 // 02342 // if the scheduling class is PSP_NUMBER_OF_SCHEDULING_CLASSES-1, then 02343 // give this process non-preemptive scheduling 02344 // 02345 if ( Job->SchedulingClass == PSP_NUMBER_OF_SCHEDULING_CLASSES-1 ) { 02346 KeSetDisableQuantumProcess(&Process->Pcb,TRUE); 02347 } 02348 else { 02349 KeSetDisableQuantumProcess(&Process->Pcb,FALSE); 02350 } 02351 02352 } 02353 02354 02355 } 02356 02357 NTSTATUS 02358 NtTerminateJobObject( 02359 IN HANDLE JobHandle, 02360 IN NTSTATUS ExitStatus 02361 ) 02362 { 02363 PEJOB Job; 02364 NTSTATUS st; 02365 KPROCESSOR_MODE PreviousMode; 02366 02367 PAGED_CODE(); 02368 02369 PreviousMode = KeGetPreviousMode(); 02370 st = ObReferenceObjectByHandle( 02371 JobHandle, 02372 JOB_OBJECT_TERMINATE, 02373 PsJobType, 02374 PreviousMode, 02375 (PVOID *)&Job, 02376 NULL 02377 ); 02378 if ( !NT_SUCCESS(st) ) { 02379 return st; 02380 } 02381 02382 KeEnterCriticalRegion(); 02383 ExAcquireResourceExclusive(&Job->JobLock, TRUE); 02384 02385 PspTerminateAllProcessesInJob(Job,ExitStatus,PsLockPollOnTimeout); 02386 02387 ExReleaseResource(&Job->JobLock); 02388 KeLeaveCriticalRegion(); 02389 02390 ObDereferenceObject(Job); 02391 02392 return st; 02393 } 02394 02395 VOID 02396 PsEnforceExecutionTimeLimits( 02397 VOID 02398 ) 02399 { 02400 PLIST_ENTRY NextJob; 02401 PLIST_ENTRY NextProcess; 02402 LARGE_INTEGER RunningJobTime; 02403 LARGE_INTEGER ProcessTime; 02404 PEJOB Job; 02405 PEPROCESS Process; 02406 NTSTATUS st; 02407 02408 ExAcquireFastMutex(&PspJobListLock); 02409 02410 // 02411 // Look at each job. If time limits are set for the job, then enforce them 02412 // 02413 NextJob = PspJobList.Flink; 02414 while ( NextJob != &PspJobList ) { 02415 Job = (PEJOB)(CONTAINING_RECORD(NextJob,EJOB,JobLinks)); 02416 if ( Job->LimitFlags & (JOB_OBJECT_LIMIT_PROCESS_TIME | JOB_OBJECT_LIMIT_JOB_TIME) ) { 02417 02418 // 02419 // Job looks like a candidate for time enforcing. Need to get the 02420 // job lock to be sure, but we don't want to hang waiting for the 02421 // job lock, so skip the job until next time around if we need to 02422 // 02423 // 02424 02425 if ( ExAcquireResourceExclusive(&Job->JobLock, FALSE) ) { 02426 02427 if ( Job->LimitFlags & (JOB_OBJECT_LIMIT_PROCESS_TIME | JOB_OBJECT_LIMIT_JOB_TIME) ) { 02428 02429 // 02430 // Job is setup for time limits 02431 // 02432 02433 RunningJobTime.QuadPart = Job->ThisPeriodTotalUserTime.QuadPart; 02434 02435 NextProcess = Job->ProcessListHead.Flink; 02436 02437 while ( NextProcess != &Job->ProcessListHead) { 02438 02439 Process = (PEPROCESS)(CONTAINING_RECORD(NextProcess,EPROCESS,JobLinks)); 02440 02441 ProcessTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,KeMaximumIncrement); 02442 02443 if ( !(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED) ) { 02444 RunningJobTime.QuadPart += ProcessTime.QuadPart; 02445 } 02446 02447 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_PROCESS_TIME ) { 02448 if ( ProcessTime.QuadPart > Job->PerProcessUserTimeLimit.QuadPart ) { 02449 02450 // 02451 // Process Time Limit has been exceeded. 02452 // 02453 // Reference the process. Assert that it is not in its 02454 // delete routine. If all is OK, then nuke and dereferece 02455 // the process 02456 // 02457 02458 ObReferenceObject(Process); 02459 02460 // 02461 // Avoid double delete since process could be in delete routine during the above ref 02462 // 02463 if ( ObGetObjectPointerCount(Process) > 1 ) { 02464 02465 if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) ) { 02466 if ( PspTerminateProcess(Process,ERROR_NOT_ENOUGH_QUOTA,PsLockReturnTimeout) == STATUS_SUCCESS ) { 02467 02468 Job->TotalTerminatedProcesses++; 02469 PS_SET_CLEAR_BITS (&Process->JobStatus, 02470 PS_JOB_STATUS_NOT_REALLY_ACTIVE, 02471 PS_JOB_STATUS_LAST_REPORT_MEMORY); 02472 Job->ActiveProcesses--; 02473 02474 if ( Job->CompletionPort ) { 02475 IoSetIoCompletion( 02476 Job->CompletionPort, 02477 Job->CompletionKey, 02478 (PVOID)Process->UniqueProcessId, 02479 STATUS_SUCCESS, 02480 JOB_OBJECT_MSG_END_OF_PROCESS_TIME, 02481 FALSE 02482 ); 02483 } 02484 PspFoldProcessAccountingIntoJob(Job,Process); 02485 02486 } 02487 } 02488 ObDereferenceObject(Process); 02489 } 02490 } 02491 } 02492 02493 NextProcess = NextProcess->Flink; 02494 } 02495 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME ) { 02496 if ( RunningJobTime.QuadPart > Job->PerJobUserTimeLimit.QuadPart ) { 02497 02498 // 02499 // Job Time Limit has been exceeded. 02500 // 02501 // Perform the appropriate action 02502 // 02503 02504 switch ( Job->EndOfJobTimeAction ) { 02505 02506 case JOB_OBJECT_TERMINATE_AT_END_OF_JOB: 02507 if ( PspTerminateAllProcessesInJob(Job,ERROR_NOT_ENOUGH_QUOTA,PsLockReturnTimeout) ) { 02508 if ( Job->ActiveProcesses == 0 ) { 02509 KeSetEvent(&Job->Event,0,FALSE); 02510 if ( Job->CompletionPort ) { 02511 PS_CLEAR_BITS (&Process->JobStatus, PS_JOB_STATUS_LAST_REPORT_MEMORY); 02512 IoSetIoCompletion( 02513 Job->CompletionPort, 02514 Job->CompletionKey, 02515 NULL, 02516 STATUS_SUCCESS, 02517 JOB_OBJECT_MSG_END_OF_JOB_TIME, 02518 FALSE 02519 ); 02520 } 02521 } 02522 } 02523 break; 02524 02525 case JOB_OBJECT_POST_AT_END_OF_JOB: 02526 02527 if ( Job->CompletionPort ) { 02528 PS_CLEAR_BITS (&Process->JobStatus, PS_JOB_STATUS_LAST_REPORT_MEMORY); 02529 st = IoSetIoCompletion( 02530 Job->CompletionPort, 02531 Job->CompletionKey, 02532 NULL, 02533 STATUS_SUCCESS, 02534 JOB_OBJECT_MSG_END_OF_JOB_TIME, 02535 FALSE 02536 ); 02537 if ( NT_SUCCESS(st) ) { 02538 02539 // 02540 // Clear job level time limit 02541 // 02542 02543 Job->LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_TIME; 02544 Job->PerJobUserTimeLimit.QuadPart = 0; 02545 } 02546 } 02547 else { 02548 if ( PspTerminateAllProcessesInJob(Job,ERROR_NOT_ENOUGH_QUOTA,PsLockReturnTimeout) ) { 02549 if ( Job->ActiveProcesses == 0 ) { 02550 KeSetEvent(&Job->Event,0,FALSE); 02551 } 02552 } 02553 } 02554 break; 02555 } 02556 } 02557 02558 } 02559 02560 } 02561 ExReleaseResource(&Job->JobLock); 02562 } 02563 } 02564 NextJob = NextJob->Flink; 02565 } 02566 ExReleaseFastMutex(&PspJobListLock); 02567 } 02568 02569 BOOLEAN 02570 PspTerminateAllProcessesInJob( 02571 PEJOB Job, 02572 NTSTATUS Status, 02573 PSLOCKPROCESSMODE LockMode 02574 ) 02575 { 02576 PLIST_ENTRY NextProcess; 02577 PEPROCESS Process; 02578 BOOLEAN TerminatedAProcess; 02579 02580 PAGED_CODE(); 02581 02582 TerminatedAProcess = FALSE; 02583 NextProcess = Job->ProcessListHead.Flink; 02584 02585 while ( NextProcess != &Job->ProcessListHead) { 02586 02587 Process = (PEPROCESS)(CONTAINING_RECORD(NextProcess,EPROCESS,JobLinks)); 02588 02589 // 02590 // Reference the process. Assert that it is not in its 02591 // delete routine. If all is OK, then nuke and dereferece 02592 // the process 02593 // 02594 02595 ObReferenceObject(Process); 02596 02597 // 02598 // Avoid double delete since process could be in delete routine during the above ref 02599 // 02600 if ( ObGetObjectPointerCount(Process) > 1 ) { 02601 if ( !(Process->JobStatus & PS_JOB_STATUS_NOT_REALLY_ACTIVE) ) { 02602 02603 if ( PspTerminateProcess(Process,Status,LockMode) == STATUS_SUCCESS ) { 02604 02605 // 02606 // If the lockmode isn't poll, it means we ran out of 02607 // job time, so increment the terminated process count 02608 // for each nuked process 02609 // 02610 if ( LockMode != PsLockPollOnTimeout ) { 02611 Job->TotalTerminatedProcesses++; 02612 } 02613 PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NOT_REALLY_ACTIVE); 02614 Job->ActiveProcesses--; 02615 02616 PspFoldProcessAccountingIntoJob(Job,Process); 02617 02618 TerminatedAProcess = TRUE; 02619 } 02620 } 02621 02622 ObDereferenceObject(Process); 02623 } 02624 02625 NextProcess = NextProcess->Flink; 02626 } 02627 return TerminatedAProcess; 02628 } 02629 02630 02631 VOID 02632 PspFoldProcessAccountingIntoJob( 02633 PEJOB Job, 02634 PEPROCESS Process 02635 ) 02636 { 02637 LARGE_INTEGER UserTime, KernelTime; 02638 02639 if ( !(Process->JobStatus & PS_JOB_STATUS_ACCOUNTING_FOLDED) ) { 02640 UserTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,KeMaximumIncrement); 02641 KernelTime.QuadPart = UInt32x32To64(Process->Pcb.KernelTime,KeMaximumIncrement); 02642 02643 Job->TotalUserTime.QuadPart += UserTime.QuadPart; 02644 Job->TotalKernelTime.QuadPart += KernelTime.QuadPart; 02645 Job->ThisPeriodTotalUserTime.QuadPart += UserTime.QuadPart; 02646 Job->ThisPeriodTotalKernelTime.QuadPart += KernelTime.QuadPart; 02647 02648 Job->ReadOperationCount += Process->ReadOperationCount.QuadPart; 02649 Job->WriteOperationCount += Process->WriteOperationCount.QuadPart; 02650 Job->OtherOperationCount += Process->OtherOperationCount.QuadPart; 02651 Job->ReadTransferCount += Process->ReadTransferCount.QuadPart; 02652 Job->WriteTransferCount += Process->WriteTransferCount.QuadPart; 02653 Job->OtherTransferCount += Process->OtherTransferCount.QuadPart; 02654 02655 Job->TotalPageFaultCount += Process->Vm.PageFaultCount; 02656 02657 02658 if ( Process->CommitChargePeak > Job->PeakProcessMemoryUsed ) { 02659 Job->PeakProcessMemoryUsed = Process->CommitChargePeak; 02660 } 02661 02662 PS_SET_CLEAR_BITS (&Process->JobStatus, 02663 PS_JOB_STATUS_ACCOUNTING_FOLDED, 02664 PS_JOB_STATUS_LAST_REPORT_MEMORY); 02665 02666 if ( Job->CompletionPort && Job->ActiveProcesses == 0) { 02667 IoSetIoCompletion( 02668 Job->CompletionPort, 02669 Job->CompletionKey, 02670 NULL, 02671 STATUS_SUCCESS, 02672 JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 02673 FALSE 02674 ); 02675 } 02676 } 02677 } 02678 02679 NTSTATUS 02680 PspCaptureTokenFilter( 02681 KPROCESSOR_MODE PreviousMode, 02682 PJOBOBJECT_SECURITY_LIMIT_INFORMATION SecurityLimitInfo, 02683 PPS_JOB_TOKEN_FILTER * TokenFilter 02684 ) 02685 { 02686 NTSTATUS Status ; 02687 PPS_JOB_TOKEN_FILTER Filter ; 02688 02689 Filter = ExAllocatePoolWithTag( NonPagedPool, 02690 sizeof( PS_JOB_TOKEN_FILTER ), 02691 'fTsP' ); 02692 02693 if ( !Filter ) 02694 { 02695 *TokenFilter = NULL ; 02696 02697 return STATUS_INSUFFICIENT_RESOURCES ; 02698 } 02699 02700 RtlZeroMemory( Filter, sizeof( PS_JOB_TOKEN_FILTER ) ); 02701 02702 try { 02703 02704 Status = STATUS_SUCCESS ; 02705 02706 // 02707 // Capture Sids to remove 02708 // 02709 02710 if (ARGUMENT_PRESENT( SecurityLimitInfo->SidsToDisable)) { 02711 02712 ProbeForRead( SecurityLimitInfo->SidsToDisable, 02713 sizeof(TOKEN_GROUPS), 02714 sizeof(ULONG) ); 02715 02716 Filter->CapturedGroupCount = SecurityLimitInfo->SidsToDisable->GroupCount; 02717 02718 Status = SeCaptureSidAndAttributesArray( 02719 SecurityLimitInfo->SidsToDisable->Groups, 02720 Filter->CapturedGroupCount, 02721 PreviousMode, 02722 NULL, 0, 02723 NonPagedPool, 02724 TRUE, 02725 &Filter->CapturedGroups, 02726 &Filter->CapturedGroupsLength 02727 ); 02728 02729 } 02730 02731 // 02732 // Capture PrivilegesToDelete 02733 // 02734 02735 if (NT_SUCCESS(Status) && 02736 ARGUMENT_PRESENT(SecurityLimitInfo->PrivilegesToDelete)) { 02737 02738 ProbeForRead( SecurityLimitInfo->PrivilegesToDelete, 02739 sizeof(TOKEN_PRIVILEGES), 02740 sizeof(ULONG) ); 02741 02742 Filter->CapturedPrivilegeCount = SecurityLimitInfo->PrivilegesToDelete->PrivilegeCount; 02743 02744 Status = SeCaptureLuidAndAttributesArray( 02745 SecurityLimitInfo->PrivilegesToDelete->Privileges, 02746 Filter->CapturedPrivilegeCount, 02747 PreviousMode, 02748 NULL, 0, 02749 NonPagedPool, 02750 TRUE, 02751 &Filter->CapturedPrivileges, 02752 &Filter->CapturedPrivilegesLength 02753 ); 02754 02755 } 02756 02757 // 02758 // Capture Restricted Sids 02759 // 02760 02761 if (NT_SUCCESS(Status) && 02762 ARGUMENT_PRESENT(SecurityLimitInfo->RestrictedSids)) { 02763 02764 ProbeForRead( SecurityLimitInfo->RestrictedSids, 02765 sizeof(TOKEN_GROUPS), 02766 sizeof(ULONG) ); 02767 02768 Filter->CapturedSidCount = SecurityLimitInfo->RestrictedSids->GroupCount; 02769 02770 Status = SeCaptureSidAndAttributesArray( 02771 SecurityLimitInfo->RestrictedSids->Groups, 02772 Filter->CapturedSidCount, 02773 PreviousMode, 02774 NULL, 0, 02775 NonPagedPool, 02776 TRUE, 02777 &Filter->CapturedSids, 02778 &Filter->CapturedSidsLength 02779 ); 02780 02781 } 02782 02783 02784 02785 } except(EXCEPTION_EXECUTE_HANDLER) { 02786 02787 Status = GetExceptionCode(); 02788 } // end_try 02789 02790 if ( !NT_SUCCESS( Status ) ) 02791 { 02792 if ( Filter->CapturedSids ) 02793 { 02794 ExFreePool( Filter->CapturedSids ); 02795 } 02796 02797 if ( Filter->CapturedPrivileges ) 02798 { 02799 ExFreePool( Filter->CapturedPrivileges ); 02800 } 02801 02802 if ( Filter->CapturedGroups ) 02803 { 02804 ExFreePool( Filter->CapturedGroups ); 02805 } 02806 02807 ExFreePool( Filter ); 02808 02809 Filter = NULL ; 02810 02811 } 02812 02813 *TokenFilter = Filter ; 02814 02815 return Status ; 02816 02817 02818 } 02819 02820 02821 02822 BOOLEAN 02823 PsChangeJobMemoryUsage( 02824 SSIZE_T Amount 02825 ) 02826 { 02827 PEPROCESS Process; 02828 PEJOB Job; 02829 SIZE_T CurrentJobMemoryUsed; 02830 BOOLEAN ReturnValue; 02831 02832 ReturnValue = TRUE; 02833 Process = PsGetCurrentProcess(); 02834 Job = Process->Job; 02835 if ( Job ) { 02836 // 02837 // This routine can be called while hoolding the process lock (during 02838 // teb deletion... So instead of using the job lock, we must use the 02839 // memory limits lock. The lock order is always (job lock followed by 02840 // process lock. The memory limits lock never nests or calls other 02841 // code while held. It can be grapped while holding the job lock, or the process 02842 // lock. 02843 // 02844 KeEnterCriticalRegion(); 02845 ExAcquireFastMutexUnsafe(&Job->MemoryLimitsLock); 02846 02847 CurrentJobMemoryUsed = Job->CurrentJobMemoryUsed + Amount; 02848 02849 if ( Job->LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY && 02850 CurrentJobMemoryUsed > Job->JobMemoryLimit ) { 02851 CurrentJobMemoryUsed = Job->CurrentJobMemoryUsed; 02852 ReturnValue = FALSE; 02853 02854 02855 02856 // 02857 // Tell the job port that commit has been exceeded, and process id x 02858 // was the one that hit it. 02859 // 02860 02861 if ( Job->CompletionPort 02862 && Process->UniqueProcessId 02863 && (Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED) 02864 && (Process->JobStatus & PS_JOB_STATUS_LAST_REPORT_MEMORY) == 0) { 02865 02866 PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_LAST_REPORT_MEMORY); 02867 IoSetIoCompletion( 02868 Job->CompletionPort, 02869 Job->CompletionKey, 02870 (PVOID)Process->UniqueProcessId, 02871 STATUS_SUCCESS, 02872 JOB_OBJECT_MSG_JOB_MEMORY_LIMIT, 02873 TRUE 02874 ); 02875 02876 } 02877 } 02878 02879 if ( ReturnValue ) { 02880 // 02881 // update current and peak counters 02882 // 02883 Job->CurrentJobMemoryUsed = CurrentJobMemoryUsed; 02884 if ( CurrentJobMemoryUsed > Job->PeakJobMemoryUsed ) { 02885 Job->PeakJobMemoryUsed = CurrentJobMemoryUsed; 02886 } 02887 02888 if ( Process->CommitCharge + Amount > Job->PeakProcessMemoryUsed ) { 02889 Job->PeakProcessMemoryUsed = Process->CommitCharge + Amount; 02890 } 02891 } 02892 ExReleaseFastMutexUnsafe(&Job->MemoryLimitsLock); 02893 KeLeaveCriticalRegion(); 02894 } 02895 02896 return ReturnValue; 02897 } 02898 02899 02900 VOID 02901 PsReportProcessMemoryLimitViolation( 02902 VOID 02903 ) 02904 { 02905 PEPROCESS Process; 02906 PEJOB Job; 02907 02908 Process = PsGetCurrentProcess(); 02909 Job = Process->Job; 02910 if ( Job && (Job->LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) ) { 02911 KeEnterCriticalRegion(); 02912 ExAcquireFastMutexUnsafe(&Job->MemoryLimitsLock); 02913 02914 // 02915 // Tell the job port that commit has been exceeded, and process id x 02916 // was the one that hit it. 02917 // 02918 02919 if ( Job->CompletionPort 02920 && Process->UniqueProcessId 02921 && (Process->JobStatus & PS_JOB_STATUS_NEW_PROCESS_REPORTED) 02922 && (Process->JobStatus & PS_JOB_STATUS_LAST_REPORT_MEMORY) == 0) { 02923 02924 PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_LAST_REPORT_MEMORY); 02925 IoSetIoCompletion( 02926 Job->CompletionPort, 02927 Job->CompletionKey, 02928 (PVOID)Process->UniqueProcessId, 02929 STATUS_SUCCESS, 02930 JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT, 02931 TRUE 02932 ); 02933 02934 } 02935 ExReleaseFastMutexUnsafe(&Job->MemoryLimitsLock); 02936 KeLeaveCriticalRegion(); 02937 02938 } 02939 }

Generated on Sat May 15 19:41:32 2004 for test by doxygen 1.3.7