[InlineData(true, "foo.exe")] // Filter should not match public async Task CmdWithTestShim_ShimNothingRunsChildProcessWithoutShim(bool shimAllProcesses, string processMatch) { var context = BuildXLContext.CreateInstanceForTesting(); string currentCodeFolder = Path.GetDirectoryName(AssemblyHelper.GetAssemblyLocation(Assembly.GetExecutingAssembly())); Contract.Assume(currentCodeFolder != null); string executable = CmdHelper.CmdX64; string shimProgram = Path.Combine(currentCodeFolder, "TestSubstituteProcessExecutionShim.exe"); Assert.True(File.Exists(shimProgram), $"Shim test program not found at {shimProgram}"); var shimProgramPath = AbsolutePath.Create(context.PathTable, shimProgram); var fam = new FileAccessManifest(context.PathTable) { FailUnexpectedFileAccesses = false, IgnoreCodeCoverage = false, ReportFileAccesses = false, ReportUnexpectedFileAccesses = false, MonitorChildProcesses = false, SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo( shimProgramPath, shimAllProcesses: false, processMatch == null ? new ShimProcessMatch[0] : new[] { new ShimProcessMatch(PathAtom.Create(context.StringTable, processMatch), PathAtom.Invalid) }) }; Guid sessionId = Guid.NewGuid(); string sessionIdStr = sessionId.ToString("N"); var loggingContext = new LoggingContext(sessionId, "TestSession", new LoggingContext.SessionInfo(sessionIdStr, "env", sessionId)); string childOutput = "Child cmd that should not be shimmed"; string childArgs = $"{executable} /D /C @echo {childOutput}"; string args = "/D /C echo Top-level cmd. Running child process && " + childArgs; var stdoutSb = new StringBuilder(128); var stderrSb = new StringBuilder(); var sandboxedProcessInfo = new SandboxedProcessInfo( context.PathTable, new LocalSandboxedFileStorage(), executable, disableConHostSharing: true, loggingContext: loggingContext, fileAccessManifest: fam) { PipDescription = executable, Arguments = args, WorkingDirectory = Environment.CurrentDirectory, StandardOutputEncoding = Encoding.UTF8, StandardOutputObserver = stdoutStr => stdoutSb.AppendLine(stdoutStr), StandardErrorEncoding = Encoding.UTF8, StandardErrorObserver = stderrStr => stderrSb.AppendLine(stderrStr), EnvironmentVariables = BuildParameters.GetFactory().PopulateFromEnvironment(), Timeout = TimeSpan.FromMinutes(1), }; ISandboxedProcess sandboxedProcess = await SandboxedProcessFactory.StartAsync(sandboxedProcessInfo, forceSandboxing : true) .ConfigureAwait(false); SandboxedProcessResult result = await sandboxedProcess.GetResultAsync().ConfigureAwait(false); Assert.Equal(0, result.ExitCode); string stdout = stdoutSb.ToString(); m_output.WriteLine($"stdout: {stdout}"); string stderr = stderrSb.ToString(); m_output.WriteLine($"stderr: {stderr}"); Assert.Equal(0, stderr.Length); string shimOutput = "TestShim: Entered with command line"; int indexOfShim = stdout.IndexOf(shimOutput, StringComparison.Ordinal); Assert.True(indexOfShim == -1); int indexOfChild = stdout.LastIndexOf(childOutput, StringComparison.Ordinal); Assert.True(indexOfChild > 0, "Child should have run and written output"); }
[InlineData(false, "cmd.exe")] // Filter should match child public async Task CmdWithTestShim(bool useQuotesForChildCmdExe, string processMatch) { var context = BuildXLContext.CreateInstanceForTesting(); string currentCodeFolder = Path.GetDirectoryName(AssemblyHelper.GetAssemblyLocation(Assembly.GetExecutingAssembly())); Contract.Assume(currentCodeFolder != null); string executable = CmdHelper.CmdX64; string childExecutable = executable; string quotedExecutable = '"' + executable + '"'; if (useQuotesForChildCmdExe) { childExecutable = quotedExecutable; } string shimProgram = Path.Combine(currentCodeFolder, "TestSubstituteProcessExecutionShim.exe"); Assert.True(File.Exists(shimProgram), $"Shim test program not found at {shimProgram}"); var shimProgramPath = AbsolutePath.Create(context.PathTable, shimProgram); var fam = new FileAccessManifest(context.PathTable) { FailUnexpectedFileAccesses = false, IgnoreCodeCoverage = false, ReportFileAccesses = false, ReportUnexpectedFileAccesses = false, MonitorChildProcesses = false, SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo( shimProgramPath, shimAllProcesses: processMatch == null, // When we have a process to match, make the shim list opt-in to ensure a match processMatch == null ? new ShimProcessMatch[0] : new[] { new ShimProcessMatch(PathAtom.Create(context.StringTable, processMatch), PathAtom.Invalid) }) }; Guid sessionId = Guid.NewGuid(); string sessionIdStr = sessionId.ToString("N"); var loggingContext = new LoggingContext(sessionId, "TestSession", new LoggingContext.SessionInfo(sessionIdStr, "env", sessionId)); string childOutput = "Child cmd that should be shimmed"; string childArgs = $"{childExecutable} /D /C @echo {childOutput}"; // Detours logic should wrap the initial cmd in quotes for easier parsing by shim logic. // However, since we're indirecting through a cmd.exe command line it gets dropped along the way. string childShimArgs = $"/D /C @echo {childOutput}"; string args = "/D /C echo Top-level cmd. Running child process && " + childArgs; var stdoutSb = new StringBuilder(128); var stderrSb = new StringBuilder(); var sandboxedProcessInfo = new SandboxedProcessInfo( context.PathTable, new LocalSandboxedFileStorage(), executable, disableConHostSharing: true, loggingContext: loggingContext, fileAccessManifest: fam) { PipDescription = executable, Arguments = args, WorkingDirectory = Environment.CurrentDirectory, StandardOutputEncoding = Encoding.UTF8, StandardOutputObserver = stdoutStr => stdoutSb.AppendLine(stdoutStr), StandardErrorEncoding = Encoding.UTF8, StandardErrorObserver = stderrStr => stderrSb.AppendLine(stderrStr), EnvironmentVariables = BuildParameters.GetFactory().PopulateFromEnvironment(), Timeout = TimeSpan.FromMinutes(1), }; ISandboxedProcess sandboxedProcess = await SandboxedProcessFactory.StartAsync(sandboxedProcessInfo, forceSandboxing : true) .ConfigureAwait(false); SandboxedProcessResult result = await sandboxedProcess.GetResultAsync().ConfigureAwait(false); string stdout = stdoutSb.ToString(); m_output.WriteLine($"stdout: {stdout}"); string stderr = stderrSb.ToString(); m_output.WriteLine($"stderr: {stderr}"); Assert.Equal(0, result.ExitCode); Assert.Equal(0, stderr.Length); // The shim is an exe on netframework, dll in a temp dir on netcore, so don't try to match it. const string shimOutput = "TestShim: Entered with command line: "; int indexOfShim = stdout.IndexOf(shimOutput, StringComparison.Ordinal); Assert.True(indexOfShim > 0, shimOutput); m_output.WriteLine($"Expecting shim args: {childShimArgs}"); int indexOfShimArgs = stdout.LastIndexOf(childShimArgs); Assert.True(indexOfShimArgs > indexOfShim); }
public async Task CmdWithStartQuoteOnlyFailsToRunFullCommandLine() { var context = BuildXLContext.CreateInstanceForTesting(); string currentCodeFolder = Path.GetDirectoryName(AssemblyHelper.GetAssemblyLocation(Assembly.GetExecutingAssembly())); Contract.Assume(currentCodeFolder != null); string shimProgram = Path.Combine(currentCodeFolder, "TestSubstituteProcessExecutionShim.exe"); Assert.True(File.Exists(shimProgram), $"Shim test program not found at {shimProgram}"); var shimProgramPath = AbsolutePath.Create(context.PathTable, shimProgram); var fam = new FileAccessManifest(context.PathTable) { FailUnexpectedFileAccesses = false, IgnoreCodeCoverage = false, ReportFileAccesses = false, ReportUnexpectedFileAccesses = false, MonitorChildProcesses = false, SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo( shimProgramPath, shimAllProcesses: true, new ShimProcessMatch[0]) }; Guid sessionId = Guid.NewGuid(); string sessionIdStr = sessionId.ToString("N"); var loggingContext = new LoggingContext(sessionId, "TestSession", new LoggingContext.SessionInfo(sessionIdStr, "env", sessionId)); string executable = CmdHelper.CmdX64; string childExecutable = '"' + executable; // Only an open quote string childOutput = "Child cmd that should be shimmed"; string childArgs = $"{childExecutable} /D /C @echo {childOutput}"; string args = "/D /C echo Top-level cmd. Running child process && " + childArgs; var stdoutSb = new StringBuilder(128); var stderrSb = new StringBuilder(); var sandboxedProcessInfo = new SandboxedProcessInfo( context.PathTable, new LocalSandboxedFileStorage(), executable, disableConHostSharing: true, loggingContext: loggingContext, fileAccessManifest: fam) { PipDescription = executable, Arguments = args, WorkingDirectory = Environment.CurrentDirectory, StandardOutputEncoding = Encoding.UTF8, StandardOutputObserver = stdoutStr => stdoutSb.AppendLine(stdoutStr), StandardErrorEncoding = Encoding.UTF8, StandardErrorObserver = stderrStr => stderrSb.AppendLine(stderrStr), EnvironmentVariables = BuildParameters.GetFactory().PopulateFromEnvironment(), Timeout = TimeSpan.FromMinutes(1), }; ISandboxedProcess sandboxedProcess = await SandboxedProcessFactory.StartAsync(sandboxedProcessInfo, forceSandboxing : true) .ConfigureAwait(false); SandboxedProcessResult result = await sandboxedProcess.GetResultAsync().ConfigureAwait(false); string stdout = stdoutSb.ToString(); m_output.WriteLine($"stdout: {stdout}"); string stderr = stderrSb.ToString(); m_output.WriteLine($"stderr: {stderr}"); Assert.Equal(1, result.ExitCode); Assert.Equal("The system cannot find the path specified.\r\n", stderr); }
protected static Task <ISandboxedProcess> StartProcessAsync(SandboxedProcessInfo info, bool forceSandboxing = true) { return(SandboxedProcessFactory.StartAsync(info, forceSandboxing)); }
/// <summary> /// Runs the a tool in a sandboxed process and returns the result. /// These optional callback Actions can be provided: /// beforeLaunch is invoked right before the process is launched /// onResult is invoked after getting a successful result /// </summary>> public static async Task <SandboxedProcessResult> RunSandboxedToolAsync(FrontEndContext context, string pathToTool, string buildStorageDirectory, FileAccessManifest fileAccessManifest, string arguments, string workingDirectory, string description, BuildParameters.IBuildParameters buildParameters, Action beforeLaunch = null, // Invoked right before the process starts Action onResult = null // Action to be taken after getting a successful result ) { var info = new SandboxedProcessInfo( context.PathTable, new ToolBuildStorage(buildStorageDirectory), pathToTool, fileAccessManifest, disableConHostSharing: true, ContainerConfiguration.DisabledIsolation, loggingContext: context.LoggingContext) { Arguments = arguments, WorkingDirectory = workingDirectory, PipSemiStableHash = 0, PipDescription = description, EnvironmentVariables = buildParameters }; var process = await SandboxedProcessFactory.StartAsync(info, forceSandboxing : true); var registration = context.CancellationToken.Register( () => { try { process.KillAsync().GetAwaiter().GetResult(); } catch (TaskCanceledException) { // If the process has already terminated or doesn't exist, an TaskCanceledException is raised. // In either case, we swallow the exception, cancellation is already requested by the user } }); beforeLaunch?.Invoke(); var result = process.GetResultAsync().ContinueWith( r => { // Dispose the registration for the cancellation token once the process is done registration.Dispose(); // onResult?.Invoke(); return(r.GetAwaiter().GetResult()); }); return(await result); }
[InlineData(false, "cmd.exe")] // Filter should match child public async Task CmdWithTestShimAsync(bool useQuotesForChildCmdExe, string processMatch) { var context = BuildXLContext.CreateInstanceForTesting(); var shimProgramPath = GetShimProgramPath(context); string executable = CmdHelper.CmdX64; string childExecutable = executable; string quotedExecutable = '"' + executable + '"'; if (useQuotesForChildCmdExe) { childExecutable = quotedExecutable; } var fam = CreateCommonFileAccessManifest(context.PathTable); fam.SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo( shimProgramPath, shimAllProcesses: processMatch == null, // When we have a process to match, make the shim list opt-in to ensure a match processMatches: processMatch == null ? new ShimProcessMatch[0] : new[] { new ShimProcessMatch(PathAtom.Create(context.StringTable, processMatch), PathAtom.Invalid) }); string childOutput = "Child cmd that should be shimmed"; string childArgs = $"{childExecutable} /D /C @echo {childOutput}"; // Detours logic should wrap the initial cmd in quotes for easier parsing by shim logic. // However, since we're indirecting through a cmd.exe command line it gets dropped along the way. string childShimArgs = $"/D /C @echo {childOutput}"; string args = $"/D /C echo Top-level cmd. Running child process && {childArgs}"; var stdOutSb = new StringBuilder(128); var stdErrSb = new StringBuilder(); SandboxedProcessInfo sandboxedProcessInfo = CreateCommonSandboxedProcessInfo( context, executable, args, fam, stdOutSb, stdErrSb); ISandboxedProcess sandboxedProcess = await SandboxedProcessFactory.StartAsync(sandboxedProcessInfo, forceSandboxing : true) .ConfigureAwait(false); SandboxedProcessResult result = await sandboxedProcess.GetResultAsync().ConfigureAwait(false); string stdOut = stdOutSb.ToString(); string stdErr = stdErrSb.ToString(); m_output.WriteLine($"stdout: {stdOut}"); m_output.WriteLine($"stderr: {stdErr}"); AssertSuccess(result, stdErr); // The shim is an exe on netframework, dll in a temp dir on netcore, so don't try to match it. AssertShimmed(stdOut); AssertShimArgs(stdOut, childShimArgs); }
/// <nodoc /> public Task <SandboxedProcessResult> Run(SandboxOptions option) { var pathToProcess = option.args[0]; var workingDir = option.working_dir != AbsolutePath.Invalid ? option.working_dir.ToString(m_pathTable) : Environment.CurrentDirectory; var fam = CreateManifest(AbsolutePath.Create(m_pathTable, pathToProcess), option, workingDir); Action <string> stdoutCallback; if (option.stdout_path != AbsolutePath.Invalid) { m_stdoutF = new StreamWriter(option.stdout_path.ToString(m_pathTable)); stdoutCallback = s => m_stdoutF.WriteLine(s); } else { stdoutCallback = s => Console.WriteLine(s); } Action <string> stderrCallback; if (option.stderr_path != AbsolutePath.Invalid) { m_stderrF = new StreamWriter(option.stderr_path.ToString(m_pathTable)); stderrCallback = s => m_stderrF.WriteLine(s); } else { stderrCallback = s => Console.Error.WriteLine(s); } var info = new SandboxedProcessInfo( m_pathTable, this, pathToProcess, fam, disableConHostSharing: true, containerConfiguration: ContainerConfiguration.DisabledIsolation, loggingContext: m_loggingContext) { Arguments = EscapeArgvRest(option.args.Skip(1)), WorkingDirectory = workingDir, // PipSemiStableHash = 0, PipDescription = "BazelSandboxedProcess", StandardOutputEncoding = Encoding.UTF8, StandardOutputObserver = stdoutCallback, StandardErrorEncoding = Encoding.UTF8, StandardErrorObserver = stderrCallback, SandboxedKextConnection = OperatingSystemHelper.IsUnixOS ? new KextConnection() : null }; if (option.timeout_secs != SandboxOptions.kInfiniteTime) { info.Timeout = TimeSpan.FromSeconds(option.timeout_secs); } if (option.kill_delay_secs != SandboxOptions.kInfiniteTime) { info.NestedProcessTerminationTimeout = TimeSpan.FromSeconds(option.kill_delay_secs); } var process = SandboxedProcessFactory.StartAsync(info, forceSandboxing: true).GetAwaiter().GetResult(); return(process.GetResultAsync()); }