防止 IOKit 驱动程序在 "kextunload" 中关闭,除非某些客户端保持连接

Prevent IOKit driver down in "kextunload" unless some clients remains connected

我有 IOKit 基础 Driver/Deamons 项目,具有一对多关系(意味着多个客户端连接到单个驱动程序)。

核心驱动是一个派生自IOService的IOKit对象,也是派生自IOUserClient的驱动客户端提供者。这些是用户-space 客户的代理(每个用户-space 客户一个)

我的目标是防止驱动程序在具有 root 权限的 kextunload 命令的情况下卸载,这样只有当最后一个客户端断开连接时(进程终止或调用 IOServiceClose),驱动程序才会自动关闭(无需手动 kextunload

为了检查我需要执行哪些 IOKit 命令 disable/postphone,我用相关的打印消息包装了 basid IOService 回调,并观察了 [=52= 场景中的日志] kextunload 其中连接了 2 个客户端:

kernel: (driver) virtual bool com_osxkernel_driver::terminate(IOOptionBits)
kernel: (driver) virtual bool com_osxkernel_driverClient::terminate(IOOptionBits)
kernel: (driver) virtual bool com_osxkernel_driverClient::terminate(IOOptionBits)
kernel: (driver) virtual void com_osxkernel_driverClient::stop(IOService *)
kernel: (driver) virtual void com_osxkernel_driverClient::stop(IOService *)
kernel: (driver) virtual void com_osxkernel_driver::stop(IOService *)

--

似乎即使我从每个 IOUserClient 的提供者那里获取参考,并在 IOUserClient::clientClose 上发布这些参考 , kextunload 命令仍然成功。

我发现实现我的目标的唯一方法是推迟 ::terminate 命令并从 ::clientClose 明确调用它。

所以我删除了对 IOService::terminate 的调用,而是从 IOUserClient::clientClose() 调用它。 这里是相关代码:

IOReturn com_osxkernel_driverClient::clientClose() 
{
    myProvider->release();
    return super::terminate(kIOServiceSynchronous) ? kIOReturnSuccess : kIOReturnError;
}


bool com_osxkernel_driverClient::terminate(IOOptionBits options) {
    os_log_info(g_logger,"%s", __PRETTY_FUNCTION__);
    return true;
}

我想知道是否有任何更简单的方法来阻止 kextunload 成功并控制驱动程序卸载的时间。

感谢

如果您查看 the kextunload source code,您会看到它向下调用 IOCatalogueTerminate()。在内核中,这最终进入 IOCatalogue::_terminateDrivers() 函数。 (IOCatalogue.cpp 在 xnu 源代码中)你会看到以下是 运行 属于正在卸载的 kext 的每个 IOKit class 实例:

if ( !service->terminate(kIOServiceRequired|kIOServiceSynchronous) ) 
{
    ret = kIOReturnUnsupported;
    break;
}

这表明停止您的 kext 的 IORegistry 条目的唯一方法确实是 returning false from terminate().

像这样硬阻止它是否是个好主意当然完全是另一个问题。我建议您仅在卸载会危及操作系统的稳定性时才这样做。考虑到 kextunload 需要 root 权限,因此在大多数情况下,最好在用户空间代码中简单地处理服务和用户客户端的终止。

如果您从 terminate() 做 return 错误,您还需要注意不要出于正当理由阻止终止。 (例如,如果它是一个设备驱动程序,则匹配您的驱动程序的 nub,例如 IOUSBInterface 可能会尝试 terminate() 如果设备是热拔出的,您的驱动程序实例。)