diff --git a/.azdo/linux/job-ptrace.yml b/.azdo/linux/job-ptrace.yml index 0f2bbbeeb6..d8786506af 100644 --- a/.azdo/linux/job-ptrace.yml +++ b/.azdo/linux/job-ptrace.yml @@ -96,7 +96,7 @@ jobs: # This step currently only builds selfhost with the --minimal flag, but will be extended in the future to run more unit tests with ptrace - bash: | set -eu - bash bxl.sh /ado /cacheMiss:"[Bxl.Selfhost.Linux.PTrace]" /logObservedFileAccesses /cacheConfigFilePath:Out/CacheConfig.json /logoutput:FullOutputOnError /logsToRetain:10 /exp:lazysodeletion- /logsDirectory:"Out/Logs/Build" --minimal --internal --use-dev "/p:BUILDXL_FINGERPRINT_SALT='Bxl.Selfhost.Linux.PTrace.CasingPR'" /forceEnableLinuxPTraceSandbox+ /injectCacheMisses:0.3 + bash bxl.sh /ado /cacheMiss:"[Bxl.Selfhost.Linux.PTrace]" /logObservedFileAccesses /cacheConfigFilePath:Out/CacheConfig.json /logoutput:FullOutputOnError /logsToRetain:10 /exp:lazysodeletion- /logsDirectory:"Out/Logs/Build" --minimal --internal --use-dev /forceEnableLinuxPTraceSandbox+ /injectCacheMisses:0.3 displayName: Build BXL with LKG and PTrace workingDirectory: /home/subst env: diff --git a/.azdo/linux/job-selfhost.yml b/.azdo/linux/job-selfhost.yml index f3d264bfdd..3b5678c014 100644 --- a/.azdo/linux/job-selfhost.yml +++ b/.azdo/linux/job-selfhost.yml @@ -149,7 +149,7 @@ jobs: # - the disks on Azure Pipeline VMs are too small to build everything, so let's instead run tests # - we also disable early worker release to avoid releasing a worker before attachment, which tends to happen # when the build is highly cached: the intention is to have as much of a distributed build as possible for validation purposes - ./bxl.sh --use-dev --use-adobuildrunner ${{ parameters.BxlCommonArgs }} /logsDirectory:"Out/Logs/${{ parameters.validationName }}" ${{ parameters.bxlExtraArgs }} "/f:tag='test' /p:BUILDXL_FINGERPRINT_SALT='PrPipelineSalt_20230825'" /earlyWorkerRelease- /p:BuildXLWorkerAttachTimeoutMin=10 /logToKusto /logToKustoBlobUri:https://adomessages.blob.core.windows.net/adomessages /logToKustoIdentityId:6e0959cf-a9ba-4988-bbf1-7facd9deda51 /logToKustoTenantId:975f013f-7f24-47e8-a7d3-abc4752bf346 /historicMetadataCache- + ./bxl.sh --use-dev --use-adobuildrunner ${{ parameters.BxlCommonArgs }} /logsDirectory:"Out/Logs/${{ parameters.validationName }}" ${{ parameters.bxlExtraArgs }} "/f:tag='test'" /earlyWorkerRelease- /p:BuildXLWorkerAttachTimeoutMin=10 /logToKusto /logToKustoBlobUri:https://adomessages.blob.core.windows.net/adomessages /logToKustoIdentityId:6e0959cf-a9ba-4988-bbf1-7facd9deda51 /logToKustoTenantId:975f013f-7f24-47e8-a7d3-abc4752bf346 /historicMetadataCache- displayName: Test (${{ parameters.validationName }}) workingDirectory: /home/subst env: diff --git a/Public/Src/App/Bxl/Args.cs b/Public/Src/App/Bxl/Args.cs index 82929dfc1a..940fc2a139 100644 --- a/Public/Src/App/Bxl/Args.cs +++ b/Public/Src/App/Bxl/Args.cs @@ -381,6 +381,9 @@ public bool TryParse(string[] args, PathTable pathTable, out ICommandLineConfigu OptionHandlerFactory.CreateBoolOption( "disableIsObsoleteCheckDuringConversion", sign => frontEndConfiguration.DisableIsObsoleteCheckDuringConversion = sign), + OptionHandlerFactory.CreateBoolOption( + "forceAddExecutionPermission", + sign => sandboxConfiguration.ForceAddExecutionPermission = sign), OptionHandlerFactory.CreateOption2( "distributedBuildRole", "dbr", diff --git a/Public/Src/App/Bxl/HelpText.cs b/Public/Src/App/Bxl/HelpText.cs index 4821e0a4b6..cbfe6ec603 100644 --- a/Public/Src/App/Bxl/HelpText.cs +++ b/Public/Src/App/Bxl/HelpText.cs @@ -922,6 +922,12 @@ public static void DisplayHelp(HelpLevel helpLevel) HelpLevel.Verbose ); + hw.WriteOption( + "/forceAddExecutionPermission[+|-]", + Strings.HelpText_DisplayHelp_ForceAddExecutionPermission, + HelpLevel.Verbose + ); + #endregion hw.WriteBanner( diff --git a/Public/Src/App/Bxl/Strings.resx b/Public/Src/App/Bxl/Strings.resx index a3721353de..3a52e58965 100644 --- a/Public/Src/App/Bxl/Strings.resx +++ b/Public/Src/App/Bxl/Strings.resx @@ -1168,4 +1168,7 @@ Example: ad2d42d2ec5d2ca0c0b7ad65402d07c7ef40b91e Enables verbose sandbox logging for specific pips based on their formatted semistable hashes. This is tantamount to switching /logObservedFileAccesses and /logProcesses for these pips, and also enabling verbose debug logging in the sandbox. A list of semistable hashes might be specified, separated by semicolons. If the special value "*" is given, sandbox logging will be enabled for every pip. Example: /debug_enableVerboseProcessLogging:Pip3855A4C7E1E820D0;PipE9638AD7DDD6AF67 + + When set to true, it enables the execution permission for the root process of process pips in Linux builds. Defaults to true. + \ No newline at end of file diff --git a/Public/Src/Engine/ProcessPipExecutor/SandboxedProcessPipExecutor.cs b/Public/Src/Engine/ProcessPipExecutor/SandboxedProcessPipExecutor.cs index 94d316190a..5d16b93284 100644 --- a/Public/Src/Engine/ProcessPipExecutor/SandboxedProcessPipExecutor.cs +++ b/Public/Src/Engine/ProcessPipExecutor/SandboxedProcessPipExecutor.cs @@ -837,7 +837,8 @@ public async Task RunAsync( sandboxConnection: sandboxConnection, sidebandWriter: sidebandWriter, detoursEventListener: m_detoursListener, - fileSystemView: fileSystemView) + fileSystemView: fileSystemView, + forceAddExecutionPermission: m_sandboxConfig.ForceAddExecutionPermission) { Arguments = arguments, WorkingDirectory = m_workingDirectory, diff --git a/Public/Src/Engine/Processes/SandboxedProcessInfo.cs b/Public/Src/Engine/Processes/SandboxedProcessInfo.cs index c1d86e971a..cba3eac45a 100644 --- a/Public/Src/Engine/Processes/SandboxedProcessInfo.cs +++ b/Public/Src/Engine/Processes/SandboxedProcessInfo.cs @@ -132,7 +132,8 @@ public SandboxedProcessInfo( IDetoursEventListener? detoursEventListener = null, ISandboxConnection? sandboxConnection = null, bool createJobObjectForCurrentProcess = true, - SandboxedProcessResourceMonitoringConfig? monitoringConfig = null) + SandboxedProcessResourceMonitoringConfig? monitoringConfig = null, + bool forceAddExecutionPermission = true) : this( new PathTable(), fileStorage, @@ -143,7 +144,8 @@ public SandboxedProcessInfo( detoursEventListener, sandboxConnection, createJobObjectForCurrentProcess: createJobObjectForCurrentProcess, - monitoringConfig: monitoringConfig) + monitoringConfig: monitoringConfig, + forceAddExecutionPermission: forceAddExecutionPermission) { } @@ -163,7 +165,8 @@ public SandboxedProcessInfo( SidebandWriter? sidebandWriter = null, bool createJobObjectForCurrentProcess = true, ISandboxFileSystemView? fileSystemView = null, - SandboxedProcessResourceMonitoringConfig? monitoringConfig = null) + SandboxedProcessResourceMonitoringConfig? monitoringConfig = null, + bool forceAddExecutionPermission = true) { PathTable = pathTable; FileAccessManifest = fileAccessManifest ?? new FileAccessManifest(pathTable); @@ -182,6 +185,7 @@ public SandboxedProcessInfo( CreateJobObjectForCurrentProcess = createJobObjectForCurrentProcess; FileSystemView = fileSystemView; MonitoringConfig = monitoringConfig; + ForceAddExecutionPermission = forceAddExecutionPermission; } /// @@ -198,7 +202,8 @@ public SandboxedProcessInfo( ISandboxConnection? sandboxConnection = null, FileAccessManifest? fileAccessManifest = null, bool createJobObjectForCurrentProcess = true, - SandboxedProcessResourceMonitoringConfig? monitoringConfig = null + SandboxedProcessResourceMonitoringConfig? monitoringConfig = null, + bool forceAddExecutionPermission = true ) : this( pathTable, @@ -211,7 +216,8 @@ public SandboxedProcessInfo( detoursEventListener, sandboxConnection, createJobObjectForCurrentProcess: createJobObjectForCurrentProcess, - monitoringConfig: monitoringConfig) + monitoringConfig: monitoringConfig, + forceAddExecutionPermission: forceAddExecutionPermission) { } @@ -291,6 +297,11 @@ public SandboxedProcessInfo( /// public int NumRetriesPipeReadOnCancel { get; set; } = DefaultPipeReadRetryOnCancellationCount; + /// + /// Force set the execute permission bit for the root process of process pips in Linux builds. + /// + public bool ForceAddExecutionPermission { get; } + /// /// Encoded command line arguments /// @@ -595,6 +606,7 @@ public void Serialize(Stream stream) writer.WriteNullableString(DetoursFailureFile); writer.WriteNullableReadOnlyList(ExternalVmSandboxStaleFilesToClean, (w, s) => w.Write(s)); writer.Write(CreateSandboxTraceFile); + writer.Write(ForceAddExecutionPermission); // File access manifest should be serialized the last. writer.Write(FileAccessManifest, (w, v) => FileAccessManifest.Serialize(stream)); @@ -643,9 +655,9 @@ public static SandboxedProcessInfo Deserialize(Stream stream, LoggingContext log var detoursFailureFile = reader.ReadNullableString(); var externalVmSandboxStaleFilesToClean = reader.ReadNullableReadOnlyList(r => r.ReadString()); var createSandboxTraceFile = reader.ReadBoolean(); + bool forceAddExecutionPermission = reader.ReadBoolean(); var fam = reader.ReadNullable(r => FileAccessManifest.Deserialize(stream)); - return new SandboxedProcessInfo( new PathTable(), sandboxedProcessStandardFiles != null ? new StandardFileStorage(sandboxedProcessStandardFiles) : null, @@ -655,7 +667,8 @@ public static SandboxedProcessInfo Deserialize(Stream stream, LoggingContext log loggingContext: loggingContext, sidebandWriter: sidebandWritter, detoursEventListener: detoursEventListener, - createJobObjectForCurrentProcess: createJobObjectForCurrentProcess) + createJobObjectForCurrentProcess: createJobObjectForCurrentProcess, + forceAddExecutionPermission: forceAddExecutionPermission) { m_arguments = arguments, m_commandLine = commandLine, diff --git a/Public/Src/Engine/Processes/SandboxedProcessUnix.cs b/Public/Src/Engine/Processes/SandboxedProcessUnix.cs index 0fdcd2207a..e1e9b7807a 100644 --- a/Public/Src/Engine/Processes/SandboxedProcessUnix.cs +++ b/Public/Src/Engine/Processes/SandboxedProcessUnix.cs @@ -213,8 +213,11 @@ public SandboxedProcessUnix(SandboxedProcessInfo info, bool ignoreReportedAccess DegreeOfParallelism: 1, // Must be one, otherwise SandboxedPipExecutor will fail asserting valid reports SingleProducerConstrained: useSingleProducer); - m_pendingReports = ActionBlockSlim.Create(configuration: executionOptions, HandleAccessReport); - + m_pendingReports = ActionBlockSlim.Create(configuration: executionOptions, + (accessReport) => + { + HandleAccessReport(accessReport, info.ForceAddExecutionPermission); + }); // install 'ProcessReady' and 'ProcessStarted' handlers to inform the sandbox ProcessReady += () => SandboxConnection.NotifyPipReady(info.LoggingContext, info.FileAccessManifest, this, m_pendingReports.Completion); ProcessStarted += (pid) => OnProcessStartedAsync(info).GetAwaiter().GetResult(); @@ -280,7 +283,10 @@ protected override System.Diagnostics.Process CreateProcess(SandboxedProcessInfo { // This is a workaround for an issue that appears on Linux where some executables in the BuildXL // nuget/npm package may not have the execute bit set causing a permission denied error - _ = FileUtilities.TrySetExecutePermissionIfNeeded(process.StartInfo.FileName); + if (info.ForceAddExecutionPermission) + { + _ = FileUtilities.TrySetExecutePermissionIfNeeded(process.StartInfo.FileName); + } process.StartInfo.Arguments = $"{process.StartInfo.FileName} {process.StartInfo.Arguments}"; process.StartInfo.FileName = EnvExecutable; @@ -602,7 +608,7 @@ internal void PostAccessReport(AccessReport report) #endif } - private async Task FeedStdInAsync(SandboxedProcessInfo info, string? processStdinFileName) + private async Task FeedStdInAsync(SandboxedProcessInfo info, string? processStdinFileName, bool forceAddExecutionPermission = true) { Contract.Requires(info.RootJailInfo == null || !NeedsShellWrapping(), "Cannot run root jail on this OS"); @@ -651,7 +657,10 @@ private async Task FeedStdInAsync(SandboxedProcessInfo info, string? processStdi lines.Add($"exec {cmdLine}"); - SetExecutePermissionIfNeeded(info.FileName, throwIfNotFound: false); + if (info.ForceAddExecutionPermission) + { + SetExecutePermissionIfNeeded(info.FileName, throwIfNotFound: false); + } foreach (string line in lines) { await Process.StandardInput.WriteLineAsync(line); @@ -836,7 +845,7 @@ private void UpdateAverageTimeSpentInReportQueue(AccessReportStatistics stats) m_sumOfReportQueueTimesUs += (stats.DequeueTime - stats.EnqueueTime) / 1000; } - private void HandleAccessReport(AccessReport report) + private void HandleAccessReport(AccessReport report, bool forceAddExecutionPermission) { if (ShouldReportFileAccesses && report.Operation != FileOperation.OpDebugMessage) { @@ -869,7 +878,7 @@ private void HandleAccessReport(AccessReport report) // The process is statically linked, a ptrace runner needs to be started up if (report.Operation == FileOperation.OpStaticallyLinkedProcess) { - StartPTraceRunner(report.Pid, reportPath); + StartPTraceRunner(report.Pid, reportPath, forceAddExecutionPermission); } var pathExists = true; @@ -1067,7 +1076,7 @@ internal static string AccessReportToString(AccessReport report) I($"{operation}:{pid}|{requestedAccess}|{status}|{explicitLogging}|{error}|{path}|{isDirectory}|e:{report.Statistics.EnqueueTime}|h:{processTime}us|q:{queueTime}us"); } - private void StartPTraceRunner(int pid, string path) + private void StartPTraceRunner(int pid, string path, bool forceAddExecutionPermission) { var paths = SandboxConnectionLinuxDetours.GetPaths(RootJailInfo, UniqueName); var args = $"-c {pid} -x {path}"; @@ -1094,8 +1103,9 @@ private void StartPTraceRunner(int pid, string path) // We will kill these manually if the pip is exiting Timeout.InfiniteTimeSpan, // The runner will only log to stderr if there's a problem, other logs go to the main log using the fifo - errorBuilder: line => { if (line != null) { Logger.Log.PTraceRunnerError(m_loggingContext, line); } } - ); + errorBuilder: line => { if (line != null) { Logger.Log.PTraceRunnerError(m_loggingContext, line); } }, + forceAddExecutionPermission: forceAddExecutionPermission + ); ptraceRunner.Start(); m_ptraceRunners.Add(runnerTask(ptraceRunner)); diff --git a/Public/Src/Engine/Processes/UnSandboxedProcess.cs b/Public/Src/Engine/Processes/UnSandboxedProcess.cs index 5f143442b2..ddfc43d7fd 100644 --- a/Public/Src/Engine/Processes/UnSandboxedProcess.cs +++ b/Public/Src/Engine/Processes/UnSandboxedProcess.cs @@ -182,7 +182,8 @@ public UnsandboxedProcess(SandboxedProcessInfo info) { LogProcessState($"Unable to generate core dump: {m_dumpCreationException.GetLogEventMessage()}"); } - }); + }, + forceAddExecutionPermission: info.ForceAddExecutionPermission); } /// diff --git a/Public/Src/FrontEnd/UnitTests/Lage/IntegrationTests/LageIntegrationTestBase.cs b/Public/Src/FrontEnd/UnitTests/Lage/IntegrationTests/LageIntegrationTestBase.cs index b8628044e5..0f577bc44c 100644 --- a/Public/Src/FrontEnd/UnitTests/Lage/IntegrationTests/LageIntegrationTestBase.cs +++ b/Public/Src/FrontEnd/UnitTests/Lage/IntegrationTests/LageIntegrationTestBase.cs @@ -75,12 +75,6 @@ protected LageIntegrationTestBase(ITestOutputHelper output) : base(output, true) SourceRoot = Path.Combine(TestRoot, RelativeSourceRoot); OutDir = "target"; - - // TODO Bug 2073919- this is a temporary workaround to tests that are flakey due to the execute permission - // not correctly transiting through cache - AssertTrue(FileUtilities.TrySetExecutePermissionIfNeeded(PathToYarn).Succeeded); - AssertTrue(FileUtilities.TrySetExecutePermissionIfNeeded(PathToLage).Succeeded); - AssertTrue(FileUtilities.TrySetExecutePermissionIfNeeded(PathToNode).Succeeded); } /// diff --git a/Public/Src/FrontEnd/UnitTests/Rush/IntegrationTests/RushIntegrationTestBase.cs b/Public/Src/FrontEnd/UnitTests/Rush/IntegrationTests/RushIntegrationTestBase.cs index 36de640ad8..e96807ce66 100644 --- a/Public/Src/FrontEnd/UnitTests/Rush/IntegrationTests/RushIntegrationTestBase.cs +++ b/Public/Src/FrontEnd/UnitTests/Rush/IntegrationTests/RushIntegrationTestBase.cs @@ -80,11 +80,6 @@ protected RushIntegrationTestBase(ITestOutputHelper output) : base(output, true) // Make sure the user profile and temp folders exist Directory.CreateDirectory(RushUserProfile); Directory.CreateDirectory(RushTempFolder); - - // TODO Bug 2073919- this is a temporary workaround to tests that are flakey due to the execute permission - // not correctly transiting through cache - AssertTrue(FileUtilities.TrySetExecutePermissionIfNeeded(PathToRush).Succeeded); - AssertTrue(FileUtilities.TrySetExecutePermissionIfNeeded(PathToNode).Succeeded); } protected SpecEvaluationBuilder Build( diff --git a/Public/Src/FrontEnd/UnitTests/Yarn/IntegrationTests/YarnIntegrationTestBase.cs b/Public/Src/FrontEnd/UnitTests/Yarn/IntegrationTests/YarnIntegrationTestBase.cs index f5211cca0e..f87f773d3e 100644 --- a/Public/Src/FrontEnd/UnitTests/Yarn/IntegrationTests/YarnIntegrationTestBase.cs +++ b/Public/Src/FrontEnd/UnitTests/Yarn/IntegrationTests/YarnIntegrationTestBase.cs @@ -67,11 +67,6 @@ protected YarnIntegrationTestBase(ITestOutputHelper output) : base(output, true) SourceRoot = Path.Combine(TestRoot, RelativeSourceRoot); OutDir = "target"; - - // TODO Bug 2073919- this is a temporary workaround to tests that are flakey due to the execute permission - // not correctly transiting through cache - AssertTrue(FileUtilities.TrySetExecutePermissionIfNeeded(PathToYarn).Succeeded); - AssertTrue(FileUtilities.TrySetExecutePermissionIfNeeded(PathToNode).Succeeded); } protected SpecEvaluationBuilder Build( diff --git a/Public/Src/Tools/FileDownloader/Extractor.cs b/Public/Src/Tools/FileDownloader/Extractor.cs index fcb16a8d06..04e5fbdd16 100644 --- a/Public/Src/Tools/FileDownloader/Extractor.cs +++ b/Public/Src/Tools/FileDownloader/Extractor.cs @@ -25,7 +25,7 @@ internal sealed class Extractor : ToolProgram private static readonly Dictionary packagesToBeChecked = new Dictionary { {"NodeJs.linux-x64", "node-v18.6.0-linux-x64/bin/node"}, - {"YarnTool", "yarn-v1.22.19/bin/yarn"} + {"DotNet-Runtime.linux", "dotnet"} }; private Extractor() : base("Extractor") @@ -141,7 +141,7 @@ private bool TryExtractToDisk(ExtractorArgs arguments) { if (target.Contains(packageName)) { - if (!CheckForNodePermissions(target, packagesToBeChecked[packageName])) + if (!SetExecutePermissionsForExtractedFiles(target, packagesToBeChecked[packageName])) { return false; } @@ -197,34 +197,21 @@ private void ErrorNothingExtracted(string archive, string target) } /// - /// This method is used to check if the execute permission bit has been set or not. + /// This method is used to set the execute permissions bit for the extracted files. /// /// - /// In the method below we are checking this specifically for Node package in linux, as that was causing the issue. + /// In the method below we are adding the bit specifically for Node and Dotnet package in linux, as they are causing the issue. + /// This is only set for the BuildXL.Internal repo build and is not expected to kick in for end user builds. + /// It is expected to be shortlived and probably generalized to setting the execute permission for all extractor output. /// TODO: Need to remove this hack once the bug is fixed. Refer bug https://dev.azure.com/mseng/1ES/_workitems/edit/2073919 for further information. /// - private bool CheckForNodePermissions(string target, string relativePath) + private bool SetExecutePermissionsForExtractedFiles(string target, string relativePath) { string fullPathForExecutableFile = Path.Combine(target, relativePath); if (File.Exists(fullPathForExecutableFile)) { - var mode = GetFilePermissionsForFilePath(fullPathForExecutableFile, false); - if (mode < 0) - { - Console.Error.WriteLine($"Failed to retrieve file permissions for : {fullPathForExecutableFile}"); - return false; - } - else - { - // Check if the execute file permission bit has been set or not. - var filePermissions = checked((FilePermissions)mode); - if ((filePermissions & FilePermissions.S_IXUSR) == FilePermissions.S_IXUSR) - { - Console.Error.WriteLine($"File : {fullPathForExecutableFile} does not have the execute bit set for the user account"); - return false; - } - } + _ = FileUtilities.TrySetExecutePermissionIfNeeded(fullPathForExecutableFile); } else { diff --git a/Public/Src/Utilities/Configuration/ISandboxConfiguration.cs b/Public/Src/Utilities/Configuration/ISandboxConfiguration.cs index ea7078ef80..c8d61cad01 100644 --- a/Public/Src/Utilities/Configuration/ISandboxConfiguration.cs +++ b/Public/Src/Utilities/Configuration/ISandboxConfiguration.cs @@ -339,5 +339,10 @@ public interface ISandboxConfiguration /// Ignores DeviceIoControl calls, in particular the case of FSCTL_GET_REPARSE_POINT /// bool IgnoreDeviceIoControlGetReparsePoint { get; } + + /// + /// Force set the execute permission bit for the root process of process pips in Linux builds. + /// + public bool ForceAddExecutionPermission { get; } } } diff --git a/Public/Src/Utilities/Configuration/Mutable/SandboxConfiguration.cs b/Public/Src/Utilities/Configuration/Mutable/SandboxConfiguration.cs index 3cccdd3524..7486b148b9 100644 --- a/Public/Src/Utilities/Configuration/Mutable/SandboxConfiguration.cs +++ b/Public/Src/Utilities/Configuration/Mutable/SandboxConfiguration.cs @@ -58,6 +58,7 @@ public SandboxConfiguration() UnconditionallyEnableLinuxPTraceSandbox = false; // TODO: flip the default once we have verified this is not a breaking change IgnoreDeviceIoControlGetReparsePoint = true; + ForceAddExecutionPermission = true; } /// @@ -114,6 +115,7 @@ public SandboxConfiguration(ISandboxConfiguration template, PathRemapper pathRem AlwaysRemoteInjectDetoursFrom32BitProcess = template.AlwaysRemoteInjectDetoursFrom32BitProcess; UnconditionallyEnableLinuxPTraceSandbox = template.UnconditionallyEnableLinuxPTraceSandbox; IgnoreDeviceIoControlGetReparsePoint = template.IgnoreDeviceIoControlGetReparsePoint; + ForceAddExecutionPermission = template.ForceAddExecutionPermission; } /// @@ -299,5 +301,8 @@ public UnsafeSandboxConfiguration UnsafeSandboxConfigurationMutable /// public bool IgnoreDeviceIoControlGetReparsePoint { get; set; } + + /// + public bool ForceAddExecutionPermission { get; set; } } } diff --git a/Public/Src/Utilities/Utilities.Core/AsyncProcessExecutor.cs b/Public/Src/Utilities/Utilities.Core/AsyncProcessExecutor.cs index f9f92e3d43..702395543c 100644 --- a/Public/Src/Utilities/Utilities.Core/AsyncProcessExecutor.cs +++ b/Public/Src/Utilities/Utilities.Core/AsyncProcessExecutor.cs @@ -80,6 +80,11 @@ public class AsyncProcessExecutor : IDisposable private readonly Action m_outputBuilder; private readonly Action m_errorBuilder; + /// + /// Force set the execute permission bit for the root process of process pips in Linux builds. + /// + private readonly bool m_forceAddExecutionPermission; + private int m_processId = -1; private int GetProcessIdSafe() @@ -136,7 +141,8 @@ public AsyncProcessExecutor( Action errorBuilder = null, string provenance = null, Action logger = null, - Action dumpProcessTree = null) + Action dumpProcessTree = null, + bool forceAddExecutionPermission = true) { Contract.RequiresNotNull(process); @@ -160,6 +166,7 @@ public AsyncProcessExecutor( m_timeout = timeout; m_provenance = provenance; m_dumpProcessTree = dumpProcessTree; + m_forceAddExecutionPermission = forceAddExecutionPermission; } /// @@ -217,7 +224,11 @@ public void Start() try { - SetExecutePermissionIfNeeded(Process.StartInfo.FileName); + if (m_forceAddExecutionPermission) + { + SetExecutePermissionIfNeeded(Process.StartInfo.FileName); + } + Process.Start(); } catch (Win32Exception e) diff --git a/bxl.sh b/bxl.sh index e3da8b6ced..54684b1dde 100755 --- a/bxl.sh +++ b/bxl.sh @@ -326,9 +326,10 @@ elif [[ -z "$BUILDXL_BIN" ]]; then getLkg fi -# A casing related PR polluted the cache, so let's force a salt. This could be removed after the poisoned content gets evicted. +# Forcing a salt here to avoid problems faced in Linux validation pipeline related to cache. +# This is related to Bug 2104538 where the cache may or may not be setting the execute bit for some executables. if [[ $arg_UserProvidedBxlArguments != *"/p:BUILDXL_FINGERPRINT_SALT"* ]]; then - arg_Positional+=("/p:BUILDXL_FINGERPRINT_SALT=casingPR") + arg_Positional+=("/p:BUILDXL_FINGERPRINT_SALT=forceSaltForLinuxBugFixPR") fi compileWithBxl ${arg_Positional[@]} ${arg_UserProvidedBxlArguments[@]}