зеркало из https://github.com/microsoft/BuildXL.git
Merged PR 666947: [Linux Sandbox] Stop wrapping processes in a bash script
[Linux Sandbox] Stop wrapping processes in a bash script
This commit is contained in:
Родитель
12d740cc89
Коммит
ccf7741680
|
@ -23,6 +23,12 @@ using Xunit.Abstractions;
|
|||
|
||||
namespace ContentStoreTest.Distributed.ContentLocation.NuCache
|
||||
{
|
||||
// TODO: all fail with
|
||||
// System.ArgumentException : The reader should have at least 1 length but has 0.
|
||||
// Stack Trace:
|
||||
// at BuildXL.Utilities.Serialization.SpanReader.ThrowArgumentException(Int32 minLength) in \.\Public\Src\Utilities\Utilities\Serialization\SpanReader.cs:line 100
|
||||
|
||||
[Trait("Category", "WindowsOSOnly")]
|
||||
public class RocksDbContentLocationDatabaseTests : TestBase
|
||||
{
|
||||
protected readonly MemoryClock Clock = new MemoryClock();
|
||||
|
|
|
@ -43,7 +43,12 @@ namespace BuildXL.Processes
|
|||
bool NotifyUsage(uint cpuUsageBasisPoints, uint availableRamMB);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the sandbox that a new pip is about to start. Since the sandbox expects to receive the
|
||||
/// Notifies the sandbox that a new pip process is ready to be launched.
|
||||
/// </summary>
|
||||
void NotifyPipReady(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the sandbox that a new pip process has started. Since the sandbox expects to receive the
|
||||
/// process ID of the pip, this method requires that the supplied <paramref name="process"/> has already been started,
|
||||
/// and hence already has an ID assigned to it. To ensure that the process is not going to request file accesses before the
|
||||
/// sandbox is notified about it being started, the process should be started in some kind of suspended mode, and
|
||||
|
@ -55,7 +60,7 @@ namespace BuildXL.Processes
|
|||
/// A concrete sandbox connection can override this method to specify additional environment variables
|
||||
/// that should be set before executing the process.
|
||||
/// </summary>
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(long pipId);
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(SandboxedProcessInfo info, string uniqueName);
|
||||
|
||||
/// <summary>
|
||||
/// SandboxedProcess uses this method to notify the connection that the root process of the pip exited.
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace BuildXL.Processes
|
|||
/// <summary>
|
||||
/// Program to use to enter root jail. Defaults to <c>sudo chroot</c>, which requires NOPASSWD sudo privileges.
|
||||
/// </summary>
|
||||
public string RootJailProgram { get; init; } = "sudo chroot";
|
||||
public (string program, string[] args) RootJailProgram { get; init; } = ("/usr/bin/sudo", new[] { "/usr/sbin/chroot" });
|
||||
|
||||
/// <nodoc />
|
||||
public RootJailInfo(string rootJail, int? userId = null, int? groupId = null, bool disableSandboxing = false, bool disableAuditing = false)
|
||||
|
@ -59,11 +59,12 @@ namespace BuildXL.Processes
|
|||
public void Serialize(BuildXLWriter writer)
|
||||
{
|
||||
writer.Write(RootJail);
|
||||
writer.Write(UserId, (w, v) => w.WriteCompact(v));
|
||||
writer.Write(GroupId, (w, v) => w.WriteCompact(v));
|
||||
writer.Write(UserId, static (w, v) => w.WriteCompact(v));
|
||||
writer.Write(GroupId, static (w, v) => w.WriteCompact(v));
|
||||
writer.Write(DisableSandboxing);
|
||||
writer.Write(DisableAuditing);
|
||||
writer.Write(RootJailProgram);
|
||||
writer.Write(RootJailProgram.program);
|
||||
writer.Write(RootJailProgram.args, static (w, a) => w.Write(a));
|
||||
}
|
||||
|
||||
/// <nodoc />
|
||||
|
@ -71,13 +72,46 @@ namespace BuildXL.Processes
|
|||
{
|
||||
return new RootJailInfo(
|
||||
rootJail: reader.ReadString(),
|
||||
userId: reader.ReadNullableStruct(r => r.ReadInt32Compact()),
|
||||
groupId: reader.ReadNullableStruct(r => r.ReadInt32Compact()),
|
||||
userId: reader.ReadNullableStruct(static r => r.ReadInt32Compact()),
|
||||
groupId: reader.ReadNullableStruct(static r => r.ReadInt32Compact()),
|
||||
disableSandboxing: reader.ReadBoolean(),
|
||||
disableAuditing: reader.ReadBoolean())
|
||||
{
|
||||
RootJailProgram = reader.ReadString(),
|
||||
RootJailProgram = (reader.ReadString(), reader.ReadArray(static r => r.ReadString())),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="RootJailInfo"/>.
|
||||
/// </summary>
|
||||
public static class RootJailInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// If <see cref="RootJailInfo.RootJail"/> is set:
|
||||
/// if <paramref name="path"/> is relative to <see cref="RootJailInfo.RootJail"/> returns an absolute path which
|
||||
/// when accessed from the root jail resolves to path at location <paramref name="path"/>; otherwise throws.
|
||||
///
|
||||
/// If <see cref="RootJailInfo.RootJail"/> is null:
|
||||
/// returns <paramref name="path"/>
|
||||
/// </summary>
|
||||
public static string ToPathInsideRootJail(this RootJailInfo? @this, string path)
|
||||
{
|
||||
string rootJailDir = @this?.RootJail;
|
||||
if (rootJailDir == null)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!path.StartsWith(rootJailDir.TrimEnd('/') + '/'))
|
||||
{
|
||||
throw new BuildXLException($"Root jail dir '{rootJailDir}' must be a parent directory of path '{path}'");
|
||||
}
|
||||
|
||||
var jailRelativePath = path.Substring(rootJailDir.Length);
|
||||
return jailRelativePath[0] == '/'
|
||||
? jailRelativePath
|
||||
: "/" + jailRelativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,14 @@ namespace BuildXL.Processes
|
|||
/// <inheritdoc />
|
||||
public bool IsInTestMode { get; }
|
||||
|
||||
/// <nodoc />
|
||||
public static bool IsInDebugMode { get; } =
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
private readonly ConcurrentDictionary<long, SandboxedProcessUnix> m_pipProcesses = new ConcurrentDictionary<long, SandboxedProcessUnix>();
|
||||
|
||||
// TODO: remove at some later point
|
||||
|
@ -96,11 +104,7 @@ namespace BuildXL.Processes
|
|||
throw new BuildXLException($@"Unable to initialize generic sandbox, please check the sources for error code: {m_sandboxConnectionInfo.Error})");
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
ProcessUtilities.SetNativeConfiguration(true);
|
||||
#else
|
||||
ProcessUtilities.SetNativeConfiguration(false);
|
||||
#endif
|
||||
ProcessUtilities.SetNativeConfiguration(IsInDebugMode);
|
||||
|
||||
m_AccessReportCallback = (Sandbox.AccessReport report, int code) =>
|
||||
{
|
||||
|
@ -166,11 +170,14 @@ namespace BuildXL.Processes
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(long pipId)
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(SandboxedProcessInfo info, string uniqueName)
|
||||
{
|
||||
return Enumerable.Empty<(string, string)>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NotifyPipReady(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process) {}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool NotifyPipStarted(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process)
|
||||
{
|
||||
|
|
|
@ -247,6 +247,9 @@ Use the the following command to load/reload the sandbox kernel extension and fi
|
|||
return Sandbox.UpdateCurrentResourceUsage(cpuUsage, availableRamMB, m_kextConnectionInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NotifyPipReady(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process) {}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool NotifyPipStarted(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process)
|
||||
{
|
||||
|
@ -290,7 +293,7 @@ Use the the following command to load/reload the sandbox kernel extension and fi
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(long pipId)
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(SandboxedProcessInfo info, string uniqueName)
|
||||
{
|
||||
return Enumerable.Empty<(string, string)>();
|
||||
}
|
||||
|
|
|
@ -76,8 +76,6 @@ namespace BuildXL.Processes
|
|||
internal string ReportsFifoPath { get; }
|
||||
internal string FamPath { get; }
|
||||
|
||||
internal string DebugLogJailPath { get; }
|
||||
|
||||
private readonly Sandbox.ManagedFailureCallback m_failureCallback;
|
||||
private readonly Dictionary<string, PathCacheRecord> m_pathCache; // TODO: use AbsolutePath instead of string
|
||||
private readonly CancellationTokenSource m_waitToCompleteCts;
|
||||
|
@ -115,14 +113,10 @@ namespace BuildXL.Processes
|
|||
Process = process;
|
||||
ReportsFifoPath = reportsFifoPath;
|
||||
FamPath = famPath;
|
||||
DebugLogJailPath = debugLogPath;
|
||||
|
||||
m_waitToCompleteCts = new CancellationTokenSource();
|
||||
m_pathCache = new Dictionary<string, PathCacheRecord>();
|
||||
m_activeProcesses = new ConcurrentDictionary<int, byte>
|
||||
{
|
||||
[process.ProcessId] = 1
|
||||
};
|
||||
m_activeProcesses = new ConcurrentDictionary<int, byte>();
|
||||
m_activeProcessesChecker = new CancellableTimedAction(
|
||||
CheckActiveProcesses,
|
||||
intervalMs: Math.Min((int)process.ChildProcessTimeout.TotalMilliseconds, (int)ActiveProcessesCheckerInterval.TotalMilliseconds));
|
||||
|
@ -282,6 +276,7 @@ namespace BuildXL.Processes
|
|||
|
||||
internal void LogError(string message)
|
||||
{
|
||||
message = $"{message} (errno: {Marshal.GetLastWin32Error()})";
|
||||
Process.LogDebug("[ERROR]: " + message);
|
||||
m_failureCallback?.Invoke(1, message);
|
||||
}
|
||||
|
@ -415,7 +410,7 @@ namespace BuildXL.Processes
|
|||
using var readHandle = IO.Open(fifoName, IO.OpenFlags.O_RDONLY, 0);
|
||||
if (readHandle.IsInvalid)
|
||||
{
|
||||
LogError($"Opening FIFO {fifoName} for reading failed");
|
||||
LogError($"Opening FIFO {fifoName} for reading failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -435,7 +430,7 @@ namespace BuildXL.Processes
|
|||
|
||||
if (numRead < 0) // error
|
||||
{
|
||||
LogError($"Read from FIFO {ReportsFifoPath} failed with return value {numRead}");
|
||||
LogError($"Read from FIFO {ReportsFifoPath} failed with return value {numRead}.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -447,7 +442,7 @@ namespace BuildXL.Processes
|
|||
numRead = Read(readHandle, messageBytes.Instance, 0, messageLength);
|
||||
if (numRead < messageLength)
|
||||
{
|
||||
LogError($"Read from FIFO {ReportsFifoPath} failed: read only {numRead} out of {messageLength} bytes");
|
||||
LogError($"Read from FIFO {ReportsFifoPath} failed: read only {numRead} out of {messageLength} bytes.");
|
||||
messageBytes.Dispose();
|
||||
break;
|
||||
}
|
||||
|
@ -487,12 +482,7 @@ namespace BuildXL.Processes
|
|||
{
|
||||
m_failureCallback = failureCallback;
|
||||
IsInTestMode = isInTestMode;
|
||||
|
||||
#if DEBUG
|
||||
BuildXL.Native.Processes.ProcessUtilities.SetNativeConfiguration(true);
|
||||
#else
|
||||
BuildXL.Native.Processes.ProcessUtilities.SetNativeConfiguration(false);
|
||||
#endif
|
||||
BuildXL.Native.Processes.ProcessUtilities.SetNativeConfiguration(BuildXL.Processes.SandboxConnection.IsInDebugMode);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -530,34 +520,27 @@ namespace BuildXL.Processes
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(long pipId)
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(SandboxedProcessInfo info, string uniqueName)
|
||||
{
|
||||
if (!m_pipProcesses.TryGetValue(pipId, out var info))
|
||||
{
|
||||
throw new BuildXLException($"No info found for pip id {pipId}");
|
||||
}
|
||||
var detoursLibPath = CopyToRootJailIfNeeded(info.RootJailInfo?.RootJail, DetoursLibFile);
|
||||
(string fifoPath, string famPath, string debugLogPath) = GetPaths(info.RootJailInfo, uniqueName);
|
||||
|
||||
var detoursLibPath = CopyToRootJailIfNeeded(info.Process.RootJail, DetoursLibFile);
|
||||
|
||||
// TODO: the ROOT_PID env var is a temporary solution for breakway processes
|
||||
// CODESYNC: Public/Src/Sandbox/Linux/bxl_observer.hpp
|
||||
yield return ("__BUILDXL_ROOT_PID", info.Process.ProcessId.ToString());
|
||||
yield return ("__BUILDXL_FAM_PATH", info.Process.ToPathInsideRootJail(info.FamPath));
|
||||
yield return ("__BUILDXL_ROOT_PID", "1"); // CODESYNC: Public/Src/Sandbox/Linux/bxl_observer.hpp (temp solution for breakaway processes)
|
||||
yield return ("__BUILDXL_FAM_PATH", info.RootJailInfo.ToPathInsideRootJail(famPath));
|
||||
yield return ("__BUILDXL_DETOURS_PATH", detoursLibPath);
|
||||
|
||||
if (info.DebugLogJailPath != null)
|
||||
if (debugLogPath != null)
|
||||
{
|
||||
yield return ("__BUILDXL_LOG_PATH", info.DebugLogJailPath);
|
||||
yield return ("__BUILDXL_LOG_PATH", info.RootJailInfo.ToPathInsideRootJail(debugLogPath));
|
||||
}
|
||||
|
||||
if (info.Process.RootJailInfo?.DisableSandboxing != true)
|
||||
if (info.RootJailInfo?.DisableSandboxing != true)
|
||||
{
|
||||
yield return ("LD_PRELOAD", detoursLibPath + ":$LD_PRELOAD");
|
||||
yield return ("LD_PRELOAD", detoursLibPath + ":" + info.EnvironmentVariables.TryGetValue("LD_PRELOAD", string.Empty));
|
||||
}
|
||||
|
||||
if (info.Process.RootJailInfo?.DisableAuditing != true)
|
||||
if (info.RootJailInfo?.DisableAuditing != true)
|
||||
{
|
||||
yield return ("LD_AUDIT", CopyToRootJailIfNeeded(info.Process.RootJail, AuditLibFile) + ":$LD_AUDIT");
|
||||
yield return ("LD_AUDIT", CopyToRootJailIfNeeded(info.RootJailInfo?.RootJail, AuditLibFile) + ":" + info.EnvironmentVariables.TryGetValue("LD_AUDIT", string.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,20 +556,37 @@ namespace BuildXL.Processes
|
|||
return "/" + basename;
|
||||
}
|
||||
|
||||
private (string fifo, string fam, string log) GetPaths(RootJailInfo? rootJailInfo, string uniqueName)
|
||||
{
|
||||
string rootDir = rootJailInfo?.RootJail ?? Path.GetTempPath();
|
||||
string fifoPath = Path.Combine(rootDir, $"bxl_{uniqueName}.fifo");
|
||||
string famPath = Path.ChangeExtension(fifoPath, ".fam");
|
||||
string debugLogPath = IsInTestMode ? Path.ChangeExtension(fifoPath, ".log") : null;
|
||||
return (fifo: fifoPath, fam: famPath, log: debugLogPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool NotifyPipStarted(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process)
|
||||
{
|
||||
Contract.Requires(process.Started);
|
||||
if (!m_pipProcesses.TryGetValue(process.PipId, out var info))
|
||||
{
|
||||
throw new BuildXLException($"No info found for pip id {process.PipId}");
|
||||
}
|
||||
|
||||
info.AddPid(process.ProcessId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NotifyPipReady(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process)
|
||||
{
|
||||
Contract.Requires(!process.Started);
|
||||
Contract.Requires(process.PipId != 0);
|
||||
|
||||
string rootDir = process.RootJail ?? Path.GetTempPath();
|
||||
string fifoPath = Path.Combine(rootDir, $"bxl_Pip{process.PipSemiStableHash:X}.{process.ProcessId}.fifo");
|
||||
string famPath = Path.ChangeExtension(fifoPath, ".fam");
|
||||
string debugLogPath = null;
|
||||
if (IsInTestMode)
|
||||
(string fifoPath, string famPath, string debugLogPath) = GetPaths(process.RootJailInfo, process.UniqueName);
|
||||
if (debugLogPath != null)
|
||||
{
|
||||
debugLogPath = process.ToPathInsideRootJail(Path.ChangeExtension(fifoPath, ".log"));
|
||||
fam.AddPath(toAbsPath(debugLogPath), mask: FileAccessPolicy.MaskAll, values: FileAccessPolicy.AllowAll);
|
||||
fam.AddPath(toAbsPath(process.ToPathInsideRootJail(debugLogPath)), mask: FileAccessPolicy.MaskAll, values: FileAccessPolicy.AllowAll);
|
||||
}
|
||||
|
||||
// serialize FAM
|
||||
|
@ -607,10 +607,10 @@ namespace BuildXL.Processes
|
|||
process.LogDebug($"Saved FAM to '{famPath}'");
|
||||
|
||||
// create a FIFO (named pipe)
|
||||
Analysis.IgnoreResult(FileUtilities.TryDeleteFile(fifoPath, retryOnFailure: false));
|
||||
if (IO.MkFifo(fifoPath, IO.FilePermissions.S_IRWXU) != 0)
|
||||
{
|
||||
m_failureCallback?.Invoke(1, $"Creating FIFO {fifoPath} failed");
|
||||
return false;
|
||||
throw new BuildXLException($"Creating FIFO {fifoPath} failed. (errno: {Marshal.GetLastWin32Error()})");
|
||||
}
|
||||
|
||||
process.LogDebug($"Created FIFO at '{fifoPath}'");
|
||||
|
@ -623,7 +623,6 @@ namespace BuildXL.Processes
|
|||
}
|
||||
|
||||
info.Start();
|
||||
return true;
|
||||
|
||||
AbsolutePath toAbsPath(string path) => AbsolutePath.Create(process.PathTable, path);
|
||||
}
|
||||
|
|
|
@ -104,6 +104,11 @@ namespace BuildXL.Processes
|
|||
/// </summary>
|
||||
internal const string ShellExecutable = "/bin/bash"; // /bin/sh doesn't support env vars that contain funky characters (e.g., [])
|
||||
|
||||
/// <summary>
|
||||
/// Full path to the standard "env" Unix program.
|
||||
/// </summary>
|
||||
internal const string EnvExecutable = "/usr/bin/env";
|
||||
|
||||
/// <summary>
|
||||
/// Optional configuration for running this process in a root jail.
|
||||
/// </summary>
|
||||
|
@ -116,31 +121,7 @@ namespace BuildXL.Processes
|
|||
|
||||
private const double NanosecondsToMillisecondsFactor = 1000000d;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="RootJail"/> is set:
|
||||
/// if <paramref name="path"/> is relative to <see cref="RootJail"/> returns an absolute path which
|
||||
/// when accessed from the root jail resolves to path at location <paramref name="path"/>; otherwise throws.
|
||||
///
|
||||
/// If <see cref="RootJail"/> is null:
|
||||
/// returns <paramref name="path"/>
|
||||
/// </summary>
|
||||
internal string ToPathInsideRootJail(string path)
|
||||
{
|
||||
if (RootJail == null)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!path.StartsWith(RootJail))
|
||||
{
|
||||
ThrowBuildXLException($"Path '{path}' is not relative to root jail: '{RootJail}'");
|
||||
}
|
||||
|
||||
var jailRelativePath = path.Substring(RootJail.Length);
|
||||
return jailRelativePath[0] == '/'
|
||||
? jailRelativePath
|
||||
: "/" + jailRelativePath;
|
||||
}
|
||||
internal string ToPathInsideRootJail(string path) => RootJailInfo.ToPathInsideRootJail(path);
|
||||
|
||||
/// <nodoc />
|
||||
public SandboxedProcessUnix(SandboxedProcessInfo info, bool ignoreReportedAccesses = false)
|
||||
|
@ -189,7 +170,8 @@ namespace BuildXL.Processes
|
|||
|
||||
m_pendingReports = new ActionBlock<AccessReport>(HandleAccessReport, executionOptions);
|
||||
|
||||
// install a 'ProcessStarted' handler that informs the sandbox of the newly started process
|
||||
// install 'ProcessReady' and 'ProcessStarted' handlers to inform the sandbox
|
||||
ProcessReady += () => SandboxConnection.NotifyPipReady(info.LoggingContext, info.FileAccessManifest, this);
|
||||
ProcessStarted += (pid) => OnProcessStartedAsync(info).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
|
@ -219,13 +201,12 @@ namespace BuildXL.Processes
|
|||
}
|
||||
}
|
||||
|
||||
private bool NeedsShellWrapping() => OperatingSystemHelper.IsMacOS;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override System.Diagnostics.Process CreateProcess(SandboxedProcessInfo info)
|
||||
{
|
||||
var process = base.CreateProcess(info);
|
||||
|
||||
process.StartInfo.FileName = ShellExecutable;
|
||||
process.StartInfo.Arguments = string.Empty;
|
||||
process.StartInfo.RedirectStandardInput = true;
|
||||
if (info.RootJailInfo?.RootJail != null)
|
||||
{
|
||||
|
@ -233,6 +214,72 @@ namespace BuildXL.Processes
|
|||
process.StartInfo.WorkingDirectory = "/";
|
||||
}
|
||||
|
||||
if (NeedsShellWrapping())
|
||||
{
|
||||
// shell script streamed to stdin
|
||||
process.StartInfo.FileName = ShellExecutable;
|
||||
process.StartInfo.Arguments = string.Empty;
|
||||
}
|
||||
else if (info.RootJailInfo == null)
|
||||
{
|
||||
foreach (var kvp in AdditionalEnvVars(info))
|
||||
{
|
||||
process.StartInfo.EnvironmentVariables[kvp.Item1] = kvp.Item2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETCOREAPP
|
||||
var rootJailInfo = info.RootJailInfo.Value;
|
||||
// top-level process is the root jail program
|
||||
process.StartInfo.FileName = rootJailInfo.RootJailProgram.program;
|
||||
process.StartInfo.Arguments = string.Empty;
|
||||
process.StartInfo.ArgumentList.Clear();
|
||||
// root jail arguments
|
||||
foreach (string rootJailArg in rootJailInfo.RootJailProgram.args)
|
||||
{
|
||||
process.StartInfo.ArgumentList.Add(rootJailArg);
|
||||
}
|
||||
if (rootJailInfo.UserId != null && rootJailInfo.GroupId != null)
|
||||
{
|
||||
process.StartInfo.ArgumentList.Add($"--userspec={rootJailInfo.UserId}:{rootJailInfo.GroupId}");
|
||||
}
|
||||
// root jail directory
|
||||
process.StartInfo.ArgumentList.Add(rootJailInfo.RootJail);
|
||||
// inside the jail, run "env" to change into user-specified directory as well as to set environment variables before running user-specified program
|
||||
process.StartInfo.ArgumentList.Add(EnvExecutable);
|
||||
// change directory into what the user specified
|
||||
process.StartInfo.ArgumentList.Add("-C");
|
||||
process.StartInfo.ArgumentList.Add(info.WorkingDirectory);
|
||||
// propagate environment variables (because root jail program won't do it)
|
||||
process.StartInfo.ArgumentList.Add("-i");
|
||||
foreach (var kvp in process.StartInfo.Environment.Select(kvp => (kvp.Key, kvp.Value)).Concat(AdditionalEnvVars(info)))
|
||||
{
|
||||
process.StartInfo.ArgumentList.Add($"{kvp.Item1}={kvp.Item2}");
|
||||
}
|
||||
// finally add the original executable and its arguments
|
||||
process.StartInfo.ArgumentList.Add(info.FileName);
|
||||
foreach (var arg in CommandLineEscaping.SplitArguments(info.Arguments))
|
||||
{
|
||||
process.StartInfo.ArgumentList.Add(arg.Value.ToString());
|
||||
}
|
||||
#else
|
||||
throw new ArgumentException($"Running {nameof(SandboxedProcessUnix)} in a non .NET Core environment should not be possible");
|
||||
#endif
|
||||
}
|
||||
|
||||
// In any case, allow read access to the process file.
|
||||
// When executed using external tool, the manifest tree has been sealed, and cannot be modified.
|
||||
// We take care of adding this path in the manifest in SandboxedProcessPipExecutor.cs;
|
||||
// see AddUnixSpecificSandcboxedProcessFileAccessPolicies
|
||||
if (!info.FileAccessManifest.IsManifestTreeBlockSealed)
|
||||
{
|
||||
info.FileAccessManifest.AddPath(
|
||||
AbsolutePath.Create(PathTable, process.StartInfo.FileName),
|
||||
mask: FileAccessPolicy.MaskNothing,
|
||||
values: FileAccessPolicy.AllowReadAlways);
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
|
@ -246,23 +293,16 @@ namespace BuildXL.Processes
|
|||
/// </summary>
|
||||
private async Task OnProcessStartedAsync(SandboxedProcessInfo info)
|
||||
{
|
||||
// Generate "Process Created" report because the rest of the system expects to see it before any other file access reports
|
||||
//
|
||||
// IMPORTANT: do this before notifying sandbox kernel extension, because otherwise it can happen that a report
|
||||
// from the extension is received before the "process created" report is handled, causing
|
||||
// a "Should see a process creation before its accesses" assertion exception.
|
||||
ReportProcessCreated();
|
||||
|
||||
// Allow read access for /bin/sh
|
||||
// When executed using external tool, the manifest tree has been sealed, and cannot be modified.
|
||||
// We take care of adding this path in the manifest in SandboxedProcessPipExecutor.cs;
|
||||
// see AddUnixSpecificSandcboxedProcessFileAccessPolicies
|
||||
if (!info.FileAccessManifest.IsManifestTreeBlockSealed)
|
||||
if (NeedsShellWrapping())
|
||||
{
|
||||
info.FileAccessManifest.AddPath(
|
||||
AbsolutePath.Create(PathTable, Process.StartInfo.FileName),
|
||||
mask: FileAccessPolicy.MaskNothing,
|
||||
values: FileAccessPolicy.AllowReadAlways);
|
||||
// The shell wrapper script started, so generate "Process Created" report before the actual pip process starts
|
||||
// (the rest of the system expects to see it before any other file access reports).
|
||||
//
|
||||
// IMPORTANT (macOS-only):
|
||||
// do this before notifying sandbox kernel extension, because otherwise it can happen that a report
|
||||
// from the extension is received before the "process created" report is handled, causing
|
||||
// a "Should see a process creation before its accesses" assertion exception.
|
||||
ReportProcessCreated();
|
||||
}
|
||||
|
||||
if (OperatingSystemHelper.IsLinuxOS)
|
||||
|
@ -301,7 +341,6 @@ namespace BuildXL.Processes
|
|||
|
||||
private string DetoursFile => Path.Combine(Path.GetDirectoryName(AssemblyHelper.GetThisProgramExeLocation()), "libBuildXLDetours.dylib");
|
||||
private const string DetoursEnvVar = "DYLD_INSERT_LIBRARIES";
|
||||
private const string EofDelim = "__EOF__";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<ReportedProcess> GetSurvivingChildProcesses()
|
||||
|
@ -400,7 +439,7 @@ namespace BuildXL.Processes
|
|||
/// </summary>
|
||||
private IReadOnlyList<ReportedProcess> GetCurrentlyActiveChildProcesses()
|
||||
{
|
||||
return m_reports.GetActiveProcesses().Where(p => p.ProcessId != ProcessId).ToList();
|
||||
return m_reports.GetActiveProcesses().Where(p => p.ProcessId > 0 && p.ProcessId != ProcessId).ToList();
|
||||
}
|
||||
|
||||
private void KillAllChildProcesses()
|
||||
|
@ -444,7 +483,7 @@ namespace BuildXL.Processes
|
|||
|
||||
private static string EnsureQuoted(string cmdLineArgs)
|
||||
{
|
||||
#if NET_CORE
|
||||
#if NETCOREAPP
|
||||
if (cmdLineArgs == null)
|
||||
{
|
||||
return null;
|
||||
|
@ -472,9 +511,29 @@ namespace BuildXL.Processes
|
|||
#endif
|
||||
}
|
||||
|
||||
// TODO: instead of generating a bash script (and be exposed to all kinds of injection attacks) we should write a wrapper runner program
|
||||
private async Task FeedStdInAsync(SandboxedProcessInfo info, [CanBeNull] string processStdinFileName)
|
||||
{
|
||||
Contract.Requires(info.RootJailInfo == null || !NeedsShellWrapping(), $"Cannot run root jail on this OS");
|
||||
|
||||
// if no shell wrapping is needed, only feed processStdinFileName (if specified)
|
||||
if (!NeedsShellWrapping())
|
||||
{
|
||||
if (processStdinFileName != null)
|
||||
{
|
||||
string stdinContent =
|
||||
#if NETCOREAPP
|
||||
await File.ReadAllTextAsync(processStdinFileName);
|
||||
#else
|
||||
File.ReadAllText(processStdinFileName);
|
||||
#endif
|
||||
await Process.StandardInput.WriteAsync(stdinContent);
|
||||
}
|
||||
|
||||
Process.StandardInput.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: instead of generating a bash script (and be exposed to all kinds of injection attacks) we should write a wrapper runner program
|
||||
string redirectedStdin = processStdinFileName != null ? $" < {ToPathInsideRootJail(processStdinFileName)}" : string.Empty;
|
||||
|
||||
// this additional round of escaping is needed because we are flushing the arguments to a shell script
|
||||
|
@ -489,33 +548,17 @@ namespace BuildXL.Processes
|
|||
|
||||
lines.Add("set -e");
|
||||
|
||||
if (info.RootJailInfo != null)
|
||||
{
|
||||
// A process executed in a chroot jail does not automatically inherit the environment from the parent process,
|
||||
// so we must export the vars before entering chroot and then source them once inside.
|
||||
const string BxlEnvFile = "bxl_pip_env.sh";
|
||||
lines.Add($"export -p > '{info.RootJailInfo.Value.RootJail}/{BxlEnvFile}'");
|
||||
lines.Add($"exec {info.RootJailInfo.Value.RootJailProgram} --userspec={userIdExpr()}:{groupIdExpr()} '{info.RootJailInfo.Value.RootJail}' {ShellExecutable} <<'{EofDelim}'");
|
||||
lines.Add("set -e");
|
||||
lines.Add($". /{BxlEnvFile}");
|
||||
lines.Add($"cd \"{info.WorkingDirectory}\"");
|
||||
}
|
||||
|
||||
if (info.SandboxConnection.Kind == SandboxKind.MacOsHybrid || info.SandboxConnection.Kind == SandboxKind.MacOsDetours)
|
||||
{
|
||||
lines.Add($"export {DetoursEnvVar}={DetoursFile}");
|
||||
}
|
||||
|
||||
foreach (var envKvp in info.SandboxConnection.AdditionalEnvVarsToSet(info.FileAccessManifest.PipId))
|
||||
foreach (var envKvp in info.SandboxConnection.AdditionalEnvVarsToSet(info, UniqueName))
|
||||
{
|
||||
lines.Add($"export {envKvp.Item1}={envKvp.Item2}");
|
||||
}
|
||||
|
||||
lines.Add($"exec {cmdLine}");
|
||||
if (info.RootJailInfo != null)
|
||||
{
|
||||
lines.Add(EofDelim);
|
||||
}
|
||||
|
||||
SetExecutePermissionIfNeeded(info.FileName, throwIfNotFound: false);
|
||||
foreach (string line in lines)
|
||||
|
@ -524,11 +567,16 @@ namespace BuildXL.Processes
|
|||
}
|
||||
|
||||
LogDebug("Done feeding stdin:" + Environment.NewLine + string.Join(Environment.NewLine, lines));
|
||||
|
||||
Process.StandardInput.Close();
|
||||
}
|
||||
|
||||
string userIdExpr() => info.RootJailInfo?.UserId?.ToString() ?? "$(id -u)";
|
||||
string groupIdExpr() => info.RootJailInfo?.GroupId?.ToString() ?? "$(id -u)";
|
||||
private IEnumerable<(string, string)> AdditionalEnvVars(SandboxedProcessInfo info)
|
||||
{
|
||||
return info.SandboxConnection
|
||||
.AdditionalEnvVarsToSet(info, UniqueName)
|
||||
.Concat(info.SandboxConnection.Kind == SandboxKind.MacOsHybrid || info.SandboxConnection.Kind == SandboxKind.MacOsDetours
|
||||
? new[] { (DetoursEnvVar, DetoursFile) }
|
||||
: Array.Empty<(string, string)>());
|
||||
}
|
||||
|
||||
internal override void FeedStdErr(SandboxedProcessOutputBuilder builder, string line)
|
||||
|
@ -700,7 +748,7 @@ namespace BuildXL.Processes
|
|||
/// </summary>
|
||||
internal void LogDebug(string message)
|
||||
{
|
||||
if (ShouldReportFileAccesses)
|
||||
if (BuildXL.Processes.SandboxConnection.IsInDebugMode || ShouldReportFileAccesses)
|
||||
{
|
||||
LogProcessState(message);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.ContractsLight;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildXL.Interop;
|
||||
using BuildXL.Utilities;
|
||||
|
@ -64,6 +65,11 @@ namespace BuildXL.Processes
|
|||
|
||||
protected event ProcessStartedHandler ProcessStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Raised right before the process is started.
|
||||
/// </summary>
|
||||
protected event Action ProcessReady;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the process has been force killed during execution.
|
||||
/// </summary>
|
||||
|
@ -89,6 +95,20 @@ namespace BuildXL.Processes
|
|||
/// </summary>
|
||||
public long PipSemiStableHash { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique name for this process during this build. May change build over build, but need not be unique across different builds.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Normally, PipId (or PipSemiStableHash) will meet all these criteria; unit tests are an exception, which is why this
|
||||
/// property is added.
|
||||
///
|
||||
/// For example, this property could be used to create auxiliary per-pip files that will not clash with each other
|
||||
/// (e.g., Linux sandbox needs to create a FIFO file per pip).
|
||||
/// </remarks>
|
||||
public string UniqueName { get; }
|
||||
|
||||
private int m_uniqueNameCounter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the path table from the supplied <see cref="SandboxedProcessInfo"/>.
|
||||
/// </summary>
|
||||
|
@ -126,6 +146,7 @@ namespace BuildXL.Processes
|
|||
TimeoutDumpDirectory = info.TimeoutDumpDirectory;
|
||||
ShouldReportFileAccesses = info.FileAccessManifest?.ReportFileAccesses == true;
|
||||
DetoursListener = info.DetoursEventListener;
|
||||
UniqueName = $"Pip{info.FileAccessManifest.PipId:X}.{Interlocked.Increment(ref m_uniqueNameCounter)}";
|
||||
|
||||
info.Timeout ??= s_defaultProcessTimeout;
|
||||
|
||||
|
@ -177,6 +198,7 @@ namespace BuildXL.Processes
|
|||
{
|
||||
Contract.Requires(!Started, "Process was already started. Cannot start process more than once.");
|
||||
|
||||
ProcessReady?.Invoke();
|
||||
Started = true;
|
||||
m_processExecutor.Start();
|
||||
ProcessStarted?.Invoke(ProcessId);
|
||||
|
|
|
@ -50,13 +50,9 @@ namespace Test.BuildXL.Processes
|
|||
public bool IsInTestMode => true;
|
||||
|
||||
public bool NotifyUsage(uint cpuUsage, uint availableRamMB) { return true; }
|
||||
|
||||
public void NotifyPipReady(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process) {}
|
||||
public bool NotifyPipStarted(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process) { return true; }
|
||||
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(long pipId)
|
||||
{
|
||||
return Enumerable.Empty<(string, string)>();
|
||||
}
|
||||
public IEnumerable<(string, string)> AdditionalEnvVarsToSet(SandboxedProcessInfo info, string uniqueName) { return Enumerable.Empty<(string, string)>(); }
|
||||
|
||||
public void NotifyPipProcessTerminated(long pipId, int processId) { ProcessTerminated?.Invoke(pipId, processId); }
|
||||
|
||||
|
|
|
@ -19,12 +19,20 @@ BxlObserver* BxlObserver::GetInstance()
|
|||
|
||||
BxlObserver::BxlObserver()
|
||||
{
|
||||
char pidStr[20] = {0};
|
||||
|
||||
empty_str_ = "";
|
||||
real_readlink("/proc/self/exe", progFullPath_, PATH_MAX);
|
||||
|
||||
disposed_ = false;
|
||||
const char *rootPidStr = getenv(BxlEnvRootPid);
|
||||
rootPid_ = is_null_or_empty(rootPidStr) ? -1 : atoi(rootPidStr);
|
||||
disposed_ = false;
|
||||
// value of "1" -> special case, set by BuildXL for the root process
|
||||
if (rootPid_ == 1) {
|
||||
rootPid_ = getpid();
|
||||
sprintf(pidStr, "%d", rootPid_);
|
||||
setenv(BxlEnvRootPid, pidStr, /* replace */ 1);
|
||||
}
|
||||
|
||||
InitLogFile();
|
||||
InitFam();
|
||||
|
|
|
@ -2,21 +2,14 @@
|
|||
|
||||
readonly __dir=$(cd `dirname ${BASH_SOURCE[0]}` && pwd)
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
function onExit {
|
||||
popd > /dev/null 2>&1
|
||||
}
|
||||
set -euo pipefail
|
||||
|
||||
readonly version="$1"
|
||||
readonly pkgName="runtime.linux-x64.BuildXL.${version}"
|
||||
|
||||
pushd "${__dir}" > /dev/null 2>&1
|
||||
trap onExit EXIT
|
||||
|
||||
cd "${__dir}"
|
||||
make debug release
|
||||
make cleanall
|
||||
bash build-manylinux.sh
|
||||
|
||||
cd bin
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace Test.BuildXL.TestUtilities.Xunit
|
|||
|
||||
private static void FailureCallback(int status, string description)
|
||||
{
|
||||
XAssert.Fail($"Kernel extension failed. Status: {status}. Description: {description}");
|
||||
XAssert.Fail($"Sandbox failed. Status: {status}. Description: {description}");
|
||||
}
|
||||
|
||||
private static SandboxKind ReadSandboxKindFromEnvVars()
|
||||
|
|
|
@ -126,6 +126,18 @@ namespace BuildXL.Utilities
|
|||
bool ContainsKey([NotNull]string key);
|
||||
}
|
||||
|
||||
// ==================================================================================
|
||||
// == IBuildParameters Extension Methods
|
||||
// ==================================================================================
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the value of a parameter named <paramref name="key"/>, if one is found; otherwise returns <paramref name="defaultValue"/>.
|
||||
/// </summary>
|
||||
public static string TryGetValue(this IBuildParameters @this, string key, string defaultValue)
|
||||
{
|
||||
return @this != null && @this.ContainsKey(key) ? @this[key] : defaultValue;
|
||||
}
|
||||
|
||||
// ==================================================================================
|
||||
// == Public Factory Methods
|
||||
// ==================================================================================
|
||||
|
|
|
@ -4605,7 +4605,7 @@
|
|||
"Type": "NuGet",
|
||||
"NuGet": {
|
||||
"Name": "runtime.linux-x64.BuildXL",
|
||||
"Version": "0.0.59"
|
||||
"Version": "0.0.65"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -412,7 +412,7 @@ config({
|
|||
// Runtime dependencies for Linux
|
||||
{
|
||||
id: "runtime.linux-x64.BuildXL",
|
||||
version: "0.0.59",
|
||||
version: "0.0.65",
|
||||
osSkip: importFile(f`config.microsoftInternal.dsc`).isMicrosoftInternal
|
||||
? []
|
||||
: [ "win", "macOS", "unix" ]
|
||||
|
|
Загрузка…
Ссылка в новой задаче