Example #1
0
        public async Task TestDynamicallyLoadedLibrariesReportedOnLinux()
        {
            if (!OperatingSystemHelper.IsLinuxOS)
            {
                return;
            }

            var proc = ToProcess(Operation.Echo("hi"));
            var info = ToProcessInfo(proc, nameof(TestDynamicallyLoadedLibrariesReportedOnLinux));

            info.FileAccessManifest.ReportFileAccesses         = true;
            info.FileAccessManifest.FailUnexpectedFileAccesses = false;
            using ISandboxedProcess process = await StartProcessAsync(info);

            var result = await process.GetResultAsync();

            XAssert.AreEqual(0, result.ExitCode);
            var accesses = result.FileAccesses
                           .Select(fa => fa.GetPath(Context.PathTable))
                           .Where(p => p.EndsWith(".so"))
                           .Select(p => Path.GetFileName(p))
                           .Select(n => n.Contains('-') ? n.Substring(0, n.IndexOf('-')) + ".so" : n)
                           .Distinct()
                           .ToHashSet();

            XAssert.Contains(accesses, new[]
            {
                "libhostfxr.so", "libhostpolicy.so", "libclrjit.so", "libcoreclr.so"
            });

            XAssert.ContainsNot(accesses, new[] { "libDetours.so" });
        }
Example #2
0
 protected static async Task <SandboxedProcessResult> RunProcess(SandboxedProcessInfo info)
 {
     using (ISandboxedProcess process = await StartProcessAsync(info))
     {
         return(await process.GetResultAsync());
     }
 }
Example #3
0
        private async Task <SandboxedProcessResult> RunWithPluginAsync(
            bool shimAllProcesses,
            bool shouldBeShimmed,
            bool shouldFindMatch,
            string[] processMatches,
            StringBuilder stdOutSb,
            StringBuilder stdErrSb,
            string shimmedText = null)
        {
            var context         = BuildXLContext.CreateInstanceForTesting();
            var shimProgramPath = GetShimProgramPath(context);
            var pluginDlls      = GetPluginDlls(context);

            string executable = CmdHelper.CmdX64;

            var fam = CreateCommonFileAccessManifest(context.PathTable);

            fam.SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo(
                shimProgramPath,
                shimAllProcesses: shimAllProcesses,
                processMatches: processMatches.Select(pm => new ShimProcessMatch(PathAtom.Create(context.StringTable, pm), PathAtom.Invalid)).ToArray())
            {
                SubstituteProcessExecutionPluginDll32Path = pluginDlls.x86Dll,
                SubstituteProcessExecutionPluginDll64Path = pluginDlls.x64Dll
            };

            string childOutput = GetChildOutputForPluginTest(shouldBeShimmed, shimmedText);
            string childArgs   = $"{executable} /D /C echo {childOutput}";
            string args        = $"/D /C echo Top-level cmd. Running child process && {childArgs}";

            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);

            if (shouldFindMatch || processMatches.Length == 0)
            {
                // Plugin should be called when a match is found or no process match is specified.

                // When plugin is entered, expect to see "Entering CommandMatches" text in the log.
                // CODESYNC: Public\Src\Engine\UnitTests\Processes.TestPrograms\SubstituteProcessExecutionPlugin\dllmain.cpp
                AssertLogContains(true, "Entering CommandMatches");
            }

            return(result);
        }
Example #4
0
        private async Task <ISandboxedProcess> StartProcessAsync(SandboxedProcessInfo info)
        {
            ISandboxedProcess process  = null;
            bool shouldRelaunchProcess = true;
            int  processRelaunchCount  = 0;

            while (shouldRelaunchProcess)
            {
                try
                {
                    shouldRelaunchProcess = false;
                    process = await SandboxedProcessFactory.StartAsync(info, forceSandboxing : false);
                }
                catch (BuildXLException ex)
                {
                    if (ex.LogEventErrorCode == NativeIOConstants.ErrorFileNotFound)
                    {
                        m_logger.LogError($"Failed to start process: '{info.FileName}' not found");
                        return(null);
                    }
                    else if (ex.LogEventErrorCode == NativeIOConstants.ErrorPartialCopy && (processRelaunchCount < ProcessRelauchCountMax))
                    {
                        ++processRelaunchCount;
                        shouldRelaunchProcess = true;

                        m_logger.LogInfo($"Retry to start process for {processRelaunchCount} time(s) due to the following error: {ex.LogEventErrorCode}");

                        // Ensure that process terminates before relaunching it.
                        if (process != null)
                        {
                            try
                            {
                                await process.GetResultAsync();
                            }
                            finally
                            {
                                process.Dispose();
                            }
                        }
                    }
                    else
                    {
                        m_logger.LogError($"Failed to start process '{info.FileName}': {ex.LogEventMessage} ({ex.LogEventErrorCode})");
                        return(null);
                    }
                }
            }

            return(process);
        }
