公司新闻

IM电竞app“我第一次给 Linux 内核做贡献不仅被剥夺了还遭到了维护者的轻视”

  开源浪潮之下,无数开发者的角色正在发生巨大变化,从过往的使用者逐渐变成开源项目参与者、贡献者。

  然而,近日,一位来自思科且充满热情的软件工程师 Ariel Miculas 在参与开源贡献过程中,备受打击,以至于他发布了一篇主题为《我的第一个内核贡献是如何被剥夺的》的长文,控诉自己耗费了大量的时间、精力首次为 Linux 内核做的贡献,不仅被 Linux 内核维护人员给“打回”了,更关键点在于 Ariel 提出的问题被维护者关注,维护者使用其自己的修复版本,却只给予 Ariel 一个“报告者”而非“贡献者”的头衔。

  这导致“我感到被轻视,对我的工作没有得到适当的认可感到愤怒”,Ariel Miculas 说道。

  事情要从一年半前开始说起,Ariel 曾向他以前的公司申请时间来解决一个影响项目调试能力的问题——gdbserver 无法调试在 PowerPC32 架构上运行的多线程应用程序。与 gdbserver 的连接断开了,它无法再控制调试会话。

  这里的 gdbserver(GDB Server),是一个用于调试程序的工具,特别是针对嵌入式系统和远程目标的调试。它允许开发人员使用 GNU 调试器(GDB)与运行在另一个计算机或设备上的程序进行交互式调试。

  事实上,在此之前,也有很多人遇到了同样的问题,但就是不能确定问题究竟出现在了哪个软件组件上,也许是工具链、gdbserver、Linux 内核或 Ariel 团队在内核树上应用的自定义补丁。

  虽然有大致方向,但是仍然没有具体的头绪,这也导致 Ariel 团队一时之间也找不到问题的根本原因和解决方案。

  正如上文所述,行业内也有很多人在遇到同样的问题时展开调查,而 Ariel 结合了他们现有分析并使用 Google 搜索查阅大量资料之后,取得了第一个突破。

  Ariel 在网络上找到了一封 2016 年的邮件列表,此列表中不仅描述了与他遇到问题的相同症状,还指出了引入问题的确切提交。

  具体而言,引入错误的补丁将 thread_struct thread 的定义从 task_struct 的中间移动到了末尾,这是一个看似无害的改变。

  我看到 gdbserver 为每个线程发送一个 SIGSTOP 信号给内核并等待响应。内核确实接收到了所有信号,但只在错误情况下对其中一些信号作出响应。

  这与我的“ps”输出相匹配,因为我看到有些线程不处于 pthread_stop 状态,然后 gdbserver 被挂起。

  这是一个低级问题,因为在与 gdbserver 交互后,某些线程处于错误的进程状态,gdbserver 无法再控制它们。

  为此,Ariel 花了 3-4 天时间去详细地了解阅读与 PowerPC 架构相关的提交描述以及关于 task_struct 的更改,试图弄清楚这个问题是否在后续的内核版本中得到解决(这里剧透下:其实并没有解决)。

  直到这时,Ariel 突然意识到这可能是一个内存损坏问题,因为不像其他线程那样,被卡住的线程只被调度一次。

  起初,Ariel 忽略了这可能是一个内存损坏问题,因为在原始线程中提到:

  在这个过程中,Ariel 犯下了一个错误,他没有验证结构是否被零字节覆盖,这里也警醒很多开发者,要学会不断验证你的假设。

  在进一步查找解决方案过程中,凭借多年的软件工程师经验,Ariel 记得 x86 架构有可以用来触发数据写入断点的调试寄存器。果然,PowerPC 也通过 DABR 寄存器实现了类似的功能。

  在此基础上,Ariel 调查了如何在 Linux 上使用硬件断点,最终他在参考程序员问答社区 StackOverflow 上的一个答案()实现了新的 Linux 内核模块。该模块能够在 __state 字段上放置硬件断点,以找出是谁在写入它。

  详细来看,Ariel 通过自定义内核模块显示了写入 task_struct 的 __state 字段的地方的堆栈跟踪。此时,Ariel 注意到一个异常值,揭示了 ptrace_put_fpr(POKEUSER API 使用的函数)中的缓冲区溢出。这导致了 task_struct 的重要字段被覆盖,例如 __state,该字段存储进程的状态,并且内核还用它来跟踪哪些进程被调试器停止了。

  溢出的原因是什么?将一个应用于 32 位元素数组的索引用于 64 位元素数组。共有 64 个索引用于 FPR,因此可寻址的内存总共是 64 * 8 = 512 字节。但 fp_state.fpr 数组中只有 32 个条目,这意味着可用内存只有 32 * 8 = 256 字节。这允许用户(也就是 gdbserver)在数组末尾之后写入多达 256 字节。

  此时,Ariel 已经给 Michael Ellerman 发送了两个修复问题的补丁,根据其描述:

  另一个版本(与第一个版本相当不同),它解决了对原始提交的回复中收到的一些建议。后一个补丁实际上是基于现有的内核代码,模拟了 PowerPC64 上的 PowerPC32 操作(他们正确处理了 FPR 索引)。

  然而,Michael Ellerman 没有接受这两个补丁,而是实施了他自己的修复版本。

  在这一操作背后,Ariel 曾与 Michael Ellerman 沟通过,“如果能接受我的补丁,我会非常感激,这样我就可以因修复这个问题而得到认可,并成为内核贡献者。我也愿意与他合作,解决他的反馈,并发送后续版本的补丁。”

  对不起,我更喜欢我的版本。如果你想成为 Linux 内核贡献者,这里有一个问题可以让你来解决。

  对于这样的答复,Ariel 觉得困惑和侮辱。Ariel 认为,“他并不是想让我为修复问题而受到认可,而是想让我多做更多工作。我和我的公司应该得到适当的荣誉,因为我们花了很多心血来解决这个问题。”

  最终,Ariel 只得到一个“Reported-by”的标签,这也让他感觉真的很不公平。

  Reported-by 标签是给予那些发现错误并报告的人以荣誉,并希望激励他们将来再次帮助我们。

  Ariel 表示,「我绝对没有感到受到鼓舞,再次参与内核社区。相反,我感到被轻视,对我的工作没有得到适当的认可感到愤怒」。

  在 Ariel 看来,自己花了大量的时间和精力进行根本原因分析,然后去修复错误、测试和调试 Bug,与此同时,也从公司的其他工程师那里获得反馈,将最新的修复去适应最新的内核版本,并向 PowerPC 维护者 Michael Ellerman 发送了两个不同的补丁。

  遗憾的是,Michael Ellerman 没有接受 Ariel 的补丁,也没有引导他找到更好的解决方案,而是自行实施了他自己的修复版本,只给 Ariel 报告问题的荣誉(而这个问题其实在六年前就已经被其他人披露出来了)。

  「我的第一个内核贡献经历非常令人沮丧和令人泄气,因为要与那些认为得不到适当认可并不重要的人打交道」,Ariel 在自己的长文最后写道。

  现实来看,「名」与「利」也是驱动开发者不断反哺开源社区的一层重要驱动力。不过,很多人也认为维护者这样做也有自己的考量,从而在 HN 上引发了两种主要观点的碰撞。

  显然,如果不是他发现并提交了修复补丁,那么也不会有人会去修复这个安全问题。

  尤其是如果 Michael 看了他的补丁,改了一些风格上的东西,然后自己提交了补丁,那么不给予肯定是不道德的。我很惊讶大家都认为维护者是对的。

  如果有人剽窃了你的工作,并将其全部归功于你,尽管这是一次合作(即使 Ariel 的代码是垃圾代码,但这仍然是一次合作),你也不会觉得这样做对,也不会想再与他们合作。

  3. 原始补丁的提交信息说 在 PPC32 上,假定 TS_FPRWIDTH 为 1 也是可以的,但随后还是包含了这样的内容:

  很抱歉,你(Ariel)发现了这种微不足道的数组错配问题,但这并不意味着你的错误补丁就必须被接受。

  不过,维护者应该承认,Ariel 并没有简单地报告这个问题,而是通过调查找出了问题的根本原因,并用他自己的版本进行了几乎同样简单的修复。

  只给一个 Reported-by 的头衔缺乏准确性,无法表达问题是由该人彻底调查并找出根本原因的。

  最后,有用户评论道,需要给足够的认可,才能吸引更多的贡献者参与到开源项目中,否则会让他们不断「后退」:

  这种情况经常发生在项目的新贡献者身上,这才是正确的做法--它可以教导新贡献者,让他们尽快适应。如果你只是重做他们的工作,就会打击他们贡献的积极性,同时也不给他们可学习的机会。IM电竞app