diff --git a/src/json/load_config.c b/src/json/load_config.c index 732f17a..16ad3d6 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -327,7 +327,8 @@ static wolfsentry_errcode_t convert_uint8(JSON_TYPE type, const unsigned char *d } static wolfsentry_errcode_t convert_double(JSON_TYPE type, const unsigned char *data, size_t data_size, double *out) { - char buf[24]; + /* fits the longest %.17g rendering, 24 characters */ + char buf[25]; char *endptr; double conv; @@ -1735,7 +1736,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( #ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS .max_total_len = WOLFSENTRY_MAX_JSON_TOTAL_LEN, .max_total_values = 0, - .max_number_len = 20, + .max_number_len = 24, .max_string_len = WOLFSENTRY_KV_MAX_VALUE_BYTES, .max_key_len = WOLFSENTRY_MAX_LABEL_BYTES, .max_nesting_level = WOLFSENTRY_MAX_JSON_NESTING, @@ -1743,7 +1744,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( #else WOLFSENTRY_MAX_JSON_TOTAL_LEN, 0, - 20, + 24, WOLFSENTRY_KV_MAX_VALUE_BYTES, WOLFSENTRY_MAX_LABEL_BYTES, WOLFSENTRY_MAX_JSON_NESTING, diff --git a/src/kv.c b/src/kv.c index d921ad5..bfcd355 100644 --- a/src/kv.c +++ b/src/kv.c @@ -26,6 +26,9 @@ #include #endif +#include +#include + #define WOLFSENTRY_SOURCE_ID WOLFSENTRY_SOURCE_ID_KV_C static inline int wolfsentry_kv_key_cmp_1(const char *left_label, const unsigned int left_label_len, const char *right_label, const unsigned int right_label_len) { @@ -60,7 +63,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_new( struct wolfsentry_kv_pair_internal **kv) { size_t kv_size; - if (key_len == 0) + if (key == NULL) WOLFSENTRY_ERROR_RETURN(INVALID_ARG); if (data_len < 0) WOLFSENTRY_ERROR_RETURN(INVALID_ARG); @@ -68,6 +71,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_new( WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); if (key_len < 0) key_len = (int)strlen(key); + if (key_len <= 0) + WOLFSENTRY_ERROR_RETURN(INVALID_ARG); if (key_len > WOLFSENTRY_MAX_LABEL_BYTES) WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); kv_size = sizeof **kv + (size_t)key_len + 1 + (size_t)data_len; @@ -170,9 +175,14 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_insert( struct wolfsentry_kv_pair_internal *kv) { wolfsentry_errcode_t ret; + wolfsentry_kv_validator_t validator = WOLFSENTRY_ATOMIC_LOAD(kv_table->validator); - if (kv_table->validator) { - ret = kv_table->validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv); + /* read the validator pointer atomically (paired with the atomic store in + * the setter) but call it without the context lock held, to avoid a + * lock-order inversion with locks the application callback may take. + */ + if (validator) { + ret = validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv); WOLFSENTRY_RERETURN_IF_ERROR(ret); } @@ -224,9 +234,14 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_set( { struct wolfsentry_kv_pair_internal *old; wolfsentry_errcode_t ret; + wolfsentry_kv_validator_t validator = WOLFSENTRY_ATOMIC_LOAD(kv_table->validator); - if (kv_table->validator) { - ret = kv_table->validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv); + /* read the validator pointer atomically (paired with the atomic store in + * the setter) but call it without the context lock held, to avoid a + * lock-order inversion with locks the application callback may take. + */ + if (validator) { + ret = validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv); WOLFSENTRY_RERETURN_IF_ERROR(ret); } @@ -242,11 +257,22 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_set( WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); WOLFSENTRY_UNLOCK_AND_RETURN_OK; } - if (((ret = wolfsentry_table_ent_delete_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &old->header)) < 0) || - ((ret = wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old, NULL)) < 0)) - { + if ((ret = wolfsentry_table_ent_delete_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &old->header)) < 0) + WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); + /* insert the replacement before dropping the old entry, so a failure + * on either step never leaves the key absent; restore the old entry if + * the insert fails. + */ + if ((ret = wolfsentry_kv_insert_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv)) < 0) { + /* re-insert with a fresh id, since the delete released the old one; + * drop it if even that fails, so the detached record is not leaked. + */ + if (wolfsentry_kv_insert_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, old) < 0) + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old, NULL)); WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); } + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old, NULL)); + WOLFSENTRY_UNLOCK_AND_RETURN_OK; } ret = wolfsentry_kv_insert_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv); @@ -257,6 +283,7 @@ static wolfsentry_errcode_t wolfsentry_kv_get_1( WOLFSENTRY_CONTEXT_ARGS_IN, struct wolfsentry_kv_table *kv_table, const struct wolfsentry_kv_pair_internal *kv_template, + int take_reference, struct wolfsentry_kv_pair_internal **kv) { wolfsentry_errcode_t ret; @@ -272,6 +299,13 @@ static wolfsentry_errcode_t wolfsentry_kv_get_1( else if ((WOLFSENTRY_KV_TYPE(&kv_template->kv) != WOLFSENTRY_KV_NONE) && (WOLFSENTRY_KV_TYPE(&kv_template->kv) != WOLFSENTRY_KV_TYPE(&ret_kv->kv))) WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(WRONG_TYPE); + /* take the reference while the shared lock is still held, so a concurrent + * delete cannot free the entry between lookup and increment. + */ + if (take_reference) { + WOLFSENTRY_REFCOUNT_INCREMENT(ret_kv->header.refcount, ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); + } *kv = ret_kv; WOLFSENTRY_UNLOCK_AND_RETURN_OK; } @@ -289,7 +323,7 @@ static wolfsentry_errcode_t wolfsentry_kv_get_2( if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); kv_template->kv.v_type = type; - ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, kv); + ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 1 /* take_reference */, kv); WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL)); WOLFSENTRY_ERROR_RERETURN(ret); } @@ -300,11 +334,7 @@ static wolfsentry_errcode_t wolfsentry_kv_get_reference_1( const struct wolfsentry_kv_pair_internal *kv_template, struct wolfsentry_kv_pair_internal **kv) { - wolfsentry_errcode_t ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, kv); - WOLFSENTRY_RERETURN_IF_ERROR(ret); - WOLFSENTRY_REFCOUNT_INCREMENT((*kv)->header.refcount, ret); - WOLFSENTRY_RERETURN_IF_ERROR(ret); - WOLFSENTRY_RETURN_OK; + WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 1 /* take_reference */, kv)); } WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_get_reference( @@ -318,7 +348,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_get_reference( struct wolfsentry_kv_pair_internal *kv_template; wolfsentry_errcode_t ret; if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0) - WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); + WOLFSENTRY_ERROR_RERETURN(ret); kv_template->kv.v_type = type; ret = wolfsentry_kv_get_reference_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, kv); WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL)); @@ -338,11 +368,11 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_get_type( struct wolfsentry_kv_pair_internal *kv = NULL; if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); - ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, &kv); + ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 1 /* take_reference */, &kv); WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL)); WOLFSENTRY_RERETURN_IF_ERROR(ret); *type = WOLFSENTRY_KV_TYPE(&kv->kv); - WOLFSENTRY_RETURN_OK; + WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_kv_type_to_string( @@ -402,6 +432,86 @@ static int _json_value_dump_writer(const unsigned char* str, size_t size, void * } #endif +#ifndef WOLFSENTRY_HAVE_JSON_DOM + +#define WOLFSENTRY_KV_EMIT_CHAR(c) do { if (needed < out_space) out[needed] = (char)(c); ++needed; } while (0) + +/* escape a KV string into a JSON string literal, mirroring json_dump_string in + * the DOM path, and return the snprintf-style length (excluding the null). + */ +static int wolfsentry_kv_render_string_escaped( + const char *in, + size_t in_len, + char *out, + size_t out_space) +{ + static const char hexdigits[] = "0123456789abcdef"; + size_t in_i; + size_t needed = 0; + unsigned char c; + + WOLFSENTRY_KV_EMIT_CHAR('"'); + for (in_i = 0; in_i < in_len; ++in_i) { + c = (unsigned char)in[in_i]; + switch (c) { + case '"': + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('"'); + break; + case '\\': + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('\\'); + break; + case '\n': + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('n'); + break; + case '\r': + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('r'); + break; + case '\t': + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('t'); + break; + case '\b': + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('b'); + break; + case '\f': + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('f'); + break; + default: + if (c < 0x20) { + WOLFSENTRY_KV_EMIT_CHAR('\\'); + WOLFSENTRY_KV_EMIT_CHAR('u'); + WOLFSENTRY_KV_EMIT_CHAR('0'); + WOLFSENTRY_KV_EMIT_CHAR('0'); + WOLFSENTRY_KV_EMIT_CHAR(hexdigits[(c >> 4) & 0xf]); + WOLFSENTRY_KV_EMIT_CHAR(hexdigits[c & 0xf]); + } else { + WOLFSENTRY_KV_EMIT_CHAR(c); + } + break; + } + } + WOLFSENTRY_KV_EMIT_CHAR('"'); + + if (out_space > 0) { + if (needed < out_space) + out[needed] = 0; + else + out[out_space - 1] = 0; + } + + return (int)needed; +} + +#undef WOLFSENTRY_KV_EMIT_CHAR + +#endif /* !WOLFSENTRY_HAVE_JSON_DOM */ + WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_kv_render_value( WOLFSENTRY_CONTEXT_ARGS_IN, const struct wolfsentry_kv_pair *kv, @@ -425,17 +535,39 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_kv_render_value( *out_len = snprintf(out, out_space, "false"); break; case WOLFSENTRY_KV_UINT: +#ifdef PRIu64 + *out_len = snprintf(out, out_space, "%" PRIu64, WOLFSENTRY_KV_V_UINT(kv)); +#else *out_len = snprintf(out, out_space, "%lu", (long unsigned int)WOLFSENTRY_KV_V_UINT(kv)); +#endif break; case WOLFSENTRY_KV_SINT: +#ifdef PRId64 + *out_len = snprintf(out, out_space, "%" PRId64, WOLFSENTRY_KV_V_SINT(kv)); +#else *out_len = snprintf(out, out_space, "%ld", (long int)WOLFSENTRY_KV_V_SINT(kv)); +#endif break; - case WOLFSENTRY_KV_FLOAT: - *out_len = snprintf(out, out_space, "%.10f", WOLFSENTRY_KV_V_FLOAT(kv)); + case WOLFSENTRY_KV_FLOAT: { + double v_float = WOLFSENTRY_KV_V_FLOAT(kv); + /* non-finite values have no bare JSON token, so emit a quoted string + * that the loader's strtod path re-reads to the same value. finiteness + * is tested with DBL_MAX comparisons to stay legal under C89 (no C99 + * isnan()/isinf()). + */ + if (!(v_float <= DBL_MAX) && !(v_float >= -DBL_MAX)) /* NaN */ + *out_len = snprintf(out, out_space, "\"nan\""); + else if (!(v_float <= DBL_MAX)) /* +Inf */ + *out_len = snprintf(out, out_space, "\"inf\""); + else if (!(v_float >= -DBL_MAX)) /* -Inf */ + *out_len = snprintf(out, out_space, "\"-inf\""); + else + *out_len = snprintf(out, out_space, "%.17g", v_float); break; + } case WOLFSENTRY_KV_STRING: { #ifndef WOLFSENTRY_HAVE_JSON_DOM - *out_len = snprintf(out, out_space, "\"%.*s\"", (int)WOLFSENTRY_KV_V_STRING_LEN(kv), WOLFSENTRY_KV_V_STRING(kv)); + *out_len = wolfsentry_kv_render_string_escaped(WOLFSENTRY_KV_V_STRING(kv), WOLFSENTRY_KV_V_STRING_LEN(kv), out, out_space); break; #else struct dump_buf_state dbs; @@ -545,7 +677,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_delete( if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0) WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); - ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, &old); + ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 0 /* take_reference */, &old); WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL)); WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); if (old->kv.v_type & WOLFSENTRY_KV_FLAG_READONLY) @@ -591,7 +723,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_set_validator( ret = wolfsentry_table_map(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_table->header, (wolfsentry_map_function_t)apply_validator, (void *)&context, action_results); WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); } - wolfsentry->user_values->validator = validator; + WOLFSENTRY_ATOMIC_STORE(kv_table->validator, validator); WOLFSENTRY_UNLOCK_AND_RETURN_OK; } @@ -713,7 +845,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_set_mutability( struct wolfsentry_kv_pair_internal *kv = NULL; if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_NONE, &kv)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); - WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_set_mutability(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, kv, mutable)); + ret = wolfsentry_kv_set_mutability(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, kv, mutable); + if (ret < 0) + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); + else + ret = wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL); + WOLFSENTRY_ERROR_RERETURN(ret); } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_mutability( @@ -727,7 +864,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_mutability( if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_NONE, &kv)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); *mutable = wolfsentry_kv_get_mutability(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, kv); - WOLFSENTRY_RETURN_OK; + WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_type( @@ -805,10 +942,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_bool( if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_NONE, &kv)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); if ((WOLFSENTRY_KV_TYPE(&kv->kv) != WOLFSENTRY_KV_TRUE) && - (WOLFSENTRY_KV_TYPE(&kv->kv) != WOLFSENTRY_KV_FALSE)) + (WOLFSENTRY_KV_TYPE(&kv->kv) != WOLFSENTRY_KV_FALSE)) { + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); WOLFSENTRY_ERROR_RETURN(WRONG_TYPE); + } *value = WOLFSENTRY_KV_TYPE(&kv->kv); - WOLFSENTRY_RETURN_OK; + WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_uint( @@ -845,7 +984,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_uint( if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_UINT, &kv)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); *value = WOLFSENTRY_KV_V_UINT(&kv->kv); - WOLFSENTRY_RETURN_OK; + WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_sint( @@ -883,7 +1022,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_sint( if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_SINT, &kv)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); *value = WOLFSENTRY_KV_V_SINT(&kv->kv); - WOLFSENTRY_RETURN_OK; + WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_double( @@ -921,7 +1060,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_float( if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_FLOAT, &kv)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); *value = WOLFSENTRY_KV_V_FLOAT(&kv->kv); - WOLFSENTRY_RETURN_OK; + WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL)); } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_string( @@ -934,8 +1073,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_string( { wolfsentry_errcode_t ret; struct wolfsentry_kv_pair_internal *kv; - if (value_len < 0) - value_len = (int)strlen(value); + if (value_len < 0) { + size_t value_strlen = strlen(value); + if (value_strlen > (size_t)WOLFSENTRY_KV_MAX_VALUE_BYTES) + WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); + value_len = (int)value_strlen; + } if (value_len > WOLFSENTRY_KV_MAX_VALUE_BYTES) WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, value_len + 1, &kv)) < 0) @@ -1008,8 +1151,14 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_bytes_base64( wolfsentry_errcode_t ret; int decoded_len; struct wolfsentry_kv_pair_internal *kv; - if (value_len < 0) - value_len = (int)strlen(value); + /* the decoded size computation adds 3 to the length */ + if (value_len < 0) { + size_t value_strlen = strlen(value); + if (value_strlen > (size_t)(INT_MAX - 3)) + WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); + value_len = (int)value_strlen; + } else if (value_len > INT_MAX - 3) + WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); decoded_len = WOLFSENTRY_BASE64_DECODED_BUFSPC(value, value_len); if (decoded_len > WOLFSENTRY_KV_MAX_VALUE_BYTES) WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); diff --git a/tests/unittests.c b/tests/unittests.c index 7a75686..704b945 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3948,6 +3948,475 @@ static int test_user_values(void) { WOLFSENTRY_RETURN_OK; } +static int test_kv_reference_lifetime(void) { + struct wolfsentry_context *wolfsentry; + struct wolfsentry_kv_pair_internal *kv_ref = NULL; + const char *value = NULL; + int value_len = -1; + static const char key[] = "lifetime_key"; + static const char str[] = "lifetime_value"; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + str, WOLFSENTRY_LENGTH_NULL_TERMINATED, + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_get_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + &value, &value_len, &kv_ref)); + + /* delete the key while the reference returned by get_string is still held; + * the held reference must keep the entry alive and readable. + */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(str)); + WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, str) == 0); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_release_record( + WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_ref)); + + WOLFSENTRY_EXIT_ON_SUCCESS( + wolfsentry_user_value_get_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + &value, &value_len, &kv_ref)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +static int test_kv_get2_reference_balance(void) { + struct wolfsentry_context *wolfsentry; + static const char key[] = "balance_key"; + uint64_t uint_value = 0; + int mutable = -1; + int i; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_uint( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + 42, 0)); + + /* the non-reference getters take and release an internal reference each + * call; repeated calls must leave the refcount balanced so the entry can + * still be deleted afterwards. + */ + for (i = 0; i < 100; ++i) { + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_get_uint( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + &uint_value)); + WOLFSENTRY_EXIT_ON_FALSE(uint_value == 42); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_get_mutability( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + &mutable)); + } + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_SUCCESS( + wolfsentry_user_value_get_uint( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + &uint_value)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +static int test_kv_get_bad_key_errcode(void) { + struct wolfsentry_context *wolfsentry; + const char *value = NULL; + int value_len = -1; + struct wolfsentry_kv_pair_internal *kv_ref = NULL; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + /* a rejected key must surface the validation error itself, not an error + * from an unlock attempted without a held lock. + */ + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INVALID_ARG, + wolfsentry_user_value_get_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "unused_key", 0 /* key_len */, + &value, &value_len, &kv_ref)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +static int render_only_user_value( + WOLFSENTRY_CONTEXT_ARGS_IN, + char *out, + int out_space) +{ + struct wolfsentry_cursor *cursor; + struct wolfsentry_kv_pair_internal *kv_ref; + const struct wolfsentry_kv_pair *kv_exports; + int render_space = out_space; + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_current(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, out, &render_space)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + WOLFSENTRY_RETURN_OK; +} + +static int test_kv_render_large_int(void) { + struct wolfsentry_context *wolfsentry; + char buf[64]; + /* 0x1234567890ABCDEF and -(2^33), built without long-long literals so the + * test stays legal under WOLFSENTRY_C89; compared against the exact decimal + * rendering to avoid strtoull()/strtoll(). + */ + uint64_t stored_uint = ((uint64_t)0x12345678u << 32) | (uint64_t)0x90abcdefu; + int64_t stored_sint = -((int64_t)1 << 33); + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_uint( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "big_uint", WOLFSENTRY_LENGTH_NULL_TERMINATED, + stored_uint, 0)); + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + WOLFSENTRY_EXIT_ON_FALSE(strcmp(buf, "1311768467294899695") == 0); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, "big_uint", WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_sint( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "big_sint", WOLFSENTRY_LENGTH_NULL_TERMINATED, + stored_sint, 0)); + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + WOLFSENTRY_EXIT_ON_FALSE(strcmp(buf, "-8589934592") == 0); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +static int test_kv_render_float_roundtrip(void) { + struct wolfsentry_context *wolfsentry; + char buf[64]; + double stored = 3.141592653589793; +#ifndef WOLFSENTRY_NO_JSON + char json[128]; + int json_len; + double reloaded; +#endif + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_double( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "pi", WOLFSENTRY_LENGTH_NULL_TERMINATED, + stored, 0)); + + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + + /* the rendered form must reparse to the exact same double. */ + WOLFSENTRY_EXIT_ON_FALSE(strtod(buf, NULL) == stored); + +#ifndef WOLFSENTRY_NO_JSON + /* the longest %.17g form, 24 characters, must reload through the JSON loader */ + stored = -2.2250738585072014e-308; + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "pi", WOLFSENTRY_LENGTH_NULL_TERMINATED)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_store_double(WOLFSENTRY_CONTEXT_ARGS_OUT, "tiny", WOLFSENTRY_LENGTH_NULL_TERMINATED, stored, 0)); + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + WOLFSENTRY_EXIT_ON_FALSE(strlen(buf) == 24); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "tiny", WOLFSENTRY_LENGTH_NULL_TERMINATED)); + json_len = snprintf(json, sizeof json, "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"tiny\" : %s } }", buf); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_config_json_oneshot(WOLFSENTRY_CONTEXT_ARGS_OUT, (const unsigned char *)json, (size_t)json_len, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_get_float(WOLFSENTRY_CONTEXT_ARGS_OUT, "tiny", WOLFSENTRY_LENGTH_NULL_TERMINATED, &reloaded)); + WOLFSENTRY_EXIT_ON_FALSE(reloaded == stored); +#endif + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +#ifndef WOLFSENTRY_C89 +static int test_kv_render_nonfinite_float(void) { + struct wolfsentry_context *wolfsentry; + char buf[64]; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_double( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "nan_val", WOLFSENTRY_LENGTH_NULL_TERMINATED, + (double)NAN, 0)); + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + /* a bare nan/inf is not valid JSON; the rendered value must be quoted and + * must reparse to the same non-finite value. + */ + WOLFSENTRY_EXIT_ON_FALSE(buf[0] == '"'); + WOLFSENTRY_EXIT_ON_FALSE(isnan(strtod(buf + 1, NULL))); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, "nan_val", WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_double( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "inf_val", WOLFSENTRY_LENGTH_NULL_TERMINATED, + (double)INFINITY, 0)); + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + WOLFSENTRY_EXIT_ON_FALSE(buf[0] == '"'); + WOLFSENTRY_EXIT_ON_FALSE(isinf(strtod(buf + 1, NULL)) && (strtod(buf + 1, NULL) > 0.0)); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, "inf_val", WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_double( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "ninf_val", WOLFSENTRY_LENGTH_NULL_TERMINATED, + -(double)INFINITY, 0)); + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + WOLFSENTRY_EXIT_ON_FALSE(buf[0] == '"'); + WOLFSENTRY_EXIT_ON_FALSE(isinf(strtod(buf + 1, NULL)) && (strtod(buf + 1, NULL) < 0.0)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} +#endif /* !WOLFSENTRY_C89 */ + +static int test_kv_empty_key_rejected(void) { + struct wolfsentry_context *wolfsentry; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + /* an empty key must be rejected consistently, whether supplied as an + * explicit zero length or as a null-terminated empty string. + */ + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INVALID_ARG, + wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "", 0 /* key_len */, + "v", WOLFSENTRY_LENGTH_NULL_TERMINATED, + 0)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INVALID_ARG, + wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "", WOLFSENTRY_LENGTH_NULL_TERMINATED, + "v", WOLFSENTRY_LENGTH_NULL_TERMINATED, + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +static int test_kv_readonly_overwrite_rejected(void) { + struct wolfsentry_context *wolfsentry; + struct wolfsentry_kv_pair_internal *kv_ref = NULL; + const char *value = NULL; + int value_len = -1; + wolfsentry_errcode_t ret; + static const char key[] = "ro_key"; + static const char orig[] = "orig"; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + orig, WOLFSENTRY_LENGTH_NULL_TERMINATED, + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_set_mutability( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + 0 /* not mutable */)); + + /* overwriting a read-only value must be rejected. */ + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + NOT_PERMITTED, + ret = wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + "different", WOLFSENTRY_LENGTH_NULL_TERMINATED, + 1 /* overwrite_p */)); + + /* and the value must be unchanged. */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_get_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + key, WOLFSENTRY_LENGTH_NULL_TERMINATED, + &value, &value_len, &kv_ref)); + WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(orig)); + WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, orig) == 0); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_release_record(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_ref)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +#ifndef WOLFSENTRY_HAVE_JSON_DOM +static int test_kv_render_string_escape(void) { + struct wolfsentry_context *wolfsentry; + char buf[64]; + static const char raw[] = "a\"b\\c\nd"; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "esc", WOLFSENTRY_LENGTH_NULL_TERMINATED, + raw, (int)(sizeof raw - 1), + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf)); + + /* the rendered JSON string must not contain a raw control byte, and the + * quote, backslash and newline must all appear escaped. + */ + WOLFSENTRY_EXIT_ON_FALSE(strchr(buf + 1, '\n') == NULL); + WOLFSENTRY_EXIT_ON_FALSE(strstr(buf, "\\\"") != NULL); + WOLFSENTRY_EXIT_ON_FALSE(strstr(buf, "\\\\") != NULL); + WOLFSENTRY_EXIT_ON_FALSE(strstr(buf, "\\n") != NULL); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} +#endif /* !WOLFSENTRY_HAVE_JSON_DOM */ + #endif /* TEST_USER_VALUES */ #ifdef TEST_USER_ADDR_FAMILIES @@ -5757,6 +6226,55 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_user_values failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_kv_reference_lifetime(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_reference_lifetime failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } + ret = test_kv_get2_reference_balance(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_get2_reference_balance failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } + ret = test_kv_get_bad_key_errcode(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_get_bad_key_errcode failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } + ret = test_kv_render_large_int(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_render_large_int failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } + ret = test_kv_render_float_roundtrip(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_render_float_roundtrip failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } +#ifndef WOLFSENTRY_C89 + ret = test_kv_render_nonfinite_float(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_render_nonfinite_float failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } +#endif /* !WOLFSENTRY_C89 */ + ret = test_kv_readonly_overwrite_rejected(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_readonly_overwrite_rejected failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } + ret = test_kv_empty_key_rejected(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_empty_key_rejected failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } +#ifndef WOLFSENTRY_HAVE_JSON_DOM + ret = test_kv_render_string_escape(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_kv_render_string_escape failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } +#endif #endif #ifdef TEST_USER_ADDR_FAMILIES