如何减少OpenSSL 3的动态内存分配,尤其是RAND_bytes的调用?
测试代码(MRE)
#include <openssl/rand.h> int main() { unsigned char buf[16]; return RAND_bytes(buf, 16); }
OpenSSL 1编译后的内存分配情况
使用OpenSSL 1编译链接后,通过valgrind检测到的malloc调用情况如下:
$ valgrind ./a.out ==4904== Memcheck, a memory error detector ==4904== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==4904== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==4904== Command: ./a.out ==4904== ==4904== ==4904== HEAP SUMMARY: ==4904== in use at exit: 0 bytes in 0 blocks ==4904== total heap usage: 32 allocs, 32 frees, 21,412 bytes allocated ==4904== ==4904== All heap blocks were freed -- no leaks are possible ==4904== ==4904== For counts of detected and suppressed errors, rerun with: -v ==4904== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
OpenSSL 3编译后的内存分配情况
使用OpenSSL 3编译链接后,valgrind检测到malloc调用次数大幅增加:
$ valgrind --tool=memcheck ./a.out ==18334== Memcheck, a memory error detector ==18334== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==18334== Using Valgrind-3.20.0 and LibVEX; rerun with -h for copyright info ==18334== Command: ./a.out ==18334== ==18334== ==18334== HEAP SUMMARY: ==18334== in use at exit: 0 bytes in 0 blocks ==18334== total heap usage: 6,934 allocs, 6,934 frees, 626,370 bytes allocated ==18334== ==18334== All heap blocks were freed -- no leaks are possible ==18334== ==18334== For lists of detected and suppressed errors, rerun with: -s ==18334== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
问题背景
推测该差异源于OpenSSL 3的动态provider接口,即使尝试加载OpenSSL的"null" provider,malloc调用次数仍有2500次,现需找到将OpenSSL 3内存使用降至接近OpenSSL 1水平的方案。
可行优化方案
静态链接+手动加载必要provider:OpenSSL 3默认会加载多个动态provider(如default、legacy等),静态链接时可手动初始化仅需的provider(比如满足随机数需求的
baseprovider),避免冗余初始化。修改代码并编译:#include <openssl/rand.h> #include <openssl/provider.h> int main() { unsigned char buf[16]; // 仅加载base provider OSSL_PROVIDER *base = OSSL_PROVIDER_load(NULL, "base"); if (!base) return 1; int ret = RAND_bytes(buf, 16); OSSL_PROVIDER_unload(base); return ret; }编译命令:
gcc -o a.out test.c -lcrypto -static编译OpenSSL 3时禁用动态provider:自行编译OpenSSL 3时,添加
--no-dynamic-providers配置选项,强制使用静态编译的基础provider,彻底关闭动态加载机制,大幅减少初始化阶段的内存分配:./configure --no-dynamic-providers --prefix=/usr/local/openssl3 make && make install使用该版本编译测试代码,malloc调用次数会接近OpenSSL 1的水平。
复用初始化上下文:如果是长期运行的服务,可在程序启动时一次性完成OpenSSL的初始化工作,复用全局上下文,避免每次调用都重复初始化相关组件,降低整体内存分配频次。
直接调用低级随机数接口:若仅需随机数生成,可跳过高层的
RAND_bytes,直接调用baseprovider提供的低级核心API,减少抽象层带来的初始化开销,但这种方式对OpenSSL 3核心API的熟悉度要求较高,代码耦合性较强。
内容的提问来源于stack exchange,提问作者Greg

