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

Linux内核6.6.6x中__pollwake函数添加pr_info打印触发内核panic的问题咨询

Linux内核6.6.6x中__pollwake函数添加pr_info打印触发内核panic的问题咨询

问题背景

我在Linux内核6.6.6x的/fs/select.c文件的__pollwake函数中添加了调试代码,用于追踪特定进程(process1)的唤醒逻辑,但在尝试打印pwq->polling_task->comm字段时触发了内核崩溃。我之前已经通过strstr访问过这个字段却没有崩溃,这让我十分困惑,以下是详细的问题信息:

修改后的__pollwake代码片段

static int __pollwake(wait_queue_entry_t *wait, unsigned mode, int sync, void *key) {
    struct poll_wqueues *pwq = wait->private;
    DECLARE_WAITQUEUE(dummy_wait, pwq->polling_task);

    /*
     * Although this function is called under waitqueue lock, LOCK
     * doesn't imply write barrier and the users expect write
     * barrier semantics on wakeup functions. The following
     * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up()
     * and is paired with smp_store_mb() in poll_schedule_timeout.
     */
    smp_wmb();
    pwq->triggered = 1;

    /*
     * Perform the default wake up operation using a dummy
     * waitqueue.
     *
     * TODO: This is hacky but there currently is no interface to
     * pass in @sync. @sync is scheduled to be removed and once
     * that happens, wake_up_process() can be used directly.
     */
    if (wait->private && (wait->func == pollwake) && (pwq->polling_task) && 
        strstr(pwq->polling_task->comm, "process1") != 0) {
        pwqLWB = pwq;
        // 注意:这里参数顺序存在错误,会导致打印乱码
        pr_info("LWB %s:%d__pollwake: waking task current: %s (pid %d)\n", 
                __func__, __LINE__, current->pid, current->comm);
        WARN_ON(!pwq || !pwq->polling_task);
        pr_info("LWB DONE %s \n", pwq->polling_task->comm); <---- 此处崩溃!!
        //pr_info("LWB %s:%d__pollwake: waking task poll_task: %s (pid %d) error:%d\n", 
        // __func__, __LINE__, 
        // pwq->polling_task->comm, 
        // pwq->polling_task->pid, 
        // pwq->error);
    }

    int rtn = default_wake_function(&dummy_wait, mode, sync, key);

    if (wait->private && (wait->func == pollwake) && (pwq->polling_task) && 
        strstr(pwq->polling_task->comm, "process1") != 0) {
        // 同样存在参数顺序错误
        pr_info("LWB %s:%d__pollwake: waking task current: %s (pid %d)\n", 
                __func__, __LINE__, current->pid, current->comm);
        WARN_ON(!pwq || !pwq->polling_task);
        pr_info("LWB DONE %s\n", pwq->polling_task->comm);
        //pr_info("LWB %s:%d__pollwake DONE: waking task poll_task: %s (pid %d) error:%d\n", 
        // __func__, __LINE__, 
        // pwq->polling_task->comm, 
        // pwq->polling_task->pid, 
        // pwq->error);
    }
    return rtn;
}

崩溃栈信息

