Files
openssl/test/test_base64_simdutf.c
Alexandr Nedvedicky 818bbcf475 ctx_simd and ctx_ref must be freed in error path
CID 1679597
CID 1679599

Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
MergeDate: Thu Jan  8 10:10:49 2026
(Merged from https://github.com/openssl/openssl/pull/29543)
2026-01-08 11:10:46 +01:00

254 lines
7.9 KiB
C

/*
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stdio.h>
#include "testutil.h"
#include <openssl/evp.h>
#include "internal/cryptlib.h"
#include "crypto/evp.h"
#include "evp_local.h"
#define MAX_INPUT_LEN 3000
static void fuzz_fill_encode_ctx(EVP_ENCODE_CTX *ctx, int max_fill)
{
static int seeded = 0;
if (!seeded) {
srand((unsigned)time(NULL));
seeded = 1;
}
int num = rand() % (max_fill + 1);
ctx->num = num;
for (int i = 0; i < num; i++)
ctx->enc_data[i] = (unsigned char)(rand() & 0xFF);
ctx->length = (rand() % 80) + 1;
ctx->line_num = rand() % (ctx->length + 1);
}
static inline uint32_t next_u32(uint32_t *state)
{
*state = (*state * 1664525u) + 1013904223u;
return *state;
}
static const unsigned char data_bin2ascii[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* SRP uses a different base64 alphabet */
static const unsigned char srpdata_bin2ascii[65] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
#ifndef CHARSET_EBCDIC
#define conv_bin2ascii(a, table) ((table)[(a) & 0x3f])
#else
/*
* We assume that PEM encoded files are EBCDIC files (i.e., printable text
* files). Convert them here while decoding. When encoding, output is EBCDIC
* (text) format again. (No need for conversion in the conv_bin2ascii macro,
* as the underlying textstring data_bin2ascii[] is already EBCDIC)
*/
#define conv_bin2ascii(a, table) ((table)[(a) & 0x3f])
#endif
static int evp_encodeblock_int_old(EVP_ENCODE_CTX *ctx, unsigned char *t,
const unsigned char *f, int dlen)
{
int i, ret = 0;
unsigned long l;
const unsigned char *table;
if (ctx != NULL && (ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0)
table = srpdata_bin2ascii;
else
table = data_bin2ascii;
for (i = dlen; i > 0; i -= 3) {
if (i >= 3) {
l = (((unsigned long)f[0]) << 16L) | (((unsigned long)f[1]) << 8L) | f[2];
*(t++) = conv_bin2ascii(l >> 18L, table);
*(t++) = conv_bin2ascii(l >> 12L, table);
*(t++) = conv_bin2ascii(l >> 6L, table);
*(t++) = conv_bin2ascii(l, table);
} else {
l = ((unsigned long)f[0]) << 16L;
if (i == 2)
l |= ((unsigned long)f[1] << 8L);
*(t++) = conv_bin2ascii(l >> 18L, table);
*(t++) = conv_bin2ascii(l >> 12L, table);
*(t++) = (i == 1) ? '=' : conv_bin2ascii(l >> 6L, table);
*(t++) = '=';
}
ret += 4;
f += 3;
}
*t = '\0';
return ret;
}
static int evp_encodeupdate_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
const unsigned char *in, int inl)
{
int i, j;
int total = 0;
*outl = 0;
if (inl <= 0)
return 0;
OPENSSL_assert(ctx->length <= (int)sizeof(ctx->enc_data));
if (ctx->length - ctx->num > inl) {
memcpy(&(ctx->enc_data[ctx->num]), in, inl);
ctx->num += inl;
return 1;
}
if (ctx->num != 0) {
i = ctx->length - ctx->num;
memcpy(&(ctx->enc_data[ctx->num]), in, i);
in += i;
inl -= i;
j = evp_encodeblock_int_old(ctx, out, ctx->enc_data, ctx->length);
ctx->num = 0;
out += j;
total = j;
if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) {
*(out++) = '\n';
total++;
}
*out = '\0';
}
while (inl >= ctx->length) {
j = evp_encodeblock_int_old(ctx, out, in, ctx->length);
in += ctx->length;
inl -= ctx->length;
out += j;
total += j;
if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) {
*(out++) = '\n';
total++;
}
*out = '\0';
}
if (inl != 0)
memcpy(&(ctx->enc_data[0]), in, inl);
ctx->num = inl;
*outl = total;
return 1;
}
static void evp_encodefinal_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
{
unsigned int ret = 0;
if (ctx->num != 0) {
ret = evp_encodeblock_int_old(ctx, out, ctx->enc_data, ctx->num);
if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
out[ret++] = '\n';
out[ret] = '\0';
ctx->num = 0;
}
*outl = ret;
}
static int test_encode_line_lengths_reinforced(void)
{
const int trials = 50;
uint32_t seed = 12345;
/* Generous output buffers (Update + Final + newlines), plus a guard byte */
unsigned char out_simd[9000 * 2 + 1] = { 0 };
unsigned char out_ref[9000 * 2 + 1] = { 0 };
EVP_ENCODE_CTX *ctx_simd = NULL;
EVP_ENCODE_CTX *ctx_ref = NULL;
for (int t = 0; t < trials; t++) {
uint32_t r = next_u32(&seed);
int inl = r % MAX_INPUT_LEN;
/* Fresh random input */
unsigned char input[MAX_INPUT_LEN];
for (int i = 0; i < inl; i++)
input[i] = (unsigned char)(r % 256);
for (int partial_ctx_fill = 0; partial_ctx_fill <= 80;
partial_ctx_fill += 1) {
for (int ctx_len = 1; ctx_len <= 80; ctx_len += 1) {
ctx_simd = EVP_ENCODE_CTX_new();
ctx_ref = EVP_ENCODE_CTX_new();
if (!ctx_simd || !ctx_ref) {
TEST_error("Out of memory for contexts");
goto fail;
}
fuzz_fill_encode_ctx(ctx_simd, partial_ctx_fill);
memset(out_simd, 0xCC, sizeof(out_simd)); /* poison to catch short writes */
memset(out_ref, 0xDD, sizeof(out_ref));
int outlen_simd = 0, outlen_ref = 0; /* bytes produced by Update */
int finlen_simd = 0, finlen_ref = 0; /* bytes produced by Final */
EVP_EncodeInit(ctx_simd);
EVP_EncodeInit(ctx_ref);
ctx_simd->length = ctx_len;
ctx_ref->length = ctx_len;
for (int i = 0; i < 2; i++) {
if (i % 2 == 0) {
/* Turn SRP alphabet OFF */
ctx_simd->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET;
ctx_ref->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET;
} else {
/* Turn SRP alphabet ON */
ctx_simd->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET;
ctx_ref->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET;
}
int ret_simd = EVP_EncodeUpdate(ctx_simd, out_simd, &outlen_simd,
input, (int)inl);
int ret_ref = evp_encodeupdate_old(ctx_ref, out_ref, &outlen_ref,
input, (int)inl);
if (!TEST_int_eq(ret_simd, ret_ref)
|| !TEST_mem_eq(out_ref, outlen_ref, out_simd, outlen_simd)
|| !TEST_int_eq(outlen_simd, outlen_ref))
goto fail;
EVP_EncodeFinal(ctx_simd, out_simd + outlen_simd,
&finlen_simd);
evp_encodefinal_old(ctx_ref, out_ref + outlen_ref,
&finlen_ref);
int total_ref = outlen_ref + finlen_ref;
int total_simd = outlen_simd + finlen_simd;
if (!TEST_int_eq(finlen_simd, finlen_ref)
|| !TEST_mem_eq(out_ref, total_ref, out_simd, total_simd))
goto fail;
}
EVP_ENCODE_CTX_free(ctx_simd);
EVP_ENCODE_CTX_free(ctx_ref);
}
}
}
return 1;
fail:
EVP_ENCODE_CTX_free(ctx_simd);
EVP_ENCODE_CTX_free(ctx_ref);
return 0;
}
int setup_tests(void)
{
ADD_TEST(test_encode_line_lengths_reinforced);
return 1;
}