You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核驱动释放filp->private_data触发use-after-free问题

问题根因与正确规则

你触发KASAN UAF报错的核心原因是混淆了两类完全不同的内核对象生命周期,错误释放了不属于单文件打开实例生命周期的内存。


两类核心对象的生命周期边界

  • 字符设备实例(内嵌struct cdev的自定义设备结构体):这是模块级全局资源,在你调用cdev_add注册字符设备前完成分配,和设备号、文件操作表绑定供所有打开该设备的进程共享。这类资源的释放时机必须是模块退出流程中,调用cdev_del将字符设备从内核系统中摘除之后,绝对不能放在文件关闭回调中释放。
  • 单文件打开实例私有资源:每次调用open打开设备时,针对本次打开操作单独分配的上下文资源(比如单文件独立的读写偏移、访问权限标记、临时IO缓存等),这类资源通常存在filp->private_data里,生命周期和当前struct file实例完全绑定。

补充说明:你提到的file_operations中关闭回调实际是.release而非.close,它的触发时机是当前struct file的引用计数归0时——也就是所有dup、fork继承的该文件描述符都被关闭的最后一次关闭操作时,这个引用计数完全由内核维护,不需要驱动自行实现计数逻辑。


你代码中的具体错误

你当前逻辑直接将全局共享的设备结构体指针存入filp->private_data,并在.release回调中执行kfree:

  1. 从你贴的调用栈可以明确看到内核__fput的执行顺序:先调用驱动注册的.release回调,再执行cdev_put操作访问struct cdev成员。你提前释放了cdev所在的内存块,自然触发use-after-free。
  2. 如果设备被多个进程同时打开,第一次执行close就会释放全局共享的设备结构体,其余打开实例后续的read/write/ioctl操作全都会访问野内存,即便没有开启KASAN也会触发随机崩溃。

你看到教材中scull_release直接返回0,正是因为基础版scull没有为单文件打开实例分配专属资源,private_data直接指向全局的scull_dev结构体,自然不需要在release流程中做释放操作。


filp->private_data的正确释放规则

  • 如果private_data指向的是open时为当前文件实例单独分配的专属内存:在对应的.release回调中释放即可,完全符合“open分配、release释放私有资源”的规则。
  • 如果private_data指向的是全局共享的设备结构体指针:不需要在.release中做任何释放操作,全局设备结构体的内存统一在模块退出流程、cdev_del执行完成后再释放。

即时修复方案

  1. 删除你的.release回调(即你写的my_close函数)中的kfree(filp->private_data);逻辑,让该回调直接返回0。
  2. 将内嵌struct cdev的自定义设备结构体的kfree操作,移动到模块退出函数中,放在cdev_del调用之后执行。
  3. 后续如果需要为每个打开的文件维护独立状态,在.open回调中单独kmalloc分配专属上下文结构体,将全局设备指针存入该上下文,再把上下文地址赋值给filp->private_data,此时在.release中释放这个专属上下文就不会触发内存错误。

内容的提问来源于stack exchange,提问作者the_midnight_developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:57:16