前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >register_netdevice引发的panic“陷阱”

register_netdevice引发的panic“陷阱”

作者头像
glinuxer
发布2019-04-10 15:02:52
1.7K0
发布2019-04-10 15:02:52
举报

上次在内核net_device设备框架的一个缺陷文章中,描述了当前内核net_device框架的一个缺陷。后来内核的net模块的负责人David提交了一个commit “net: Fix inconsistent teardown and release of private netdev state”。这个commit关键的一点,就是给已经很庞大的net_device结构新增一个布尔变量“needs_free_netdev”。这个变量用于在函数netdev_run_todo中,判断是否需要释放netdev。这个变量的赋值,一般是在驱动的setup回调函数中赋值为true。

在这个commit提交之后,net_device的注册,就新增了一个“陷阱”。一个不小心,就会中招,会在某些特定的情况下,导致内核panic。

下面请看一个“问题”驱动,就是常见的vlan driver。

这段代码来自于register_vlan_device,当使用vconfig创建一个vlan设备时,就会调用到该函数。在上面的截图中,其代码逻辑使用的是常用的错误处理风格:使用goto跳转到对应的error handler。

这里的逻辑也很简单,先调用alloc_netdev申请了一个新的new_dev,然后调用register_vlan_dev注册netdev。如果失败,就跳转到out_free_newdev释放掉new_dev占用的资源和内存。

函数register_vlan_dev也比较简单,下面是其代码片断。

其错误处理风格,依然是Linux内核的标准方式。如netdev_upper_dev_link失败的话,就跳转到out_unregister_netdev,进行unregister。

这两个函数分开看,都没有问题。但结合在一起时,bug就出现了。不知道是否有敏锐的同学已经想到了问题所在,可以结合本文的开头,那个新加入的needs_free_netdev变量。

请看vlan_setup函数的实现。

在这个函数中,dev->needs_free_netdev被设置为True。这意味着当unregister netdev后,内核会在netdev_run_todo调用free_netdev释放资源。这意味着vlan的driver有一个double free的Bug。

register_vlan_dev函数的netdev_upper_dev_link失败后,内核调用unregister_netdevice进行清理,然后在register_vlan_device中,因为register_vlan_dev返回失败,会调用free_netdev释放资源。可是,由于vlan_setup设置了needs_free_netdev,这意味着内核稍后还会针对这个netdev再次调用free_netdev。这就引发了一个double free的bug。

为了验证自己的想法,我人为修改了register_vlan_dev,让netdev_upper_dev_link肯定返回失败。这样就可以轻易验证自己的猜测。果然,内核直接panic了。

不过coredump与我预期的不同,直接崩溃在vlan的ioctl中了。也就说,这个panic不是我期望的double free。而是下面的代码导致的。

在此例中,内核调用free_netdev时,dev的reg_state状态是NETREG_UNREGISTERING。因此触发了BUG_ON条件,直接导致内核panic。如果去掉了这个BUG_ON,在此处不直接panic,就会引发double free的问题。这样的内存Bug,查起来就痛苦多了。所以,内存错误的bug,还是越早暴露越早。

总结一下,register_netdevice引发的这个panic陷阱。调用free_netdev函数,必须保证该netdev,没有成功注册过,即register_netdevice没有被调用或者没有执行成功。一旦设备通过register_netdevice注册成功过,就绝不能直接调用free_netdev释放资源。

写驱动的同学如果不注意这个“陷阱”,并且没有测试到错误处理,就很可能引发内核崩溃的Bug了。

PS:

  1. vlan driver的这个Bug,我已经提交patch并被apply了。解决办法也很简单,在free_netdev前,增加一个判断条件if (new_dev->reg_state == NETREG_UNINITIALIZED),保证new_dev没有被成功注册过。
  2. 但目前内核还有其它驱动存在相似的Bug,如ipoib_vlan_delete这个函数,在调用unregister_netdevice后,立刻调用free_netdev。—— 我不会改这个bug了,测试起来比较麻烦:) 有志给内核提commit的同学可以修改这个问题,并提交commit:D

或者搜索free_netdev关键字,查找类似的bug,祝大家成功。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 LinuxerPub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档