1、内核Oops了
遇到一个Oops的问题 ,发现phy_attached_print发送了Oops,死的原因是访问了NULL指针偏移0x88的地方
Oops的原始日志:
2、反汇编vmlinux
反汇编kernel后发现phy_attached_print+0x90的地址对应ffffff80084ad2b8,这句汇编的意思就是把X1+136(0x88)的值赋值给X2
从上面call trace也可以看到X1确实为NULL的,导致Oops的地址是0x88,和X1+0x88是对应起来的,那么现在的问题就是定位为什么X1变成了NULL
对应的C代码,函数phy_attached_print,从上下文分析phydev肯定不是空的,那么phy->drv的嫌疑就比较大了,至于是不是phy->drv为NULL,可以通过分析偏移确定。
3、gdb确认偏移
使用GDB工具print一下phydev->drv->name,看到name对应的偏移就是0x88,可以确认phy->drv是NULL
4、分析流程
看了看日志上下文也没有什么异常,为什么phydev→drv会为NULL,下面是net phy的driver的流程,可以看到
只要get_phy_id从reg获取的phy_id和调用 phy_drivers_register 注册的driver中的phy_id不匹配,换句话说就是reg中读取的phy_id没有对应的驱动注册,那么phy_attached_print就会Oops
(眨眼)net phy的流程如下
优快云上传附件非常不方便,见我的下载里面
5、打桩复现
既然流程分析清楚了,我们把broadcom BCM50612E phy_driver的phyid改成一个不合法的值,人为制造reg获取的phy_id和调用 phy_drivers_register 注册的driver中的phy_id不匹配的场景(从寄存器获取的phyid非法不好构造,但是我们可以修改代码中期望的phyid,只要两者不同就可以了)。
果然Oops了,并且Oops的地址和call trace的打印和前面的一致。说明某种情况下,比如收到干扰或者链路linkdown,如果从寄存器读取的phyid和代码中的phyid不匹配,就会出现Oops
6、kernel主线怎么修复?
既然这么明显一个bug,其他人应该早就遇到了,看了下最新的内核代码,果然这个bug已经修复了,修复方法竟然是判NULL,而不是reg获取的phy_id和调用 phy_drivers_register 注册的driver中的phy_id不匹配认为probe失败
内核的patch如下
patch 1、修复空指针
https://lore.kernel.org/patchwork/patch/824257/
patch 2、删除deadcode
https://lore.kernel.org/patchwork/patch/825029/