Example #5
0
        public async Task CmdWithTestShim_ShimNothingRunsChildProcessWithoutShimAsync(bool shimAllProcess, bool filterMatch)
        {
            var context         = BuildXLContext.CreateInstanceForTesting();
            var shimProgramPath = GetShimProgramPath(context);

            string executable   = CmdHelper.CmdX64;
            string processMatch = filterMatch ? null : "foo.exe"; // Filter should never match foo.exe.

            var fam = CreateCommonFileAccessManifest(context.PathTable);

            fam.SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo(
                shimProgramPath,
                shimAllProcesses: shimAllProcess,
                processMatches: processMatch == null
                    ? new ShimProcessMatch[0]
                    : new[] { new ShimProcessMatch(PathAtom.Create(context.StringTable, processMatch), PathAtom.Invalid) });

            string predicate   = shimAllProcess ? string.Empty : "not ";
            string childOutput = $"Child cmd that should {predicate}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();

            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);
            AssertShimmedIf(stdOut, shimAllProcess);
            XAssert.Contains(stdOut, childOutput);
        }
        [InlineData("\"\"", "\"\"")] // Force case where child process is executed with double quotes around it
        public async Task CmdWithSingleTokenChildProcessNoArgsAsync(string preCommand, string postCommand)
        {
            var context         = BuildXLContext.CreateInstanceForTesting();
            var shimProgramPath = GetShimProgramPath(context);

            string executable       = CmdHelper.CmdX64;
            string childExecutable  = executable;
            string quotedExecutable = '"' + executable + '"';

            childExecutable = quotedExecutable;

            var fam = CreateCommonFileAccessManifest(context.PathTable);

            // Use 'doskey' (alias manager) built into Windows.
            string args = $"/D /C {preCommand}doskey.exe{postCommand}";

            fam.SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo(
                shimProgramPath,
                shimAllProcesses: false,
                processMatches: new[] { new ShimProcessMatch(PathAtom.Create(context.StringTable, "cmd.exe"), PathAtom.Invalid) });

            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);
        }
Example #7
0
        public async Task CmdWithStartQuoteOnlyFailsToRunFullCommandLineAsync()
        {
            var context         = BuildXLContext.CreateInstanceForTesting();
            var shimProgramPath = GetShimProgramPath(context);

            var fam = CreateCommonFileAccessManifest(context.PathTable);

            fam.SubstituteProcessExecutionInfo = new SubstituteProcessExecutionInfo(
                shimProgramPath,
                shimAllProcesses: true,
                processMatches: new ShimProcessMatch[0]);

            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();

            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}");

            XAssert.AreEqual(1, result.ExitCode);
            XAssert.AreEqual("The system cannot find the path specified.\r\n", stdErr);
        }
        /// <summary>
        /// Entry point for an I/O task which creates a process.
        /// </summary>
        /// <remarks>
        /// This is a separate function and not inlined as an anonymous delegate, as VS seems to have trouble with those when
        /// measuring code coverage
        /// </remarks>
        private static ISandboxedProcess ProcessStart(object state)
        {
            Counters.IncrementCounter(SandboxedProcessCounters.SandboxedProcessCount);
            var stateTuple              = (Tuple <SandboxedProcessInfo, bool>)state;
            SandboxedProcessInfo info   = stateTuple.Item1;
            ISandboxedProcess    result = null;

            try
            {
                result = Create(info, forceSandboxing: stateTuple.Item2);
                result.Start(); // this can take a while; performs I/O
            }
            catch
            {
                result?.Dispose();
                throw;
            }

            return(result);
        }
