Acer Nitro 16风扇与RGB键盘控制内核模块触发内核恐慌求助
排查Acer Nitro 16 Linux内核模块的General Protection Fault内核恐慌问题
问题概述
为Acer Nitro 16编写的风扇转速与RGB键盘控制内核模块功能正常,但使用insmod加载后一段时间会触发内核恐慌(Caps Lock指示灯常闪烁),dmesg日志显示General protection fault。已排除内存硬件故障(未加载模块时系统运行稳定)。
错误日志
Oops: general protection fault, probably for non-canonical address 0x1cff96aa80deee40: 0000 [#1] PREEMPT SMP NOPTI CPU: 10 UID: 1000 PID: 2070 Comm: Xwayland Tainted: POE 6.12.9-zen1-1-zen #1 3932de41e0e2f413f8ccd21a06 c6e6c1ba8fb994 RIP: 0010:__kmalloc_noprof+0x10b/0x3f0 Code: 73 e6 99 59 48 8b 50 08 48 83 78 10 00 48 8b 38 0f 84 47 02 00 00 48 85 ff 0f 84 3e 02 00 00 41 8b 45 28 49 8b 7500 48 01 f8 <48> 8b 18 48 89 c1 49 33 9d b8 00 00 00 48 89 f8 48 0f c9 48 31 cb
代码问题分析
从日志和代码来看,内核恐慌的根源是未初始化内存访问和内存泄漏,具体问题点如下:
cdev_user_write函数的致命逻辑错误- 调用
kmalloc后直接操作未初始化的kbfr内存:kbfr[strlen(kbfr)-1]='\0',strlen读取未初始化内存会触发未定义行为,甚至访问非法地址 kmalloc失败判断放在copy_from_user之后,若kmalloc返回NULL,前面的操作已经触发空指针访问- 未释放
kmalloc分配的内存,长时间运行会导致内存泄漏,最终触发内存分配相关的内核错误
- 调用
设备号释放范围错误
module_finish中调用unregister_chrdev_region时使用MINORMASK(255),但实际只分配了2个次设备号,释放多余设备号可能导致系统设备号冲突
错误码返回不正确
wmi_probe中检查wmi_has_guid失败时返回-ENOMEM,实际应该返回-ENODEV,错误码语义错误
WMI方法调用未处理返回值
__wmi_eval_method中未检查wmidev_evaluate_method的返回值,也未释放ACPI分配的输出缓冲区,可能导致资源泄漏和隐藏错误
修复方案
1. 修复cdev_user_write函数
ssize_t cdev_user_write(struct file * file,const char __user * buff, size_t count, loff_t *offset){ int cdev_minor = MINOR(file->f_path.dentry->d_inode->i_rdev); printk(KERN_INFO"writing to : %d",cdev_minor); // 使用kzalloc分配初始化内存,多留1字节存字符串终止符 char * kbfr = kzalloc(count + 1, GFP_KERNEL); if(!kbfr) return -ENOMEM; // 从用户空间拷贝数据,处理拷贝失败情况 if(copy_from_user(kbfr, buff, count)){ kfree(kbfr); return -EFAULT; } // 确保字符串以'\0'结尾,去除可能的换行符 kbfr[count] = '\0'; if(count > 0 && kbfr[count-1] == '\n'){ kbfr[count-1] = '\0'; } printk(KERN_INFO"%s",kbfr); int ispeed = 0; switch(cdev_minor){ case 0: printk(KERN_INFO"CPUFAN"); // 验证转速值转换有效性 if(!kstrtoint(kbfr, 10, &ispeed)){ fan_set_speed(ispeed, 1); } else { printk(KERN_WARNING"Invalid CPU fan speed value: %s", kbfr); } break; case 1: printk(KERN_INFO"GPUFAN"); if(!kstrtoint(kbfr, 10, &ispeed)){ fan_set_speed(ispeed, 4); } else { printk(KERN_WARNING"Invalid GPU fan speed value: %s", kbfr); } break; } kfree(kbfr); // 释放分配的内存 return count; }
2. 修复设备号释放范围
修改module_finish中的unregister_chrdev_region调用:
unregister_chrdev_region(MKDEV(cmajor, 0), 2); // 仅释放分配的2个次设备号
3. 修正wmi_probe的错误码
if(!wmi_has_guid(WMI_GAMING_GUID)) return -ENODEV; // 正确返回设备不存在错误码
4. 修复WMI方法调用的资源管理与错误处理
extern acpi_status __wmi_eval_method(struct wmi_device * wdev,int methodid ,int instance ,struct acpi_buffer *inbuffer){ struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status = wmidev_evaluate_method(wdev, instance, methodid, inbuffer, &out); if(ACPI_FAILURE(status)){ printk(KERN_WARNING"WMI method %d failed, status: 0x%x", methodid, status); } kfree(out.pointer); // 释放ACPI分配的输出缓冲区 return status; }
验证步骤
- 编译修复后的内核模块
- 使用
insmod加载模块,验证风扇和RGB键盘功能正常 - 长时间运行系统,观察是否再触发内核恐慌
- 定期查看
dmesg日志,确认无新的错误信息
内容的提问来源于stack exchange,提问作者Detux
相关产品推荐
相关产品推荐

