Linux内核模块信号量down正常但up触发空指针解引用
问题根因
这个错误和file_operations缺少回调没有任何关系,未实现的文件操作回调会由VFS层做默认处理,不会触发内核崩溃。问题100%出在你的信号量初始化逻辑错误,引发了**释放后使用(UAF)**和信号量内部数据结构损坏。
错误原因拆解
信号量初始化的错误写法
你当前的信号量初始化代码做了完全无意义的动态分配+浅拷贝:struct semaphore *sema; sema = kmalloc(sizeof(struct semaphore), GFP_KERNEL); sema_init(sema, 1); my_device->sem = *sema; // 大概率你在这之后调用了kfree(sema),这是直接诱因内核中
struct semaphore内部不是纯值类型,它包含一个自旋锁、一个计数器、一个等待队列链表头wait_list。sema_init初始化你kmalloc出的信号量时,会把wait_list的next/prev指针设置为指向这个kmalloc出来的信号量自身的wait_list成员。
你通过结构体赋值把信号量浅拷贝到my_device->sem时,只会拷贝结构体内容,不会修正wait_list的指针——也就是说my_device->sem.wait_list的两个指针仍然指向你之前kmalloc的临时内存地址,而不是my_device里嵌入的信号量自己的wait_list。为什么down操作正常、up操作崩溃
- 第一次调用
down/down_interruptible时,信号量计数器初始值为1,操作只需要把计数器减为0就直接返回,完全不会触碰等待队列链表,所以即使wait_list指针是错的,也不会触发异常。 - 如果你在拷贝完信号量后调用了
kfree(sema),那块临时kmalloc的内存会被内核回收,可能被其他数据覆写,导致wait_list的链表指针变成垃圾值。当你调用up时,内核需要检查等待队列上是否有需要唤醒的睡眠进程,会访问这个已经被破坏的wait_list链表:遍历被污染的链表时会拿到非法的等待项结构,从中读出的task_struct指针为NULL,之后调用wake_up_process(NULL)时,需要访问task_struct里偏移为0x7a4的锁成员,正好触发你看到的NULL pointer dereference at virtual address 00000000000007a4报错,和你提供的调用栈完全吻合。
- 第一次调用
修复方法
删掉所有无意义的信号量动态分配逻辑,直接初始化嵌入在MyDev结构体里的信号量即可:
// 删掉原来的kmalloc、指针赋值相关代码,直接写这一句 sema_init(&my_device->sem, 1);
这种写法会让sema_init直接正确初始化设备结构体内嵌信号量的所有成员,wait_list的指针会指向自身,不存在浅拷贝、内存泄漏、野指针问题。
内容的提问来源于stack exchange,提问作者the_midnight_developer
相关产品推荐
相关产品推荐