Example #9
0
        /// <summary>
        /// Entry point for an I/O task which creates a process.
        /// </summary>
        /// <remarks>
        /// This is a separate function and not inlined as an anonymous delegate, as VS seems to have trouble with those when
        /// measuring code coverage
        /// </remarks>
        private static ISandboxedProcess ProcessStart(object state)
        {
            var stateTuple              = (Tuple <SandboxedProcessInfo, bool>)state;
            SandboxedProcessInfo info   = stateTuple.Item1;
            ISandboxedProcess    result = null;

            try
            {
                result = Create(info, forceSandboxing: stateTuple.Item2);
                result.Start(); // this can take a while; performs I/O
            }
            catch
            {
                result?.Dispose();
                info.ProcessIdListener?.Invoke(-1);
                throw;
            }

            return(result);
        }
Example #10
0
        /// <summary>
        /// Starts process asynchronously.
        /// </summary>
        public static Task <ISandboxedProcess> StartAsync(
            SandboxedProcessInfo info,
            Func <SandboxedProcessInfo, ExternalSandboxedProcess> externalSandboxedProcessFactory)
        {
            return(Task.Factory.StartNew(() =>
            {
                ISandboxedProcess process = externalSandboxedProcessFactory(info);

                try
                {
                    process.Start();
                }
                catch
                {
                    process?.Dispose();
                    throw;
                }

                return process;
            }));
        }
Example #11
0
        private async Task <(ExitCode, SandboxedProcessResult)> ExecuteAsync(SandboxedProcessInfo info)
        {
            try
            {
                using (Stream standardInputStream = TryOpenStandardInputStream(info, out bool succeedInOpeningStdIn))
                {
                    if (!succeedInOpeningStdIn)
                    {
                        return(ExitCode.FailedSandboxPreparation, null);
                    }

                    using (StreamReader standardInputReader = standardInputStream == null ? null : new StreamReader(standardInputStream, CharUtilities.Utf8NoBomNoThrow))
                    {
                        info.StandardInputReader = standardInputReader;

                        ISandboxedProcess process = await StartProcessAsync(info);

                        if (process == null)
                        {
                            return(ExitCode.FailedStartProcess, null);
                        }

                        SandboxedProcessResult result = await process.GetResultAsync();

                        // Patch result.
                        result.WarningCount                 = m_outputErrorObserver.WarningCount;
                        result.LastMessageCount             = process.GetLastMessageCount();
                        result.DetoursMaxHeapSize           = process.GetDetoursMaxHeapSize();
                        result.MessageCountSemaphoreCreated = info.FileAccessManifest.MessageCountSemaphore != null;

                        return(ExitCode.Success, result);
                    }
                }
            }
            finally
            {
                info.FileAccessManifest.UnsetMessageCountSemaphore();
            }
        }
Example #12
0
        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);
        }
