此时变量 x 所在地址 &(x) 为需要写入 val 的内存虚拟地址 ffffffffaa6f0210, 可以通过 Crash 查看该虚拟地址各项属性:
crash> vtop ffffffffaa6f0210VIRTUALPHYSICALffffffffaa6f02103faf0210PGD DIRECTORY: ffffffffaa80a000PAGE DIRECTORY: 3fc0e067PUD: 3fc0eff0 => 3fc0f063PMD: 3fc0fa98 => 34367063PTE: 34367780 => 800000003faf0061PAGE: 3faf0000PTEPHYSICALFLAGS800000003faf00613faf0000(PRESENT|ACCESSED|DIRTY|NX)PAGEPHYSICALMAppINGINDEX CNT FLAGSffffe02200febc00 3faf0000001 ffffc000000800 reserved
可以看到该虚拟地址所在 PAGE 的 PTE 页表项内容 , 从 FLAGS 可以看出该 PAGE 不具备 R/W 属性 。因而可以确认 , 是由于尝试向只读地址空间写数据 , 从而导致内核报错 。
这里稍作补充 , 上述 PUD/PMD/PTE 信息跟第一节日志中的第二行信息一致:
PGD 3fc0e067 P4D 3fc0e067 PUD 3fc0f063 PMD 34367063 PTE 800000003faf0061
知道是在调用 hlist_add_tail_rcu 接口出错后也可以通过查看符号表得到所要写入的地址空间是否是只读的 , 在内核的安全模块中默认配置了 apparmor , 当以 ko 的模式挂载外部的 LSM 钩子时 , 都需要以双链表方式挂在 apparmor 的 LSM 尾部:
$ sudo cat /proc/kallsyms | grep apparmor_hooksffffffffaa6eff40 r apparmor_hooks
可以看出此时 apparmor_hooks 所在内存空间是只读的 。
7. 问题解决方案进一步查看到 apparmor_hooks 的定义如下:
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init
__lsm_ro_after_init 表示 LSM 架构在完成初始化后所在的内存空间会成只读 。
因此我们提出两种解决方案:
- 解决方案1: 将 __lsm_ro_after_init 标志位去掉 , 让该 PAGE 可读可写 , 这样修改虽然可行 , 但不确定是否会引入其他问题 。
- 解决方案2: 可以通过内核配置添加 SECURITY_WRITABLE_HOOKS 选项 , 同样会将该 PAGE 配置成可读可写 。
【LSM Oops 内存错误根因分析与解决】
推荐阅读
- 内存|DDR3内存要淘汰?华邦电子:再战10多年、持续扩产
- 一次完整的JVM堆外内存泄漏故障排查记录
- 安卓|谷歌确认!Android 13最大改进 解决杀后台:CPU/内存使用率暴降
- 全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析
- 内存界的金条长啥样?一条顶人家三条
- Android Camera 内存问题剖析
- 铠侠 极至光速系列内存卡评测:经典红白复刻,唯有品质依旧
- 多进程编程 - 共享内存
- 虚拟内存 和 page fault 的解释
- 对于内存结构的简单理解