ere*_*eOn 14 c c++ memory openssl memory-management
我一直在调试,现在几天崩溃,出现在了OpenSSL(讨论与维护者深处这里).我花了一些时间进行调查,所以我会尝试让这个问题变得有趣且内容丰富.
首先,为了给出一些上下文,我重现崩溃的最小样本如下:
#include <openssl/crypto.h>
#include <openssl/ec.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/engine.h>
int main()
{
ERR_load_crypto_strings(); OpenSSL_add_all_algorithms();
ENGINE_load_builtin_engines();
EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_sect571k1);
EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
EC_KEY* eckey = EC_KEY_new();
EC_KEY_set_group(eckey, group);
EC_KEY_generate_key(eckey);
BIO* out = BIO_new(BIO_s_file());
BIO_set_fp(out, stdout, BIO_NOCLOSE);
PEM_write_bio_ECPrivateKey(out, eckey, NULL, NULL, 0, NULL, NULL); // <= CRASH.
}
Run Code Online (Sandbox Code Playgroud)
基本上,此代码生成椭圆曲线键并尝试将其输出stdout
.类似的代码可以openssl.exe ecparam
在维基在线上找到.它在Linux上运行正常(valgrind报告根本没有错误).它只在Windows上崩溃(Visual Studio 2013 - x64).我确保正确的运行时链接到(/MD
在我的情况下,对于所有依赖项).
我不害怕任何邪恶,我在x64-debug中重新编译了OpenSSL(这次将所有内容都链接起来/MDd
),并逐步完成代码以查找有问题的指令集.我的搜索引导我使用此代码(在OpenSSL的tasn_fre.c
文件中):
static void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int combine)
{
// ... some code, not really relevant.
tt = it->templates + it->tcount - 1;
for (i = 0; i < it->tcount; tt--, i++) {
ASN1_VALUE **pseqval;
seqtt = asn1_do_adb(pval, tt, 0);
if (!seqtt) continue;
pseqval = asn1_get_field_ptr(pval, seqtt);
ASN1_template_free(pseqval, seqtt);
}
if (asn1_cb)
asn1_cb(ASN1_OP_FREE_POST, pval, it, NULL);
if (!combine) {
OPENSSL_free(*pval); // <= CRASH OCCURS ON free()
*pval = NULL;
}
// Some more code...
}
Run Code Online (Sandbox Code Playgroud)
对于那些不太熟悉OpenSSL及其ASN.1例程的人来说,这个for
-loop 基本上做的是它通过一个序列的所有元素(从最后一个元素开始)并"删除"它们(稍后更多).
在崩溃发生之前,删除了3个元素的序列(at *pval
,即0x00000053379575E0
).看一下内存,可以看到以下情况发生:
该序列是12个字节长,每个元素是4个字节长(在这种情况下,2
,5
,和10
).在每个循环迭代中,元素"已删除"的OpenSSL通过(在这种情况下,既没有delete
或free
分别称为:他们只是设置为特定值).以下是一次迭代后内存的显示方式:
这里设置的最后一个元素是ff ff ff 7f
我假设OpenSSL确保在以后未分配内存时没有密钥信息泄漏的方法.
在循环之后(以及在调用之前OPENSSL_free()
),内存如下:
所有元素被设定为ff ff ff 7f
,asn1_cb
在NULL
这样没有调用.接下来的事情是打电话给OPENSSL_free(*pval)
.
这种free()
对似乎是有效和分配的内存的调用失败并导致执行中止并显示消息:"HEAP CORRUPTION DETECTED".
对此感到好奇,我迷上了malloc
,realloc
并且free
(正如OpenSSL允许的那样)确保这不是一个双重免费或免费的永不分配的内存.事实证明,内存0x00000053379575E0
实际上是一个12字节的块,它确实被分配(并且以前从未被释放).
我无法弄清楚这里发生了什么:从我的研究来看,它似乎free()
失败了通常返回的指针malloc()
.除此之外,此存储器位置之前被写入几条指令而没有任何问题,这证实了存储器被正确分配的假设.
我知道在没有所有信息的情况下远程调试很难,如果不是不可能的,但我不知道我的下一步应该是什么.
所以我的问题是:Visual Studio的调试器是如何检测到这种"HEAP CORRUPTION"的?来自呼叫的所有可能原因是什么free()
?
一般来说,可能性包括:
malloc()
和朋友在这里放置额外的簿记信息,例如大小,可能是一个完整性检查,你将通过覆盖失败.malloc()
.free()
-d 的块.