iokit 驱动程序中的全局变量
Global variables in iokit drivers
我在基于 iokit 的驱动程序中使用了一些全局变量,即在主 class 实例之外。但是,由于使用未初始化的全局变量或尝试双重释放,这会在驱动程序启动时导致一些意外的恐慌
拆卸时的全局变量。
iokit驱动生命周期中全局变量的生命周期是多少?
如果我在声明时设置一个全局变量,
例如,如果我从 lck_grp_t * my_lock_grp
...
类型获得全局变量
我可以假设我的全局变量已经分配并准备好在我的 iokit 驱动程序到达 ::start
方法时设置吗?
(my_lock_grp = lck_grp_alloc_init("my-locks", my_lock_grp_attr);
)
当我尝试在我的 iokit ::free
方法上释放它时,我可以假设我的全局变量仍然有效吗? (lck_grp_free(my_lock_grp)
)
一般的问题是,与驱动程序实例本身相比,基于 iokit 的驱动程序中全局变量的生命周期是多少。
生命周期肯定和kext的生命周期一样。 classes 上的 IOKit init/start/stop/free 函数将在 kext 启动和停止函数之间发生(您可能没有明确的 kext 启动和停止函数),并且全局构造函数在 kext 之前 运行 start 函数,同样全局析构函数在 kext stop 函数之后 运行。 global/static 变量的内存 allocation/deallocation 由动态内核链接器在加载和卸载 kext 代码本身的同时完成。
我能想到 3 件事:
IOService
start()
和 free()
函数不匹配 - 即使从未调用过 start()
,也会调用 free()
。例如,如果你有一个 probe()
函数,它被调用并且 returns nullptr
,那么 start()
永远不会被调用,但 free()
肯定会被调用,它会尝试释放一个从未分配过的锁组。类似地,如果 init()
函数返回 false - start()
永远不会 运行,但 free()
会。 free()
的等价物是 init()
家族的成员函数,因此只有在 free()
中无条件地销毁(没有 nullptr 检查)在所有可能的 init…
函数中无条件地创建。
start()
可以在不同的实例上调用任意次数,所以如果你总是 运行 my_lock_grp = lck_grp_alloc_init()
在 start()
和 2 个实例创建后,my_lock_grp
只记住最后一个,所以如果你的 class 的两个实例都被释放,你最终会尝试释放一个锁组两次,而另一个根本不释放。这显然是个坏消息。对于 initialising/destroying 真正的全局状态,我建议使用 kext 启动和停止函数或全局 constructors/destructors.
否则我怀疑你可能会 运行ning 进入这样一种情况,即 运行ning 内核的其他部分仍然有一个悬空引用超过你的 kext 有已经卸载,例如,如果您创建了一个新的内核线程并且该线程仍在 运行ning,或者如果您没有取消注册您注册的所有回调,或者如果回调已被取消注册但不保证有完成所有调用。 (kauth 听众因后一种情况而臭名昭著)
如果其中 none 听起来像是问题所在,我建议发布受影响的代码和崩溃日志,如果我们有一些硬数据,也许我们可以更清楚地了解问题。
我在基于 iokit 的驱动程序中使用了一些全局变量,即在主 class 实例之外。但是,由于使用未初始化的全局变量或尝试双重释放,这会在驱动程序启动时导致一些意外的恐慌 拆卸时的全局变量。
iokit驱动生命周期中全局变量的生命周期是多少? 如果我在声明时设置一个全局变量,
例如,如果我从 lck_grp_t * my_lock_grp
...
我可以假设我的全局变量已经分配并准备好在我的 iokit 驱动程序到达
::start
方法时设置吗? (my_lock_grp = lck_grp_alloc_init("my-locks", my_lock_grp_attr);
)当我尝试在我的 iokit
::free
方法上释放它时,我可以假设我的全局变量仍然有效吗? (lck_grp_free(my_lock_grp)
)一般的问题是,与驱动程序实例本身相比,基于 iokit 的驱动程序中全局变量的生命周期是多少。
生命周期肯定和kext的生命周期一样。 classes 上的 IOKit init/start/stop/free 函数将在 kext 启动和停止函数之间发生(您可能没有明确的 kext 启动和停止函数),并且全局构造函数在 kext 之前 运行 start 函数,同样全局析构函数在 kext stop 函数之后 运行。 global/static 变量的内存 allocation/deallocation 由动态内核链接器在加载和卸载 kext 代码本身的同时完成。
我能想到 3 件事:
IOService
start()
和free()
函数不匹配 - 即使从未调用过start()
,也会调用free()
。例如,如果你有一个probe()
函数,它被调用并且 returnsnullptr
,那么start()
永远不会被调用,但free()
肯定会被调用,它会尝试释放一个从未分配过的锁组。类似地,如果init()
函数返回 false -start()
永远不会 运行,但free()
会。free()
的等价物是init()
家族的成员函数,因此只有在free()
中无条件地销毁(没有 nullptr 检查)在所有可能的init…
函数中无条件地创建。start()
可以在不同的实例上调用任意次数,所以如果你总是 运行my_lock_grp = lck_grp_alloc_init()
在start()
和 2 个实例创建后,my_lock_grp
只记住最后一个,所以如果你的 class 的两个实例都被释放,你最终会尝试释放一个锁组两次,而另一个根本不释放。这显然是个坏消息。对于 initialising/destroying 真正的全局状态,我建议使用 kext 启动和停止函数或全局 constructors/destructors.否则我怀疑你可能会 运行ning 进入这样一种情况,即 运行ning 内核的其他部分仍然有一个悬空引用超过你的 kext 有已经卸载,例如,如果您创建了一个新的内核线程并且该线程仍在 运行ning,或者如果您没有取消注册您注册的所有回调,或者如果回调已被取消注册但不保证有完成所有调用。 (kauth 听众因后一种情况而臭名昭著)
如果其中 none 听起来像是问题所在,我建议发布受影响的代码和崩溃日志,如果我们有一些硬数据,也许我们可以更清楚地了解问题。