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[@]}