Crypto++ RSAES 类中的内存泄漏

Posted

技术标签:

【中文标题】Crypto++ RSAES 类中的内存泄漏【英文标题】:Memory leak in Crypto++ RSAES class 【发布时间】:2017-01-23 02:36:31 【问题描述】:

我正在尝试学习如何使用Crypto++ 类。我的目标是为 RSA 加密生成公钥和私钥,然后对明文进行基本的加密和解密。

所以我以他们的 from here 为例——“RSA Encryption Scheme (OAEP and SHA) using Filters”,为了可读性稍作修改:

这部分工作正常:

CryptoPP::AutoSeededRandomPool rng;

//Generate Parameters
CryptoPP::InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 3072);

//Create Keys
CryptoPP::RSA::PrivateKey privateKey(params);
CryptoPP::RSA::PublicKey publicKey(params);

std::string plain="Hello world!", cipher, recovered;

//Encryption
CryptoPP::RSAES_OAEP_SHA_Encryptor e(publicKey);

但是当我调用这个块时:

CryptoPP::StringSink* pSS = new CryptoPP::StringSink( cipher );
CryptoPP::PK_EncryptorFilter* pEF = new CryptoPP::PK_EncryptorFilter( rng, e, pSS);

CryptoPP::StringSource ss1( plain, true, pEF);

它会导致内存泄漏。我在Visual Studio 输出窗口中得到以下信息:

Detected memory leaks!
Dumping objects ->
24781 normal block at 0x029BCFF8, 28 bytes long.
 Data: <class CryptoPP::> 63 6C 61 73 73 20 43 72 79 70 74 6F 50 50 3A 3A 
24780 normal block at 0x029BCFB0, 8 bytes long.
 Data: <        > F8 CF 9B 02 00 00 00 00 
Object dump complete.

好的,所以我做了最明显的事情并添加了这些:

delete pEF;
delete pSS;

但它导致了一个未处理的异常,所以我假设 Crypto++ 类中的一个析构函数负责删除其中一些对象。

所以问题是——这个泄漏是从哪里来的?

我尝试使用 Visual Studio 调试器进入 StringSinkPK_EncryptorFilterStringSource 以查看发生了什么,但代码非常复杂,无法立即弄清楚。

知道如何修复这些内存泄漏吗?

【问题讨论】:

您当然可以将泄漏减少到minimal reproducible example @jww:我能够自己回答我的第二个问题。请参阅我的 OP 中的编辑。 @jww:嘿!我刚刚发布了第二个问题的解决方案,你删除了它。什么鬼? 另见Windows Debug build memory leaks cleared。该库从动态初始化转移到静态初始化,以避免在 Microsoft 平台上出现问题。如果可用,它是使用静态初始化的“最大努力”。否则,事情会退回到动态初始化。在这里,“静态初始化”意味着将库对象放入 CRT 静态初始化列表中,该列表运行构造函数并调用全局对象 ctor(而不是静态 C++ 对象)。 @jww:好的。那是好消息。这是否意味着我可以下载包含该修复程序的新版本 CryptoCPP 库? 【参考方案1】:

它会导致内存泄漏。我在 Visual Studio 中得到以下信息 输出窗口:

Detected memory leaks!
Dumping objects ->
24781 normal block at 0x029BCFF8, 28 bytes long.
 Data: <class CryptoPP::> 63 6C 61 73 73 20 43 72 79 70 74 6F 50 50 3A 3A 
24780 normal block at 0x029BCFB0, 8 bytes long.
 Data: <        > F8 CF 9B 02 00 00 00 00 
Object dump complete.

您使用的代码看起来有点不寻常,但我相信它的格式很好。

我相信您看到的是微软几十年前的错误typeinfo.name() memory leaks。它从 VC 5.0 或 VC 6.0 时代就已经存在。


CryptoPP::StringSink* pSS = new CryptoPP::StringSink( cipher );
CryptoPP::PK_EncryptorFilter* pEF = new CryptoPP::PK_EncryptorFilter( rng, e, pSS);

CryptoPP::StringSource ss1( plain, true, pEF);

管道通常是这样的:

CryptoPP::StringSource ss( plain, true,
    new CryptoPP::PK_EncryptorFilter( rng, e,
        new CryptoPP::StringSink( cipher )));

上面代码后面的所有内容都是红鲱鱼。你掉进了一个兔子洞,因为微软不会修复他们的错误。


好的,所以我做了最明显的事情并添加了这些:

delete pEF;
delete pSS;

但它导致了未处理的异常

是的,那是不对的。来自Readme.txt:

* 重要使用说明 *

    如果 A 的构造函数采用指向对象 B 的指针(除了基本类型,如 int 和 char),则 A 拥有 B 并将删除 B 在 A 的破坏。如果 A 的构造函数引用了 对象 B,则调用者保留 B 的所有权,不应 销毁它,直到 A 不再需要它为止。

    Crypto++ 在类级别是线程安全的。这意味着您可以在多线程应用程序中安全地使用 Crypto++,但您必须提供 当多个线程访问一个公共 Crypto++ 对象时同步。

pEFpSS 是指针,它们属于其他人。他们被删除了两次,导致异常。


Crypto++ RSAES 类中的内存泄漏 ...

如果您运行 cryptest.exe 程序,那么您将看到 60 或 80 个泄漏报告。大约 10 或 15 年来,我一直试图找到解决该错误的方法。最近是在 Stack Overflow 上的 How to remediate Microsoft typeinfo.name() memory leaks?。

编辑还可以在用户列表中查看Windows Debug build memory leaks cleared 和Commit 301437e693fe8bff。该库从动态初始化转移到静态初始化,以避免在 Microsoft 平台上出现问题。在 Windows 上使用 inti_seg 访问静态初始化器列表;和 constructorinit_priority 属性与 GCC。

如果可用,使用静态初始化是“最大努力”。否则,事情会退回到动态初始化。在这里,“静态初始化”意味着将库放入 CRT 静态初始化列表中,该列表运行构造函数并调用全局对象 ctor(而不是普通 C++ 静态对象)。

【讨论】:

感谢您的认可。是的,我在其他来源看到人们指责微软编译器。但是,如果我从CryptoPP v.5.6.5 降级到 v.5.6.2,内存泄漏怎么会消失呢? @c00000fd - 我不确定。 5.6.2 和 5.6.5 之间发生了很大变化。我们也没有看到你的完整节目。但我相当肯定您显示的症状是 Microsoft 错误。首先,分配 N 转储内部(运行时)标识符 class CryptoPP::。其次,紧随其后的是 N+1 的数据转储。注意:跟踪类和名称的内部方案在 VS2015 中发生了变化。在内存转储期间它们不再是连续的。 我们走简单的路线来证实你的理论怎么样。你能发布一个简单的例子来演示我可以在 VS2008 中编译的 typeinfo.name() 泄漏吗?只有两三行代码。 @jww “上面代码后面的一切都是红鲱鱼。你掉进了兔子洞,因为微软不会修复他们的错误。”请考虑穷人,他们的母语不是英语。在我明白你的意思之前,我经历了很多鸭鸭式。

以上是关于Crypto++ RSAES 类中的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL VBO 会泄漏内存吗?

如何避免内部类中的内存泄漏

内存泄漏与垃圾回收机制

分析 ThreadLocal 内存泄漏问题

内存泄漏和内存溢出的区别

Android开发常见的Activity中内存泄漏及解决办法