/// <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(); } }
public void MultipleActionStartsTest() { var mre = new ManualResetEvent(false); int counter = 0; using (var timedAction = new CancellableTimedAction(() => { // Signal that thread is started mre.Set(); ++counter; }, 10, nameof(MultipleActionStartsTest))) { XAssert.IsTrue(timedAction.Start()); XAssert.IsFalse(timedAction.Start()); Thread.Sleep(500); XAssert.IsFalse(timedAction.Start()); timedAction.Cancel(); timedAction.Join(); XAssert.IsTrue(counter > 2, "Value of counter is " + counter); } }
public void LongIntervalTest() { int counter = 0; using (var timedAction = new CancellableTimedAction(() => Interlocked.Increment(ref counter), 1000, nameof(LongIntervalTest))) { var sw = System.Diagnostics.Stopwatch.StartNew(); XAssert.IsTrue(timedAction.Start()); Thread.Sleep(100); timedAction.Cancel(); timedAction.Join(); sw.Stop(); XAssert.AreEqual(1, counter); XAssert.IsTrue(sw.ElapsedMilliseconds < 1000); } }
/// <summary> /// Removes <paramref name="pid" /> from the set of active processes. /// If no active processes are left thereafter, calls <see cref="RequestStop"/>. /// </summary> internal void RemovePid(int pid) { bool removed = m_activeProcesses.TryRemove(pid, out var _); LogDebug($"RemovePid({pid}) :: removed: {removed}; size: {m_activeProcesses.Count()}"); if (m_activeProcesses.Count == 0) { RequestStop(); } else if (removed && pid == Process.ProcessId) { // We just removed the root process and there are still active processes left // => start periodically checking if they are still alive, because we don't // have a reliable mechanism for receiving those events straight from the // child processes (e.g., if they crash, we might not hear about it) // // Observe also that we do have a reliable mechanism for detecting when the // root process exits (even if it crashes): see NotifyRootProcessExited below, // which is guaranteed to be called by SandboxedProcessUnix. m_activeProcessesChecker.Start(); } }
public void BasicFunctionalityTest() { var mre = new ManualResetEvent(false); int counter = 0; using (var timedAction = new CancellableTimedAction(() => { // Signal that thread is started mre.Set(); ++counter; }, 10, nameof(BasicFunctionalityTest))) { XAssert.IsTrue(timedAction.Start()); // Wait for thread to start mre.WaitOne(); Thread.Sleep(500); timedAction.Cancel(); timedAction.Join(); XAssert.IsTrue(counter > 2, "Value of counter is " + counter); } }