public async Task EnumerateFileByHandleWithReadAllowedByScopeAndExplicitReport()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath  = CreateDirectory(pathTable, @"dir");
            AbsolutePath filePath = WriteEmptyFile(pathTable, @"dir\file");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportAccess | FileAccessPolicy.AllowReadAlways);
            },
                EnumerateFileOrDirectoryByHandle(@"dir\file")); // Note that we are trying to enumerate a file

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,

                // Note that there is no directory enumeration reported (this is really a single file probe).
                expected: ExpectReport(ReportedFileOperation.CreateFile, RequestedAccess.Read, filePath));

            VerifyReportedAccesses(
                pathTable,
                result.AllUnexpectedFileAccesses,
                allowExtraEnumerations: false);
        }
        private async Task <Possible <Unit> > GenerateBuildDirectoryAsync()
        {
            Contract.Assert(m_buildDirectory.IsValid);
            AbsolutePath outputDirectory = m_host.GetFolderForFrontEnd(Name);
            AbsolutePath argumentsFile   = outputDirectory.Combine(m_context.PathTable, Guid.NewGuid().ToString());

            if (!TryRetrieveCMakeSearchLocations(out IEnumerable <AbsolutePath> searchLocations))
            {
                return(new CMakeGenerationError(m_resolverSettings.ModuleName, m_buildDirectory.ToString(m_context.PathTable)));
            }

            SandboxedProcessResult result = await ExecuteCMakeRunner(argumentsFile, searchLocations);

            string standardError = result.StandardError.CreateReader().ReadToEndAsync().GetAwaiter().GetResult();

            if (result.ExitCode != 0)
            {
                if (!m_context.CancellationToken.IsCancellationRequested)
                {
                    Tracing.Logger.Log.CMakeRunnerInternalError(
                        m_context.LoggingContext,
                        m_resolverSettings.Location(m_context.PathTable),
                        standardError);
                }

                return(new CMakeGenerationError(m_resolverSettings.ModuleName, m_buildDirectory.ToString(m_context.PathTable)));
            }

            FrontEndUtilities.TrackToolFileAccesses(m_host.Engine, m_context, Name, result.AllUnexpectedFileAccesses, outputDirectory);
            return(Possible.Create(Unit.Void));
        }
        public async Task EnumerateEmptyDirectoryByHandleWithoutExplicitReport()
        {
            var pathTable = new PathTable();

            AbsolutePath           emptyDirPath = CreateDirectory(pathTable, @"emptyDir");
            SandboxedProcessResult result       = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(emptyDirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.AllowReadAlways);
            },
                EnumerateFileOrDirectoryByHandle(@"emptyDir"));

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,
                expected: new[]
            {
                // No CreateFile report, since emptyDir is a directory.
                // Note that we get an explicit report for the enumeration, despite not adding ReportAccess (automatic for enumerations).
                ExpectReport(ReportedFileOperation.NtQueryDirectoryFile, RequestedAccess.Enumerate, emptyDirPath),
            });

            VerifyReportedAccesses(
                pathTable,
                result.AllUnexpectedFileAccesses,
                allowExtraEnumerations: false);
        }
Пример #4
0
        public async Task TestShimChildProcessWithPluginAndNonEmptyMatchesAsync(bool shimAllProcesses, bool shouldBeShimmed, bool shouldFindMatch)
        {
            var stdOutSb = new StringBuilder(128);
            var stdErrSb = new StringBuilder();

            SandboxedProcessResult result = await RunWithPluginAsync(
                shimAllProcesses : shimAllProcesses,
                shouldBeShimmed : shouldBeShimmed,
                shouldFindMatch : shouldFindMatch,
                processMatches : shouldFindMatch?new[] { "cmd.exe" } : new[] { "foo.exe" },
                stdOutSb : stdOutSb,
                stdErrSb : stdErrSb);

            string stdOut = stdOutSb.ToString();
            string stdErr = stdErrSb.ToString();

            m_output.WriteLine($"stdout: {stdOut}");
            m_output.WriteLine($"stderr: {stdErr}");

            AssertSuccess(result, stdErr);

            if (shimAllProcesses)
            {
                AssertShimmedIf(stdOut, !shouldFindMatch || !shouldBeShimmed);
            }
            else
            {
                AssertShimmedIf(stdOut, shouldFindMatch && shouldBeShimmed);
            }

            XAssert.Contains(stdOut, GetChildOutputForPluginTest(shouldBeShimmed));
        }
