Commit 34f8ce6c49 for openssl.org
commit 34f8ce6c49161f649a18dc83a86c202398451519
Author: Mounir IDRASSI <mounir.idrassi@idrix.fr>
Date: Mon Apr 27 22:50:31 2026 +0900
Fix base64 BIO write retry handling
Commit 3a69b19028 changed b64_write from chunked ctx->buf output
to a dynamic encoded_buf. The new buffer was scratch-only, so
retryable downstream failures or positive short writes could drop
encoded output while still reporting the input bytes as consumed.
Track encoded_buf as pending output with offset and length, include
it in BIO_wpending, and write any pending output before accepting
more input. Add tests for retry via -1, retry via 0, positive short
writes, and short writes followed by retry, with and without
BIO_FLAGS_BASE64_NO_NL, across both aligned input and final-tail
input.
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Merge-date: Fri Sep 25 11:02:43 2026
Merged-from: https://github.com/openssl/openssl/pull/31000
diff --git a/crypto/evp/bio_b64.c b/crypto/evp/bio_b64.c
index 7f32d7a5d0..2a6f06c110 100644
--- a/crypto/evp/bio_b64.c
+++ b/crypto/evp/bio_b64.c
@@ -44,6 +44,8 @@ typedef struct b64_struct {
unsigned char tmp[B64_BLOCK_SIZE];
unsigned char *encoded_buf;
size_t encoded_buf_len;
+ size_t encoded_len;
+ size_t encoded_off;
} BIO_B64_CTX;
static const BIO_METHOD methods_b64 = {
@@ -111,6 +113,72 @@ static int b64_free(BIO *a)
return 1;
}
+static int b64_write_buffer(BIO *b, BIO *next, unsigned char *buf,
+ size_t *buf_len, size_t *buf_off)
+{
+ size_t n;
+ int i, dlen;
+
+ if (!ossl_assert(*buf_len >= *buf_off)) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ n = *buf_len - *buf_off;
+ if (n > 0 && !ossl_assert(buf != NULL)) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+ while (n > 0) {
+ if (n > INT_MAX)
+ dlen = INT_MAX;
+ else
+ dlen = (int)n;
+ i = BIO_write(next, &(buf[*buf_off]), dlen);
+ if (i <= 0) {
+ BIO_copy_next_retry(b);
+ return i;
+ }
+ if (!ossl_assert((size_t)i <= n)) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+ *buf_off += (size_t)i;
+ n -= (size_t)i;
+ }
+ *buf_off = 0;
+ *buf_len = 0;
+
+ return 1;
+}
+
+static int b64_write_buffer_int(BIO *b, BIO *next, unsigned char *buf,
+ int *buf_len, int *buf_off)
+{
+ int ret;
+ size_t len;
+ size_t off;
+
+ if (!ossl_assert(*buf_off >= 0)) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ len = (size_t)*buf_len;
+ off = (size_t)*buf_off;
+ ret = b64_write_buffer(b, next, buf, &len, &off);
+
+ *buf_len = (int)len;
+ *buf_off = (int)off;
+ return ret;
+}
+
+static int b64_write_pending(BIO_B64_CTX *ctx)
+{
+ return ctx->encoded_len != ctx->encoded_off
+ || ctx->buf_len != ctx->buf_off;
+}
+
/*
* Unless `BIO_FLAGS_BASE64_NO_NL` is set, this BIO ignores leading lines that
* aren't exclusively composed of valid Base64 characters (followed by <CRLF>
@@ -141,6 +209,8 @@ static int b64_read(BIO *b, char *out, int outl)
ctx->buf_len = 0;
ctx->buf_off = 0;
ctx->tmp_len = 0;
+ ctx->encoded_len = 0;
+ ctx->encoded_off = 0;
EVP_DecodeInit(ctx->base64);
}
@@ -330,7 +400,6 @@ static int b64_read(BIO *b, char *out, int outl)
static int b64_write(BIO *b, const char *in, int inl)
{
int ret = 0;
- int n;
int i;
BIO_B64_CTX *ctx;
BIO *next;
@@ -350,6 +419,8 @@ static int b64_write(BIO *b, const char *in, int inl)
ctx->buf_len = 0;
ctx->buf_off = 0;
ctx->tmp_len = 0;
+ ctx->encoded_len = 0;
+ ctx->encoded_off = 0;
EVP_EncodeInit(ctx->base64);
if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)
evp_encode_ctx_set_flags(ctx->base64, EVP_ENCODE_CTX_NO_NEWLINES);
@@ -366,27 +437,26 @@ static int b64_write(BIO *b, const char *in, int inl)
ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
return -1;
}
- n = ctx->buf_len - ctx->buf_off;
- while (n > 0) {
- i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
- if (i <= 0) {
- BIO_copy_next_retry(b);
- return i;
- }
- ctx->buf_off += i;
- if (!ossl_assert(ctx->buf_off <= (int)sizeof(ctx->buf))) {
- ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
- return -1;
- }
- if (!ossl_assert(ctx->buf_len >= ctx->buf_off)) {
- ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
- return -1;
- }
- n -= i;
+ if (!ossl_assert(ctx->encoded_len >= ctx->encoded_off)) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+ if (!ossl_assert(ctx->encoded_len <= ctx->encoded_buf_len)) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ i = b64_write_buffer(b, next, ctx->encoded_buf, &ctx->encoded_len,
+ &ctx->encoded_off);
+ if (i <= 0)
+ return i;
+ i = b64_write_buffer_int(b, next, ctx->buf, &ctx->buf_len, &ctx->buf_off);
+ if (i <= 0)
+ return i;
+ if (!ossl_assert(ctx->buf_off <= (int)sizeof(ctx->buf))) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
}
- /* at this point all pending data has been written */
- ctx->buf_off = 0;
- ctx->buf_len = 0;
if (in == NULL || inl <= 0)
return 0;
@@ -400,6 +470,8 @@ static int b64_write(BIO *b, const char *in, int inl)
if (ctx->encoded_buf == NULL || encoded_length > ctx->encoded_buf_len) {
OPENSSL_free(ctx->encoded_buf);
+ ctx->encoded_buf = NULL;
+ ctx->encoded_buf_len = 0;
ctx->encoded_buf = OPENSSL_malloc(encoded_length);
if (ctx->encoded_buf == NULL) {
ERR_raise(ERR_LIB_BIO, ERR_R_MALLOC_FAILURE);
@@ -419,10 +491,19 @@ static int b64_write(BIO *b, const char *in, int inl)
(unsigned char *)in, inl)) {
return -1;
}
+ /*
+ * The encoder state has consumed the input. Keep any unwritten encoded
+ * output pending so a later retry or flush can complete it.
+ */
ret += inl;
- i = BIO_write(next, encoded, n_bytes_enc);
- if (i <= 0)
- BIO_copy_next_retry(b);
+ ctx->encoded_len = (size_t)n_bytes_enc;
+ ctx->encoded_off = 0;
+ /*
+ * Try to write it now, but the input is consumed into encoder state, so
+ * report it as written even if downstream cannot accept all encoded output.
+ */
+ (void)b64_write_buffer(b, next, ctx->encoded_buf, &ctx->encoded_len,
+ &ctx->encoded_off);
return ret;
}
@@ -447,6 +528,8 @@ static long b64_ctrl(BIO *b, int cmd, long num, void *ptr)
ctx->cont = 1;
ctx->start = 1;
ctx->encode = B64_NONE;
+ ctx->encoded_len = 0;
+ ctx->encoded_off = 0;
ret = BIO_ctrl(next, cmd, num, ptr);
break;
case BIO_CTRL_EOF: /* More to read */
@@ -456,11 +539,16 @@ static long b64_ctrl(BIO *b, int cmd, long num, void *ptr)
ret = BIO_ctrl(next, cmd, num, ptr);
break;
case BIO_CTRL_WPENDING: /* More to write in buffer */
+ if (!ossl_assert(ctx->encoded_len >= ctx->encoded_off)) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
if (!ossl_assert(ctx->buf_len >= ctx->buf_off)) {
ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
return -1;
}
- ret = ctx->buf_len - ctx->buf_off;
+ ret = (long)(ctx->encoded_len - ctx->encoded_off)
+ + (ctx->buf_len - ctx->buf_off);
if (ret == 0 && ctx->encode != B64_NONE
&& EVP_ENCODE_CTX_num(ctx->base64) != 0)
ret = 1;
@@ -480,9 +568,9 @@ static long b64_ctrl(BIO *b, int cmd, long num, void *ptr)
if (ctx->encode == B64_ENCODE) {
/* do a final write */
again:
- while (ctx->buf_len != ctx->buf_off) {
+ while (b64_write_pending(ctx)) {
i = b64_write(b, NULL, 0);
- if (i < 0)
+ if (i < 0 || (i == 0 && b64_write_pending(ctx)))
return i;
}
if (EVP_ENCODE_CTX_num(ctx->base64) != 0) {
diff --git a/test/bio_base64_test.c b/test/bio_base64_test.c
index 9d9ef4dfa0..2ef639e45f 100644
--- a/test/bio_base64_test.c
+++ b/test/bio_base64_test.c
@@ -50,6 +50,159 @@ static unsigned linelengths[] = {
};
static unsigned wscnts[] = { 0, 1, 2, 4, 8, 16, 0xFFFF };
+#define B64_WRITE_INJECT_RETRY_NEG 0
+#define B64_WRITE_INJECT_RETRY_ZERO 1
+#define B64_WRITE_INJECT_SHORT 2
+#define B64_WRITE_INJECT_SHORT_THEN_RETRY 3
+#define B64_WRITE_INJECT_NONE (-1)
+#define B64_WRITE_INJECT_AFTER_SHORT_RETRY (-2)
+#define B64_WRITE_INJECT_COUNT 4
+#define B64_WRITE_TEST_LINE_INPUT_LEN 48
+#define B64_WRITE_TEST_INPUT_LEN_ALIGNED 96
+#define B64_WRITE_TEST_INPUT_LEN_TAIL 97
+#define B64_WRITE_TEST_INPUT_COUNT 2
+#define B64_WRITE_TEST_SECOND_INPUT_LEN 49
+#define B64_WRITE_TEST_SHORT_LEN 5
+#define B64_WRITE_SCENARIO_FLUSH_ONLY 0
+#define B64_WRITE_SCENARIO_SECOND_WRITE_DRAINS 1
+#define B64_WRITE_SCENARIO_SECOND_WRITE_INJECTS 2
+#define B64_WRITE_SCENARIO_COUNT 3
+
+typedef struct {
+ int inject;
+} b64_write_test_data;
+
+static BIO_METHOD *b64_write_test_method = NULL;
+static const int b64_write_test_input_lens[B64_WRITE_TEST_INPUT_COUNT] = {
+ B64_WRITE_TEST_INPUT_LEN_ALIGNED,
+ B64_WRITE_TEST_INPUT_LEN_TAIL
+};
+
+static int b64_write_test_update_len(int inl, int no_nl)
+{
+ int update_inl = inl - inl % B64_WRITE_TEST_LINE_INPUT_LEN;
+ int ret = update_inl / 3 * 4;
+
+ if (!no_nl)
+ ret += update_inl / B64_WRITE_TEST_LINE_INPUT_LEN;
+ return ret;
+}
+
+static int b64_write_test_update_len_since(int previous_inl, int inl,
+ int no_nl)
+{
+ return b64_write_test_update_len(previous_inl + inl, no_nl)
+ - b64_write_test_update_len(previous_inl, no_nl);
+}
+
+static int b64_write_test_final_pending(int inl)
+{
+ return inl % B64_WRITE_TEST_LINE_INPUT_LEN != 0;
+}
+
+static int b64_write_test_new(BIO *bio)
+{
+ b64_write_test_data *data = OPENSSL_zalloc(sizeof(*data));
+
+ if (data == NULL)
+ return 0;
+
+ BIO_set_data(bio, data);
+ BIO_set_init(bio, 1);
+ return 1;
+}
+
+static int b64_write_test_free(BIO *bio)
+{
+ b64_write_test_data *data = BIO_get_data(bio);
+
+ OPENSSL_free(data);
+ BIO_set_data(bio, NULL);
+ BIO_set_init(bio, 0);
+ return 1;
+}
+
+static int b64_write_test_write(BIO *bio, const char *in, int inl)
+{
+ b64_write_test_data *data = BIO_get_data(bio);
+ BIO *next = BIO_next(bio);
+ int ret;
+
+ BIO_clear_retry_flags(bio);
+ if (data == NULL || next == NULL)
+ return 0;
+
+ switch (data->inject) {
+ case B64_WRITE_INJECT_RETRY_NEG:
+ case B64_WRITE_INJECT_AFTER_SHORT_RETRY:
+ data->inject = B64_WRITE_INJECT_NONE;
+ BIO_set_retry_write(bio);
+ return -1;
+
+ case B64_WRITE_INJECT_RETRY_ZERO:
+ data->inject = B64_WRITE_INJECT_NONE;
+ BIO_set_retry_write(bio);
+ return 0;
+
+ case B64_WRITE_INJECT_SHORT:
+ data->inject = B64_WRITE_INJECT_NONE;
+ if (inl > B64_WRITE_TEST_SHORT_LEN)
+ inl = B64_WRITE_TEST_SHORT_LEN;
+ break;
+
+ case B64_WRITE_INJECT_SHORT_THEN_RETRY:
+ data->inject = B64_WRITE_INJECT_AFTER_SHORT_RETRY;
+ if (inl > B64_WRITE_TEST_SHORT_LEN)
+ inl = B64_WRITE_TEST_SHORT_LEN;
+ break;
+ }
+
+ ret = BIO_write(next, in, inl);
+ BIO_copy_next_retry(bio);
+ return ret;
+}
+
+static long b64_write_test_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ BIO *next = BIO_next(bio);
+ long ret;
+
+ if (next == NULL)
+ return 0;
+
+ BIO_clear_retry_flags(bio);
+ ret = BIO_ctrl(next, cmd, num, ptr);
+ BIO_copy_next_retry(bio);
+ return ret;
+}
+
+static const BIO_METHOD *bio_f_b64_write_test(void)
+{
+ int type;
+
+ if (b64_write_test_method == NULL) {
+ type = BIO_get_new_index();
+ if (type == -1)
+ return NULL;
+
+ if ((b64_write_test_method = BIO_meth_new(type | BIO_TYPE_FILTER, "b64 write test")) == NULL
+ || !BIO_meth_set_write(b64_write_test_method,
+ b64_write_test_write)
+ || !BIO_meth_set_ctrl(b64_write_test_method,
+ b64_write_test_ctrl)
+ || !BIO_meth_set_create(b64_write_test_method,
+ b64_write_test_new)
+ || !BIO_meth_set_destroy(b64_write_test_method,
+ b64_write_test_free)) {
+ BIO_meth_free(b64_write_test_method);
+ b64_write_test_method = NULL;
+ return NULL;
+ }
+ }
+
+ return b64_write_test_method;
+}
+
/* Generate `len` random octets */
static unsigned char *genbytes(unsigned len)
{
@@ -428,6 +581,195 @@ static int test_bio_base64_corner_case_bug(int idx)
return generic_case(&t, 0);
}
+static int base64_encode_reference(const char *in, int inl, int no_nl,
+ unsigned char **out, size_t *out_len)
+{
+ BIO *b64 = NULL;
+ BIO *mem = NULL;
+ BUF_MEM *bptr = NULL;
+ int ok = 0;
+
+ *out = NULL;
+ *out_len = 0;
+
+ if (!TEST_ptr(b64 = BIO_new(BIO_f_base64()))
+ || !TEST_ptr(mem = BIO_new(BIO_s_mem())))
+ goto done;
+
+ if (no_nl)
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+
+ if (!TEST_ptr_eq(BIO_push(b64, mem), b64)
+ || !TEST_int_eq(BIO_write(b64, in, inl), inl)
+ || !TEST_true(BIO_flush(b64)))
+ goto done;
+
+ BIO_get_mem_ptr(mem, &bptr);
+ if (!TEST_ptr(bptr))
+ goto done;
+
+ if (bptr->length > 0) {
+ if (!TEST_ptr(*out = OPENSSL_memdup(bptr->data, bptr->length)))
+ goto done;
+ *out_len = bptr->length;
+ }
+ ok = 1;
+
+done:
+ BIO_free_all(b64);
+ return ok;
+}
+
+static int b64_write_test_check_injected_state(BIO *b64, int mode,
+ int update_len,
+ int final_pending)
+{
+ switch (mode) {
+ case B64_WRITE_INJECT_RETRY_NEG:
+ case B64_WRITE_INJECT_RETRY_ZERO:
+ return TEST_true(BIO_should_retry(b64))
+ && TEST_int_eq(BIO_wpending(b64), update_len);
+
+ case B64_WRITE_INJECT_SHORT_THEN_RETRY:
+ return TEST_true(BIO_should_retry(b64))
+ && TEST_int_eq(BIO_wpending(b64),
+ update_len - B64_WRITE_TEST_SHORT_LEN);
+
+ case B64_WRITE_INJECT_SHORT:
+ return TEST_false(BIO_should_retry(b64))
+ && TEST_int_eq(BIO_wpending(b64), final_pending);
+
+ default:
+ TEST_error("Invalid base64 write injection mode: %d", mode);
+ return 0;
+ }
+}
+
+static int test_bio_base64_write_retry(int idx)
+{
+ char msg[B64_WRITE_TEST_INPUT_LEN_TAIL + B64_WRITE_TEST_SECOND_INPUT_LEN];
+ BIO *b64 = NULL;
+ BIO *inject = NULL;
+ BIO *mem = NULL;
+ BIO *membio = NULL;
+ BUF_MEM *bptr = NULL;
+ b64_write_test_data *data;
+ unsigned char *expected = NULL;
+ size_t expected_len = 0;
+ int mode;
+ int no_nl;
+ int scenario;
+ int input_idx;
+ int in_len;
+ int total_len;
+ int update_len;
+ int first_final_pending;
+ int q = idx;
+ int ret;
+ int ok = 0;
+
+ mode = quotrem(q, B64_WRITE_INJECT_COUNT, &q);
+ no_nl = quotrem(q, 2, &q);
+ scenario = quotrem(q, B64_WRITE_SCENARIO_COUNT, &q);
+ input_idx = quotrem(q, B64_WRITE_TEST_INPUT_COUNT, &q);
+ if (q != 0) {
+ fprintf(stderr, "Test index out of range: %d", idx);
+ return 0;
+ }
+ in_len = b64_write_test_input_lens[input_idx];
+ total_len = in_len;
+ if (scenario != B64_WRITE_SCENARIO_FLUSH_ONLY)
+ total_len += B64_WRITE_TEST_SECOND_INPUT_LEN;
+ update_len = b64_write_test_update_len_since(0, in_len, no_nl);
+ first_final_pending = b64_write_test_final_pending(in_len);
+ memset(msg, 'A', in_len);
+ memset(msg + in_len, 'B', B64_WRITE_TEST_SECOND_INPUT_LEN);
+
+ if (!TEST_true(base64_encode_reference(msg, total_len, no_nl,
+ &expected, &expected_len))
+ || !TEST_ptr(b64 = BIO_new(BIO_f_base64()))
+ || !TEST_ptr(inject = BIO_new(bio_f_b64_write_test()))
+ || !TEST_ptr(mem = BIO_new(BIO_s_mem())))
+ goto done;
+
+ data = BIO_get_data(inject);
+ if (!TEST_ptr(data))
+ goto done;
+ data->inject = scenario == B64_WRITE_SCENARIO_SECOND_WRITE_INJECTS
+ ? B64_WRITE_INJECT_NONE
+ : mode;
+
+ if (no_nl)
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+
+ membio = mem;
+ if (!TEST_ptr_eq(BIO_push(inject, mem), inject))
+ goto done;
+ mem = NULL;
+
+ if (!TEST_ptr_eq(BIO_push(b64, inject), b64))
+ goto done;
+ inject = NULL;
+
+ ret = BIO_write(b64, msg, in_len);
+ if (!TEST_int_eq(ret, in_len))
+ goto done;
+
+ if (scenario == B64_WRITE_SCENARIO_SECOND_WRITE_INJECTS) {
+ if (!TEST_false(BIO_should_retry(b64))
+ || !TEST_int_eq(BIO_wpending(b64), first_final_pending))
+ goto done;
+ } else if (!b64_write_test_check_injected_state(b64, mode, update_len,
+ first_final_pending)) {
+ goto done;
+ }
+
+ /*
+ * Verify both second-write paths: pending encoded output is flushed before
+ * newly supplied input is accepted, and an injected second write with no
+ * prior encoded-output pending retains its own unwritten output.
+ */
+ if (scenario != B64_WRITE_SCENARIO_FLUSH_ONLY) {
+ if (scenario == B64_WRITE_SCENARIO_SECOND_WRITE_INJECTS)
+ data->inject = mode;
+
+ ret = BIO_write(b64, msg + in_len, B64_WRITE_TEST_SECOND_INPUT_LEN);
+ if (!TEST_int_eq(ret, B64_WRITE_TEST_SECOND_INPUT_LEN))
+ goto done;
+
+ if (scenario == B64_WRITE_SCENARIO_SECOND_WRITE_INJECTS) {
+ update_len = b64_write_test_update_len_since(in_len,
+ B64_WRITE_TEST_SECOND_INPUT_LEN,
+ no_nl);
+ if (!b64_write_test_check_injected_state(
+ b64, mode, update_len,
+ b64_write_test_final_pending(total_len)))
+ goto done;
+ } else if (!TEST_false(BIO_should_retry(b64))
+ || !TEST_int_eq(BIO_wpending(b64),
+ b64_write_test_final_pending(total_len))) {
+ goto done;
+ }
+ }
+
+ if (!TEST_true(BIO_flush(b64)))
+ goto done;
+
+ BIO_get_mem_ptr(membio, &bptr);
+ if (!TEST_ptr(bptr)
+ || !TEST_mem_eq(expected, expected_len, bptr->data, bptr->length))
+ goto done;
+
+ ok = 1;
+
+done:
+ BIO_free_all(b64);
+ BIO_free_all(inject);
+ BIO_free(mem);
+ OPENSSL_free(expected);
+ return ok;
+}
+
#define MEM_CHK "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB" \
"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB" \
"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB"
@@ -522,6 +864,15 @@ int setup_tests(void)
numidx = 2 * 2;
ADD_ALL_TESTS(test_bio_base64_corner_case_bug, numidx);
+ ADD_ALL_TESTS(test_bio_base64_write_retry,
+ B64_WRITE_INJECT_COUNT * 2 * B64_WRITE_SCENARIO_COUNT
+ * B64_WRITE_TEST_INPUT_COUNT);
ADD_TEST(test_bio_base64_no_nl);
return 1;
}
+
+void cleanup_tests(void)
+{
+ BIO_meth_free(b64_write_test_method);
+ b64_write_test_method = NULL;
+}