From f2c1b8fb286165fb0321ee85434153ad4f8bc2c0 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 28 Oct 2026 14:36:03 +0000 Subject: [PATCH] Fix double fclose in wolfSSL_BIO_read_filename() wolfSSL_BIO_read_filename() closed the BIO's file even when the BIO did not own it, and kept the closed FILE* when the new file failed to open. wolfSSL_BIO_free() then closed it a second time. Close the old file only with BIO_CLOSE and always drop it from the BIO. wolfSSL_X509_load_crl_file() called wolfSSL_BIO_read_filename() twice on the same file, a leftover from a merge. Remove the second call. It opened the file twice and hit the double fclose when the second open failed. Fixes #11903 --- src/bio.c | 8 ++++++-- src/x509.c | 5 ----- tests/api/test_ossl_bio.c | 36 ++++++++++++++++++++++++++++++++++++ tests/api/test_ossl_bio.h | 2 ++ 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/bio.c b/src/bio.c index 32187e3316c..8d2b9e213af 100644 --- a/src/bio.c +++ b/src/bio.c @@ -3646,11 +3646,15 @@ int wolfSSL_BIO_dump(WOLFSSL_BIO *bio, const char *buf, int length) #ifndef NO_FILESYSTEM XFILE fp; - WOLFSSL_ENTER("wolfSSL_BIO_new_file"); + WOLFSSL_ENTER("wolfSSL_BIO_read_filename"); if ((wolfSSL_BIO_get_fp(b, &fp) == WOLFSSL_SUCCESS) && (fp != XBADFILE)) { - XFCLOSE(fp); + /* Only close the old file if the BIO owns it. */ + if (b->shutdown == WOLFSSL_BIO_CLOSE) { + XFCLOSE(fp); + } + b->ptr.fh = XBADFILE; } fp = XFOPEN(name, "rb"); diff --git a/src/x509.c b/src/x509.c index 62159e99aac..eeb5d781400 100644 --- a/src/x509.c +++ b/src/x509.c @@ -10112,11 +10112,6 @@ int wolfSSL_X509_load_crl_file(WOLFSSL_X509_LOOKUP *ctx, return ret; } - if (wolfSSL_BIO_read_filename(bio, file) <= 0) { - wolfSSL_BIO_free(bio); - return ret; - } - if (type == WOLFSSL_FILETYPE_PEM) { do { crl = wolfSSL_PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); diff --git a/tests/api/test_ossl_bio.c b/tests/api/test_ossl_bio.c index e5143e071ec..438c89f5330 100644 --- a/tests/api/test_ossl_bio.c +++ b/tests/api/test_ossl_bio.c @@ -1244,6 +1244,42 @@ int test_wolfSSL_BIO_get_len(void) return EXPECT_RESULT(); } +int test_wolfSSL_BIO_read_filename(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_BIO) && !defined(NO_FILESYSTEM) + const char* missing = "./certs/does-not-exist.pem"; + BIO* bio = NULL; + XFILE f = XBADFILE; + XFILE fp = XBADFILE; + byte buf[16]; + + /* The old file is dropped even when the new one can't be opened. */ + ExpectNotNull(bio = BIO_new_file(svrCertFile, "rb")); + ExpectIntLE(BIO_read_filename(bio, missing), 0); + ExpectIntEQ((int)BIO_get_fp(bio, &fp), WOLFSSL_SUCCESS); + ExpectTrue(fp == XBADFILE); + ExpectIntEQ(BIO_read_filename(bio, caCertFile), WOLFSSL_SUCCESS); + ExpectIntEQ(BIO_read(bio, buf, sizeof(buf)), sizeof(buf)); + BIO_free(bio); + bio = NULL; + + /* A BIO_NOCLOSE file is not closed. */ + ExpectTrue((f = XFOPEN(svrCertFile, "rb")) != XBADFILE); + ExpectNotNull(bio = BIO_new_fp(f, BIO_NOCLOSE)); + ExpectIntEQ(BIO_read_filename(bio, caCertFile), WOLFSSL_SUCCESS); + BIO_free(bio); + bio = NULL; + ExpectNotNull(bio = BIO_new_fp(f, BIO_NOCLOSE)); + ExpectIntLE(BIO_read_filename(bio, missing), 0); + BIO_free(bio); + ExpectIntEQ(XFREAD(buf, 1, sizeof(buf), f), sizeof(buf)); + if (f != XBADFILE) + XFCLOSE(f); +#endif + return EXPECT_RESULT(); +} + #if defined(OPENSSL_EXTRA) && !defined(NO_BIO) static long bioCallback(BIO *bio, int cmd, const char* argp, int argi, long argl, long ret) diff --git a/tests/api/test_ossl_bio.h b/tests/api/test_ossl_bio.h index e33b3bad430..e7706a92772 100644 --- a/tests/api/test_ossl_bio.h +++ b/tests/api/test_ossl_bio.h @@ -42,6 +42,7 @@ int test_wolfSSL_BIO_f_md(void); int test_wolfSSL_BIO_up_ref(void); int test_wolfSSL_BIO_reset(void); int test_wolfSSL_BIO_get_len(void); +int test_wolfSSL_BIO_read_filename(void); int test_wolfSSL_BIO(void); int test_wolfSSL_BIO_BIO_ring_read(void); int test_wolfSSL_BIO_custom_method(void); @@ -67,6 +68,7 @@ int test_wolfSSL_BIO_get_new_index_threaded(void); TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_up_ref), \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_reset), \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_get_len), \ + TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_read_filename), \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO), \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_BIO_ring_read), \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_custom_method), \