Пример #5
0
        public async Task DeleteViaNtCreateFileIsAllowedWithAllowedWrite()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath  = CreateDirectory(pathTable, "D");
            AbsolutePath filePath = WriteEmptyFile(pathTable, @"D\FileToDelete");

            AssertFileExists(@"D\FileToDelete");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.AllowWrite | FileAccessPolicy.ReportAccess);
            },
                DeleteViaNtCreateFile(@"D\FileToDelete"));

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,
                expected: ExpectReport(ReportedFileOperation.NtCreateFile, RequestedAccess.Write, filePath, FileAccessStatus.Allowed));

            AssertFileDoesNotExist(@"D\FileToDelete");
        }
Пример #6
0
        public async Task TestShimChildProcessWithPluginWithModifiedArgumentAsync()
        {
            var stdOutSb = new StringBuilder(128);
            var stdErrSb = new StringBuilder();

            SandboxedProcessResult result = await RunWithPluginAsync(
                shimAllProcesses : false,
                shouldBeShimmed : true,
                shouldFindMatch : false,
                processMatches : new string[0],
                stdOutSb : stdOutSb,
                stdErrSb : stdErrSb,
                shimmedText : "@responseFile");

            string stdOut = stdOutSb.ToString();
            string stdErr = stdErrSb.ToString();

            m_output.WriteLine($"stdout: {stdOut}");
            m_output.WriteLine($"stderr: {stdErr}");

            AssertSuccess(result, stdErr);
            AssertShimmed(stdOut);

            // Since shimmedText has '@', it will be replaced by "Content".
            // CODESYNC: Public\Src\Engine\UnitTests\Processes.TestPrograms\SubstituteProcessExecutionPlugin\dllmain.cpp
            XAssert.Contains(stdOut, GetChildOutputForPluginTest(true, "Content"));
        }
Пример #7
0
        public async Task HardlinkCreationIsAllowedWithAllowedWriteAndRead()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath    = CreateDirectory(pathTable, "D");
            AbsolutePath srcPath    = WriteEmptyFile(pathTable, @"D\Origin");
            AbsolutePath targetPath = GetFullPath(pathTable, @"D\Target");

            AssertFileExists(@"D\Origin");
            AssertFileDoesNotExist(@"D\Target");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportAccess);
                manifest.AddPath(srcPath, values: FileAccessPolicy.AllowRead, mask: FileAccessPolicy.MaskNothing);
                manifest.AddScope(targetPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.AllowWrite);
            },
                CreateHardlink(@"D\Origin", @"D\Target"));

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,
                expected: new[]
            {
                ExpectReport(ReportedFileOperation.CreateHardLinkSource, RequestedAccess.Read, srcPath, FileAccessStatus.Allowed),
                ExpectReport(ReportedFileOperation.CreateHardLinkDestination, RequestedAccess.Write, targetPath, FileAccessStatus.Allowed)
            });

            AssertFileExists(@"D\Origin");
            AssertFileExists(@"D\Target");
        }
