云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

云主机_网络连接服务器_优惠券

小七 141 0

然而不太可能:处理器错误的故事

处理器问题最近一直是新闻,由于崩溃和幽灵漏洞。但是,一般来说,编写软件的工程师都认为计算机硬件以可靠、易于理解的方式运行,任何问题都存在于软硬件划分的软件方面。现代处理器芯片通常在一秒钟内执行数十亿条指令,因此任何不规则的行为都很难触发,否则很快就会变得显而易见。但有时这种处理器硬件可靠的假设并不成立。去年在Cloudflare上,我们受到了英特尔一款处理器型号的缺陷的影响。以下是我们如何发现一个神秘问题的故事,以及我们如何追查原因的故事。Alterego的CC-BY-SA-3.0图像开场白早在2017年2月,Cloudflare就披露了一个安全问题,后来被称为Cloudbleed。这一事件背后的错误在于在我们的服务器上运行的一些解析HTML的代码。在某些涉及无效HTML的情况下,解析器将从被解析的缓冲区末尾以外的内存区域读取数据。相邻的内存可能包含其他客户的数据,这些数据将在HTTP响应中返回,结果是Cloudbleed。但这并不是虫子的唯一后果。有时它会导致无效的内存读取,导致NGINX进程崩溃,在Cloudbleed发现之前的几周内,我们有指标显示这些崩溃。因此,我们为防止此类问题再次发生而采取的措施之一就是要求对每起车祸进行详细调查。我们非常迅速地解决了Cloudbleed问题,并因此结束了由于该漏洞导致的崩溃,但这并没有阻止所有的崩溃。我们开始调查其他车祸。撞车不是一个专业术语但在这种情况下,"崩溃"到底是什么意思呢?当处理器检测到试图访问无效内存(更准确地说,是页表中没有有效页的地址)时,它会向操作系统内核发出页面错误的信号。在Linux的情况下,这些页面错误导致将SIGSEGV信号传递到相关进程(SIGSEGV的名称来自历史上的Unix术语"segmentation violation",也称为segmentation fault或segfault)。SIGSEGV的默认行为是终止进程。正是这种突然的终止是云出血病毒的一个症状。无效内存访问和最终终止的可能性主要与C或C++中编写的进程有关。更高级的编译语言,如Go和基于JVM的语言,使用类型系统来防止可能导致访问无效内存的低级编程错误。此外,这类语言有复杂的运行时,它们利用页面错误来实现技巧,从而使它们更加高效(进程可以为SIGSEGV安装信号处理程序,这样它就不会终止,而是可以从这种情况下恢复过来)。对于Python这样的解释语言,解释器检查导致无效内存访问的情况不会发生。因此,未处理的SigSeV信号往往局限于C和C++编程。SIGSEGV不是唯一一个指示进程错误并导致终止的信号。我们还看到了由于SIGABRT和SIGILL导致的进程终止,这表明我们的代码中存在其他类型的错误。如果我们对这些终止的NGINX进程的唯一信息是所涉及的信号,那么调查原因将是困难的。但是Linux(以及其他Unix派生的操作系统)还有一个提供了前进路径的特性:核心转储。核心转储是进程突然终止时操作系统写入的文件。它记录进程终止时的完整状态,允许事后调试。记录的状态包括:进程中所有线程的处理器寄存器值(某些程序变量的值将保存在寄存器中)进程常规内存区域的内容(给出其他程序变量和堆数据的值)对文件的只读映射(如可执行文件和共享库)的内存区域的描述与导致终止的信号相关联的信息,例如导致SIGSEGV的尝试内存访问的地址因为核心转储记录了所有这些状态,所以它们的大小取决于所涉及的程序,但是它们可以相当大。我们的NGINX内核转储通常是几GB。一旦记录了核心转储,就可以使用gdb之类的调试工具来检查它。这样就可以根据原始程序源代码来研究核心转储的状态,这样您就可以以一种相当方便的方式查询程序堆栈、变量和堆的内容。简而言之:为什么核心转储被称为核心转储?这是一个历史术语,起源于20世纪60年代,当时随机存取存储器的主要形式是磁芯存储器。当时,core这个词被用作内存的简写,所以"core dump"意味着内存内容的转储。康斯坦丁·兰泽的CC BY-SA 3.0图像比赛正在进行中当我们检查核心转储时,我们能够将其中一些跟踪回代码中更多的bug。他们没有像Cloudbleed那样泄露数据,也没有给我们的客户带来其他安全隐患。有些可能允许攻击者试图影响我们的服务,但核心转储表明,这些bug是在无害的情况下触发的,而不是攻击。在生成的核心转储数量显著下降之前,我们不必修复许多这样的错误。但是在我们的服务器上仍然有一些核心转储正在生成——大约每天在我们整个服务器组中生成一个。而找到这些遗留问题的根本原因则更加困难。我们逐渐开始怀疑这些剩余的核心转储不是由于代码中的错误造成的。产生这些怀疑是因为我们发现在核心转储中记录的状态似乎不可能基于程序代码(在检查这些情况时,我们并不依赖于C代码,而是查看编译器生成的机器代码,以防我们处理编译器的错误)。起初,当我们在Cloudflare的工程师中讨论这些核心垃圾时,人们对其原因可能不在我们的代码范围内的想法产生了一些健康的怀疑,而且至少有一个关于宇宙射线的笑话。但随着我们积累了越来越多的例子,很明显发生了一些不寻常的事情。找到另一个我们称之为"神秘核心转储"的工作已经成为惯例,尽管这些核心转储的细节是多种多样的,触发它们的代码散布在我们的代码库中。他们的共同特点是明显的不可能。对于产生这些神秘核心转储的服务器来说,没有明显的模式。我们的服务器群平均每天都有一个。所以样本量不是很大,但它们似乎均匀地分布在我们所有的服务器和数据中心,没有一个服务器被两次攻击。单个服务器得到一个神秘的核心转储的概率似乎非常低(大约每10年服务器正常运行时间就有一个,假设我们所有的服务器都有同样的可能性)。但由于我们有大量的服务器,我们得到了稳定的涓涓细流。寻求解决办法神秘的旧件丢弃率很低,对我们的客户服务没有太大影响。但我们仍然致力于检查发生的每一次堆芯废料。虽然我们在识别这些神秘的堆芯废料方面做得更好,但对它们的调查和分类是对工程资源的消耗。我们想找到根本原因并解决它。所以我们开始考虑一些看似合理的原因:我们研究了硬件问题。尤其是记忆错误是一种现实的可能性。但是我们的服务器使用ECC(纠错码)内存,它可以检测并在大多数情况下纠正任何内存错误。此外,任何内存错误都应该记录在服务器的IPMI日志中。我们确实在服务器群上看到一些内存错误,但它们与核心转储无关。如果不是内存错误,那么处理器硬件会有问题吗?我们主要使用各种型号的英特尔至强处理器。它们在可靠性方面享有很好的声誉,虽然核心转储的比率很低,但它似乎太高了,不能将其归因于处理器错误。我们搜索类似问题的报告,并在小道消息上询问,但没有听到任何似乎与我们的问题相符的消息。在我们进行调查时,英特尔Skylake处理器的一个问题暴露出来。但那时我们还没有在生产中使用基于Skylake的服务器,而且这个问题与特定的代码模式有关,而这些代码模式并不是我们神秘的核心转储的一个共同特征。可能是Linux内核错误地记录了内核转储,所以代码中的一个bug导致的一个普通的崩溃最终看起来很神秘?但我们在核心垃圾堆中没有发现任何类似的模式。另外,对于未处理的SIGSEGV,内核会生成一个日志行,其中包含关于原因的少量信息,如下所示:nginx fl中FFFFFF 810C644A ip 0000560AF22884A sp 00007ffd771b9550错误15的SEG故障[5600aeed2000+e09000]我们对照核心转储检查了这些日志行,它们总是一致的。内核的作用是控制处理器的内存管理单元,为应用程序提供虚拟内存。因此,该领域的内核错误可能会导致令人惊讶的结果(我们在Cloudflare的另一个上下文中遇到过这样的bug)。但是我们检查了内核代码,并搜索了针对Linux的相关bug的报告,但是没有找到任何东西。几个星期以来,我们寻找原因的努力没有取得成果。由于神秘核心转储的频率非常低,当基于每台服务器考虑时,我们无法遵循通常的最后一种方法