openssl/test/json_test.c
Hugo Landau 1260d0f579 JSON_ENC: Fix unit test for MSVC
Previously scripts were defined like this:

    {
        static const char *const script_name = "xxx";

        static const struct script_info script_info = {
            script_name, ...
        };

        return &script_info;
    }

MSVC cannot handle this, presumably because this technically involves a
load from a variable to determine that script_name equals "xxx" and it
is unable to do this during evaluation of a constant initializer list.
Resolve this by changing script_name and script_title to be arrays
instead, allowing the correct pointer values to be filled into
script_info as symbol addresses/relocations rather than dereferences.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Tom Cosgrove <tom.cosgrove@arm.com>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23517)
2024-02-09 11:03:52 +00:00

670 lines
18 KiB
C

/*
* Copyright 2022 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 <string.h>
#include "testutil.h"
#include "internal/json_enc.h"
struct helper {
OSSL_JSON_ENC j;
int init;
uint32_t flags;
BIO *mem_bio;
};
static int helper_ensure(struct helper *h)
{
if (h->init)
return 1;
if (!TEST_ptr(h->mem_bio = BIO_new(BIO_s_mem())))
return 0;
if (!ossl_json_init(&h->j, h->mem_bio, h->flags)) {
BIO_free_all(h->mem_bio);
h->mem_bio = NULL;
return 0;
}
h->init = 1;
return 1;
}
static void helper_cleanup(struct helper *h)
{
BIO_free_all(h->mem_bio);
h->mem_bio = NULL;
if (h->init) {
ossl_json_cleanup(&h->j);
h->init = 0;
}
}
static void helper_set_flags(struct helper *h, uint32_t flags)
{
helper_cleanup(h);
h->flags = flags;
}
struct script_word {
void *p;
uint64_t u64;
int64_t i64;
double d;
void (*fp)(void);
};
#define OP_P(x) { (x) },
#define OP_U64(x) { NULL, (x) },
#define OP_I64(x) { NULL, 0, (x) },
#define OP_D(x) { NULL, 0, 0, (x) },
#define OP_FP(x) { NULL, 0, 0, 0, (void (*)(void))(x) },
struct script_info {
const char *name, *title;
const struct script_word *words;
size_t num_words;
const char *expected_output;
size_t expected_output_len;
};
typedef const struct script_info *(*info_func)(void);
enum {
OPK_END,
OPK_CALL, /* (OSSL_JSON_ENC *) */
OPK_CALL_P, /* (OSSL_JSON_ENC *, const void *) */
OPK_CALL_I, /* (OSSL_JSON_ENC *, int) */
OPK_CALL_U64, /* (OSSL_JSON_ENC *, uint64_t) */
OPK_CALL_I64, /* (OSSL_JSON_ENC *, int64_t) */
OPK_CALL_D, /* (OSSL_JSON_ENC *, double) */
OPK_CALL_PZ, /* (OSSL_JSON_ENC *, const void *, size_t) */
OPK_ASSERT_ERROR, /* (OSSL_JSON_ENC *, int expect_error) */
OPK_INIT_FLAGS, /* (uint32_t flags) */
};
typedef void (*fp_type)(OSSL_JSON_ENC *);
typedef void (*fp_p_type)(OSSL_JSON_ENC *, const void *);
typedef void (*fp_i_type)(OSSL_JSON_ENC *, int);
typedef void (*fp_u64_type)(OSSL_JSON_ENC *, uint64_t);
typedef void (*fp_i64_type)(OSSL_JSON_ENC *, int64_t);
typedef void (*fp_d_type)(OSSL_JSON_ENC *, double);
typedef void (*fp_pz_type)(OSSL_JSON_ENC *, const void *, size_t);
#define OP_END() OP_U64(OPK_END)
#define OP_CALL(f) OP_U64(OPK_CALL) OP_FP(f)
#define OP_CALL_P(f, x) OP_U64(OPK_CALL_P) OP_FP(f) OP_P (x)
#define OP_CALL_I(f, x) OP_U64(OPK_CALL_I) OP_FP(f) OP_I64(x)
#define OP_CALL_U64(f, x) OP_U64(OPK_CALL_U64) OP_FP(f) OP_U64(x)
#define OP_CALL_I64(f, x) OP_U64(OPK_CALL_I64) OP_FP(f) OP_I64(x)
#define OP_CALL_D(f, x) OP_U64(OPK_CALL_D) OP_FP(f) OP_D (x)
#define OP_CALL_PZ(f, x, xl) OP_U64(OPK_CALL_PZ) OP_FP(f) OP_P (x) OP_U64(xl)
#define OP_ASSERT_ERROR(err) OP_U64(OPK_ASSERT_ERROR) OP_U64(err)
#define OP_INIT_FLAGS(flags) OP_U64(OPK_INIT_FLAGS) OP_U64(flags)
#define OPJ_BEGIN_O() OP_CALL(ossl_json_object_begin)
#define OPJ_END_O() OP_CALL(ossl_json_object_end)
#define OPJ_BEGIN_A() OP_CALL(ossl_json_array_begin)
#define OPJ_END_A() OP_CALL(ossl_json_array_end)
#define OPJ_NULL() OP_CALL(ossl_json_null)
#define OPJ_BOOL(x) OP_CALL_I(ossl_json_bool, (x))
#define OPJ_U64(x) OP_CALL_U64(ossl_json_u64, (x))
#define OPJ_I64(x) OP_CALL_I64(ossl_json_i64, (x))
#define OPJ_F64(x) OP_CALL_D(ossl_json_f64, (x))
#define OPJ_KEY(x) OP_CALL_P(ossl_json_key, (x))
#define OPJ_STR(x) OP_CALL_P(ossl_json_str, (x))
#define OPJ_STR_LEN(x, xl) OP_CALL_PZ(ossl_json_str_len, (x), (xl))
#define OPJ_STR_HEX(x, xl) OP_CALL_PZ(ossl_json_str_hex, (x), (xl))
#define BEGIN_SCRIPT(name, title, flags) \
static const struct script_info *get_script_##name(void) \
{ \
static const char script_name[] = #name; \
static const char script_title[] = #title; \
\
static const struct script_word script_words[] = { \
OP_INIT_FLAGS(flags)
#define END_SCRIPT_EXPECTING(s, slen) \
OP_END() \
}; \
static const struct script_info script_info = { \
script_name, script_title, script_words, OSSL_NELEM(script_words), \
(s), (slen) \
}; \
return &script_info; \
}
#define END_SCRIPT_EXPECTING_S(s) END_SCRIPT_EXPECTING(s, SIZE_MAX)
#define END_SCRIPT_EXPECTING_Q(s) END_SCRIPT_EXPECTING(#s, sizeof(#s) - 1)
#define SCRIPT(name) get_script_##name,
BEGIN_SCRIPT(null, "serialize a single null", 0)
OPJ_NULL()
END_SCRIPT_EXPECTING_Q(null)
BEGIN_SCRIPT(obj_empty, "serialize an empty object", 0)
OPJ_BEGIN_O()
OPJ_END_O()
END_SCRIPT_EXPECTING_Q({})
BEGIN_SCRIPT(array_empty, "serialize an empty array", 0)
OPJ_BEGIN_A()
OPJ_END_A()
END_SCRIPT_EXPECTING_Q([])
BEGIN_SCRIPT(bool_false, "serialize false", 0)
OPJ_BOOL(0)
END_SCRIPT_EXPECTING_Q(false)
BEGIN_SCRIPT(bool_true, "serialize true", 0)
OPJ_BOOL(1)
END_SCRIPT_EXPECTING_Q(true)
BEGIN_SCRIPT(u64_0, "serialize u64(0)", 0)
OPJ_U64(0)
END_SCRIPT_EXPECTING_Q(0)
BEGIN_SCRIPT(u64_1, "serialize u64(1)", 0)
OPJ_U64(1)
END_SCRIPT_EXPECTING_Q(1)
BEGIN_SCRIPT(u64_10, "serialize u64(10)", 0)
OPJ_U64(10)
END_SCRIPT_EXPECTING_Q(10)
BEGIN_SCRIPT(u64_12345, "serialize u64(12345)", 0)
OPJ_U64(12345)
END_SCRIPT_EXPECTING_Q(12345)
BEGIN_SCRIPT(u64_18446744073709551615, "serialize u64(18446744073709551615)", 0)
OPJ_U64(18446744073709551615ULL)
END_SCRIPT_EXPECTING_Q(18446744073709551615)
BEGIN_SCRIPT(i64_0, "serialize i64(0)", 0)
OPJ_I64(0)
END_SCRIPT_EXPECTING_Q(0)
BEGIN_SCRIPT(i64_1, "serialize i64(1)", 0)
OPJ_I64(1)
END_SCRIPT_EXPECTING_Q(1)
BEGIN_SCRIPT(i64_2, "serialize i64(2)", 0)
OPJ_I64(2)
END_SCRIPT_EXPECTING_Q(2)
BEGIN_SCRIPT(i64_10, "serialize i64(10)", 0)
OPJ_I64(10)
END_SCRIPT_EXPECTING_Q(10)
BEGIN_SCRIPT(i64_12345, "serialize i64(12345)", 0)
OPJ_I64(12345)
END_SCRIPT_EXPECTING_Q(12345)
BEGIN_SCRIPT(i64_9223372036854775807, "serialize i64(9223372036854775807)", 0)
OPJ_I64(9223372036854775807LL)
END_SCRIPT_EXPECTING_Q(9223372036854775807)
BEGIN_SCRIPT(i64_m1, "serialize i64(-1)", 0)
OPJ_I64(-1)
END_SCRIPT_EXPECTING_Q(-1)
BEGIN_SCRIPT(i64_m2, "serialize i64(-2)", 0)
OPJ_I64(-2)
END_SCRIPT_EXPECTING_Q(-2)
BEGIN_SCRIPT(i64_m10, "serialize i64(-10)", 0)
OPJ_I64(-10)
END_SCRIPT_EXPECTING_Q(-10)
BEGIN_SCRIPT(i64_m12345, "serialize i64(-12345)", 0)
OPJ_I64(-12345)
END_SCRIPT_EXPECTING_Q(-12345)
BEGIN_SCRIPT(i64_m9223372036854775807, "serialize i64(-9223372036854775807)", 0)
OPJ_I64(-9223372036854775807LL)
END_SCRIPT_EXPECTING_Q(-9223372036854775807)
BEGIN_SCRIPT(i64_m9223372036854775808, "serialize i64(-9223372036854775808)", 0)
OPJ_I64(-9223372036854775807LL - 1LL)
END_SCRIPT_EXPECTING_Q(-9223372036854775808)
BEGIN_SCRIPT(str_empty, "serialize \"\"", 0)
OPJ_STR("")
END_SCRIPT_EXPECTING_Q("")
BEGIN_SCRIPT(str_a, "serialize \"a\"", 0)
OPJ_STR("a")
END_SCRIPT_EXPECTING_Q("a")
BEGIN_SCRIPT(str_abc, "serialize \"abc\"", 0)
OPJ_STR("abc")
END_SCRIPT_EXPECTING_Q("abc")
BEGIN_SCRIPT(str_quote, "serialize with quote", 0)
OPJ_STR("abc\"def")
END_SCRIPT_EXPECTING_Q("abc\"def")
BEGIN_SCRIPT(str_quote2, "serialize with quote", 0)
OPJ_STR("abc\"\"def")
END_SCRIPT_EXPECTING_Q("abc\"\"def")
BEGIN_SCRIPT(str_escape, "serialize with various escapes", 0)
OPJ_STR("abc\"\"de'f\r\n\t\b\f\\\x01\v\x7f\\")
END_SCRIPT_EXPECTING_Q("abc\"\"de'f\r\n\t\b\f\\\u0001\u000b\u007f\\")
BEGIN_SCRIPT(str_len, "length-signalled string", 0)
OPJ_STR_LEN("abcdef", 6)
END_SCRIPT_EXPECTING_Q("abcdef")
BEGIN_SCRIPT(str_len0, "0-length-signalled string", 0)
OPJ_STR_LEN("", 0)
END_SCRIPT_EXPECTING_Q("")
BEGIN_SCRIPT(str_len_nul, "string with NUL", 0)
OPJ_STR_LEN("x\0y", 3)
END_SCRIPT_EXPECTING_Q("x\u0000y")
BEGIN_SCRIPT(hex_data0, "zero-length hex data", 0)
OPJ_STR_HEX("", 0)
END_SCRIPT_EXPECTING_Q("")
BEGIN_SCRIPT(hex_data, "hex data", 0)
OPJ_STR_HEX("\x00\x01\x5a\xfb\xff", 5)
END_SCRIPT_EXPECTING_Q("00015afbff")
BEGIN_SCRIPT(array_nest1, "serialize nested empty arrays", 0)
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_END_A()
END_SCRIPT_EXPECTING_Q([[]])
BEGIN_SCRIPT(array_nest2, "serialize nested empty arrays", 0)
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
END_SCRIPT_EXPECTING_Q([[[]]])
BEGIN_SCRIPT(array_nest3, "serialize nested empty arrays", 0)
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_END_A()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_END_A()
END_SCRIPT_EXPECTING_S("[[[],[],[]],[]]")
BEGIN_SCRIPT(array_nest4, "deep nested arrays", 0)
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_END_A()
OPJ_NULL()
OPJ_END_A()
END_SCRIPT_EXPECTING_S("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]],null]")
BEGIN_SCRIPT(obj_nontrivial1, "serialize nontrivial object", 0)
OPJ_BEGIN_O()
OPJ_KEY("")
OPJ_NULL()
OPJ_END_O()
END_SCRIPT_EXPECTING_S("{\"\":null}")
BEGIN_SCRIPT(obj_nontrivial2, "serialize nontrivial object", 0)
OPJ_BEGIN_O()
OPJ_KEY("")
OPJ_NULL()
OPJ_KEY("x")
OPJ_NULL()
OPJ_END_O()
END_SCRIPT_EXPECTING_S("{\"\":null,\"x\":null}")
BEGIN_SCRIPT(obj_nest1, "serialize nested objects", 0)
OPJ_BEGIN_O()
OPJ_KEY("")
OPJ_BEGIN_O()
OPJ_KEY("x")
OPJ_U64(42)
OPJ_END_O()
OPJ_KEY("x")
OPJ_BEGIN_A()
OPJ_U64(42)
OPJ_U64(101)
OPJ_END_A()
OPJ_KEY("y")
OPJ_NULL()
OPJ_KEY("z")
OPJ_BEGIN_O()
OPJ_KEY("z0")
OPJ_I64(-1)
OPJ_KEY("z1")
OPJ_I64(-2)
OPJ_END_O()
OPJ_END_O()
END_SCRIPT_EXPECTING_S("{\"\":{\"x\":42},\"x\":[42,101],\"y\":null,\"z\":{\"z0\":-1,\"z1\":-2}}")
BEGIN_SCRIPT(err_obj_no_key, "error test: object item without key", 0)
OPJ_BEGIN_O()
OP_ASSERT_ERROR(0)
OPJ_NULL()
OP_ASSERT_ERROR(1)
OPJ_END_O()
OP_ASSERT_ERROR(1)
END_SCRIPT_EXPECTING_S("{")
BEGIN_SCRIPT(err_obj_multi_key, "error test: object item with repeated key", 0)
OPJ_BEGIN_O()
OPJ_KEY("x")
OP_ASSERT_ERROR(0)
OPJ_KEY("y")
OP_ASSERT_ERROR(1)
OPJ_NULL()
OP_ASSERT_ERROR(1)
END_SCRIPT_EXPECTING_S("{\"x\":")
BEGIN_SCRIPT(err_obj_no_value, "error test: object item with no value", 0)
OPJ_BEGIN_O()
OPJ_KEY("x")
OP_ASSERT_ERROR(0)
OPJ_END_O()
OP_ASSERT_ERROR(1)
END_SCRIPT_EXPECTING_S("{\"x\":")
BEGIN_SCRIPT(err_utf8, "error test: only basic ASCII supported", 0)
OPJ_STR("\x80")
OP_ASSERT_ERROR(1)
END_SCRIPT_EXPECTING_S("\"")
BEGIN_SCRIPT(ijson_int, "I-JSON: large integer", OSSL_JSON_FLAG_IJSON)
OPJ_BEGIN_A()
OPJ_U64(1)
OPJ_I64(-1)
OPJ_U64(9007199254740991)
OPJ_U64(9007199254740992)
OPJ_I64(-9007199254740991)
OPJ_I64(-9007199254740992)
OPJ_END_A()
END_SCRIPT_EXPECTING_S("[1,-1,9007199254740991,\"9007199254740992\",-9007199254740991,\"-9007199254740992\"]")
BEGIN_SCRIPT(multi_item, "multiple top level items", 0)
OPJ_NULL()
OPJ_NULL()
OPJ_BEGIN_A()
OPJ_END_A()
OPJ_BEGIN_A()
OPJ_END_A()
END_SCRIPT_EXPECTING_S("nullnull[][]")
BEGIN_SCRIPT(seq, "JSON-SEQ", OSSL_JSON_FLAG_SEQ)
OPJ_NULL()
OPJ_NULL()
OPJ_NULL()
OPJ_BEGIN_O()
OPJ_KEY("x")
OPJ_U64(1)
OPJ_KEY("y")
OPJ_BEGIN_O()
OPJ_END_O()
OPJ_END_O()
END_SCRIPT_EXPECTING_S("\x1Enull\n" "\x1Enull\n" "\x1Enull\n" "\x1E{\"x\":1,\"y\":{}}\n")
static const info_func scripts[] = {
SCRIPT(null)
SCRIPT(obj_empty)
SCRIPT(array_empty)
SCRIPT(bool_false)
SCRIPT(bool_true)
SCRIPT(u64_0)
SCRIPT(u64_1)
SCRIPT(u64_10)
SCRIPT(u64_12345)
SCRIPT(u64_18446744073709551615)
SCRIPT(i64_0)
SCRIPT(i64_1)
SCRIPT(i64_2)
SCRIPT(i64_10)
SCRIPT(i64_12345)
SCRIPT(i64_9223372036854775807)
SCRIPT(i64_m1)
SCRIPT(i64_m2)
SCRIPT(i64_m10)
SCRIPT(i64_m12345)
SCRIPT(i64_m9223372036854775807)
SCRIPT(i64_m9223372036854775808)
SCRIPT(str_empty)
SCRIPT(str_a)
SCRIPT(str_abc)
SCRIPT(str_quote)
SCRIPT(str_quote2)
SCRIPT(str_escape)
SCRIPT(str_len)
SCRIPT(str_len0)
SCRIPT(str_len_nul)
SCRIPT(hex_data0)
SCRIPT(hex_data)
SCRIPT(array_nest1)
SCRIPT(array_nest2)
SCRIPT(array_nest3)
SCRIPT(array_nest4)
SCRIPT(obj_nontrivial1)
SCRIPT(obj_nontrivial2)
SCRIPT(obj_nest1)
SCRIPT(err_obj_no_key)
SCRIPT(err_obj_multi_key)
SCRIPT(err_obj_no_value)
SCRIPT(err_utf8)
SCRIPT(ijson_int)
SCRIPT(multi_item)
SCRIPT(seq)
};
/* Test runner. */
static int run_script(const struct script_info *info)
{
int ok = 0, asserted = -1;
const struct script_word *words = info->words;
size_t wp = 0;
struct script_word w;
struct helper h = {0};
BUF_MEM *bufp = NULL;
TEST_info("running script '%s' (%s)", info->name, info->title);
#define GET_WORD() (w = words[wp++])
#define GET_U64() (GET_WORD().u64)
#define GET_I64() (GET_WORD().i64)
#define GET_FP() (GET_WORD().fp)
#define GET_P() (GET_WORD().p)
for (;;)
switch (GET_U64()) {
case OPK_END:
goto stop;
case OPK_INIT_FLAGS:
helper_set_flags(&h, (uint32_t)GET_U64());
break;
case OPK_CALL:
{
fp_type f = (fp_type)GET_FP();
if (!TEST_true(helper_ensure(&h)))
goto err;
f(&h.j);
break;
}
case OPK_CALL_I:
{
fp_i_type f = (fp_i_type)GET_FP();
if (!TEST_true(helper_ensure(&h)))
goto err;
f(&h.j, (int)GET_I64());
break;
}
case OPK_CALL_U64:
{
fp_u64_type f = (fp_u64_type)GET_FP();
if (!TEST_true(helper_ensure(&h)))
goto err;
f(&h.j, GET_U64());
break;
}
case OPK_CALL_I64:
{
fp_i64_type f = (fp_i64_type)GET_FP();
if (!TEST_true(helper_ensure(&h)))
goto err;
f(&h.j, GET_I64());
break;
}
case OPK_CALL_P:
{
fp_p_type f = (fp_p_type)GET_FP();
if (!TEST_true(helper_ensure(&h)))
goto err;
f(&h.j, GET_P());
break;
}
case OPK_CALL_PZ:
{
fp_pz_type f = (fp_pz_type)GET_FP();
void *p;
uint64_t u64;
if (!TEST_true(helper_ensure(&h)))
goto err;
p = GET_P();
u64 = GET_U64();
f(&h.j, p, (size_t)u64);
break;
}
case OPK_ASSERT_ERROR:
{
if (!TEST_true(helper_ensure(&h)))
goto err;
asserted = (int)GET_U64();
if (!TEST_int_eq(ossl_json_in_error(&h.j), asserted))
goto err;
break;
}
#define OP_ASSERT_ERROR(err) OP_U64(OPK_ASSERT_ERROR) OP_U64(err)
default:
TEST_error("unknown opcode");
goto err;
}
stop:
if (!TEST_true(helper_ensure(&h)))
goto err;
if (!TEST_true(ossl_json_flush(&h.j)))
goto err;
/* Implicit error check if not done explicitly. */
if (asserted < 0 && !TEST_false(ossl_json_in_error(&h.j)))
goto err;
if (!TEST_true(BIO_get_mem_ptr(h.mem_bio, &bufp)))
goto err;
if (!TEST_mem_eq(bufp->data, bufp->length,
info->expected_output,
info->expected_output_len == SIZE_MAX
? strlen(info->expected_output)
: info->expected_output_len))
goto err;
ok = 1;
err:
if (!ok)
TEST_error("script '%s' failed", info->name);
helper_cleanup(&h);
return ok;
}
static int test_json_enc(void)
{
int ok = 1;
size_t i;
for (i = 0; i < OSSL_NELEM(scripts); ++i)
if (!TEST_true(run_script(scripts[i]())))
ok = 0;
return ok;
}
int setup_tests(void)
{
ADD_TEST(test_json_enc);
return 1;
}