心脏出血漏洞 Heartbleed 固定大小缓冲区分析

Heartbleed 是来自OpenSSL的紧急安全警告:OpenSSL出现“Heartbleed”安全漏洞。这一漏洞让任何人都能读取系统的运行内存,文名称叫做“心脏出血”、““击穿心脏””等。

为什么固定大小缓冲区这么流行

心脏出血漏洞是最新发现的安全问题,由长字符串导致缓冲区越界。最常见的缓冲区越界发生在如下两种条件同时满足中:

程序中一个组件A向另外一个组件B传递了一个指针,也可能同时传递长度信息
组件B忽略了,或者没有正确使用这个长度信息。此信息规定了指针所指向的内存区域能够存储多少数据。

上述条件都满足的程序结构之所以能引起缓冲区越界,一个重要原因是,调用者A分配了一块内存,但是只有当数据被真正读取的时候,才能知道程序到底需要分配多大的内存空间,因为不会被读取的数据,我们完全可以不保存它。 换句话说,一个函数负责分配空间,然后调用另外一个函数向该空间填充数据的结构,都会有点不安全。

即使这点危险能够通过正确的检查内存边界的方式成功避免,但是边界检查也会引入其自身存在的负面效果。 比如,我的一位前同事, 他创建了一个文本文件, 此文本文件压缩了数万个字符构成的单行字符串。 然后他又将这个文件作为输入,传递给了许多其他部件,比如编译器,文本处理程序等等。 几乎所有的这些程序都会出现这样那样的异常行为,例如,直接崩溃,或者悄无声息的忽略掉输入字符串的最后一截。

应对该问题的简单解决方案是:如果程序中任何部分涉及读入长度不确定的输入,那就应该负责分配足够大的内存来保存这些输入。当然,在C++语言中使用STL标准库就能轻松实现。但是在C语言中,却没有简单有效的实现代码,可以从输入读入一个单行字符串,返回包含该输入的内存指针,无视输入的长度。 任何在C语言中实现此功能的尝试,都或多或少的存在一些副作用。

我也曾静下心来在当时工作的部门,尝试在C语言库中增加一个针对上述问题的解决方案。 如果有人想要将使用了我写的函数的代码分享到别的地方,我想让他们也能将我写的函数作为其中一部分发布出去。 我所增加的函数的名称是readline,且为方便使用而设计:只需要传入一个文件指针(例如 stdin)作为输入,此函数就能读入一整行的输入,返回一个指向以NULL结尾的此字符串的第一个字符,无需考虑输入的长度。 如果读到了文件结束符(EOF),就返回一个NULL指针。

显然,任何分配内存,并返回指向该内存指针的函数都存在一个问题:该内存何时被释放? 我考虑过让readline函数的调用者负责释放,但是觉得很多调用函数可能会忘记释放内存。那么此时的缓冲区越界问题又变成了内存泄漏问题。

最后,我决定采取在别的地方看到的策略:readline将会返回一个指向内存空间的指针,并且保证其中的内容在下一次调用readline函数之前都会保持不变。这种策略不仅可以减少用户的担忧,而且也能让实现更简单:程序将存储一个静态指针(static pointer) 指向(动态分配的)缓冲区。缓冲区的大小将随读入的行的长度需要增减。 这种机制能让readline函数在最常用的场景中简单好用,并且安全。

代码如下

char *line;
while ((line = readline(stdin)) != NULL) {
/* Process a line */
}

当然,这种机制也有他自身存在的问题。比如,在同一个表达式中,两次调用readline函数将导致未定义行为(undefined behavior)。因为当程序员计划在第二次调用readline()函数之后,试图保存两次调用readline所读入的全部数据时,第一次调用所创建的内存空间,将在第二次调用时被释放掉。 此外,该代码会在读入输入的最后一行后,因为不再被调用,会一直占用内存空间。实际上,它所浪费的内存空间是整个输入中最长的那一行的长度。在实现该函数时,虽然我在缓冲区小于输入行长度时,都会重新分配更大的缓冲区,但是却没有允许缓冲区变校因为我觉得反复分配释放内存的所导致的性能下降,相比于在少数清醒下浪费一点点内存空间来说不值得。

