/// <inheritdoc /> public bool NotifyPipStarted(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) { debugLogPath = process.ToPathInsideRootJail(Path.ChangeExtension(fifoPath, ".log")); fam.AddPath(toAbsPath(debugLogPath), mask: FileAccessPolicy.MaskAll, values: FileAccessPolicy.AllowAll); } // serialize FAM using (var wrapper = Pools.MemoryStreamPool.GetInstance()) { var debugFlags = true; ArraySegment <byte> manifestBytes = fam.GetPayloadBytes( loggingContext, new FileAccessSetup { DllNameX64 = string.Empty, DllNameX86 = string.Empty, ReportPath = process.ToPathInsideRootJail(fifoPath) }, wrapper.Instance, timeoutMins: 10, // don't care debugFlagsMatch: ref debugFlags); Contract.Assert(manifestBytes.Offset == 0); File.WriteAllBytes(famPath, manifestBytes.ToArray()); } process.LogDebug($"Saved FAM to '{famPath}'"); // create a FIFO (named pipe) if (IO.MkFifo(fifoPath, IO.FilePermissions.S_IRWXU) != 0) { m_failureCallback?.Invoke(1, $"Creating FIFO {fifoPath} failed"); return(false); } process.LogDebug($"Created FIFO at '{fifoPath}'"); // create and save info for this pip var info = new Info(m_failureCallback, process, fifoPath, famPath, debugLogPath); if (!m_pipProcesses.TryAdd(process.PipId, info)) { throw new BuildXLException($"Process with PidId {process.PipId} already exists"); } info.Start(); return(true); AbsolutePath toAbsPath(string path) => AbsolutePath.Create(process.PathTable, path); }
/// <inheritdoc /> public bool NotifyPipStarted(LoggingContext loggingContext, FileAccessManifest fam, SandboxedProcessUnix process) { Contract.Requires(process.Started); Contract.Requires(fam.PipId != 0); if (!m_pipProcesses.TryAdd(fam.PipId, process)) { throw new BuildXLException($"Process with PidId {fam.PipId} already exists"); } var setup = new FileAccessSetup() { DllNameX64 = string.Empty, DllNameX86 = string.Empty, ReportPath = process.ExecutableAbsolutePath, // piggybacking on ReportPath to pass full executable path }; using (var wrapper = Pools.MemoryStreamPool.GetInstance()) { var debugFlags = true; ArraySegment <byte> manifestBytes = fam.GetPayloadBytes( loggingContext, setup, wrapper.Instance, timeoutMins: 10, // don't care because on Mac we don't kill the process from the Kext once it times out debugFlagsMatch: ref debugFlags); Contract.Assert(manifestBytes.Offset == 0); var result = Sandbox.SendPipStarted( processId: process.ProcessId, pipId: fam.PipId, famBytes: manifestBytes.Array, famBytesLength: manifestBytes.Count, type: Sandbox.ConnectionType.Kext, info: ref m_kextConnectionInfo); return(result); } }
/// <inheritdoc /> public bool NotifyKextPipStarted(FileAccessManifest fam, SandboxedProcessMacKext process) { Contract.Requires(process.Started); m_pipProcesses[fam.PipId] = process; var setup = new FileAccessSetup() { DllNameX64 = string.Empty, DllNameX86 = string.Empty, ReportPath = string.Empty }; using (var wrapper = Pools.MemoryStreamPool.GetInstance()) { var debugFlags = true; ArraySegment <byte> manifestBytes = fam.GetPayloadBytes( setup, wrapper.Instance, timeoutMins: 10, // don't care because on Mac we don't kill the process from the Kext once it times out debugFlagsMatch: ref debugFlags); var payloadHandle = GCHandle.Alloc(manifestBytes.Array, GCHandleType.Pinned); var result = Sandbox.SendPipStarted( processId: process.ProcessId, pipId: fam.PipId, famBytes: IntPtr.Add(payloadHandle.AddrOfPinnedObject(), manifestBytes.Offset), famBytesLength: manifestBytes.Count); if (payloadHandle.IsAllocated) { payloadHandle.Free(); } return(result); } }
public void Start() { Contract.Assume(!m_processStarted); Encoding reportEncoding = Encoding.Unicode; SafeFileHandle childHandle = null; DetouredProcess detouredProcess = m_detouredProcess; using (m_reportReaderSemaphore.AcquireSemaphore()) { SafeFileHandle reportHandle; try { Pipes.CreateInheritablePipe( Pipes.PipeInheritance.InheritWrite, Pipes.PipeFlags.ReadSideAsync, readHandle: out reportHandle, writeHandle: out childHandle); var setup = new FileAccessSetup { ReportPath = "#" + childHandle.DangerousGetHandle().ToInt64(), DllNameX64 = s_binaryPaths.DllNameX64, DllNameX86 = s_binaryPaths.DllNameX86, }; bool debugFlagsMatch = true; ArraySegment <byte> manifestBytes = new ArraySegment <byte>(); if (m_fileAccessManifest != null) { manifestBytes = m_fileAccessManifest.GetPayloadBytes(setup, FileAccessManifestStream, m_timeoutMins, ref debugFlagsMatch); } if (!debugFlagsMatch) { throw new BuildXLException("Mismatching build type for BuildXL and DetoursServices.dll."); } m_standardInputTcs = TaskSourceSlim.Create <bool>(); detouredProcess.Start( s_payloadGuid, manifestBytes, childHandle, s_binaryPaths.DllNameX64, s_binaryPaths.DllNameX86); // At this point, we believe calling 'kill' will result in an eventual callback for job teardown. // This knowledge is significant for ensuring correct cleanup if we did vs. did not start a process; // if started, we expect teardown to happen eventually and clean everything up. m_processStarted = true; ProcessId = detouredProcess.GetProcessId(); m_processIdListener?.Invoke(ProcessId); } finally { // release memory m_fileAccessManifest = null; // Note that in the success path, childHandle should already be closed (by Start). if (childHandle != null && !childHandle.IsInvalid) { childHandle.Dispose(); } } var reportFile = AsyncFileFactory.CreateAsyncFile( reportHandle, FileDesiredAccess.GenericRead, ownsHandle: true, kind: FileKind.Pipe); StreamDataReceived reportLineReceivedCallback = m_reports == null ? (StreamDataReceived)null : ReportLineReceived; m_reportReader = new AsyncPipeReader(reportFile, reportLineReceivedCallback, reportEncoding, m_bufferSize); m_reportReader.BeginReadLine(); } // don't wait, we want feeding in of standard input to happen asynchronously Analysis.IgnoreResult(FeedStandardInputAsync(detouredProcess, m_standardInputReader, m_standardInputTcs)); }
public void Start() { Contract.Assume(!m_processStarted); Encoding reportEncoding = Encoding.Unicode; SafeFileHandle childHandle = null; DetouredProcess detouredProcess = m_detouredProcess; bool useNonDefaultPipeReader = PipeReaderFactory.GetKind() != PipeReaderFactory.Kind.Default; using (m_reportReaderSemaphore.AcquireSemaphore()) { NamedPipeServerStream pipeStream = null; SafeFileHandle reportHandle = null; try { if (useNonDefaultPipeReader) { pipeStream = Pipes.CreateNamedPipeServerStream( PipeDirection.In, PipeOptions.Asynchronous, PipeOptions.None, out childHandle); } else { Pipes.CreateInheritablePipe( Pipes.PipeInheritance.InheritWrite, Pipes.PipeFlags.ReadSideAsync, readHandle: out reportHandle, writeHandle: out childHandle); } var setup = new FileAccessSetup { ReportPath = "#" + childHandle.DangerousGetHandle().ToInt64(), DllNameX64 = s_binaryPaths.DllNameX64, DllNameX86 = s_binaryPaths.DllNameX86, }; bool debugFlagsMatch = true; ArraySegment <byte> manifestBytes = new ArraySegment <byte>(); if (m_fileAccessManifest != null) { manifestBytes = m_fileAccessManifest.GetPayloadBytes(m_loggingContext, setup, FileAccessManifestStream, m_timeoutMins, ref debugFlagsMatch); } if (!debugFlagsMatch) { throw new BuildXLException("Mismatching build type for BuildXL and DetoursServices.dll."); } m_standardInputTcs = TaskSourceSlim.Create <bool>(); detouredProcess.Start( s_payloadGuid, manifestBytes, childHandle, s_binaryPaths.DllNameX64, s_binaryPaths.DllNameX86); // At this point, we believe calling 'kill' will result in an eventual callback for job teardown. // This knowledge is significant for ensuring correct cleanup if we did vs. did not start a process; // if started, we expect teardown to happen eventually and clean everything up. m_processStarted = true; ProcessId = detouredProcess.GetProcessId(); } catch (AccessViolationException) { int ramPercent = 0, availableRamMb = 0, availablePageFileMb = 0, totalPageFileMb = 0; MEMORYSTATUSEX memoryStatusEx = new MEMORYSTATUSEX(); if (GlobalMemoryStatusEx(memoryStatusEx)) { ramPercent = (int)memoryStatusEx.dwMemoryLoad; availableRamMb = new FileSize(memoryStatusEx.ullAvailPhys).MB; availablePageFileMb = new FileSize(memoryStatusEx.ullAvailPageFile).MB; totalPageFileMb = new FileSize(memoryStatusEx.ullTotalPageFile).MB; } string memUsage = $"RamPercent: {ramPercent}, AvailableRamMb: {availableRamMb}, AvailablePageFileMb: {availablePageFileMb}, TotalPageFileMb: {totalPageFileMb}"; Native.Tracing.Logger.Log.DetouredProcessAccessViolationException(m_loggingContext, (m_reports?.PipDescription ?? "") + " - " + memUsage); throw; } finally { // release memory m_fileAccessManifest = null; // Note that in the success path, childHandle should already be closed (by Start). if (childHandle != null && !childHandle.IsInvalid) { childHandle.Dispose(); } } StreamDataReceived reportLineReceivedCallback = m_reports == null ? null : ReportLineReceived; if (useNonDefaultPipeReader) { m_reportReader = PipeReaderFactory.CreateNonDefaultPipeReader( pipeStream, message => reportLineReceivedCallback(message), reportEncoding, m_bufferSize); } else { var reportFile = AsyncFileFactory.CreateAsyncFile( reportHandle, FileDesiredAccess.GenericRead, ownsHandle: true, kind: FileKind.Pipe); m_reportReader = new AsyncPipeReader( reportFile, reportLineReceivedCallback, reportEncoding, m_bufferSize, numOfRetriesOnCancel: m_numRetriesPipeReadOnCancel, debugPipeReporter: new AsyncPipeReader.DebugReporter(errorMsg => DebugPipeConnection($"ReportReader: {errorMsg}"))); } m_reportReader.BeginReadLine(); } // don't wait, we want feeding in of standard input to happen asynchronously Analysis.IgnoreResult(FeedStandardInputAsync(detouredProcess, m_standardInputReader, m_standardInputTcs)); }