Example #13
0
        private async Task VerifyReporting(AccessType access, Action <FileAccessManifest> populateManifest, params ExpectedReportEntry[] expectedExplicitReports)
        {
            // We need a process which will generate the expected accesses.
            var testProcess = CreateTestProcess(access, expectedExplicitReports);
            var pathTable   = Context.PathTable;

            var info = ToProcessInfo(testProcess, "FileAccessExplicitReportingTest");

            info.FileAccessManifest.ReportFileAccesses           = false;
            info.FileAccessManifest.ReportUnexpectedFileAccesses = true;
            info.FileAccessManifest.FailUnexpectedFileAccesses   = false;

            populateManifest(info.FileAccessManifest);

            using (ISandboxedProcess process = await StartProcessAsync(info))
            {
                SandboxedProcessResult result = await process.GetResultAsync();

                XAssert.AreEqual(0, result.ExitCode,
                                 "\r\ncmd: {0} \r\nStandard out: '{1}' \r\nStandard err: '{2}'.",
                                 info.Arguments,
                                 await result.StandardOutput.ReadValueAsync(),
                                 await result.StandardError.ReadValueAsync());
                XAssert.IsNotNull(result.ExplicitlyReportedFileAccesses);

                Dictionary <AbsolutePath, ExpectedReportEntry> pathsToExpectations = expectedExplicitReports.ToDictionary(
                    e => e.File.Path,
                    e => e);

                var verifiedPaths = new HashSet <AbsolutePath>();
                foreach (var actualReport in result.ExplicitlyReportedFileAccesses)
                {
                    string actualReportedPathString = actualReport.GetPath(pathTable);
                    XAssert.AreEqual(
                        FileAccessStatus.Allowed,
                        actualReport.Status,
                        "Incorrect status for path " + actualReportedPathString);

                    if (!TryVerifySingleReport(pathTable, actualReport, access, pathsToExpectations, out var actualReportPath))
                    {
                        if ((actualReport.RequestedAccess & RequestedAccess.Enumerate) != 0)
                        {
                            // To account for 'explicitly reported' enumerations globally, we need to be lenient about unexpected enumerations.
                            // Alternatively instead opt-in to enumeration reports under certain scopes.
                        }
                        else
                        {
                            AbsolutePath actualReportedPath = AbsolutePath.Create(pathTable, actualReportedPathString);
                            XAssert.Fail("No expectations for an explicitly reported path {0}", actualReportedPath.ToString(pathTable));
                        }
                    }
                    else
                    {
                        verifiedPaths.Add(actualReportPath);
                    }
                }

                foreach (var actualReport in result.AllUnexpectedFileAccesses)
                {
                    XAssert.AreEqual(FileAccessStatus.Denied, actualReport.Status);
                    if (TryVerifySingleReport(pathTable, actualReport, access, pathsToExpectations, out var actualReportPath))
                    {
                        verifiedPaths.Add(actualReportPath);
                    }

                    // Note that we allow extra unexpected file accesses for the purposes of these tests.
                }

                var expectedPathsSet = new HashSet <AbsolutePath>(pathsToExpectations.Keys);
                var disagreeingPaths = new HashSet <AbsolutePath>(verifiedPaths);
                disagreeingPaths.SymmetricExceptWith(expectedPathsSet);
                if (disagreeingPaths.Any())
                {
                    var disagreeingReports = "Disagreeing reports:" + string.Join(string.Empty, disagreeingPaths
                                                                                  .Select(p => (tag: expectedPathsSet.Contains(p) ? "Missing" : "Unexpected", path: p))
                                                                                  .Select(t => $"{Environment.NewLine}  {t.tag} report for path {t.path.ToString(pathTable)}"));
                    var expectedReports = "Expected reports:" + string.Join(string.Empty, pathsToExpectations.Keys
                                                                            .Select(p => $"{Environment.NewLine}  {p.ToString(pathTable)}"));
                    var verifiedReports = "Verified reports:" + string.Join(string.Empty, verifiedPaths
                                                                            .Select(p => $"{Environment.NewLine}  {p.ToString(pathTable)}"));
                    XAssert.Fail(string.Join(Environment.NewLine, disagreeingReports, expectedReports, verifiedReports));
                }
            }
        }
Example #14
0
        private async Task <(ExitCode, SandboxedProcessResult)> ExecuteAsync(SandboxedProcessInfo info)
        {
            m_logger.LogInfo($"Start execution: {info.GetCommandLine()}");

            FileAccessManifest fam = info.FileAccessManifest;

            try
            {
                if (fam.CheckDetoursMessageCount && !OperatingSystemHelper.IsUnixOS)
                {
                    string semaphoreName = !string.IsNullOrEmpty(info.DetoursFailureFile)
                        ? info.DetoursFailureFile.Replace('\\', '_')
                        : "Detours_" + info.PipSemiStableHash.ToString("X16", CultureInfo.InvariantCulture) + "-" + Guid.NewGuid().ToString();

                    int maxRetry = 3;
                    while (!fam.SetMessageCountSemaphore(semaphoreName))
                    {
                        m_logger.LogInfo($"Semaphore '{semaphoreName}' for counting Detours messages is already opened");
                        fam.UnsetMessageCountSemaphore();
                        --maxRetry;
                        if (maxRetry == 0)
                        {
                            break;
                        }

                        semaphoreName += $"_{maxRetry}";
                    }

                    if (maxRetry == 0)
                    {
                        m_logger.LogError($"Semaphore for counting Detours messages cannot be newly created");
                        return(ExitCode.FailedSandboxPreparation, null);
                    }
                }

                using (Stream standardInputStream = TryOpenStandardInputStream(info, out bool succeedInOpeningStdIn))
                {
                    if (!succeedInOpeningStdIn)
                    {
                        return(ExitCode.FailedSandboxPreparation, null);
                    }

                    using (StreamReader standardInputReader = standardInputStream == null ? null : new StreamReader(standardInputStream, CharUtilities.Utf8NoBomNoThrow))
                    {
                        info.StandardInputReader = standardInputReader;

                        ISandboxedProcess process = await StartProcessAsync(info);

                        if (process == null)
                        {
                            return(ExitCode.FailedStartProcess, null);
                        }

                        SandboxedProcessResult result = await process.GetResultAsync();

                        // Patch result.
                        result.WarningCount                 = m_outputErrorObserver.WarningCount;
                        result.LastMessageCount             = process.GetLastMessageCount();
                        result.DetoursMaxHeapSize           = process.GetDetoursMaxHeapSize();
                        result.MessageCountSemaphoreCreated = info.FileAccessManifest.MessageCountSemaphore != null;

                        return(ExitCode.Success, result);
                    }
                }
            }
            finally
            {
                fam.UnsetMessageCountSemaphore();
            }
        }
