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

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

代码问题分析

从日志和代码来看,内核恐慌的根源是未初始化内存访问和内存泄漏,具体问题点如下:

  1. cdev_user_write函数的致命逻辑错误

    • 调用kmalloc后直接操作未初始化的kbfr内存:kbfr[strlen(kbfr)-1]='\0',strlen读取未初始化内存会触发未定义行为,甚至访问非法地址
    • kmalloc失败判断放在copy_from_user之后,若kmalloc返回NULL,前面的操作已经触发空指针访问
    • 未释放kmalloc分配的内存,长时间运行会导致内存泄漏,最终触发内存分配相关的内核错误
  2. 设备号释放范围错误

    • module_finish中调用unregister_chrdev_region时使用MINORMASK(255),但实际只分配了2个次设备号,释放多余设备号可能导致系统设备号冲突
  3. 错误码返回不正确

    • wmi_probe中检查wmi_has_guid失败时返回-ENOMEM,实际应该返回-ENODEV,错误码语义错误
  4. 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;
}

验证步骤

  1. 编译修复后的内核模块
  2. 使用insmod加载模块,验证风扇和RGB键盘功能正常
  3. 长时间运行系统,观察是否再触发内核恐慌
  4. 定期查看dmesg日志,确认无新的错误信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:34:55