/// <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);
        }
Esempio n. 2
0
        /// <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}");
                }
            }
        }
Esempio n. 4
0
 internal void LogError(string message)
 {
     Process.LogDebug("[ERROR]: " + message);
     m_failureCallback?.Invoke(1, message);
 }
Esempio n. 5
0
 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}");
 }