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可能已经被销毁:
- 无锁保护的指针访问:
__pollwake执行到if块内部时,可能已经不再持有保护task_struct的锁,进程可能因为退出、被杀死等原因,其task_struct通过RCU机制被释放,导致pwq->polling_task变成悬空指针。 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);
修复方案
- 使用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(); } - 提前拷贝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
相关产品推荐
相关产品推荐

