/// <summary> /// Starts listening for reports from the kernel extension on a dedicated thread /// </summary> private void StartReceivingAccessReports(ulong address, uint port) { Sandbox.AccessReportCallback callback = (Sandbox.AccessReport report, int code) => { if (code != Sandbox.ReportQueueSuccessCode) { var message = "Kernel extension report queue failed with error: " + code; throw new BuildXLException(message, ExceptionRootCause.MissingRuntimeDependency); } // Update last received timestamp Volatile.Write(ref m_lastReportReceivedTimestampTicks, DateTime.UtcNow.Ticks); // Remember the latest enqueue time Volatile.Write(ref m_reportQueueLastEnqueueTime, report.Statistics.EnqueueTime); // The only way it can happen that no process is found for 'report.PipId' is when that pip is // explicitly terminated (e.g., because it timed out or Ctrl-c was pressed) if (m_pipProcesses.TryGetValue(report.PipId, out var process)) { // if the process is found, its ProcessId must match the RootPid of the report. if (process.ProcessId != report.RootPid) { m_failureCallback?.Invoke(-1, $"Unexpected PID for Pip {report.PipId:X}: Expected {process.ProcessId}, Reported {report.RootPid}"); } else { process.PostAccessReport(report); } } }; Sandbox.ListenForFileAccessReports(callback, Marshal.SizeOf <Sandbox.AccessReport>(), address, port); }
/// <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); }
/// <summary> /// Initializes the sandbox kernel extension connection manager, setting up the kernel extension connection and workers that drain the /// kernel event queue and report file accesses /// </summary> public KextConnection(Config config = null, bool skipDisposingForTests = false) { m_reportQueueLastEnqueueTime = 0; m_kextConnectionInfo = new Sandbox.KextConnectionInfo() { Error = Sandbox.KextSuccess }; m_sharedMemoryInfo = new Sandbox.KextSharedMemoryInfo() { Error = Sandbox.KextSuccess }; MeasureCpuTimes = config.MeasureCpuTimes; IsInTestMode = skipDisposingForTests; // initialize kext connection Sandbox.InitializeKextConnection(ref m_kextConnectionInfo); if (m_kextConnectionInfo.Error != Sandbox.KextSuccess) { throw new BuildXLException($@"Unable to connect to sandbox kernel extension (Code: {m_kextConnectionInfo.Error}) - make sure it is loaded and retry! {KextInstallHelper}"); } // check and set if the sandbox is running in debug configuration bool isDebug = false; Sandbox.CheckForDebugMode(ref isDebug, m_kextConnectionInfo); ProcessUtilities.SetNativeConfiguration(isDebug); #if DEBUG if (!ProcessUtilities.IsNativeInDebugConfiguration()) #else if (ProcessUtilities.IsNativeInDebugConfiguration()) #endif { throw new BuildXLException($"Sandbox kernel extension build flavor missmatch - the extension must match the engine build flavor, Debug != Release. {KextInstallHelper}"); } // check if the sandbox version matches var stringBufferLength = MaxVersionNumberLength + 1; var version = new StringBuilder(stringBufferLength); Sandbox.KextVersionString(version, stringBufferLength); if (!RequiredKextVersionNumber.Equals(version.ToString().TrimEnd('\0'))) { throw new BuildXLException($"Sandbox kernel extension version mismatch, the loaded kernel extension version '{version}' does not match the required version '{RequiredKextVersionNumber}'. {KextInstallHelper}"); } if (config?.KextConfig != null) { if (!Sandbox.Configure(config.KextConfig.Value, m_kextConnectionInfo)) { throw new BuildXLException($"Unable to configure sandbox kernel extension"); } } m_failureCallback = config?.FailureCallback; // Initialize the shared memory region Sandbox.InitializeKextSharedMemory(m_kextConnectionInfo, ref m_sharedMemoryInfo); if (m_sharedMemoryInfo.Error != Sandbox.KextSuccess) { throw new BuildXLException($"Unable to allocate shared memory region for worker (Code:{m_sharedMemoryInfo.Error})"); } if (!SetFailureNotificationHandler()) { throw new BuildXLException($"Unable to set sandbox kernel extension failure notification callback handler"); } m_workerThread = new Thread(() => StartReceivingAccessReports(m_sharedMemoryInfo.Address, m_sharedMemoryInfo.Port)); m_workerThread.IsBackground = true; m_workerThread.Priority = ThreadPriority.Highest; m_workerThread.Start(); unsafe bool SetFailureNotificationHandler() { return(Sandbox.SetFailureNotificationHandler(KextFailureCallback, m_kextConnectionInfo)); void KextFailureCallback(void *refCon, int status) { m_failureCallback?.Invoke(status, $"Unrecoverable kernel extension failure happened - try reloading the kernel extension or restart your system. {KextInstallHelper}"); } } }
internal void LogError(string message) { Process.LogDebug("[ERROR]: " + message); m_failureCallback?.Invoke(1, message); }
private unsafe void KextFailureCallback(void *refCon, int status) { m_failureCallback?.Invoke(status, $"Unrecoverable kernel extension failure happened - try reloading the kernel extension or restart your system. {KextInstallHelper}"); }