专栏首页专注网络研发register_netdevice引发的panic“陷阱”

register_netdevice引发的panic“陷阱”

上次在内核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,祝大家成功。

本文分享自微信公众号 - LinuxerPub(LinuxerPub),作者:glinuxer

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-07-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux内核那些事之连接跟踪

    连接跟踪(也叫会话管理)是状态防火墙关键核心,也是很多网元设备必不可少的一部分。各厂商的实现原理基本雷同,只是根据各自的业务进行修改和优化。其中,还有不少厂商干...

    glinuxer
  • 一道腾讯面试题目:没有listen,能否建立TCP连接

    这个题目是之前在我的QQ群里一个同学在腾讯面试过程中被问到的。当时在群里做了简单的讨论,今天系统的把这个问题分析一遍。

    glinuxer
  • TCP接收窗口的实现(二)

    上篇介绍了TCP接收窗口的初始化,本篇将分析TCP在传输过程中的动态接收窗口大小,由什么决定。

    glinuxer
  • pickle —— Python 对象序列化(python=3.8)

    模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化。 “Pickling” 是将 Python 对象及其所拥有的层次结构转化为一个...

    用户7886150
  • 零基础学习Swift中的数据科学

    Python被广泛认为是数据科学中最好、最有效的语言。近年来我遇到的大多数调查都将Python列为这个领域的领导者。

    磐创AI
  • Swift 3.1的新改动

    BY
  • 干货 | 一波N折的携程酒店Swift-Objc混编实践

    说起Swift,对iOS开发者来说那是既熟悉又陌生,虽然早在2014年苹果就发布了Swift1.0版本,但在这之后的五六年时间里,一直处于不温不火的状态。ABI...

    携程技术
  • Swift学习资源

    Swift,一种强大的开源编程语言, 让大家都能开发出众的 App。 Swift 是一种强劲而直观的编程语言,它由 Apple 创造,可用来为 iOS、...

    庞小明
  • 密码学重大里程碑!科学家暴力破解迄今最长RSA密钥,功劳却不在摩尔定律

    研究人员已经在密码学上达到一个新的里程碑,他们解开了有史以来计算过的最长RSA密钥,并对有史以来最大的整数离散对数进行了匹配计算。

    新智元
  • Swift 是猴还是猿?

    Swift 自2014年发布到目前为止其行业现状如何?开发者是否可以开始着手大面积使用Swift进行项目开发?Swift 和 Objective-C 在性能方面...

    段义鹏

扫码关注云+社区

领取腾讯云代金券