Пример #8
0
        public async Task HardlinkCreationDeniedWithoutAllowedWrite()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath    = CreateDirectory(pathTable, "D");
            AbsolutePath srcPath    = WriteEmptyFile(pathTable, @"D\Origin");
            AbsolutePath targetPath = GetFullPath(pathTable, @"D\Target");

            AssertFileExists(@"D\Origin");
            AssertFileDoesNotExist(@"D\Target");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.AllowReadAlways);
            },
                CreateHardlink(@"D\Origin", @"D\Target"));

            VerifyReportedAccesses(
                pathTable,
                result.AllUnexpectedFileAccesses,
                allowExtraEnumerations: false,
                expected: ExpectReport(ReportedFileOperation.CreateHardLinkDestination, RequestedAccess.Write, targetPath, FileAccessStatus.Denied));

            AssertFileExists(@"D\Origin");
            AssertFileExists(@"D\Target");
        }
        public async Task EnumerateAndProbeWithFindFirstFileAndFindNextFile()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath   = CreateDirectory(pathTable, @"dir");
            AbsolutePath fileAPath = WriteEmptyFile(pathTable, @"dir\fileA");
            AbsolutePath fileBPath = WriteEmptyFile(pathTable, @"dir\fileB");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportAccess | FileAccessPolicy.AllowReadAlways);
            },
                EnumerateWithFindFirstFileEx(@"dir\*"));

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,
                expected: new[]
            {
                // Note that we assume a deterministic order here; in truth we paper over possible orders in VerifyReportedAccess.
                ExpectReport(ReportedFileOperation.FindFirstFileEx, RequestedAccess.Enumerate, dirPath),
                ExpectReport(ReportedFileOperation.FindFirstFileEx, RequestedAccess.EnumerationProbe, fileAPath),
                ExpectReport(ReportedFileOperation.FindNextFile, RequestedAccess.EnumerationProbe, fileBPath),
            });
        }
        public async Task EnumerateFileByHandleWithReadAllowedByScopeWithoutExplicitReport()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath  = CreateDirectory(pathTable, @"dir");
            AbsolutePath filePath = WriteEmptyFile(pathTable, @"dir\file");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.AllowReadAlways);
            },
                EnumerateFileOrDirectoryByHandle(@"dir\file")); // Note that we are trying to enumerate a file

            // We don't see any reports; the access is just a probe, and we didn't ask for explicit reports.
            // Note that this case is very important for enumerations like dir\file\* where dir\file is a static
            // (precise file) dependency (not part of a sealed directory) since we only expect reports for sealed
            // directory members.
            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false);

            VerifyReportedAccesses(
                pathTable,
                result.AllUnexpectedFileAccesses,
                allowExtraEnumerations: false);
        }
Пример #11
0
        /// <summary>
        /// Runs a sequence of RemoteApi commands in a <see cref="SandboxedProcess" />.
        /// </summary>
        public static async Task <SandboxedProcessResult> RunInSandboxAsync(
            LoggingContext loggingContext,
            PathTable pathTable,
            string workingDirectory,
            ISandboxedProcessFileStorage sandboxStorage,
            Action <FileAccessManifest> populateManifest,
            params Command[] commands)
        {
            Contract.Requires(!string.IsNullOrEmpty(workingDirectory));
            Contract.Requires(populateManifest != null);

            if (!File.Exists(ExecutablePath))
            {
                throw new BuildXLException("Expected to find RemoteApi.exe at " + ExecutablePath);
            }

            var info =
                new SandboxedProcessInfo(pathTable, sandboxStorage, ExecutablePath, disableConHostSharing: false, loggingContext: loggingContext)
            {
                PipSemiStableHash = 0,
                PipDescription    = "RemoteApi Test",
                Arguments         = string.Empty,
                WorkingDirectory  = workingDirectory,
            };

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

            info.FileAccessManifest.AddScope(AbsolutePath.Invalid, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportDirectoryEnumerationAccess);

            populateManifest(info.FileAccessManifest);

            // Allow access to the RemoteApi executable.
            AbsolutePath exeDirectory = AbsolutePath.Create(pathTable, Path.GetDirectoryName(ExecutablePath));

            info.FileAccessManifest.AddScope(exeDirectory, FileAccessPolicy.MaskNothing, FileAccessPolicy.AllowReadAlways);

            using (TextReader commandReader = GetCommandReader(commands))
            {
                info.StandardInputReader   = commandReader;
                info.StandardInputEncoding = Encoding.ASCII;

                // TODO: Maybe watch stdout and validate the command results.
                using (SandboxedProcess process = await SandboxedProcess.StartAsync(info))
                {
                    SandboxedProcessResult result = await process.GetResultAsync();

                    if (result.ExitCode != 0)
                    {
                        var stdErr = await result.StandardError.ReadValueAsync();

                        XAssert.AreEqual(0, result.ExitCode, "RemoteApi.exe failed: " + stdErr);
                    }

                    return(result);
                }
            }
        }
