这是 Quora 上的一个问题,其中 Mick 的回答获得了 13.5k 的“赞同”,他是这么回忆的:
曾经有位心理学的博士雇我查一个 BUG,程序是他的一个学生写的,经常会出现奇怪的输出。程序的功能是,从文件中读取数据,提出 50 个问题,进行一系列计算,然后基于这位博士的研究来得出一个分数。程序跑在大学的一台 3B2 上。他给我演示了程序,确认这个 Bug 可以重现——每次在问题之间切换的时候,总会闪出一些奇怪的字符。我觉得这应该很简单,所以答应了他,并且我们同意按小时付费。
Day1
我来到这台 3B2 面前,用那个学生的帐号登录,找到了用C写的源代码,并开始测试。代码可读性非常差,所有的代码都写在了一行,变量名都是三个字母,而且还是随机组合的!真庆幸我当初选择按小时收费。我把代码格式化为习惯的风格,这样看起来多少还能读。
做完之后,我用 curses 库移到屏幕上一个点,打印一个问题和答案,然后等待回应。但是在打印出第一行之后,出现了一些乱码,过了大约1/2 秒之后,乱码被问题覆盖掉。这个问题应该很容易解决,只有五个地方需要打印信息,所有的地方都出现了一闪而过的乱码。小意思,只要删掉 mvpwintw ()应该就 ok 了。我删掉之后开始编译,觉得问题差不多解决了。但是运行的时候,干扰信息又出现了!只是乱码变了,但是症状还是一样!
我检查了一下代码,发现竟然回到了我改之前的样子!15 个文件,混乱的格式,三个字母的变量。当初为什么不备份一下代码呢,真想射自己一枪。我再一次格式化它们,这次把代码放到三个文件中,以不同的名字命名。然后备份了整个文件夹,并且将权限设置为只读。编译之后,一切正常。一运行,这个文件夹中又出现了 15 个文件!我改过之后的源代码没有被删除,然后干扰信息又回来了。
我明白了,这一定是在硬盘的某个地方的代码,在编译的时候会将程序添加到我修改的代码中去。所以我准备搜索一下 include 区域(/usr/include),因为我们用的是研究的版本,所以除了 kernel 别的代码都在机器上。头文件太多了,所以在 3B2 上搜索需要花一些时间。以上,就是第一天的工作了。
Day2
从磁盘的搜索中没有得到什么结果,这说明这些乱码要么是被加密了,要么就是在 lib 中的某个地方。但是我没有搜到,我决定搜索所有的文本文件,这次花的时间比昨天更长,第二天就这这样过去了。
Day3
没有结果。字符串是加密的。我只好根据所有的头文件来一点一点查了。这会花费相当多的时间,我们同时还警告了学校,可能有人得到了 Phelps 博士计算机的 root 权限。但他们却对此并不关心,可能只是一台实验室的计算机吧。
我打开#include 文件,却没有发现代码。后来发现这些都被编译成一个文件了。也没什么关系,毕竟我们有源代码,大不了就重新编译所有的库。
Days4-6
接下来是最难的部分了,我们好不容易向学校的书呆子说明了问题。然后让 Mark(我觉得他能做 Unix 管理员完全是因为娶了 Dean 的女儿)开始学习编译。最后他终于同意交给我来做,因为他什么都不会。在第 6 天最后,编译工作终于完成了。
我拿出修改之后的代码,重新开始编译。一切正常,然后我运行了一下,天呐!这问题又出现了。源代码分成了 15 个文件,干扰信息又出来了。简直跟魔法一样,我觉得自己被打败了,问题肯定不在源代码里。Phelps 博士也有点不乐意了,他觉得这么长时间,即使重新写一个也该完成了。“当然”,我失落地说,“你是对的,说不定重写比较好。”“好的,我们明天开始重写。”博士说。
Day7
见鬼去吧,我不会认输的!我跟 Phelps 博士说:“你不用付给我钱了,只要给我时间,我一定要找到这个 bug。”
Days8-14
我学聪明了,他一定是修改了一些库,我开始研究编译出来的汇编(虽然我之前根本就不懂汇编),从开始学习,到最后看懂汇编代码,花了六天。虽然根本没发现什么异常,完全就是在浪费时间。
Day 15
突然,我意识到,问题可能出在编译器上,一定是。每次编译代码的时候,编译器就会往源代码中添加干扰信息。我以前也听说过这种情况。
啊耶!我找到了!我们也有编译器的源代码,我查了一下,谢天谢地,终于找到了。编译器链接器中的代码是这样的:
1) 检测所有对 fopen()的调用,在打开的文件中查找 Phelps 博士的问题,如果找到
2)编译的时候,重写 15 个文件
3)使用这 15 个文件编译博士的程序,在链接的时候会输出-o 形式的名字
编译器被这个学生修改了,以便往 Phelps 博士的程序中添加代码。
几天之后,AT&T的技术支持提供了原版的编译器和链接器代码,我们重新编译,替换了被修改的编译器和链接器。
但是,问题还没有解决。编译器被其他我们没有的源代码污染了。这些代码存在于现在可执行的编译器中,在编译编译器的时候,会加入污染代码。但是它并不修改/usr/src 中的代码,而是将其复制到一个隐藏文件夹,修改编译器源代码,编译,最后删除隐藏文件夹。AT&T发现这个问题花了好长时间。这个学生修改了编译器,让编译器重新编译的时候加入污染代码。最后我们不得不从另一台 3B2 机器上复制过来编译器的字节码文件版本,才终于解决这个问题。
通过编译器的代码我们还发现,如果编译/sbin/login 的话会加入一些后门代码,可以允许任何人使用特定的密码来登入 root。这台电脑可以通过调制解调器或 Tymnet 接入。终于,这件事引起了校方注意。
这人真是个天才,不过,也太恐怖了!
其他网友回答
- Bug 只在产品中有,开发版本中却没有或者不可重现
- bug 触发的几率很小很小,却又不能小到可以忽视
- 去改不是你写的代码中的 bug,原来写代码的这家伙已经离职了
- 发生 bug 的地方是 99.9% 可靠的库,是你绝对不会怀疑的地方
- 几年来很多人尝试去修改的 bug,但是没有人成功过
- 存在逻辑错误的 bug,只有在运行一段时间之后才会触发
- debug 需要特定领域的知识,正是你不懂的
- debug 的 deadline 很紧
- Stack Overflow 挂了
- 在 Stack Overflow 上搜索问题,发现有人一年前问了同样的问题,但是没有人回答。
- 分号键失灵
- 看自己一年以前的代码,有种“这真他妈是我写的”的感觉
- 库没有文档
- 客户又改需求了
- ==写成了=
- IE(如果你做前端)
- Github merge 冲突
- 在错误的目录下 rm -rf
- “My code works, I don’t know why”(这句话不需要翻译了)
- 开会,开会,开会……作为程序员让我去死吧
那么,你的噩梦又是什么呢?