Exemplo n.º 1
0
        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);
                }
            }
        }
Exemplo n.º 2
0
        /// <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);
            }
        }
Exemplo n.º 3
0
        private void HandleSurvivingChildProcesses(JobObject jobObject)
        {
            m_survivingChildProcesses = GetSurvivingChildProcesses(jobObject);

            if (m_survivingChildProcesses != null && m_survivingChildProcesses.Count > 0)
            {
                m_detouredProcess.Kill(ExitCodes.KilledSurviving);
            }
        }
Exemplo n.º 4
0
            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.");
                }
            }
Exemplo n.º 5
0
        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);
                }
            }
        }
Exemplo n.º 6
0
        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);
        }
Exemplo n.º 7
0
        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);
        }
Exemplo n.º 8
0
        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);
        }
Exemplo n.º 9
0
 private Dictionary <uint, ReportedProcess> GetSurvivingChildProcesses(JobObject jobObject)
 {
     if (!jobObject.TryGetProcessIds(m_loggingContext, out uint[] survivingChildProcessIds) || survivingChildProcessIds.Length == 0)
Exemplo n.º 10
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);
        }