很显然,我高估了人们所能忍受的内存分配延迟开销:当我几个月后回头看这些代码时,发现有人已经将我所写的readline版本完全修改为固定的4096-字符缓冲区。据我所了解,他的动机是完全避免运行时存储分配的开销。换句话说,为了避免只有在少数情况下才存在的多次内存分配器调用,他悄悄的让所有使用readline函数的程序,在行的长度大于4096个字符时,出现了很大的安全隐患。

之所以花了大量的篇幅讲这样一个故事,是因为它透露出我觉得非常重要的几点:

缓冲区越界通常发生在程序中某个部分A分配内存,而实际需要的存储空间大小只有另一个部分B知道。
在程序中的同一个函数内部分配内存,并将其填充。这种方式解决了缓冲区分配的问题,而付出的代价是必须要让程序的另一个函数负责内存的释放。内存的分配和释放在程序的两个不同的函数中。
这种分配和释放在两个不同的函数将会导致程序的可用性问题,除非在编程语言上有系统的支持,否则很难绕开。
即使用户为了安全和通用性,需要接收这个现实,但是他们可能也无法接受动态分配内存引入的开销。

我想,程序员不愿为了安全而引入运行时开销,是很多安全性问题之所以普遍存在的原因。 我们将在下周详细聊聊这种现象。

(0)

相关推荐

  • ps裁剪怎么固定大小

    我们在使用图片的额时候,经常需要固定大小来裁剪图片,或者固定比例裁剪图片,使用ps裁剪怎么固定大小,就是本教程要说的 操作方法 01 首先打开一个图片,然后点击选择工具.如图所示,点击即可 02 然后 ...

  • Photoshop如何绘制固定大小的矩形或椭圆选区

    在设计网页的时候我们会经常用到制作固定大小或者长宽比的图片,尤其是在一些固定模板的网页设计时.那么怎样用Photoshop直接绘制固定尺寸大小的选区或者固定比例的选区哪? 第一绘制固定大小矩形选区 绘 ...

  • 如何使用矩形选框工具选择固定大小的区域

    随着生活中的需要,图片处理成为生活中常遇到的事情,photoshop是我们最常用的图片处理软件,针对一张图片,如果框选固定大小的图片部分是我们今天的主要问题.下面小编为大家讲解一下. 操作方法 01 ...

  • Photoshop如何快速创建固定大小的矩形等形状

    形状工具一般都是创建比较有规则的形状,比较常用矩形工具和椭圆工具,比如一般网页按钮尺寸都是有大小限制的,那么我们怎么快速创建固定大小尺寸的形状哪? 操作方法 01 选择矩形工具 02 直接在画布上单击 ...

  • ps如何按照固定大小截图

    ps如何按照固定大小截图,我给他一个固定的尺寸,我们怎么截图呢的,怎么做设置呢,大家看我操作就可以了 操作方法 01 我们找到ps找到我们的应用程序,双击打开 02 我们新建一个文档,我们新建一个80 ...

  • ps怎么设置矩形大小,ps画固定大小的矩形

    有网友问:ps怎么设置矩形大小,ps画固定大小的矩形?下面,我给这位网友解答. 设置矩形大小 01 启动PS,新建画布后,再按下U键,调出矩形工具.在画布上画出一个矩形 02 鼠标再移到菜单栏的窗口这 ...

  • 如何用PicPick截取固定大小的图片并添加箭头?

    在工作和生活中,我们经常会遇到这样的情况,我们总希望截一些固定大小的图片,也就是说长度和宽度都是固定的,比如说我们写搜狗指南,总是希望发布的图片是固定大小的,这样我们的界面排版就会漂亮很多,下面就详细 ...

  • 如何截取固定大小的图片

    在日常生活中我们经常需要截取一些固定大小的图片,但是很多小伙伴却不知道怎么操作,现在小编就来教教大伙. 软件使用 01 搜索下载"红蜻蜓抓图精灵 ". 02 对软件进行安装. 03 ...

  • word制作固定大小标签

    如何使用word2007在一张A4纸上制作打印多个标签. 操作方法 01 word2007中新建A4横向. 02 插入<形状<新建绘图画布. 03 注意:如果绘图画布未能完全显示,请取消选 ...