fbdev驱动在rmmod的时候内核崩溃
1. 现象
自定义fb_mmap接口之后,rmmod的时候设备会死机。
对内核mem理解的很粗浅,起初在fb_mmap中做了各种尝试,然后找了些帖子怀疑是内存泄漏导致内核崩溃,又去试了各种申请fb内存的接口,各有各的问题,唯独没试标准kzmalloc接口
// 1. rmmod设备会死机// lcd->fb_info->screen_base = (char *)devm_kzalloc(dev, lcd->fb_info->fix.smem_len, GFP_KERNEL);// 2. make:implicit declaration of function 内核fbdev目录下没搜到有驱动使用// lcd->fb_info->screen_base = (char *)devm_dma_alloc_coherent(dev, lcd->fb_info->fix.smem_len, GFP_KERNEL);// 3. insmod:Failed to allocate framebuffer memory 内核fbdev目录下里有驱动使用// lcd->fb_info->screen_base = (char *)dma_alloc_coherent(dev, lcd->fb_info->fix.smem_len, &dma_handle, GFP_KERNEL);// 4. insmod:Failed to allocate framebuffer memory 内核fbdev目录下里有驱动使用// lcd->fb_info->screen_base = dma_alloc_wc(dev, lcd->fb_info->fix.smem_len,(dma_addr_t *)&lcd->fb_info->fix.smem_start,GFP_KERNEL);// 5. successlcd->fb_info->screen_base = (char *)kzalloc(lcd->fb_info->fix.smem_len, GFP_KERNEL);
2. 查资料方法
不知道怎么搜这个问题,让chatgpt给我生成了一些英文检索关键字,然后一个个尝试,最终搜到了这个帖子
CVE -Search Results
其中有一段:
In the Linux kernel, the following vulnerability has been resolved: net: phy: leds: fix memory leak A network restart test on a router led to an out-of-memory condition, which was traced to a memory leak in the PHY LED trigger code. The root cause is misuse of the devm API. The registration function (phy_led_triggers_register) is called from phy_attach_direct, not phy_probe, and the unregister function (phy_led_triggers_unregister) is called from phy_detach, not phy_remove. This means the register and unregister functions can be called multiple times for the same PHY device, but devm-allocated memory is not freed until the driver is unbound. This also prevents kmemleak from detecting the leak, as the devm API internally stores the allocated pointer. Fix this by replacing devm_kzalloc/devm_kcalloc with standard kzalloc/kcalloc, and add the corresponding kfree calls in the unregister path.
在 Linux 内核中,以下漏洞已被修复:net: phy: leds: 修复内存泄漏问题
在一项路由器的网络重启测试中,出现了内存耗尽(out-of-memory)的问题,经过排查发现是 PHY LED 触发器代码中的内存泄漏所导致。
问题的根本原因在于对 devm API 的误用:
phy_led_triggers_register 函数是在 phy_attach_direct 中调用的,而不是在 phy_probe 中;而 phy_led_triggers_unregister 是在 phy_detach 中调用的,而不是在 phy_remove 中。
这意味着对于同一个 PHY 设备,注册和注销函数可能会被多次调用。但使用 devm_kzalloc 或 devm_kcalloc 分配的内存,直到驱动卸载(unbind)时才会被释放,因此每次调用注册函数都会造成内存泄漏。
此外,由于 devm API 会在内部保存已分配的指针,这也导致了 kmemleak 无法检测到该内存泄漏。
为了解决这个问题,此补丁将 devm_kzalloc 和 devm_kcalloc 替换为标准的 kzalloc 和 kcalloc,并在注销路径中添加了对应的 kfree 调用,从而确保每次注册/注销调用都能正确分配和释放内存。
3. 尝试成功
让chatgpt给我解释了一下,现在更清楚了。
先解释 devm_kzalloc
和 kzalloc
的区别:
devm_kzalloc
-
是一种“自动管理”内存分配:当设备驱动调用
devm_kzalloc(dev, size, GFP_KERNEL)
,分配的内存会在设备被注销(driver unbind)时自动释放。 -
适合只分配一次、随设备整个生命周期存在的内存,比如 probe 阶段申请的设备结构体。
kzalloc
-
是标准的内存分配,需要你自己手动调用
kfree
来释放。 -
更适合控制精细、你自己能明确知道何时释放的资源。
🔥 回到你的问题核心:
你说:
“我在 framebuffer 驱动中使用
devm_kzalloc
申请显存(screen_base),在rmmod
时内核崩溃,换成kzalloc
就好了。”
这是因为你掉进了 “devm API 的使用误区”,具体原因和你引用的那段英文一致:
🚨 问题分析(结合 PHY LED 案例与你的情况):
❌ 使用 devm_kzalloc
分配 framebuffer 显存:
-
你可能在
probe()
之外的函数(比如register_framebuffer()
前后)调用了devm_kzalloc
。 -
你又在
remove()
或fb_release()
中手动释放 framebuffer,包括调用unregister_framebuffer()
。 -
这样
devm_kzalloc
分配的内存,你没有kfree
,却手动注销了 framebuffer,这时指针可能悬空。 -
当
rmmod
时devm
框架释放它之前,已经被unregister_framebuffer()
或其他代码使用/释放过,双重释放或非法访问就会触发内核崩溃。
✅ 使用 kzalloc
的好处:
-
你知道在哪申请这块 framebuffer 显存。
-
你可以在
fb_release()
或remove()
函数里手动调用kfree()
。 -
你掌控了内存的生命周期,避免了 devm 框架自动释放时再次访问已释放指针的情况。
✅ 总结:
使用方式 | 生命周期由谁控制 | 场景 | 风险 |
---|---|---|---|
devm_kzalloc | 内核设备管理框架 | 在 probe() 中申请长期资源 | 多次调用注册/注销时容易重复申请却不释放,导致内存泄漏或非法访问 |
kzalloc | 你自己控制 | 任意位置申请,手动释放 | 要注意释放时机,但更安全可控 |
✅ 建议你这么做(针对 framebuffer 驱动):
-
framebuffer 显存(
screen_base
)不要用devm_kzalloc
,直接用kzalloc
。 -
在
fb_release()
或remove()
函数中确保手动调用kfree()
。 -
如果你用的是
register_framebuffer()
,确保调用了unregister_framebuffer()
后再释放显存。