예제 #1
0
        /// <summary>
        /// Called right after the process starts executing.
        ///
        /// Since we set the process file name to be /bin/sh and its arguments to be empty (<see cref="CreateProcess"/>),
        /// the process will effectively start in a "suspended" mode, with /bin/sh just waiting for some content to be
        /// piped to its standard input.  Therefore, in this handler we first notify the sandbox of the new process (so that
        /// it starts tracking it) and then just send the actual process command line to /bin/sh via its standard input.
        /// </summary>
        private async Task OnProcessStartedAsync(SandboxedProcessInfo info)
        {
            // Generate "Process Created" report because the rest of the system expects to see it before any other file access reports
            //
            // IMPORTANT: do this before notifying sandbox kernel extension, because otherwise it can happen that a report
            //            from the extension is received before the "process created" report is handled, causing
            //            a "Should see a process creation before its accesses" assertion exception.
            ReportProcessCreated();

            // Allow read access for /bin/sh
            // When executed using external tool, the manifest tree has been sealed, and cannot be modified.
            // We take care of adding this path in the manifest in SandboxedProcessPipExecutor.cs;
            // see AddUnixSpecificSandcboxedProcessFileAccessPolicies
            if (!info.FileAccessManifest.IsManifestTreeBlockSealed)
            {
                info.FileAccessManifest.AddPath(
                    AbsolutePath.Create(PathTable, Process.StartInfo.FileName),
                    mask: FileAccessPolicy.MaskNothing,
                    values: FileAccessPolicy.AllowReadAlways);
            }

            if (OperatingSystemHelper.IsLinuxOS)
            {
                m_perfCollector?.Start();
            }

            string processStdinFileName = await FlushStandardInputToFileIfNeededAsync(info);

            if (!SandboxConnection.NotifyPipStarted(info.LoggingContext, info.FileAccessManifest, this))
            {
                ThrowCouldNotStartProcess("Failed to initialize the sandbox for process observation, make sure BuildXL is setup correctly!");
            }

            try
            {
                await FeedStdInAsync(info, processStdinFileName);

                m_processTreeTimeoutTask = ProcessTreeTimeoutTask();
            }
            catch (IOException e)
            {
                // IOException can happen if the process is forcefully killed while we're feeding its std in.
                // When that happens, instead of crashing, just make sure the process is killed.
                LogProcessState($"IOException caught while feeding the standard input: {e.ToString()}");
                await KillAsync();
            }
            finally
            {
                // release the FileAccessManifest memory
                // NOTE: just by not keeping any references to 'info' should make the FileAccessManifest object
                //       unreachable and thus available for garbage collection.  We call Release() here explicitly
                //       just to emphasize the importance of reclaiming this memory.
                info.FileAccessManifest.Release();
            }
        }
예제 #2
0
        private void KillAllChildProcesses()
        {
            var distinctProcessIds = CoalesceProcesses(GetCurrentlyActiveChildProcesses())
                                     .Select(p => p.ProcessId)
                                     .ToHashSet();

            foreach (int processId in distinctProcessIds)
            {
                bool killed = BuildXL.Interop.Unix.Process.ForceQuit(processId);
                LogProcessState($"KillAllChildProcesses: kill({processId}) = {killed}");
                SandboxConnection.NotifyPipProcessTerminated(PipId, processId);
            }
        }
예제 #3
0
        /// <inheritdoc />
        public override async Task KillAsync()
        {
            // In the case that the process gets shut down by either its timeout or e.g. SandboxedProcessPipExecutor
            // detecting resource usage issues and calling KillAsync(), we flag the process with m_processKilled so we
            // don't process any more kernel reports that get pushed into report structure asynchronously!
            long incrementedValue = Interlocked.Increment(ref m_processKilledFlag);

            // Make sure this is done no more than once.
            if (incrementedValue == 1)
            {
                // surviving child processes may only be set when the process is explicitly killed
                m_survivingChildProcesses = NullIfEmpty(CoalesceProcesses(GetCurrentlyActiveChildProcesses()));
                await base.KillAsync();

                KillAllChildProcesses();
                SandboxConnection.NotifyRootProcessExited(PipId, this);
                await m_pendingReports.Completion;
            }
        }
예제 #4
0
        /// <summary>
        /// Waits for all child processes to finish within a timeout limit and then termiantes all still running children after that point.
        /// After all the children have been taken care of, the method waits for pending report processing to finish, then returns the
        /// collected reports.
        /// </summary>
        internal override async Task <SandboxedProcessReports> GetReportsAsync()
        {
            SandboxConnection.NotifyRootProcessExited(PipId, this);

            if (!Killed)
            {
                var awaitedTask = await Task.WhenAny(m_pendingReports.Completion, m_processTreeTimeoutTask);

                if (awaitedTask == m_processTreeTimeoutTask)
                {
                    LogProcessState("Waiting for reports timed out; any surviving processes will be forcefully killed.");
                    await KillAsync();
                }
            }

            // in any case must wait for pending reports to complete, because we must not freeze m_reports before that happens
            await m_pendingReports.Completion;

            // at this point this pip is done executing (it's only left to construct SandboxedProcessResult,
            // which is done by the base class) so notify the sandbox connection about it.
            SandboxConnection.NotifyPipFinished(PipId, this);

            return(IgnoreReportedAccesses ? null : m_reports);
        }