From 2f81ddc6275a2911f29b7ea5cf5b44278342f060 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:22:27 -0700 Subject: [PATCH 01/23] F-4277 - https://fenrir.wolfssl.com/finding/4277 - Reject out-of-range uint64 config values without relying on errno --- src/json/load_config.c | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index 732f17a..4c83691 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -154,7 +154,51 @@ static wolfsentry_errcode_t reset_o_u_c(struct wolfsentry_json_process_state *jp WOLFSENTRY_RETURN_OK; } +/* Range check that does not depend on errno: re-scan the already + * syntactically-validated numeric string in buf (strtol-style base-0 rules) and + * reject it when its magnitude exceeds limit. A leading sign is skipped so the + * caller decides sign handling. + */ +static wolfsentry_errcode_t convert_check_magnitude(const char *buf, uint64_t limit) { + uint64_t acc = 0; + uint64_t base = 10; + const char *p = buf; + + while ((*p == ' ') || (*p == '\t')) + ++p; + if ((*p == '-') || (*p == '+')) + ++p; + if ((p[0] == '0') && ((p[1] == 'x') || (p[1] == 'X'))) { + base = 16; + p += 2; + } else if (p[0] == '0') { + base = 8; + ++p; + } + + while (*p != 0) { + uint64_t digit; + if ((*p >= '0') && (*p <= '9')) + digit = (uint64_t)(*p - '0'); + else if ((*p >= 'a') && (*p <= 'f')) + digit = (uint64_t)(*p - 'a' + 10); + else if ((*p >= 'A') && (*p <= 'F')) + digit = (uint64_t)(*p - 'A' + 10); + else + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + if (digit >= base) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + if (acc > (limit - digit) / base) + WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); + acc = (acc * base) + digit; + ++p; + } + + WOLFSENTRY_RETURN_OK; +} + static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char *data, size_t data_size, uint64_t *out) { + wolfsentry_errcode_t ret; char buf[24]; char *endptr; #ifdef WOLFSENTRY_HAVE_LONG_LONG @@ -187,6 +231,8 @@ static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char * if ((size_t)(endptr - buf) != data_size) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + ret = convert_check_magnitude(buf, (uint64_t)MAX_UINT_OF(*out)); + WOLFSENTRY_RERETURN_IF_ERROR(ret); *out = (uint64_t)conv; WOLFSENTRY_RETURN_OK; From c0c9c4e80741c560f360d732dd9252ffa1c06c59 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:24:40 -0700 Subject: [PATCH 02/23] F-10488 - https://fenrir.wolfssl.com/finding/10488 - Reject a leading minus sign in unsigned integer config values --- src/json/load_config.c | 13 ++++++++++++ tests/unittests.c | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index 4c83691..93601ce 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -197,6 +197,13 @@ static wolfsentry_errcode_t convert_check_magnitude(const char *buf, uint64_t li WOLFSENTRY_RETURN_OK; } +/* local equivalent of strspn(p, " \t"), which freestanding targets may lack. */ +static const char *convert_skip_blanks(const char *p) { + while ((*p == ' ') || (*p == '\t')) + ++p; + return p; +} + static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char *data, size_t data_size, uint64_t *out) { wolfsentry_errcode_t ret; char buf[24]; @@ -216,6 +223,12 @@ static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char * memcpy(buf, data, data_size); buf[data_size] = 0; + /* strtoull()/strtoul() silently negate a leading minus; reject it so a + * negative literal does not wrap to a large unsigned value. + */ + if (*convert_skip_blanks(buf) == '-') + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + #ifndef WOLFSENTRY_NO_ERRNO_H errno = 0; #endif diff --git a/tests/unittests.c b/tests/unittests.c index 7a75686..f4ed7d3 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5466,6 +5466,49 @@ static int test_json(const char *fname, const char *extra_fname) { WOLFSENTRY_RETURN_OK; } +static wolfsentry_errcode_t test_json_oneshot_string( + const char *json, + wolfsentry_config_load_flags_t load_flags) +{ + wolfsentry_errcode_t ret, load_ret; + struct wolfsentry_context *wolfsentry; +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT }; +#else + struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#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), + &config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + load_ret = wolfsentry_config_json_oneshot( + WOLFSENTRY_CONTEXT_ARGS_OUT, + (const unsigned char *)json, + strlen(json), + load_flags, + NULL, + 0); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + ret = WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_ERROR_RERETURN(load_ret); +} + +static int test_json_uint64_negative(void) { + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"v\" : { \"uint\" : -1 } } }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + #endif /* TEST_JSON */ #ifdef TEST_JSON_CORPUS @@ -5784,6 +5827,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json failed for " TEST_NUMERIC_JSON_CONFIG_PATH ", " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_uint64_negative(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_uint64_negative failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From aa12969d05bdf4d26d428156fb6e937f3d35ef8c Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:25:26 -0700 Subject: [PATCH 03/23] F-4282 - https://fenrir.wolfssl.com/finding/4282 - Reject out-of-range signed integer config values without relying on errno --- src/json/load_config.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index 93601ce..478ebd5 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -252,8 +252,11 @@ static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char * } static wolfsentry_errcode_t convert_sint64(JSON_TYPE type, const unsigned char *data, size_t data_size, int64_t *out) { + wolfsentry_errcode_t ret; char buf[24]; char *endptr; + const char *sign_p; + uint64_t magnitude_limit; #ifdef WOLFSENTRY_HAVE_LONG_LONG long long conv; #else @@ -284,6 +287,13 @@ static wolfsentry_errcode_t convert_sint64(JSON_TYPE type, const unsigned char * if ((size_t)(endptr - buf) != data_size) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + sign_p = convert_skip_blanks(buf); + if (*sign_p == '-') + magnitude_limit = (uint64_t)MAX_SINT_OF(*out) + 1U; + else + magnitude_limit = (uint64_t)MAX_SINT_OF(*out); + ret = convert_check_magnitude(buf, magnitude_limit); + WOLFSENTRY_RERETURN_IF_ERROR(ret); *out = (int64_t)conv; WOLFSENTRY_RETURN_OK; From 32ccd3fbfb9e1a1670b71b2f7ac55a833316e3f4 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:26:02 -0700 Subject: [PATCH 04/23] F-4283 - https://fenrir.wolfssl.com/finding/4283 - Use a wider accumulator so the uint32 range check is live on 32-bit ABIs --- src/json/load_config.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index 478ebd5..deaf1dc 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -302,7 +302,14 @@ static wolfsentry_errcode_t convert_sint64(JSON_TYPE type, const unsigned char * static wolfsentry_errcode_t convert_uint32(JSON_TYPE type, const unsigned char *data, size_t data_size, uint32_t *out) { char buf[16]; char *endptr; + /* use the widest available unsigned type so the range check below is live + * even on ILP32 ABIs, where unsigned long is the same width as *out. + */ +#ifdef WOLFSENTRY_HAVE_LONG_LONG + unsigned long long conv; +#else unsigned long conv; +#endif /* allow JSON_STRING to accommodate hex and octal values. */ if ((type != JSON_NUMBER) && (type != JSON_STRING)) @@ -316,7 +323,11 @@ static wolfsentry_errcode_t convert_uint32(JSON_TYPE type, const unsigned char * #ifndef WOLFSENTRY_NO_ERRNO_H errno = 0; #endif +#ifdef WOLFSENTRY_HAVE_LONG_LONG + conv = strtoull(buf, &endptr, 0); +#else conv = strtoul(buf, &endptr, 0); +#endif #ifndef WOLFSENTRY_NO_ERRNO_H if (errno != 0) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); From 72ac6a9ff7e84f30a6ddc3395de1c59b3f833d9a Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:26:29 -0700 Subject: [PATCH 05/23] F-4278 - https://fenrir.wolfssl.com/finding/4278 - Range check CAN addresses without relying on errno on 32-bit ABIs --- src/json/load_config.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index deaf1dc..d5a687d 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -784,7 +784,14 @@ static wolfsentry_errcode_t convert_sockaddr_address( else if (sa->sa_family == WOLFSENTRY_AF_CAN) { /* accept CAN addrs as either strings consisting of hex or octal numbers, or as a JSON-native decimal integer. */ char d_buf[13], *endptr; + /* use the widest available unsigned type so the range check below is + * live even on ILP32 ABIs, where unsigned long equals uint32_t. + */ +#ifdef WOLFSENTRY_HAVE_LONG_LONG + unsigned long long addr; +#else unsigned long addr; +#endif if ((type != JSON_STRING) && (type != JSON_NUMBER)) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); if (data_size >= sizeof d_buf) @@ -805,7 +812,18 @@ static wolfsentry_errcode_t convert_sockaddr_address( } memcpy(d_buf, data, data_size); d_buf[data_size] = 0; +#ifndef WOLFSENTRY_NO_ERRNO_H + errno = 0; +#endif +#ifdef WOLFSENTRY_HAVE_LONG_LONG + addr = strtoull(d_buf, &endptr, 0); +#else addr = strtoul(d_buf, &endptr, 0); +#endif +#ifndef WOLFSENTRY_NO_ERRNO_H + if (errno != 0) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); +#endif if ((size_t)(endptr - d_buf) != data_size) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); if (addr > MAX_UINT_OF(uint32_t)) From 8a83012bc51d95371ab67b5c3828454620cc9a7c Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:28:07 -0700 Subject: [PATCH 06/23] F-6687 - https://fenrir.wolfssl.com/finding/6687 - Add coverage for uint16 config value range rejection and boundary --- tests/unittests.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unittests.c b/tests/unittests.c index f4ed7d3..a32b9b1 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5509,6 +5509,18 @@ static int test_json_uint64_negative(void) { WOLFSENTRY_RETURN_OK; } +static int test_json_uint16_bounds(void) { + static const char too_big[] = + "{ \"wolfsentry-config-version\" : 1, \"events\" : [ { \"label\" : \"e\", \"priority\" : 70000 } ] }"; + static const char at_max[] = + "{ \"wolfsentry-config-version\" : 1, \"events\" : [ { \"label\" : \"e\", \"priority\" : 65535 } ] }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(too_big, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_FAILURE( + test_json_oneshot_string(at_max, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + #endif /* TEST_JSON */ #ifdef TEST_JSON_CORPUS @@ -5832,6 +5844,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_uint64_negative failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_uint16_bounds(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_uint16_bounds failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From 61d9d13fcc4ff8740ee96e2e4ae93b37658c9880 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:29:08 -0700 Subject: [PATCH 07/23] F-3435 - https://fenrir.wolfssl.com/finding/3435 - Size the uint8 parse buffer to accept hex and octal interface values --- src/json/load_config.c | 2 +- tests/unittests.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index d5a687d..b296f45 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -375,7 +375,7 @@ static wolfsentry_errcode_t convert_uint16(JSON_TYPE type, const unsigned char * } static wolfsentry_errcode_t convert_uint8(JSON_TYPE type, const unsigned char *data, size_t data_size, uint8_t *out) { - char buf[4]; + char buf[8]; char *endptr; unsigned long conv; diff --git a/tests/unittests.c b/tests/unittests.c index a32b9b1..ba4fc09 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5509,6 +5509,14 @@ static int test_json_uint64_negative(void) { WOLFSENTRY_RETURN_OK; } +static int test_json_uint8_hex_interface(void) { + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"interface\" : \"0xff\" } } ] }"; + WOLFSENTRY_EXIT_ON_FAILURE( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + static int test_json_uint16_bounds(void) { static const char too_big[] = "{ \"wolfsentry-config-version\" : 1, \"events\" : [ { \"label\" : \"e\", \"priority\" : 70000 } ] }"; @@ -5849,6 +5857,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_uint16_bounds failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_uint8_hex_interface(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_uint8_hex_interface failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From 5639e9881182a35f3a6031b062390954c995122a Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:30:16 -0700 Subject: [PATCH 08/23] F-4322 - https://fenrir.wolfssl.com/finding/4322 - Reject negative durations in the JSON loader --- src/json/load_config.c | 6 ++++++ tests/unittests.c | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index b296f45..018e614 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -493,6 +493,12 @@ static wolfsentry_errcode_t convert_wolfsentry_duration(struct wolfsentry_contex } if ((size_t)(endptr - buf) != data_size) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + /* durations are non-negative; a negative value (including a LONG_MIN + * saturation when errno is unavailable) would otherwise reach the interval + * conversion and overflow. + */ + if (conv < 0) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); if ((ret = wolfsentry_interval_from_seconds(wolfsentry, conv, 0 /* howlong_nsecs */, out)) < 0) WOLFSENTRY_ERROR_RERETURN(ret); else diff --git a/tests/unittests.c b/tests/unittests.c index ba4fc09..c2394fe 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5509,6 +5509,14 @@ static int test_json_uint64_negative(void) { WOLFSENTRY_RETURN_OK; } +static int test_json_duration_negative(void) { + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"config-update\" : { \"penalty-box-duration\" : \"-100s\" } }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + static int test_json_uint8_hex_interface(void) { static const char json[] = "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"interface\" : \"0xff\" } } ] }"; @@ -5862,6 +5870,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_uint8_hex_interface failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_duration_negative(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_duration_negative failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From 8ad14c4d59e22bb173628c6120ea880710a04c80 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:35:38 -0700 Subject: [PATCH 09/23] F-4565 - https://fenrir.wolfssl.com/finding/4565 - Reject an explicit LINK64 family when the address byte count is not EUI-64 --- src/json/load_config.c | 7 +++++++ tests/unittests.c | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index 018e614..9c2a6c8 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -718,6 +718,7 @@ static wolfsentry_errcode_t convert_sockaddr_address( byte *sa_addr) { if ((sa->sa_family == WOLFSENTRY_AF_LINK48) || (sa->sa_family == WOLFSENTRY_AF_LINK64)) { + wolfsentry_addr_family_t requested_family = sa->sa_family; int n; if (type != JSON_STRING) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); @@ -728,6 +729,12 @@ static wolfsentry_errcode_t convert_sockaddr_address( WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); ++n; if (data_size == 0) { + /* an explicit EUI-64 family must not be silently reclassified + * as EUI-48 by the parsed byte count (LINK48 and the generic + * LINK alias share a value, so only LINK64 can be enforced). + */ + if ((requested_family == WOLFSENTRY_AF_LINK64) && (n != 8)) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); if (n == 6) sa->sa_family = WOLFSENTRY_AF_LINK48; else if (n == 8) diff --git a/tests/unittests.c b/tests/unittests.c index c2394fe..0e4c396 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5509,6 +5509,17 @@ static int test_json_uint64_negative(void) { WOLFSENTRY_RETURN_OK; } +static int test_json_link64_short_address(void) { + /* family 119 == WOLFSENTRY_AF_LINK64; a six-byte EUI-48 address must not be + * silently accepted as LINK64. + */ + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 119, \"remote\" : { \"address\" : \"00:11:22:33:44:55\" }, \"local\" : { \"address\" : \"00:11:22:33:44:55\" } } ] }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + static int test_json_duration_negative(void) { static const char json[] = "{ \"wolfsentry-config-version\" : 1, \"config-update\" : { \"penalty-box-duration\" : \"-100s\" } }"; @@ -5875,6 +5886,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_duration_negative failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_link64_short_address(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_link64_short_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From 3ae5e69fe90fce32949bb5cf14c9c7ead298ff25 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:37:12 -0700 Subject: [PATCH 10/23] F-8780 - https://fenrir.wolfssl.com/finding/8780 - Treat a null service lookup result as item-not-found --- src/json/load_config.c | 4 ++++ tests/unittests.c | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index 9c2a6c8..d764cd6 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -924,6 +924,10 @@ static wolfsentry_errcode_t convert_sockaddr_port_name(struct wolfsentry_json_pr WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND); } + /* glibc reports an unknown service with a zero return and a NULL result. */ + if (s == NULL) + WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND); + sa->sa_port = (wolfsentry_port_t)ntohs((uint16_t)s->s_port); WOLFSENTRY_RETURN_OK; } diff --git a/tests/unittests.c b/tests/unittests.c index 0e4c396..658e7d6 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5509,6 +5509,16 @@ static int test_json_uint64_negative(void) { WOLFSENTRY_RETURN_OK; } +#ifndef WOLFSENTRY_NO_GETPROTOBY +static int test_json_unknown_service_name(void) { + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"protocol\" : 6, \"remote\" : { \"port\" : \"no-such-service-zzz\" } } ] }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} +#endif /* !WOLFSENTRY_NO_GETPROTOBY */ + static int test_json_link64_short_address(void) { /* family 119 == WOLFSENTRY_AF_LINK64; a six-byte EUI-48 address must not be * silently accepted as LINK64. @@ -5891,6 +5901,13 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_link64_short_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } +#ifndef WOLFSENTRY_NO_GETPROTOBY + ret = test_json_unknown_service_name(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_unknown_service_name failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } +#endif #endif #ifdef TEST_JSON_CORPUS From 441d30b217d24addd4d3c49fef5fe081ff006659 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:37:57 -0700 Subject: [PATCH 11/23] F-8781 - https://fenrir.wolfssl.com/finding/8781 - Treat a null protocol lookup result as item-not-found --- src/json/load_config.c | 4 ++++ tests/unittests.c | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index d764cd6..527dd9d 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -1096,6 +1096,10 @@ static wolfsentry_errcode_t handle_route_protocol_clause(struct wolfsentry_json_ WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND); } + /* glibc reports an unknown protocol with a zero return and a NULL result. */ + if (p == NULL) + WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND); + jps->o_u_c.route.remote.sa_proto = (wolfsentry_proto_t)p->p_proto; jps->o_u_c.route.local.sa_proto = (wolfsentry_proto_t)p->p_proto; } diff --git a/tests/unittests.c b/tests/unittests.c index 658e7d6..402d341 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5517,6 +5517,14 @@ static int test_json_unknown_service_name(void) { test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); WOLFSENTRY_RETURN_OK; } + +static int test_json_unknown_protocol_name(void) { + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"protocol\" : \"no-such-protocol-zzz\" } ] }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} #endif /* !WOLFSENTRY_NO_GETPROTOBY */ static int test_json_link64_short_address(void) { @@ -5907,6 +5915,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_unknown_service_name failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_unknown_protocol_name(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_unknown_protocol_name failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #endif From 59cb6eccbc5c2178cc67975a393a6b7d7c5a5a95 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:40:27 -0700 Subject: [PATCH 12/23] F-3666 - https://fenrir.wolfssl.com/finding/3666 - Right-justify a bitmask address by byte count to prevent an out-of-bounds write --- src/json/load_config.c | 10 ++++++++-- tests/unittests.c | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index 527dd9d..613cf28 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -983,8 +983,14 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ * move it to right-justified position. */ if ((sa->addr_len != 0) && (sa->addr_len != addr_size)) { - size_t pad_bytes = WOLFSENTRY_BITS_TO_BYTES((size_t)addr_size - (size_t)sa->addr_len); - memmove(sa->addr + pad_bytes, sa->addr, sa->addr_len); + size_t addr_bytes; + size_t pad_bytes; + if (sa->addr_len > addr_size) + WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); + /* addr_len is a bit count; the move operates on whole bytes. */ + addr_bytes = WOLFSENTRY_BITS_TO_BYTES((size_t)sa->addr_len); + pad_bytes = WOLFSENTRY_BITS_TO_BYTES((size_t)addr_size) - addr_bytes; + memmove(sa->addr + pad_bytes, sa->addr, addr_bytes); memset(sa->addr, 0, pad_bytes); } diff --git a/tests/unittests.c b/tests/unittests.c index 402d341..cfa6ce8 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5527,6 +5527,20 @@ static int test_json_unknown_protocol_name(void) { } #endif /* !WOLFSENTRY_NO_GETPROTOBY */ +#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING +static int test_json_bitmask_short_address(void) { + /* a partial address (prefix-bits smaller than the family width) followed by + * a bitmask exercises the right-justification move; the move must copy + * bytes, not the bit count, or it corrupts adjacent parse state. + */ + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"prefix-bits\" : 16, \"address\" : \"1.2.0.0\", \"bitmask\" : \"255.255.0.0\" } } ] }"; + WOLFSENTRY_EXIT_ON_FAILURE( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} +#endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */ + static int test_json_link64_short_address(void) { /* family 119 == WOLFSENTRY_AF_LINK64; a six-byte EUI-48 address must not be * silently accepted as LINK64. @@ -5904,6 +5918,13 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_duration_negative failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } +#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING + ret = test_json_bitmask_short_address(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_bitmask_short_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } +#endif ret = test_json_link64_short_address(); if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { (void)fprintf(stderr, "test_json_link64_short_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); From ee177ee1b26af686a9de062e7e508655ca05e88d Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:41:31 -0700 Subject: [PATCH 13/23] F-3873 - https://fenrir.wolfssl.com/finding/3873 - Bound prefix-bits to the address family width in the JSON loader --- src/json/load_config.c | 13 ++++++++++++- tests/unittests.c | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index 613cf28..640efff 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -1007,6 +1007,8 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ } #endif else if (! strcmp(jps->cur_keyname, "prefix-bits")) { + wolfsentry_addr_bits_t max_addr_bits; + wolfsentry_errcode_t ret; if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE); #ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING @@ -1019,7 +1021,16 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ WOLFSENTRY_ERROR_RETURN(CONFIG_MISPLACED_KEY); } #endif - WOLFSENTRY_ERROR_RERETURN(convert_uint16(type, data, data_size, &sa->addr_len)); + ret = convert_uint16(type, data, data_size, &sa->addr_len); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + /* bound prefix-bits to the address family width so a later copy cannot + * read past the fixed-size staging buffer. + */ + ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, sa->sa_family, &max_addr_bits); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + if (sa->addr_len > max_addr_bits) + WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); + WOLFSENTRY_RETURN_OK; } else if (! strcmp(jps->cur_keyname, "interface")) { WOLFSENTRY_CLEAR_BITS(jps->o_u_c.route.flags, diff --git a/tests/unittests.c b/tests/unittests.c index cfa6ce8..2c13d15 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5541,6 +5541,17 @@ static int test_json_bitmask_short_address(void) { } #endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */ +static int test_json_prefix_bits_overbound(void) { + /* prefix-bits beyond the family width must be rejected before the value can + * drive an out-of-bounds copy from the fixed staging buffer. + */ + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"address\" : \"1.2.3.4\", \"prefix-bits\" : 65535 } } ] }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + static int test_json_link64_short_address(void) { /* family 119 == WOLFSENTRY_AF_LINK64; a six-byte EUI-48 address must not be * silently accepted as LINK64. @@ -5925,6 +5936,11 @@ int main(int argc, char* argv[]) { err = 1; } #endif + ret = test_json_prefix_bits_overbound(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_prefix_bits_overbound failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } ret = test_json_link64_short_address(); if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { (void)fprintf(stderr, "test_json_link64_short_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); From 7f6ac17b4e4151437ebe2b5b5d1d91e320fa3ded Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:43:12 -0700 Subject: [PATCH 14/23] F-13290 - https://fenrir.wolfssl.com/finding/13290 - Route the actions-update rejection through the parser error-position epilogue --- src/json/load_config.c | 2 +- tests/unittests.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index 640efff..19dac34 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -1768,7 +1768,7 @@ static wolfsentry_errcode_t json_process( WOLFSENTRY_RETURN_OK; } if (! strcmp(jps->cur_keyname, "actions-update")) { - WOLFSENTRY_ERROR_RETURN(CONFIG_MISSING_HANDLER); + WOLFSENTRY_ERROR_OUT(CONFIG_MISSING_HANDLER); /* jps->table_under_construction = T_U_C_ACTIONS; WOLFSENTRY_RETURN_OK; diff --git a/tests/unittests.c b/tests/unittests.c index 2c13d15..7ca238a 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5541,6 +5541,14 @@ static int test_json_bitmask_short_address(void) { } #endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */ +static int test_json_actions_update_rejected(void) { + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"actions-update\" : [ ] }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + static int test_json_prefix_bits_overbound(void) { /* prefix-bits beyond the family width must be rejected before the value can * drive an out-of-bounds copy from the fixed staging buffer. @@ -5936,6 +5944,11 @@ int main(int argc, char* argv[]) { err = 1; } #endif + ret = test_json_actions_update_rejected(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_actions_update_rejected failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } ret = test_json_prefix_bits_overbound(); if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { (void)fprintf(stderr, "test_json_prefix_bits_overbound failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); From 1055271467129b43a4be8a2d084b11a717f7ad85 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:45:42 -0700 Subject: [PATCH 15/23] F-2441 - https://fenrir.wolfssl.com/finding/2441 - Fail closed when a default-mode JSON config load fails after flushing --- src/json/load_config.c | 20 ++++++++++++++++ tests/unittests.c | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/json/load_config.c b/src/json/load_config.c index 19dac34..afc1d4a 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -2172,6 +2172,26 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_fini( if ((*jps)->wolfsentry && ((*jps)->wolfsentry != (*jps)->wolfsentry_actual)) WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free(JPSP_P_WOLFSENTRY_CONTEXT_ARGS_OUT)); + /* In the default (non-transactional) load mode the live context was flushed + * before parsing; a failure leaves it partially populated while its default + * policy is unchanged. Fail closed so a half-loaded policy cannot admit + * traffic: clear the routes and set a REJECT default policy. + */ + if ((ret < 0) && + (! WOLFSENTRY_MASKIN_BITS((*jps)->load_flags, + WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN | + WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT | + WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH))) + { + struct wolfsentry_route_table *main_table; + wolfsentry_action_res_t action_results; + WOLFSENTRY_CLEAR_ALL_BITS(action_results); + if (wolfsentry_route_get_main_table(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, &main_table) >= 0) { + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_route_flush_table(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, main_table, &action_results)); + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_route_table_default_policy_set(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, main_table, WOLFSENTRY_ACTION_RES_REJECT)); + } + } + #ifdef WOLFSENTRY_THREADSAFE { wolfsentry_errcode_t _lock_ret; diff --git a/tests/unittests.c b/tests/unittests.c index 7ca238a..0c250a8 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5541,6 +5541,53 @@ static int test_json_bitmask_short_address(void) { } #endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */ +static int test_json_failed_load_fails_closed(void) { + wolfsentry_errcode_t ret; + struct wolfsentry_context *wolfsentry; + wolfsentry_action_res_t default_policy = WOLFSENTRY_ACTION_RES_NONE; + static const char good[] = + "{ \"wolfsentry-config-version\" : 1, \"default-policies\" : { \"default-policy\" : \"accept\" } }"; + static const char bad[] = + "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"x\" : { \"uint\" : -1 } } }"; +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT }; +#else + struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#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), + &config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_config_json_oneshot( + WOLFSENTRY_CONTEXT_ARGS_OUT, + (const unsigned char *)good, strlen(good), + WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0)); + + /* a failing default-mode load must leave the context fail-closed. */ + WOLFSENTRY_EXIT_ON_SUCCESS( + wolfsentry_config_json_oneshot( + WOLFSENTRY_CONTEXT_ARGS_OUT, + (const unsigned char *)bad, strlen(bad), + WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_get(WOLFSENTRY_CONTEXT_ARGS_OUT, &default_policy)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + WOLFSENTRY_EXIT_ON_FALSE(default_policy == WOLFSENTRY_ACTION_RES_REJECT); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + ret = WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_RETURN_OK; +} + static int test_json_actions_update_rejected(void) { static const char json[] = "{ \"wolfsentry-config-version\" : 1, \"actions-update\" : [ ] }"; @@ -5944,6 +5991,11 @@ int main(int argc, char* argv[]) { err = 1; } #endif + ret = test_json_failed_load_fails_closed(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_failed_load_fails_closed failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } ret = test_json_actions_update_rejected(); if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { (void)fprintf(stderr, "test_json_actions_update_rejected failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); From a8bf643e6741825eadf9542de4a8a690e7bb7359 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:46:58 -0700 Subject: [PATCH 16/23] F-15478 - https://fenrir.wolfssl.com/finding/15478 - Re-enable actions on a post-exchange LOAD_THEN_COMMIT failure --- src/json/load_config.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index afc1d4a..a11f6f0 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -2144,13 +2144,21 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_fini( if (full_cycle_of_insert_actions_p) { wolfsentry_action_res_t action_results = WOLFSENTRY_ACTION_RES_NONE; - if ((ret = wolfsentry_route_bulk_clear_insert_action_status(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, &action_results)) < 0) - goto out; - if ((ret = wolfsentry_context_flush(JPSP_WOLFSENTRY_CONTEXT_ARGS_OUT)) < 0) + ret = wolfsentry_route_bulk_clear_insert_action_status(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, &action_results); + if (ret >= 0) + ret = wolfsentry_context_flush(JPSP_WOLFSENTRY_CONTEXT_ARGS_OUT); + /* the candidate is already live after the exchange; re-enable its + * actions even if the preceding steps failed, so a post-exchange + * failure cannot leave the running configuration action-inhibited. + * keep the first error. + */ + if (ret >= 0) + ret = wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT); + else + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT)); + if (ret < 0) goto out; - } - - if ((ret = wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT)) < 0) + } else if ((ret = wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT)) < 0) goto out; { From e98a6fbeb2a74413e9dcb1dfef2e62b9c4e57000 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 10:48:34 -0700 Subject: [PATCH 17/23] F-2453 - https://fenrir.wolfssl.com/finding/2453 - Parse AF_LOCAL path addresses so JSON dump and reload stay symmetric --- src/json/load_config.c | 51 ++++++++++++++++++++++++++++-- tests/unittests.c | 70 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index a11f6f0..a96c75f 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -849,6 +849,35 @@ static wolfsentry_errcode_t convert_sockaddr_address( sa->addr_len = 32; WOLFSENTRY_RETURN_OK; } + else if (sa->sa_family == WOLFSENTRY_AF_LOCAL) { + /* mirror wolfsentry_route_format_address(): the address is a raw path + * stored verbatim, with addr_len recording its length. + */ + size_t i; + if (type != JSON_STRING) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + /* an empty path would be a zero-length, match-all prefix. */ + if (data_size == 0) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + if (data_size > (size_t)WOLFSENTRY_BITS_TO_BYTES(WOLFSENTRY_MAX_ADDR_BITS)) + WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); + /* the route formatter emits the path verbatim between quotes without + * escaping, so reject bytes that would corrupt or inject into that + * dump (quote, backslash, control bytes including NUL). + */ + for (i = 0; i < data_size; ++i) { + if ((data[i] < 0x20) || (data[i] == 0x7f) || (data[i] == '"') || (data[i] == '\\')) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + } + /* addr_len must equal the pathname length, so a prefix-bits supplied + * before the address cannot leave trailing implicit bytes in the dump. + */ + if ((sa->addr_len != 0) && (sa->addr_len != (wolfsentry_addr_bits_t)(data_size * 8))) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + memcpy(sa_addr, data, data_size); + sa->addr_len = (wolfsentry_addr_bits_t)(data_size * 8); + WOLFSENTRY_RETURN_OK; + } else if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) { WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE); } @@ -1008,6 +1037,7 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ #endif else if (! strcmp(jps->cur_keyname, "prefix-bits")) { wolfsentry_addr_bits_t max_addr_bits; + wolfsentry_addr_bits_t prior_addr_len = sa->addr_len; wolfsentry_errcode_t ret; if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE); @@ -1024,10 +1054,25 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ ret = convert_uint16(type, data, data_size, &sa->addr_len); WOLFSENTRY_RERETURN_IF_ERROR(ret); /* bound prefix-bits to the address family width so a later copy cannot - * read past the fixed-size staging buffer. + * read past the fixed-size staging buffer. AF_LOCAL path addresses have + * no fixed family width (and no entry in the family table), so bound + * them by the staging buffer, matching the formatter's dump output. */ - ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, sa->sa_family, &max_addr_bits); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + if (sa->sa_family == WOLFSENTRY_AF_LOCAL) { + max_addr_bits = WOLFSENTRY_MAX_ADDR_BITS; + /* an AF_LOCAL prefix-bits only denotes the pathname length, so it + * must be nonzero whole bytes and match any pathname already parsed; + * keeping it nonzero leaves addr_len == 0 meaning "not supplied". + */ + if ((sa->addr_len == 0) || (sa->addr_len & 0x7)) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + if ((prior_addr_len != 0) && (sa->addr_len != prior_addr_len)) + WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); + } + else { + ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, sa->sa_family, &max_addr_bits); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + } if (sa->addr_len > max_addr_bits) WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); WOLFSENTRY_RETURN_OK; diff --git a/tests/unittests.c b/tests/unittests.c index 0c250a8..c61695b 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5541,6 +5541,71 @@ static int test_json_bitmask_short_address(void) { } #endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */ +static int test_json_af_local_address(void) { + /* family 1 == WOLFSENTRY_AF_LOCAL; the loader must parse the path address + * that the formatter emits, keeping dump/reload symmetric. + */ + static const char json[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/tmp/sock\" } } ] }"; + /* the formatter dumps AF_LOCAL routes with an explicit prefix-bits; that + * dumped form must reload, which requires the loader to accept prefix-bits + * for AF_LOCAL ("/tmp/sock" is 9 bytes == 72 bits). + */ + static const char json_dump_form[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/tmp/sock\", \"prefix-bits\" : 72 } } ] }"; + /* a path containing a JSON-special byte would dump unescaped and corrupt + * the output, so it must be rejected at load. + */ + static const char json_unsafe[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/tmp/a\\\"b\" } } ] }"; + /* a prefix-bits inconsistent with the pathname length (here 2 bytes == 16 + * bits) would leave implicit trailing bytes, so it must be rejected in + * either key order; a non-byte-aligned prefix must also be rejected. + */ + static const char json_overlong_prefix[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/x\", \"prefix-bits\" : 128 } } ] }"; + static const char json_prefix_first[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"prefix-bits\" : 128, \"address\" : \"/x\" } } ] }"; + static const char json_unaligned_prefix[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/x\", \"prefix-bits\" : 12 } } ] }"; + /* an empty path or a zero prefix would make a zero-length, match-all + * address with the wildcard flag cleared; reject both in either order. + */ + static const char json_empty_path[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"\" } } ] }"; + static const char json_empty_path_then_prefix[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"\", \"prefix-bits\" : 128 } } ] }"; + static const char json_prefix_then_empty_path[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"prefix-bits\" : 128, \"address\" : \"\" } } ] }"; + static const char json_zero_prefix_first[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"prefix-bits\" : 0, \"address\" : \"/x\" } } ] }"; + static const char json_zero_prefix_last[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/x\", \"prefix-bits\" : 0 } } ] }"; + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_empty_path, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_empty_path_then_prefix, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_prefix_then_empty_path, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_zero_prefix_first, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_zero_prefix_last, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_FAILURE( + test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_FAILURE( + test_json_oneshot_string(json_dump_form, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_unsafe, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_overlong_prefix, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_prefix_first, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_EXIT_ON_SUCCESS( + test_json_oneshot_string(json_unaligned_prefix, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + static int test_json_failed_load_fails_closed(void) { wolfsentry_errcode_t ret; struct wolfsentry_context *wolfsentry; @@ -5991,6 +6056,11 @@ int main(int argc, char* argv[]) { err = 1; } #endif + ret = test_json_af_local_address(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_af_local_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } ret = test_json_failed_load_fails_closed(); if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { (void)fprintf(stderr, "test_json_failed_load_fails_closed failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); From e7f4135c60065326b2a94f8001f5ccc8f3b1ee5f Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 13:34:42 -0700 Subject: [PATCH 18/23] F-2854 - https://fenrir.wolfssl.com/finding/2854 - Keep an explicit tcplike-port-numbers setting over the protocol default --- src/json/load_config.c | 13 ++++++-- tests/unittests.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index a96c75f..a3845ec 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -111,6 +111,7 @@ struct wolfsentry_json_process_state { WOLFSENTRY_SOCKADDR(WOLFSENTRY_MAX_ADDR_BITS) remote; WOLFSENTRY_SOCKADDR(WOLFSENTRY_MAX_ADDR_BITS) local; wolfsentry_route_flags_t flags; + int tcplike_port_numbers_explicit; } route; struct { char label[WOLFSENTRY_MAX_LABEL_BYTES]; @@ -1169,7 +1170,9 @@ static wolfsentry_errcode_t handle_route_protocol_clause(struct wolfsentry_json_ else WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); - if (((jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET) || (jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET6)) && + /* implicit default only; an explicit tcplike-port-numbers setting wins. */ + if ((! jps->o_u_c.route.tcplike_port_numbers_explicit) && + ((jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET) || (jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET6)) && ((jps->o_u_c.route.remote.sa_proto == IPPROTO_TCP) || (jps->o_u_c.route.remote.sa_proto == IPPROTO_UDP))) WOLFSENTRY_SET_BITS(jps->o_u_c.route.flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS); @@ -1252,8 +1255,12 @@ static wolfsentry_errcode_t handle_route_clause(struct wolfsentry_json_process_s else { wolfsentry_route_flags_t flag; ret = wolfsentry_route_flag_assoc_by_name(jps->cur_keyname, WOLFSENTRY_LENGTH_NULL_TERMINATED, &flag); - if (ret >= 0) - WOLFSENTRY_ERROR_RERETURN(handle_route_boolean_clause(type, &jps->o_u_c.route.flags, flag)); + if (ret >= 0) { + ret = handle_route_boolean_clause(type, &jps->o_u_c.route.flags, flag); + if ((ret >= 0) && (flag == WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS)) + jps->o_u_c.route.tcplike_port_numbers_explicit = 1; + WOLFSENTRY_ERROR_RERETURN(ret); + } else { if (WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND)) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_KEY); diff --git a/tests/unittests.c b/tests/unittests.c index c61695b..113d81e 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5711,6 +5711,77 @@ static int test_json_uint16_bounds(void) { WOLFSENTRY_RETURN_OK; } +static int test_json_load_one_route_flags( + const char *json, + wolfsentry_route_flags_t *route_flags) +{ + wolfsentry_errcode_t ret; + struct wolfsentry_context *wolfsentry; + struct wolfsentry_route_table *main_routes; + struct wolfsentry_cursor *cursor; + struct wolfsentry_route *route; +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT }; +#else + struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#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), + &config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_config_json_oneshot( + WOLFSENTRY_CONTEXT_ARGS_OUT, + (const unsigned char *)json, strlen(json), + WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_current(main_routes, cursor, &route)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(route, route_flags)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + ret = WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_RETURN_OK; +} + +static int test_json_tcplike_explicit_flag(void) { + /* family 2 == INET, protocol 6 == TCP; the flag key precedes "protocol", + * as in the route dump, so an explicit setting must survive the protocol + * clause's implicit default. + */ + static const char explicit_false[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"tcplike-port-numbers\" : false, \"protocol\" : 6 } ] }"; + static const char explicit_true[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"tcplike-port-numbers\" : true, \"protocol\" : 6 } ] }"; + static const char omitted[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"protocol\" : 6 } ] }"; + wolfsentry_route_flags_t route_flags = WOLFSENTRY_ROUTE_FLAG_NONE; + + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_load_one_route_flags(explicit_false, &route_flags), OK)); + WOLFSENTRY_EXIT_ON_FALSE(! WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS)); + + route_flags = WOLFSENTRY_ROUTE_FLAG_NONE; + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_load_one_route_flags(explicit_true, &route_flags), OK)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS)); + + route_flags = WOLFSENTRY_ROUTE_FLAG_NONE; + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_load_one_route_flags(omitted, &route_flags), OK)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS)); + + WOLFSENTRY_RETURN_OK; +} + #endif /* TEST_JSON */ #ifdef TEST_JSON_CORPUS @@ -6093,6 +6164,11 @@ int main(int argc, char* argv[]) { err = 1; } #endif + ret = test_json_tcplike_explicit_flag(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_tcplike_explicit_flag failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From 452011022113a60780cac255ef926a44d2abcaea Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 09:57:58 -0700 Subject: [PATCH 19/23] F-3873 - https://fenrir.wolfssl.com/finding/3873 - Judge a generic LINK prefix-bits the same before or after its address --- src/json/load_config.c | 31 +++++++++++++++++++++++++++++-- tests/unittests.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index a3845ec..4e95a53 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -981,13 +981,34 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ else WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); } else if (! strcmp(jps->cur_keyname, "address")) { + wolfsentry_addr_bits_t prior_addr_len = sa->addr_len; + wolfsentry_addr_bits_t max_addr_bits; + int prefix_given = (prior_addr_len != 0); + wolfsentry_errcode_t ret; if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE); +#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING + /* a bitmask also sets addr_len, to the combined address and mask width */ + if (jps->o_u_c.route.flags & (sa == (struct wolfsentry_sockaddr *)&jps->o_u_c.route.remote ? + WOLFSENTRY_ROUTE_FLAG_REMOTE_ADDR_BITMASK : + WOLFSENTRY_ROUTE_FLAG_LOCAL_ADDR_BITMASK)) + prefix_given = 0; +#endif WOLFSENTRY_CLEAR_BITS(jps->o_u_c.route.flags, sa == (struct wolfsentry_sockaddr *)&jps->o_u_c.route.remote ? WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD : WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD); - WOLFSENTRY_ERROR_RERETURN(convert_sockaddr_address(jps, type, data, data_size, sa, sa->addr)); + ret = convert_sockaddr_address(jps, type, data, data_size, sa, sa->addr); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + /* an earlier prefix-bits on generic LINK was allowed the EUI-64 width; + * now that the address has settled the family, the prefix must fit it. */ + if (prefix_given && (sa->sa_family == WOLFSENTRY_AF_LINK48)) { + ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, sa->sa_family, &max_addr_bits); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + if (prior_addr_len > max_addr_bits) + WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); + } + WOLFSENTRY_RETURN_OK; } #ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING else if (! strcmp(jps->cur_keyname, "bitmask")) { @@ -1039,6 +1060,7 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ else if (! strcmp(jps->cur_keyname, "prefix-bits")) { wolfsentry_addr_bits_t max_addr_bits; wolfsentry_addr_bits_t prior_addr_len = sa->addr_len; + wolfsentry_addr_family_t width_family = sa->sa_family; wolfsentry_errcode_t ret; if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE); @@ -1071,7 +1093,12 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); } else { - ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, sa->sa_family, &max_addr_bits); + /* generic LINK becomes EUI-48 or EUI-64 from the address length, so + * before an address arrives allow the wider width; the address + * handler then checks the prefix against the settled family. */ + if ((width_family == WOLFSENTRY_AF_LINK48) && (prior_addr_len == 0)) + width_family = WOLFSENTRY_AF_LINK64; + ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, width_family, &max_addr_bits); WOLFSENTRY_RERETURN_IF_ERROR(ret); } if (sa->addr_len > max_addr_bits) diff --git a/tests/unittests.c b/tests/unittests.c index 113d81e..3ad3b1c 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5782,6 +5782,40 @@ static int test_json_tcplike_explicit_flag(void) { WOLFSENTRY_RETURN_OK; } +static int test_json_link_prefix_bits_key_order(void) { + static const char fmt[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : %u," + " \"remote\" : { %s }, \"local\" : { \"address\" : \"%s\" } } ] }"; + static const char eui64[] = "00:11:22:33:44:55:66:77"; + static const char eui48[] = "00:11:22:33:44:55"; + char json[512]; + int json_len; + + /* generic LINK resolves to EUI-48 or EUI-64 from the address, so a + * prefix-bits key must be judged the same before or after the address. */ + json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"prefix-bits\" : 64, \"address\" : \"00:11:22:33:44:55:66:77\"", eui64); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), OK)); + + json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"address\" : \"00:11:22:33:44:55:66:77\", \"prefix-bits\" : 64", eui64); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), OK)); + + json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"prefix-bits\" : 64, \"address\" : \"00:11:22:33:44:55\"", eui48); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), NUMERIC_ARG_TOO_BIG)); + + json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"address\" : \"00:11:22:33:44:55\", \"prefix-bits\" : 64", eui48); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), NUMERIC_ARG_TOO_BIG)); + + json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"prefix-bits\" : 48, \"address\" : \"00:11:22:33:44:55\"", eui48); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), OK)); + + WOLFSENTRY_RETURN_OK; +} + #endif /* TEST_JSON */ #ifdef TEST_JSON_CORPUS @@ -6169,6 +6203,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_tcplike_explicit_flag failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_link_prefix_bits_key_order(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_link_prefix_bits_key_order failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From 686dc6d72d2d9add53b3f8fd2d491722c4c18249 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 10:31:05 -0700 Subject: [PATCH 20/23] F-2453 - https://fenrir.wolfssl.com/finding/2453 - Defer AF_LOCAL addresses to a registered handler and keep the path limit byte-aligned --- src/json/load_config.c | 15 +++++-- tests/unittests.c | 93 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index 4e95a53..21939f9 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -710,6 +710,13 @@ static inline int convert_hex_byte(const unsigned char **in, size_t *in_len, byt return 0; } +/* a handler registered for AF_LOCAL takes precedence over the built-in literal path */ +static int af_local_is_builtin(struct wolfsentry_json_process_state *jps) +{ + wolfsentry_addr_family_parser_t parser; + return (wolfsentry_addr_family_get_parser(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_AF_LOCAL, &parser) < 0); +} + static wolfsentry_errcode_t convert_sockaddr_address( struct wolfsentry_json_process_state *jps, JSON_TYPE type, @@ -850,7 +857,7 @@ static wolfsentry_errcode_t convert_sockaddr_address( sa->addr_len = 32; WOLFSENTRY_RETURN_OK; } - else if (sa->sa_family == WOLFSENTRY_AF_LOCAL) { + else if ((sa->sa_family == WOLFSENTRY_AF_LOCAL) && af_local_is_builtin(jps)) { /* mirror wolfsentry_route_format_address(): the address is a raw path * stored verbatim, with addr_len recording its length. */ @@ -860,7 +867,7 @@ static wolfsentry_errcode_t convert_sockaddr_address( /* an empty path would be a zero-length, match-all prefix. */ if (data_size == 0) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); - if (data_size > (size_t)WOLFSENTRY_BITS_TO_BYTES(WOLFSENTRY_MAX_ADDR_BITS)) + if (data_size > (size_t)(WOLFSENTRY_MAX_ADDR_BITS / 8)) WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); /* the route formatter emits the path verbatim between quotes without * escaping, so reject bytes that would corrupt or inject into that @@ -1081,8 +1088,8 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ * no fixed family width (and no entry in the family table), so bound * them by the staging buffer, matching the formatter's dump output. */ - if (sa->sa_family == WOLFSENTRY_AF_LOCAL) { - max_addr_bits = WOLFSENTRY_MAX_ADDR_BITS; + if ((sa->sa_family == WOLFSENTRY_AF_LOCAL) && af_local_is_builtin(jps)) { + max_addr_bits = (wolfsentry_addr_bits_t)((WOLFSENTRY_MAX_ADDR_BITS / 8) * 8); /* an AF_LOCAL prefix-bits only denotes the pathname length, so it * must be nonzero whole bytes and match any pathname already parsed; * keeping it nonzero leaves addr_len == 0 meaning "not supplied". diff --git a/tests/unittests.c b/tests/unittests.c index 3ad3b1c..d946fa5 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5816,6 +5816,94 @@ static int test_json_link_prefix_bits_key_order(void) { WOLFSENTRY_RETURN_OK; } +static int af_local_custom_parser_calls; + +static wolfsentry_errcode_t af_local_custom_parser( + WOLFSENTRY_CONTEXT_ARGS_IN, + const char *addr_text, + int addr_text_len, + byte *addr_internal, + wolfsentry_addr_bits_t *addr_internal_bits) +{ + WOLFSENTRY_CONTEXT_ARGS_NOT_USED; + (void)addr_text; + (void)addr_text_len; + ++af_local_custom_parser_calls; + addr_internal[0] = 0x5a; + *addr_internal_bits = 8; + WOLFSENTRY_RETURN_OK; +} + +static wolfsentry_errcode_t af_local_custom_formatter( + WOLFSENTRY_CONTEXT_ARGS_IN, + const byte *addr_internal, + unsigned int addr_internal_bits, + char *addr_text, + int *addr_text_len) +{ + WOLFSENTRY_CONTEXT_ARGS_NOT_USED; + (void)addr_internal; + (void)addr_internal_bits; + if (*addr_text_len < 1) + WOLFSENTRY_ERROR_RETURN(BUFFER_TOO_SMALL); + addr_text[0] = 'z'; + *addr_text_len = 1; + WOLFSENTRY_RETURN_OK; +} + +static int test_json_af_local_registered_handler(void) { + struct wolfsentry_context *wolfsentry; + char json[256]; + int json_len; +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT }; +#else + struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#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), + &config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_addr_family_handler_install( + WOLFSENTRY_CONTEXT_ARGS_OUT, + WOLFSENTRY_AF_LOCAL, + "test_local", + -1, + af_local_custom_parser, + af_local_custom_formatter, + 8)); + + json_len = snprintf( + json, sizeof json, + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : %u, \"remote\" : { \"address\" : \"anything\" } } ] }", + (unsigned int)WOLFSENTRY_AF_LOCAL); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + + af_local_custom_parser_calls = 0; + (void)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_FALSE(af_local_custom_parser_calls == 1); + + 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 /* TEST_JSON */ #ifdef TEST_JSON_CORPUS @@ -6208,6 +6296,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_json_link_prefix_bits_key_order failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_json_af_local_registered_handler(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_af_local_registered_handler failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_JSON_CORPUS From d9aea7feb1c8bc7741f5f661f18d8c5f9b91f531 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 13:38:37 -0700 Subject: [PATCH 21/23] F-2453 - https://fenrir.wolfssl.com/finding/2453 - Check the config load result in the AF_LOCAL handler test --- tests/unittests.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/unittests.c b/tests/unittests.c index d946fa5..c62e75e 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5888,13 +5888,14 @@ static int test_json_af_local_registered_handler(void) { WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); af_local_custom_parser_calls = 0; - (void)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_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_FALSE(af_local_custom_parser_calls == 1); WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); From 05d282d11b0b60d5f6d6f498044839fe4fcfdd77 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 18:23:06 -0700 Subject: [PATCH 22/23] F-4277 - https://fenrir.wolfssl.com/finding/4277 - Take the 64-bit config value range limits from the conversion result type --- src/json/load_config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index 21939f9..4a3520f 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -245,7 +245,7 @@ static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char * if ((size_t)(endptr - buf) != data_size) WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); - ret = convert_check_magnitude(buf, (uint64_t)MAX_UINT_OF(*out)); + ret = convert_check_magnitude(buf, (uint64_t)MAX_UINT_OF(conv)); WOLFSENTRY_RERETURN_IF_ERROR(ret); *out = (uint64_t)conv; @@ -290,9 +290,9 @@ static wolfsentry_errcode_t convert_sint64(JSON_TYPE type, const unsigned char * WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE); sign_p = convert_skip_blanks(buf); if (*sign_p == '-') - magnitude_limit = (uint64_t)MAX_SINT_OF(*out) + 1U; + magnitude_limit = (uint64_t)MAX_SINT_OF(conv) + 1U; else - magnitude_limit = (uint64_t)MAX_SINT_OF(*out); + magnitude_limit = (uint64_t)MAX_SINT_OF(conv); ret = convert_check_magnitude(buf, magnitude_limit); WOLFSENTRY_RERETURN_IF_ERROR(ret); *out = (int64_t)conv; From 621b306da622712434a3a8a3ac28a5a91b43d215 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 18:39:34 -0700 Subject: [PATCH 23/23] F-3666 - https://fenrir.wolfssl.com/finding/3666 - Refuse a bitmask after prefix-bits on the same endpoint --- src/json/load_config.c | 14 ++++++++++++++ tests/unittests.c | 15 ++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index 4a3520f..fd0cdea 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -112,6 +112,8 @@ struct wolfsentry_json_process_state { WOLFSENTRY_SOCKADDR(WOLFSENTRY_MAX_ADDR_BITS) local; wolfsentry_route_flags_t flags; int tcplike_port_numbers_explicit; + int remote_prefix_bits_given; + int local_prefix_bits_given; } route; struct { char label[WOLFSENTRY_MAX_LABEL_BYTES]; @@ -1025,6 +1027,14 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE); + /* the right-justify below would keep only the prefix-sized leading bytes of a full address */ + if ((jps->section_under_construction == S_U_C_REMOTE_ENDPOINT) ? + jps->o_u_c.route.remote_prefix_bits_given : + jps->o_u_c.route.local_prefix_bits_given) + { + WOLFSENTRY_ERROR_RETURN(CONFIG_MISPLACED_KEY); + } + ret = wolfsentry_addr_family_max_addr_bits( JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, sa->sa_family, @@ -1110,6 +1120,10 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ } if (sa->addr_len > max_addr_bits) WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); + if (jps->section_under_construction == S_U_C_REMOTE_ENDPOINT) + jps->o_u_c.route.remote_prefix_bits_given = 1; + else + jps->o_u_c.route.local_prefix_bits_given = 1; WOLFSENTRY_RETURN_OK; } else if (! strcmp(jps->cur_keyname, "interface")) { diff --git a/tests/unittests.c b/tests/unittests.c index c62e75e..844708d 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -5529,14 +5529,19 @@ static int test_json_unknown_protocol_name(void) { #ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING static int test_json_bitmask_short_address(void) { - /* a partial address (prefix-bits smaller than the family width) followed by - * a bitmask exercises the right-justification move; the move must copy - * bytes, not the bit count, or it corrupts adjacent parse state. + /* prefix-bits before a bitmask would right-justify only the prefix-sized + * leading bytes of a full-width address, so the combination is refused, + * as prefix-bits after a bitmask already is; address then bitmask loads. */ - static const char json[] = + static const char json_prefixed[] = "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"prefix-bits\" : 16, \"address\" : \"1.2.0.0\", \"bitmask\" : \"255.255.0.0\" } } ] }"; + static const char json_plain[] = + "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"address\" : \"1.2.0.0\", \"bitmask\" : \"255.255.0.0\" } } ] }"; + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + CONFIG_MISPLACED_KEY, + test_json_oneshot_string(json_prefixed, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); WOLFSENTRY_EXIT_ON_FAILURE( - test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); + test_json_oneshot_string(json_plain, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE)); WOLFSENTRY_RETURN_OK; } #endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */