private void VisitJobObjectProcesses(JobObject jobObject, uint[] childProcessIds, Action <SafeProcessHandle, uint> actionForProcess) { foreach (uint processId in childProcessIds) { using (SafeProcessHandle processHandle = ProcessUtilities.OpenProcess( ProcessSecurityAndAccessRights.PROCESS_QUERY_INFORMATION | ProcessSecurityAndAccessRights.PROCESS_SET_QUOTA, false, processId)) { if (processHandle.IsInvalid) { // we are too late: could not open process continue; } if (!jobObject.ContainsProcess(processHandle)) { // we are too late: process id got reused by another process continue; } if (!ProcessUtilities.GetExitCodeProcess(processHandle, out int exitCode)) { // we are too late: process id got reused by another process continue; } actionForProcess(processHandle, processId); } } }
/// <summary> /// Add the current process to a job object, and sets limit information. /// </summary> public static bool SetLimitInformationOnCurrentProcessJob(bool?terminateOnClose = null, ProcessPriorityClass?priorityClass = null) { Contract.Requires(OSSupportsNestedJobs); bool ret = false; using (Process currentProcess = Process.GetCurrentProcess()) { using (var jobObject = new JobObject(null)) { jobObject.SetLimitInformation(terminateOnClose, priorityClass); #if FEATURE_SAFE_PROCESS_HANDLE ret = jobObject.AddProcess(currentProcess.SafeHandle); #else ret = jobObject.AddProcess(currentProcess.Handle); #endif bool success = false; jobObject.DangerousAddRef(ref success); if (!success) { throw new InvalidOperationException(); } } return(ret); } }
private void HandleSurvivingChildProcesses(JobObject jobObject) { m_survivingChildProcesses = GetSurvivingChildProcesses(jobObject); if (m_survivingChildProcesses != null && m_survivingChildProcesses.Count > 0) { m_detouredProcess.Kill(ExitCodes.KilledSurviving); } }
public void Register(IntPtr key, JobObject value) { Contract.Requires(value != null); if (!m_jobs.TryAdd(key, value)) { throw new InvalidOperationException("Invoking Wait concurrently on the same instance is not supported."); } }
private async Task OnProcessExitingAsync() { JobObject jobObject = m_detouredProcess.GetJobObject(); if (jobObject != null) { // If there are any remaining child processes, we wait for a moment. // This should be a rare case, and even if we have to wait it should typically only be for a fraction of a second, so we simply wait synchronously (blocking the current thread). if (!(await jobObject.WaitAsync(m_nestedProcessTerminationTimeout))) { HandleSurvivingChildProcesses(jobObject); } } }
private bool ShouldWaitForSurvivingChildProcesses(JobObject jobObject) { Contract.Requires(jobObject != null); if (m_allowedSurvivingChildProcessNames == null || m_allowedSurvivingChildProcessNames.Length == 0) { // Wait for surviving child processes if no allowable process names are explicitly specified. return(true); } Dictionary <uint, ReportedProcess> survivingChildProcesses = GetSurvivingChildProcesses(jobObject); if (survivingChildProcesses != null && survivingChildProcesses.Count > 0) { foreach (string processPath in survivingChildProcesses.Select(kvp => kvp.Value.Path)) { bool allowed = false; foreach (string allowedProcessName in m_allowedSurvivingChildProcessNames) { if (string.Equals(Path.GetFileName(processPath), allowedProcessName, StringComparison.OrdinalIgnoreCase)) { allowed = true; break; } } if (!allowed) { return(true); } } } return(false); }
private static Dictionary <uint, ReportedProcess> GetSurvivingChildProcesses(JobObject jobObject) { if (!jobObject.TryGetProcessIds(out uint[] survivingChildProcessIds) || survivingChildProcessIds.Length == 0) { return(null); } var survivingChildProcesses = new Dictionary <uint, ReportedProcess>(); foreach (uint processId in survivingChildProcessIds) { using (SafeProcessHandle processHandle = ProcessUtilities.OpenProcess( ProcessSecurityAndAccessRights.PROCESS_QUERY_INFORMATION | ProcessSecurityAndAccessRights.PROCESS_VM_READ, false, processId)) { if (processHandle.IsInvalid) { // we are too late: could not open process continue; } if (!jobObject.ContainsProcess(processHandle)) { // we are too late: process id got reused by another process continue; } int exitCode; if (!ProcessUtilities.GetExitCodeProcess(processHandle, out exitCode)) { // we are too late: process id got reused by another process continue; } using (PooledObjectWrapper <StringBuilder> wrap = Pools.GetStringBuilder()) { StringBuilder sb = wrap.Instance; if (sb.Capacity < MaxProcessPathLength) { sb.Capacity = MaxProcessPathLength; } if (ProcessUtilities.GetModuleFileNameEx(processHandle, IntPtr.Zero, sb, (uint)sb.Capacity) <= 0) { // we are probably too late continue; } // Attempt to read the process arguments (command line) from the process // memory. This is not fatal if it does not succeed. string processArgs = string.Empty; var basicInfoSize = (uint)Marshal.SizeOf <Native.Processes.Windows.ProcessUtilitiesWin.PROCESS_BASIC_INFORMATION>(); var basicInfoPtr = Marshal.AllocHGlobal((int)basicInfoSize); uint basicInfoReadLen; try { if (Native.Processes.Windows.ProcessUtilitiesWin.NtQueryInformationProcess( processHandle, Native.Processes.Windows.ProcessUtilitiesWin.ProcessInformationClass.ProcessBasicInformation, basicInfoPtr, basicInfoSize, out basicInfoReadLen) == 0) { Native.Processes.Windows.ProcessUtilitiesWin.PROCESS_BASIC_INFORMATION basicInformation = Marshal.PtrToStructure <Native.Processes.Windows.ProcessUtilitiesWin.PROCESS_BASIC_INFORMATION>(basicInfoPtr); Contract.Assert(basicInformation.UniqueProcessId == processId); // NativeMethods.ReadProcessStructure and NativeMethods.ReadUnicodeString handle null\zero addresses // passed into them. Since these are all value types, then there is no need to do any type // of checking as passing zero through will just result in an empty process args string. var peb = Native.Processes.Windows.ProcessUtilitiesWin.ReadProcessStructure <Native.Processes.Windows.ProcessUtilitiesWin.PEB>(processHandle, basicInformation.PebBaseAddress); var processParameters = Native.Processes.Windows.ProcessUtilitiesWin.ReadProcessStructure <Native.Processes.Windows.ProcessUtilitiesWin.RTL_USER_PROCESS_PARAMETERS>(processHandle, peb.ProcessParameters); processArgs = Native.Processes.Windows.ProcessUtilitiesWin.ReadProcessUnicodeString(processHandle, processParameters.CommandLine); } } finally { Marshal.FreeHGlobal(basicInfoPtr); } string path = sb.ToString(); survivingChildProcesses.Add(processId, new ReportedProcess(processId, path, processArgs)); } } } return(survivingChildProcesses); }
private async Task OnProcessExited() { // Wait until all incoming report messages from the detoured process have been handled. await WaitUntilReportEof(m_detouredProcess.Killed); // Ensure no further modifications to the report m_reports?.Freeze(); // We can get extended accounting information (peak memory, etc. rolled up for the entire process tree) if this process was wrapped in a job. JobObject.AccountingInformation?jobAccountingInformation = null; JobObject jobObject = m_detouredProcess.GetJobObject(); if (jobObject != null) { jobAccountingInformation = jobObject.GetAccountingInformation(); } ProcessTimes primaryProcessTimes = m_detouredProcess.GetTimesForPrimaryProcess(); IOException standardInputException = null; try { await m_standardInputTcs.Task; } catch (IOException ex) { standardInputException = ex; } // Construct result; note that the process is expected to have exited at this point, even if we decided to forcefully kill it // (this callback is always a result of the process handle being signaled). int exitCode = 0; if (m_reports?.MessageProcessingFailure != null) { exitCode = ExitCodes.MessageProcessingFailure; } else { Contract.Assert(m_detouredProcess.HasExited, "Detoured process has not been marked as exited"); exitCode = m_detouredProcess.GetExitCode(); } SandboxedProcessResult result = new SandboxedProcessResult { // If there is a message parsing failure, fail the pip. ExitCode = exitCode, Killed = m_detouredProcess.Killed, TimedOut = m_detouredProcess.TimedOut, HasDetoursInjectionFailures = m_detouredProcess.HasDetoursInjectionFailures, SurvivingChildProcesses = m_survivingChildProcesses?.Values.ToArray(), PrimaryProcessTimes = primaryProcessTimes, JobAccountingInformation = jobAccountingInformation, StandardOutput = m_output.Freeze(), StandardError = m_error.Freeze(), AllUnexpectedFileAccesses = m_reports?.FileUnexpectedAccesses, FileAccesses = m_reports?.FileAccesses, DetouringStatuses = m_reports?.ProcessDetoursStatuses, ExplicitlyReportedFileAccesses = m_reports?.ExplicitlyReportedFileAccesses, Processes = m_reports?.Processes, DumpFileDirectory = m_detouredProcess.DumpFileDirectory, DumpCreationException = m_detouredProcess.DumpCreationException, StandardInputException = standardInputException, MessageProcessingFailure = m_reports?.MessageProcessingFailure, ProcessStartTime = m_detouredProcess.StartTime, HasReadWriteToReadFileAccessRequest = m_reports?.HasReadWriteToReadFileAccessRequest ?? false, }; SetResult(result); }
private Dictionary <uint, ReportedProcess> GetSurvivingChildProcesses(JobObject jobObject) { if (!jobObject.TryGetProcessIds(m_loggingContext, out uint[] survivingChildProcessIds) || survivingChildProcessIds.Length == 0)
private async Task OnProcessExitedAsync() { // Wait until all incoming report messages from the detoured process have been handled. await WaitUntilReportEof(m_detouredProcess.Killed); // Ensure no further modifications to the report m_reports?.Freeze(); // We can get extended accounting information (peak memory, etc. rolled up for the entire process tree) if this process was wrapped in a job. JobObject.AccountingInformation?jobAccountingInformation = null; JobObject jobObject = m_detouredProcess.GetJobObject(); if (jobObject != null) { var accountingInfo = jobObject.GetAccountingInformation(); // Only overwrite memory counters if <see cref="GetMemoryCountersSnapshot"/> did get triggered previously. This isn't the case if the // detours sandbox is used outside of BuildXL (currently only the scheduler calls this). The <see cref="JobObject.GetAccountingInformation"/> // function does populate memory counters for the process tree if possible, so don't overwrite them with empty aggregator values. if (m_peakWorkingSet.Count > 0 || m_workingSet.Count > 0 || m_peakCommitSize.Count > 0 || m_commitSize.Count > 0) { accountingInfo.MemoryCounters = Pips.ProcessMemoryCounters.CreateFromBytes( peakWorkingSet: Convert.ToUInt64(m_peakWorkingSet.Maximum), averageWorkingSet: Convert.ToUInt64(m_workingSet.Average), peakCommitSize: Convert.ToUInt64(m_peakCommitSize.Maximum), averageCommitSize: Convert.ToUInt64(m_commitSize.Average)); } jobAccountingInformation = accountingInfo; } ProcessTimes primaryProcessTimes = m_detouredProcess.GetTimesForPrimaryProcess(); IOException standardInputException = null; try { await m_standardInputTcs.Task; } catch (IOException ex) { standardInputException = ex; } // Construct result; note that the process is expected to have exited at this point, even if we decided to forcefully kill it // (this callback is always a result of the process handle being signaled). int exitCode = 0; if (m_reports?.MessageProcessingFailure != null) { exitCode = ExitCodes.MessageProcessingFailure; } else { Contract.Assert(m_detouredProcess.HasExited, "Detoured process has not been marked as exited"); exitCode = m_detouredProcess.GetExitCode(); } SandboxedProcessResult result = new SandboxedProcessResult { // If there is a message parsing failure, fail the pip. ExitCode = exitCode, Killed = m_detouredProcess.Killed, TimedOut = m_detouredProcess.TimedOut, HasDetoursInjectionFailures = m_detouredProcess.HasDetoursInjectionFailures, SurvivingChildProcesses = m_survivingChildProcesses?.Values.ToArray(), PrimaryProcessTimes = primaryProcessTimes, JobAccountingInformation = jobAccountingInformation, StandardOutput = m_output.Freeze(), StandardError = m_error.Freeze(), AllUnexpectedFileAccesses = m_reports?.FileUnexpectedAccesses, FileAccesses = m_reports?.FileAccesses, DetouringStatuses = m_reports?.ProcessDetoursStatuses, ExplicitlyReportedFileAccesses = m_reports?.ExplicitlyReportedFileAccesses, Processes = m_reports?.Processes, DumpFileDirectory = m_detouredProcess.DumpFileDirectory, DumpCreationException = m_detouredProcess.DumpCreationException, StandardInputException = standardInputException, MessageProcessingFailure = m_reports?.MessageProcessingFailure, ProcessStartTime = m_detouredProcess.StartTime, HasReadWriteToReadFileAccessRequest = m_reports?.HasReadWriteToReadFileAccessRequest ?? false, DiagnosticMessage = m_detouredProcess.Diagnostics }; SetResult(result); }