Example #15
0
        [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);
        }
Example #16
0
        [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");
        }
Example #17
0
        [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);
        }
Example #18
0
        private async Task <(ExitCode, SandboxedProcessResult)> ExecuteAsync(SandboxedProcessInfo info)
        {
            m_logger.LogInfo($"Start execution: {info.GetCommandLine()}");

            FileAccessManifest fam = info.FileAccessManifest;

            try
            {
                if (fam.CheckDetoursMessageCount && !OperatingSystemHelper.IsUnixOS)
                {
                    string semaphoreName = !string.IsNullOrEmpty(info.DetoursFailureFile)
                        ? info.DetoursFailureFile.Replace('\\', '_')
                        : "Detours_" + info.PipSemiStableHash.ToString("X16", CultureInfo.InvariantCulture) + "-" + Guid.NewGuid().ToString();
                    int maxRetry = 3;

                    // We check this first due to bug#1873910 when executing in a VM on Cloudbuild.
                    // Creating this Semaphore may fail due to a semaphore with the same name already existing.
                    // The reason for this is currently unknown, however since the name for the failures file is
                    // created in SandboxedProcessPipExecutor.GetDetoursInternalErrorFilePath with a unique guid
                    // along with the pip hash it should be safe to dispose because the name is unique to this pip.
                    if (System.Threading.Semaphore.TryOpenExisting(semaphoreName, out var existingSemaphore))
                    {
                        m_logger.LogInfo($"Disposing existing semaphore with name '{semaphoreName}'.");
                        // Calling dispose on this will allow us to create a new semaphore with the same name
                        existingSemaphore.Dispose();
                    }

                    while (!fam.SetMessageCountSemaphore(semaphoreName))
                    {
                        m_logger.LogInfo($"Semaphore '{semaphoreName}' for counting Detours messages is already opened");
                        fam.UnsetMessageCountSemaphore();
                        --maxRetry;
                        if (maxRetry == 0)
                        {
                            break;
                        }

                        semaphoreName += $"_{maxRetry}";
                    }

                    if (maxRetry == 0)
                    {
                        m_logger.LogError($"Semaphore for counting Detours messages cannot be newly created");
                        return(ExitCode.FailedSandboxPreparation, null);
                    }
                }

                using (Stream standardInputStream = TryOpenStandardInputStream(info, out bool succeedInOpeningStdIn))
                {
                    if (!succeedInOpeningStdIn)
                    {
                        return(ExitCode.FailedSandboxPreparation, null);
                    }

                    using (StreamReader standardInputReader = standardInputStream == null ? null : new StreamReader(standardInputStream, CharUtilities.Utf8NoBomNoThrow))
                    {
                        info.StandardInputReader = standardInputReader;

                        ISandboxedProcess process = await StartProcessAsync(info);

                        if (process == null)
                        {
                            return(ExitCode.FailedStartProcess, null);
                        }

                        SandboxedProcessResult result = await process.GetResultAsync();

                        // Patch result.
                        result.WarningCount                 = m_outputErrorObserver.WarningCount;
                        result.LastMessageCount             = process.GetLastMessageCount();
                        result.DetoursMaxHeapSize           = process.GetDetoursMaxHeapSize();
                        result.MessageCountSemaphoreCreated = info.FileAccessManifest.MessageCountSemaphore != null;

                        return(ExitCode.Success, result);
                    }
                }
            }
            finally
            {
                fam.UnsetMessageCountSemaphore();
            }
        }