OpenSSL堆栈API - 将对象推入堆栈后释放对象

4
我正在尝试理解在将X509对象推入STACK_OF(X509)结构后是否需要释放它们,或者sk_X509_free()函数调用是否会包括内容在内一并释放。我在OpenSSL中没有找到相关文档。请注意,sk_X509_free()会释放所有内容。
std::vector<std::string> caPems;    

// Fill the vector from input
// ...


BIO *bufio = NULL;
X509 *x509 = NULL, *x509_ca = NULL;
bool success = false;
STACK_OF(X509)* x509_ca_stack;

x509_ca_stack = sk_X509_new_null();
if (x509_ca_stack) {
    success = true;
    for (const std::string& caPem : caPems) {
        BIO_new_mem_buf(caPem.c_str(), caPem.size());
        PEM_read_bio_X509(bufio, &x509_ca, NULL, NULL);
        BIO_free_all(bufio);
        if (x509_ca != nullptr) {
            sk_X509_push(x509_ca_stack, x509_ca);
            x509_ca = NULL; // should I free after push???
        } else
            success = false;
    }
    if (success)
        foo(x509_ca_stack);
    sk_X509_free(x509_ca_stack); // or is this free enough for the entire stack?
} else {
    printf("ERROR: failed loading cert\n");
}

编辑:valgrind没有帮助,无论我是否释放内存,它都没有显示任何信息。

1
不太确定,但看源代码似乎在调用sk_X509_push()时会复制数据。sk_X509_push() 调用 OPENSSL_sk_push(),它又调用 OPENSSL_sk_insert()。因此,您应该释放本地数据的副本。期待其他评论。 - Frankie_C
请参见https://dev59.com/o5Pfa4cB1Zd3GeqPHsaHsk_xxx_push()函数执行分配和复制操作。 - Oleg
1个回答

3
自openssl 1.1版本起,X509(和许多其他对象)是引用计数的。sk_XXX_push() API不会自动增加引用计数,因此在进行推送时,你正在交出X509的引用。因此,你的代码是无效的,并且存在泄漏,因为sk_X509_free()不会减少内部X509的引用计数。
编辑:我已经更正了上述内容,我最初认为sk_X509_free()会自动释放引用。它没有。来自openssl文档https://www.openssl.org/docs/man1.1.0/man3/DEFINE_STACK_OF.html sk_TYPE_free()释放sk结构。它不会释放sk的任何元素。此调用后,sk将不再有效。
如果确实由于某种原因想要创建X509的深层副本,则可以使用X509_dup()复制X509对象并将其推入堆栈中:
sk_X509_push(stack, X509_dup(cert));

在该情况下,需要显式释放两个X509对象。
还可以使用sk_X509_pop_free(stack, X509_free)来自动取消引用所有堆栈元素并删除堆栈本身。
总之,您的代码可以通过以下 2 种方式修复:
  • 在sk_X509_push()之后添加X509_free()
  • 将sk_X509_free()替换为sk_X509_pop_free()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接