pstate: 000000c5 (nzcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : string_nocheck+0x3c/0x70
lr : string+0x54/0x68
sp : ffff800082cc38a0
x29: ffff800082cc38a0 x28: ffff800082cc3a65
x27: ffff800080e8e126 x26: ffff800080e8e126
x25: 0000000000000020 x24: 0000000000000008
x23: 00000000ffffffe0 x22: ffff800080ca2ff8
x21: ffff0a00ffffff04 x20: 0000000000001083
x19: ffff800082cc3a38 x18: 00000000fffffffc
x17: 0000000000000000 x16: 0000000000000000
x15: 0000000000000001 x14: ffffffffffffffff
x13: ffff800082cc3a38 x12: ffff800082cc3a41
x11: 0000000000000000 x10: ffff800082cc3c10
x9 : 00000000fffffffe x8 : ffff800082cc3a38
x7 : 000000000000000a x6 : 0000000000000002
x5 : 0000000000000002 x4 : 0000000000000000
x3 : ffff0a00ffffff04 x2 : 0000000000001083
x1 : ffff800082cc3a38 x0 : ffff800082cc3a65

Call trace:
string_nocheck+0x3c/0x70
string+0x54/0x68
vsnprintf+0x178/0x738
vprintk_store+0xd8/0x428
vprintk_emit+0x80/0x2b8
vprintk_default+0x3c/0x50
vprintk+0x94/0x100
_printk+0x50/0x60
pollwake+0x180/0x1c0
__wake_up_common+0x9c/0x190
__wake_up_locked_key+0x24/0x38
eventfd_write+0xb0/0x1b8
vfs_write+0xe0/0x458
ksys_write+0x5c/0xe0
__arm64_sys_write+0x20/0x30
el0_svc_common.constprop.0+0x60/0x138
do_el0_svc+0x20/0x30
el0_svc+0x24/0x98
el0t_64_sync_handler+0xb8/0xc0
el0t_64_sync+0x14c/0x150

我的疑问

我在if判断中已经通过strstr(pwq->polling_task->comm, "process1")成功访问了comm字段,当时并没有崩溃,但在后续调用pr_info打印这个字段时却触发了panic,这中间到底发生了什么?


问题分析与解决建议

核心原因:并发场景下的指针有效性变化

虽然你在if条件中验证了pwq->polling_task非空,且strstr调用成功,但从strstr执行到pr_info打印的窗口内,pwq->polling_task指向的task_struct可能已经被销毁:

  1. 无锁保护的指针访问:__pollwake执行到if块内部时,可能已经不再持有保护task_struct的锁,进程可能因为退出、被杀死等原因,其task_struct通过RCU机制被释放,导致pwq->polling_task变成悬空指针。
  2. strstr的特殊行为:strstr在找到匹配的子串(比如"process1")后会立即返回,不会遍历整个comm字符串。而pr_info打印%s时会一直读取直到遇到\0终止符,此时如果comm所在内存已经被释放或覆盖,就会触发非法内存访问,导致panic。

额外的小问题:pr_info参数顺序错误

你当前的pr_info存在参数顺序匹配错误:

// 错误:%s对应整数pid,%d对应字符串comm,会导致打印乱码甚至内存问题
pr_info("LWB %s:%d__pollwake: waking task current: %s (pid %d)\n", 
        __func__, __LINE__, current->pid, current->comm);

正确的参数顺序应该是:

pr_info("LWB %s:%d__pollwake: waking task current: %s (pid %d)\n", 
        __func__, __LINE__, current->comm, current->pid);

修复方案

  1. 使用RCU锁保护task_struct访问
    因为task_struct的销毁依赖RCU机制,所以可以用RCU读锁来保证访问期间指针的有效性:
    if (wait->private && (wait->func == pollwake) && (pwq->polling_task) && 
        strstr(pwq->polling_task->comm, "process1") != 0) {
        pwqLWB = pwq;
        // 修正参数顺序+RCU保护
        rcu_read_lock();
        if (pwq->polling_task) {
            pr_info("LWB %s:%d__pollwake: waking task current: %s (pid %d)\n", 
                    __func__, __LINE__, current->comm, current->pid);
            pr_info("LWB DONE %s \n", pwq->polling_task->comm);
        }
        rcu_read_unlock();
    }
    
  2. 提前拷贝comm字段到栈缓冲区
    可以先把comm字段拷贝到内核栈上的缓冲区,再打印,避免直接访问可能失效的指针:
    char comm_buf[TASK_COMM_LEN];
    if (wait->private && (wait->func == pollwake) && (pwq->polling_task) && 
        strstr(pwq->polling_task->comm, "process1") != 0) {
        pwqLWB = pwq;
        if (pwq->polling_task) {
            memcpy(comm_buf, pwq->polling_task->comm, TASK_COMM_LEN);
            pr_info("LWB DONE %s \n", comm_buf);
        }
    }
    

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 08:34:31