技术知识库
未初始化 fasync 指针可能导致 fasync_insert_entry Oops
未初始化 fasync 指针可能导致 fasync_insert_entry Oops
摘要
实验手册的进阶思考描述:同一份 FASYNC 代码在一位实验者处运行成功,另一位运行 test 后却在 fasync_insert_entry() 中 Oops。当前本地实验运行成功,尚未亲自复现该 Oops;已建立用户态 GCC/GDB 类比实验,后续先练习从崩溃点、调用栈和指针值定位问题,再进入 vmlinux 分析。
参考现象
手册中的关键日志:
Unable to handle kernel paging request at virtual address ...
pc : fasync_insert_entry+0x508/0x918
Call trace:
fasync_insert_entry
fasync_add_entry
fasync_helper
demodrv_fasync [mydemo_fasync]
setfl
do_fcntl
__se_sys_fcntl
用户程序中的触发点:
fcntl(fd, F_SETFL, flag | FASYNC);
当前源码观察点
设备结构体保存:
struct fasync_struct *fasync;
驱动把该指针的地址交给:
fasync_helper(fd, file, on, &device->fasync);
设备对象的分配发生在:
device = kmalloc(sizeof(struct mydemo_device), GFP_KERNEL);
需要回答该指针在进入 fasync_helper() 前具有什么确定状态,以及 Linux 5.0 fs/fcntl.c::fasync_insert_entry() 对 *fapp 作了什么假设。
源码路径
/home/martin-zou/workspace/runninglinuxkernel_5.0/kmodules/rlk_lab/rlk_basic/chapter_6_device_driver/lab9_mydemodrv_fasync/mydemodrv_fasync.c
/home/martin-zou/workspace/runninglinuxkernel_5.0/fs/fcntl.c
/home/martin-zou/workspace/runninglinuxkernel_5.0/build/arm64/vmlinux
关键位置:
mydemodrv_fasync.c:26:device->fasync。mydemodrv_fasync.c:159:fasync_helper()。mydemodrv_fasync.c:199:设备对象分配。fs/fcntl.c:915:fasync_insert_entry()。fs/fcntl.c:921-922:遍历并访问fa->fa_file。
用户态类比实验
为避免直接让 QEMU 内核 Oops,先使用:
/home/martin-zou/workspace/gcc-lab/debug/fasync-pointer-oops
其中保留两个场景和调试符号,暂未编译或运行:
make
./fasync_pointer_lab xiaoli
./fasync_pointer_lab xiaoming
gdb ./fasync_pointer_lab
调试计划
- 保存正常与崩溃场景输出。
- 在 GDB 中对
register_async_listener()下断点。 - 检查
device->fasync和循环变量entry。 - 用
backtrace建立用户态调用路径。 - 把结果映射回
demodrv_fasync()与fasync_insert_entry()。 - 若需要实际内核定位,再使用与当前 Image 同次构建的
build/arm64/vmlinux。
当前调试环境
主机 gdb:/usr/bin/gdb,已安装
gdb-multiarch:当前未安装
ARM64 vmlinux:存在,带 debug_info,未 strip
内核配置:CONFIG_DEBUG_INFO=y
因此用户态类比实验现在即可使用主机 gdb。真正解析 ARM64 内核的指令、寄存器和源码偏移时,再由用户手动安装 gdb-multiarch,并确保使用与发生 Oops 的 Image 同次构建的 vmlinux。
当前结论
该问题已经定位到 FASYNC 登记阶段及其指针状态,但本地故障复现和 GDB 证据尚未完成,所以保持 seed/pending,不把手册答案当作本地验证结果。
关联
待验证
- 手动完成 GCC/GDB 类比实验。
- 保存崩溃行、调用栈和指针值。
- 再决定是否故意在 QEMU 中复现内核 Oops。