背景
最近移植TI电量计芯片bq40z50的驱动,移植完毕后,能正常读取电池信息了,但是无意中发现驱动卸载会导致Linux卡死,死前终端闪过大量打印,将putty的缓冲区都耗尽了,必须启用syslog转发并用visual syslog server接收才能全部看到:
虽然实际应用中一般不会卸载此类驱动,但考虑到后果严重,还是想解决一下。
问题根因
其实,这是一个double free
BUG。
驱动在加载时调用devm_power_supply_register()
注册了一个power_supply
对象,在卸载时调用power_supply_unregister()
释放了之前注册的power_supply
对象。
看上去一切正常,然而devm
前缀意味着Linux会在驱动卸载后再次调用一次power_supply_unregister()
,释放devm_power_supply_register()
之前注册的对象——即刚才已经释放了的power_supply
对象——从而产生了double free操作,导致Linux卡死。
解决方法
将fg_psy_register()
函数里的devm_power_supply_register()
替换成power_supply_register()
,即去掉devm前缀。
直接删除fg_psy_unregister
函数及对其的调用更好吧?
是的,这样更好,因为devres会在驱动加载失败时或卸载时自动(逆序)释放devm_xxx申请的资源。
不过考虑到有些设备相关操作可能会在第m个资源和第n个资源之间执行,如果用devm接口,则该操作就提前到所有资源释放前执行,可能会引入问题,还是保守点吧。