Пример #12
0
        private bool TryWriteSandboxedProcessResult(PathTable pathTable, SandboxedProcessResult result)
        {
            Contract.Requires(result != null);

            // When BuildXL serializes SandboxedProcessInfo, it does not serialize the path table used by SandboxedProcessInfo.
            // On deserializing that info, a new path table is created; see the Deserialize method of SandboxedProcessInfo.
            // Unix sandbox uses the new path table in the deserialized SandboxedProcessInfo to create ManifestPath (AbsolutePath)
            // from reported path access (string) in ReportFileAccess. Without special case, the serialization of SandboxedProcessResult
            // will serialize the AbsolutePath as is. Then, when SandboxedProcessResult is read by BuildXL, BuildXL will not understand
            // the ManifestPath because it is created from a different path table.
            //
            // In Windows, instead of creating ManifestPath from the reported path access (string), ManifestPath is reported from Detours
            // using the AbsolutePath id embedded in the file access manifest. That AbsolutePath id is obtained using the same
            // path table used by BuildXL, and thus BuildXL will understand the ManifestPath serialized by this tool.
            //
            // For Unix, we need to give a special care of path serialization.
            bool isWindows = !OperatingSystemHelper.IsUnixOS;

            Action <BuildXLWriter, AbsolutePath> writePath = (writer, path) =>
            {
                if (isWindows)
                {
                    writer.Write(true);
                    writer.Write(path);
                }
                else
                {
                    writer.Write(false);
                    writer.Write(path.ToString(pathTable));
                }
            };

            bool success = false;

            ExceptionUtilities.HandleRecoverableIOException(
                () =>
            {
                string sandboxedProcessResultOutputPath = Path.GetFullPath(m_configuration.SandboxedProcessResultOutputFile);
                m_logger.LogInfo($"Writing sandboxed process result to '{sandboxedProcessResultOutputPath}'");

                using (FileStream stream = File.OpenWrite(sandboxedProcessResultOutputPath))
                {
                    result.Serialize(stream, writePath);
                }

                success = true;
            },
                ex =>
            {
                m_logger.LogError(ex.ToString());
                success = false;
            });

            return(success);
        }
Пример #13
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);
        }
Пример #14
0
        private async Task <Possible <NinjaGraphResult> > ComputeBuildGraphAsync()
        {
            AbsolutePath outputFile = SerializedGraphPath.Value;

            SandboxedProcessResult result = await RunNinjaGraphBuilderAsync(outputFile);

            string standardError = result.StandardError.CreateReader().ReadToEndAsync().GetAwaiter().GetResult();

            if (result.ExitCode != 0)
            {
                if (!m_context.CancellationToken.IsCancellationRequested)
                {
                    Tracing.Logger.Log.GraphConstructionInternalError(
                        m_context.LoggingContext,
                        m_resolverSettings.Location(m_context.PathTable),
                        standardError);
                }

                return(new NinjaGraphConstructionFailure(m_resolverSettings.ModuleName, ProjectRoot.ToString(m_context.PathTable)));
            }

            // If the tool exited gracefully, but standard error is not empty, that is interpreted as a warning
            if (!string.IsNullOrEmpty(standardError))
            {
                Tracing.Logger.Log.GraphConstructionFinishedSuccessfullyButWithWarnings(
                    m_context.LoggingContext,
                    m_resolverSettings.Location(m_context.PathTable),
                    standardError);
            }

            FrontEndUtilities.TrackToolFileAccesses(m_host.Engine, m_context, Name, result.AllUnexpectedFileAccesses, outputFile.GetParent(m_context.PathTable));
            var serializer = JsonSerializer.Create(GraphSerializationSettings.Settings);

            // Add custom deserializer for converting string arrays to AbsolutePath ReadOnlySets
            serializer.Converters.Add(new RootAwareAbsolutePathConverter(m_context.PathTable, SpecFile.GetParent(m_context.PathTable)));
            serializer.Converters.Add(new ToReadOnlySetJsonConverter <AbsolutePath>());

            var outputFileString = outputFile.ToString(m_context.PathTable);

            Tracing.Logger.Log.LeftGraphToolOutputAt(m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), outputFileString);

            NinjaGraphResult projectGraphWithPredictionResult;

            using (var sr = new StreamReader(outputFileString))
                using (var reader = new JsonTextReader(sr))
                {
                    projectGraphWithPredictionResult = serializer.Deserialize <NinjaGraphResult>(reader);
                }

            return(projectGraphWithPredictionResult);
        }
Пример #15
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);
        }
Пример #16
0
        private async Task <Possible <RushGraph> > ComputeBuildGraphAsync(
            AbsolutePath outputFile,
            BuildParameters.IBuildParameters buildParameters)
        {
            SandboxedProcessResult result = await RunRushGraphBuilderAsync(outputFile, buildParameters);

            string standardError = result.StandardError.CreateReader().ReadToEndAsync().GetAwaiter().GetResult();

            if (result.ExitCode != 0)
            {
                // In case of a cancellation, the tool may have exited with a non-zero
                // code, but that's expected
                if (!m_context.CancellationToken.IsCancellationRequested)
                {
                    // This should never happen! Report the standard error and exit gracefully
                    Tracing.Logger.Log.GraphConstructionInternalError(
                        m_context.LoggingContext,
                        m_resolverSettings.Location(m_context.PathTable),
                        standardError);
                }

                return(new RushGraphConstructionFailure(m_resolverSettings, m_context.PathTable));
            }

            // If the tool exited gracefully, but standard error is not empty, that
            // is interpreted as a warning. We propagate that to the BuildXL log
            if (!string.IsNullOrEmpty(standardError))
            {
                Tracing.Logger.Log.GraphConstructionFinishedSuccessfullyButWithWarnings(
                    m_context.LoggingContext,
                    m_resolverSettings.Location(m_context.PathTable),
                    standardError);
            }

            TrackFilesAndEnvironment(result.AllUnexpectedFileAccesses, outputFile.GetParent(m_context.PathTable));

            JsonSerializer serializer = ConstructProjectGraphSerializer(s_jsonSerializerSettings);

            using (var sr = new StreamReader(outputFile.ToString(m_context.PathTable)))
                using (var reader = new JsonTextReader(sr))
                {
                    var flattenedRushGraph = serializer.Deserialize <GenericRushGraph <GenericRushProject <string> > >(reader);

                    RushGraph graph = ResolveDependencies(flattenedRushGraph);

                    return(graph);
                }
        }
        [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);
        }
Пример #18
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);
        }
        public async Task EnumerateEmptyDirectoryWithFindFirstFileAndExplicitReportAndNoMatches()
        {
            var pathTable = new PathTable();

            AbsolutePath           emptyDirPath = CreateDirectory(pathTable, @"emptyDir");
            SandboxedProcessResult result       = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(emptyDirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportAccess | FileAccessPolicy.AllowReadAlways);
            },
                EnumerateWithFindFirstFileEx(@"emptyDir\xxx*")); // xxx* excludes the magic . and .. entries.

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,
                expected: ExpectReport(ReportedFileOperation.FindFirstFileEx, RequestedAccess.Enumerate, emptyDirPath));
        }
Пример #20
0
        private void PostProcessSandboxedProcessResult(SandboxedProcessInfo info, SandboxedProcessResult result)
        {
            m_logger.LogInfo("Post processing sandboxed process result");

            if (result.FileAccesses != null)
            {
                if (info.RemoteSandboxedProcessData != null)
                {
                    // TODO: Hack! Hack!
                    //       This changes is done so that AnyBuild does not try to send untracked files as inputs/outputs.
                    //       This changes the file accesses that BuildXL will see.
                    //       Ideally, this filtration should be done in AnyBuild when processing the result of SandboxedProcessResult
                    //       coming from this executor.
                    HashSet <ReportedFileAccess> trackedAccesses = result
                                                                   .FileAccesses
                                                                   .Where(fa => !info.RemoteSandboxedProcessData.IsUntracked(fa.GetPath(info.PathTable))).ToHashSet();
                    result.FileAccesses = trackedAccesses;
                }
            }
        }
        public async Task EnumerateEmptyDirectoryWithFindFirstFileAndNoExplicitReport()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath   = CreateDirectory(pathTable, @"dir");
            AbsolutePath fileAPath = WriteEmptyFile(pathTable, @"dir\fileA");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest => { manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.AllowReadAlways); },
                EnumerateWithFindFirstFileEx(@"dir\*"));

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,

                // Note that we do not get an EnumerationProbe for fileA since it is not under Report scope.
                expected: ExpectReport(ReportedFileOperation.FindFirstFileEx, RequestedAccess.Enumerate, dirPath));
        }
Пример #22
0
        private void PrintObservedAccesses(PathTable pathTable, SandboxedProcessResult result)
        {
            var accesses = new List <ReportedFileAccess>();

            if (result.FileAccesses != null)
            {
                accesses.AddRange(result.FileAccesses);
            }

            if (result.AllUnexpectedFileAccesses != null)
            {
                accesses.AddRange(result.AllUnexpectedFileAccesses);
            }

            m_logger.LogInfo($"{accesses.Count} observed access(es):");

            foreach (var access in accesses)
            {
                m_logger.LogInfo($"{access.GetPath(pathTable)}: {access.Describe()}");
            }
        }
        public async Task EnumerateNonexistentDirectory()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath     = CreateDirectory(pathTable, @"dir");
            AbsolutePath fakeDirPath = GetFullPath(pathTable, @"dir\fake");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportAccess | FileAccessPolicy.AllowReadAlways);
            },
                EnumerateWithFindFirstFileEx(@"dir\fake\*")); // dir\fake doesn't exist so we should expect ERROR_PATH_NOT_FOUND (nonexistent directory to enumerate).

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,
                expected: ExpectReport(ReportedFileOperation.FindFirstFileEx, RequestedAccess.Enumerate, fakeDirPath, exists: false));
        }
Пример #24
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();
            }
        }
Пример #25
0
        public async Task TestShimChildProcessWithPluginAndEmptyMatchesAsync(bool shimAllProcesses, bool shouldBeShimmed)
        {
            var stdOutSb = new StringBuilder(128);
            var stdErrSb = new StringBuilder();

            SandboxedProcessResult result = await RunWithPluginAsync(
                shimAllProcesses : shimAllProcesses,
                shouldBeShimmed : shouldBeShimmed,
                shouldFindMatch : false,
                processMatches : new string[0],
                stdOutSb : stdOutSb,
                stdErrSb : stdErrSb);

            string stdOut = stdOutSb.ToString();
            string stdErr = stdErrSb.ToString();

            m_output.WriteLine($"stdout: {stdOut}");
            m_output.WriteLine($"stderr: {stdErr}");

            AssertSuccess(result, stdErr);
            AssertShimmedIf(stdOut, shimAllProcesses != shouldBeShimmed);
            XAssert.Contains(stdOut, GetChildOutputForPluginTest(shouldBeShimmed));
        }
        public async Task ProbeWithFindFirstFileWhereSearchPathIsFileAndReadNotAllow()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath  = CreateDirectory(pathTable, @"dir");
            AbsolutePath filePath = WriteEmptyFile(pathTable, @"dir\file");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportAccess);
            },
                EnumerateWithFindFirstFileEx(@"dir\file\*")); // Note that we are trying to wildcard udner a file.

            VerifyReportedAccesses(
                pathTable,
                result.AllUnexpectedFileAccesses,
                allowExtraEnumerations: false,

                // Note that there is no directory enumeration reported (this is really a single file probe).
                // Note that the probe path is dir\file despite querying dir\file\*
                expected: ExpectReport(ReportedFileOperation.FindFirstFileEx, RequestedAccess.Probe, filePath, status: FileAccessStatus.Denied));
        }
Пример #27
0
        private bool TryWriteSandboxedProcessResult(SandboxedProcessResult result)
        {
            Contract.Requires(result != null);

            bool success = false;

            ExceptionUtilities.HandleRecoverableIOException(
                () =>
            {
                using (FileStream stream = File.OpenWrite(Path.GetFullPath(m_configuration.SandboxedProcessResultOutputFile)))
                {
                    result.Serialize(stream);
                }

                success = true;
            },
                ex =>
            {
                m_logger.LogError(ex.ToString());
                success = false;
            });

            return(success);
        }
        public async Task ProbeWithFindFirstFileSingle()
        {
            var pathTable = new PathTable();

            AbsolutePath dirPath   = CreateDirectory(pathTable, @"dir");
            AbsolutePath fileAPath = WriteEmptyFile(pathTable, @"dir\fileA");

            SandboxedProcessResult result = await RunRemoteApiInSandboxAsync(
                pathTable,
                manifest =>
            {
                manifest.AddScope(dirPath, FileAccessPolicy.MaskNothing, FileAccessPolicy.ReportAccess | FileAccessPolicy.AllowReadAlways);
            },
                EnumerateWithFindFirstFileEx(@"dir\fileA")); // Note no wildcards; should get a probe but not enumeration.

            VerifyReportedAccesses(
                pathTable,
                result.ExplicitlyReportedFileAccesses,
                allowExtraEnumerations: false,

                // Note that there is no directory enumeration reported (this is really a single file probe),
                // and the report for fileA is correspondingly a Probe rather than EnumerationProbe.
                expected: ExpectReport(ReportedFileOperation.FindFirstFileEx, RequestedAccess.Probe, fileAPath));
        }
Пример #29
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);
        }
Пример #30
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");
        }