From 837cfeb59cae3300ff1ed62358ca8b1ceed2e641 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 21 Apr 2026 14:49:23 +0000 Subject: [PATCH 1/6] harden lock and thread-safety handling --- src/actions.c | 6 +++++- src/events.c | 8 ++++---- src/lwip/packet_filter_glue.c | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/actions.c b/src/actions.c index d92b174..34feccc 100644 --- a/src/actions.c +++ b/src/actions.c @@ -425,7 +425,11 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_list_insert_after( } ret = wolfsentry_action_list_insert_after_1(WOLFSENTRY_CONTEXT_ARGS_OUT, action_list, action, point_action); ret2 = wolfsentry_action_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, point_action, NULL /* action_results */); - WOLFSENTRY_RERETURN_IF_ERROR(ret2); + /* a drop-reference failure here leaks one refcount on point_action, but + * don't promote it to the caller's return code: the insert result is the + * caller-visible outcome, and returning ret2 would invite a bogus rollback. + */ + WOLFSENTRY_WARN_ON_FAILURE(ret2); if (ret < 0) { WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, action, NULL /* action_results */)); WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); diff --git a/src/events.c b/src/events.c index e6479c9..6d92a88 100644 --- a/src/events.c +++ b/src/events.c @@ -348,7 +348,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_config(WOLFSENTRY_CONTE WOLFSENTRY_SHARED_OR_RETURN(); ret = wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, label, label_len, &event); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); if (event->config == NULL) ret = wolfsentry_eventconfig_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &wolfsentry->config, config); else @@ -363,7 +363,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_update_config(WOLFSENTRY_CO WOLFSENTRY_MUTEX_OR_RETURN(); ret = wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, label, label_len, &event); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); if (event->config == NULL) { if ((event->config = (struct wolfsentry_eventconfig_internal *)WOLFSENTRY_MALLOC(sizeof *event->config)) == NULL) @@ -618,7 +618,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_set_aux_event( WOLFSENTRY_MUTEX_OR_RETURN(); ret = wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, event_label_len, &event); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); if (WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)) { ret = WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE); goto out; @@ -697,7 +697,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_action_list_start( } if (w_a_l == NULL) - WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(INVALID_ARG); + WOLFSENTRY_ERROR_RETURN(INVALID_ARG); *cursor = (struct wolfsentry_action_list_ent *)w_a_l->header.head; if (*cursor == NULL) diff --git a/src/lwip/packet_filter_glue.c b/src/lwip/packet_filter_glue.c index 7b71bc6..1311aec 100644 --- a/src/lwip/packet_filter_glue.c +++ b/src/lwip/packet_filter_glue.c @@ -1228,7 +1228,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_icmp_callback WOLFSENTRY_MUTEX_OR_RETURN(); if (icmp_mask) { wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); } #endif #if LWIP_ICMP @@ -1270,7 +1270,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_tcp_callback( WOLFSENTRY_MUTEX_OR_RETURN(); if (tcp_mask) { wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); tcp_filter(tcp_filter_with_wolfsentry); /* make sure wolfSentry sees the close/reset events that balance earlier * accepts, for concurrent-connection accounting purposes. From 5cc7cbc69f511078f54e7ae7ca29af628dd66bfc Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 21 Apr 2026 15:20:07 +0000 Subject: [PATCH 2/6] harden JSON config and KV handling --- src/json/load_config.c | 33 ++++++++++++++++++++------------- src/kv.c | 7 +++++-- wolfsentry/wolfsentry_json.h | 9 +++++++++ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index eb33d9c..a332509 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -1711,6 +1711,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( struct wolfsentry_json_process_state **jps) { wolfsentry_errcode_t ret; + int locked = 0; static const JSON_CALLBACKS json_callbacks = { #ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS .process = @@ -1720,7 +1721,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( static const JSON_CONFIG default_json_config = { #ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS - .max_total_len = 0, + .max_total_len = WOLFSENTRY_MAX_JSON_TOTAL_LEN, .max_total_values = 0, .max_number_len = 20, .max_string_len = WOLFSENTRY_KV_MAX_VALUE_BYTES, @@ -1728,7 +1729,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( .max_nesting_level = WOLFSENTRY_MAX_JSON_NESTING, .flags = JSON_NOSCALARROOT #else - 0, + WOLFSENTRY_MAX_JSON_TOTAL_LEN, 0, 20, WOLFSENTRY_KV_MAX_VALUE_BYTES, @@ -1760,28 +1761,28 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( #ifdef WOLFSENTRY_HAVE_JSON_DOM (*jps)->dom_parser_flags |= JSON_DOM_DUPKEY_ABORT; #else - WOLFSENTRY_ERROR_RETURN(IMPLEMENTATION_MISSING); + { ret = WOLFSENTRY_ERROR_ENCODE(IMPLEMENTATION_MISSING); goto out; } #endif } if (WOLFSENTRY_MASKIN_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_JSON_DOM_DUPKEY_USEFIRST)) { #ifdef WOLFSENTRY_HAVE_JSON_DOM (*jps)->dom_parser_flags |= JSON_DOM_DUPKEY_USEFIRST; #else - WOLFSENTRY_ERROR_RETURN(IMPLEMENTATION_MISSING); + { ret = WOLFSENTRY_ERROR_ENCODE(IMPLEMENTATION_MISSING); goto out; } #endif } if (WOLFSENTRY_MASKIN_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_JSON_DOM_DUPKEY_USELAST)) { #ifdef WOLFSENTRY_HAVE_JSON_DOM (*jps)->dom_parser_flags |= JSON_DOM_DUPKEY_USELAST; #else - WOLFSENTRY_ERROR_RETURN(IMPLEMENTATION_MISSING); + { ret = WOLFSENTRY_ERROR_ENCODE(IMPLEMENTATION_MISSING); goto out; } #endif } if (WOLFSENTRY_MASKIN_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_JSON_DOM_MAINTAINDICTORDER)) { #ifdef WOLFSENTRY_HAVE_JSON_DOM (*jps)->dom_parser_flags |= JSON_DOM_MAINTAINDICTORDER; #else - WOLFSENTRY_ERROR_RETURN(IMPLEMENTATION_MISSING); + { ret = WOLFSENTRY_ERROR_ENCODE(IMPLEMENTATION_MISSING); goto out; } #endif } @@ -1789,15 +1790,20 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( if ((! WOLFSENTRY_MASKIN_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN|WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT)) || (thread == NULL)) { - WOLFSENTRY_MUTEX_OR_RETURN(); - } else if (WOLFSENTRY_MASKIN_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN)) - WOLFSENTRY_SHARED_OR_RETURN(); - else { + if ((ret = WOLFSENTRY_MUTEX_EX(wolfsentry)) < 0) + goto out; + } else if (WOLFSENTRY_MASKIN_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN)) { + /* thread == NULL is already routed to the mutex path above. */ + if ((ret = WOLFSENTRY_SHARED_EX(wolfsentry)) < 0) + goto out; + } else { ret = WOLFSENTRY_PROMOTABLE_EX(wolfsentry); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + if (ret < 0) + goto out; if (WOLFSENTRY_SUCCESS_CODE_IS(ret, LOCK_OK_AND_GOT_RESV)) (*jps)->got_reservation = 1; } + locked = 1; #endif (*jps)->wolfsentry_actual = wolfsentry; @@ -1834,7 +1840,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( /* initialize with defaults already set in context, particularly to pick up route_private_data* fields. */ ret = wolfsentry_defaultconfig_get(JPSP_WOLFSENTRY_CONTEXT_ARGS_OUT, &(*jps)->default_config); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + if (ret < 0) + goto out; if (! WOLFSENTRY_MASKIN_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN|WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH|WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT)) { if (WOLFSENTRY_CHECK_BITS(load_flags, WOLFSENTRY_CONFIG_LOAD_FLAG_FLUSH_ONLY_ROUTES)) { @@ -1856,7 +1863,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex( if (ret < 0) { #ifdef WOLFSENTRY_THREADSAFE - { + if (locked) { wolfsentry_errcode_t _lock_ret; if ((*jps)->got_reservation) _lock_ret = wolfsentry_context_unlock_and_abandon_reservation(wolfsentry, thread); diff --git a/src/kv.c b/src/kv.c index 1a6e9d9..dc6b15d 100644 --- a/src/kv.c +++ b/src/kv.c @@ -434,7 +434,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_kv_render_value( *out_len = snprintf(out, out_space, "%.10f", WOLFSENTRY_KV_V_FLOAT(kv)); break; case WOLFSENTRY_KV_STRING: { -#ifndef HAVE_JSON_DOM +#ifndef WOLFSENTRY_HAVE_JSON_DOM *out_len = snprintf(out, out_space, "\"%.*s\"", (int)WOLFSENTRY_KV_V_STRING_LEN(kv), WOLFSENTRY_KV_V_STRING(kv)); break; #else @@ -517,8 +517,11 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_clone( if (WOLFSENTRY_KV_TYPE(&src_kv_pair->kv) == WOLFSENTRY_KV_JSON) { int ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(dest_context)), &src_kv_pair->kv.a.v_json, &(*new_kv_pair)->kv.a.v_json); - if (ret < 0) + if (ret < 0) { + WOLFSENTRY_FREE_1(dest_context->hpi.allocator, *new_kv_pair); + *new_kv_pair = NULL; WOLFSENTRY_ERROR_RERETURN(wolfsentry_centijson_errcode_translate(ret)); + } } #endif diff --git a/wolfsentry/wolfsentry_json.h b/wolfsentry/wolfsentry_json.h index 55f7381..ce8cfda 100644 --- a/wolfsentry/wolfsentry_json.h +++ b/wolfsentry/wolfsentry_json.h @@ -48,6 +48,15 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_centijson_errcode_translate(wolfs /*!< \brief Can be overridden. */ #endif +#ifndef WOLFSENTRY_MAX_JSON_TOTAL_LEN +#define WOLFSENTRY_MAX_JSON_TOTAL_LEN (10 * 1024 * 1024) + /*!< \brief Default cap on total JSON input length, matching centijson's + * upstream default. Set to 0 for unlimited, or override at + * build time. Can also be overridden per-call via the + * `JSON_CONFIG` parameter of wolfsentry_config_json_init_ex(). + */ +#endif + typedef uint32_t wolfsentry_config_load_flags_t; /*!< \brief Type for holding flag bits from #wolfsentry_config_load_flags */ From 86961f6b548a289cf393b573c9883879a63116e1 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 21 Apr 2026 15:06:41 +0000 Subject: [PATCH 3/6] correct route table tracking and address family --- src/addr_families.c | 2 ++ src/routes.c | 42 ++++++++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/addr_families.c b/src/addr_families.c index 74b1f67..ae58f5c 100644 --- a/src/addr_families.c +++ b/src/addr_families.c @@ -773,6 +773,8 @@ static wolfsentry_errcode_t wolfsentry_addr_family_ntop_1( { *family_name = "HYLINK"; WOLFSENTRY_RETURN_OK; } case WOLFSENTRY_AF_LINK: { *family_name = "LINK"; WOLFSENTRY_RETURN_OK; } + case WOLFSENTRY_AF_LINK64: + { *family_name = "LINK64"; WOLFSENTRY_RETURN_OK; } case WOLFSENTRY_AF_COIP: { *family_name = "COIP"; WOLFSENTRY_RETURN_OK; } case WOLFSENTRY_AF_CNT: diff --git a/src/routes.c b/src/routes.c index 34baa90..7b1ae25 100644 --- a/src/routes.c +++ b/src/routes.c @@ -583,10 +583,10 @@ static void wolfsentry_route_update_flags_1( static void wolfsentry_route_free_1( WOLFSENTRY_CONTEXT_ARGS_IN, - struct wolfsentry_eventconfig_internal *config, + size_t route_private_data_alignment, struct wolfsentry_route *route) { - if (config->config.route_private_data_alignment == 0) + if (route_private_data_alignment == 0) WOLFSENTRY_FREE(route); else WOLFSENTRY_FREE_ALIGNED(route); @@ -599,6 +599,10 @@ static wolfsentry_errcode_t wolfsentry_route_drop_reference_1( wolfsentry_action_res_t *action_results) { struct wolfsentry_eventconfig_internal *config = (route->parent_event && route->parent_event->config) ? route->parent_event->config : &wolfsentry->config; + /* snapshot the alignment before dropping the event reference, since the + * event (and its config) may be freed by the drop. + */ + size_t route_private_data_alignment = config->config.route_private_data_alignment; wolfsentry_errcode_t ret; wolfsentry_refcount_t refs_left; if (route->header.refcount == 0) @@ -612,7 +616,7 @@ static wolfsentry_errcode_t wolfsentry_route_drop_reference_1( WOLFSENTRY_RETURN_OK; if (route->parent_event) WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route->parent_event, NULL /* action_results */)); - wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, config, route); + wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, route_private_data_alignment, route); if (action_results) WOLFSENTRY_SET_BITS(*action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED); WOLFSENTRY_RETURN_OK; @@ -995,7 +999,7 @@ static wolfsentry_errcode_t wolfsentry_route_new( WOLFSENTRY_ERROR_RETURN(SYS_RESOURCE_FAILED); ret = wolfsentry_route_init(parent_event, remote, local, flags, (int)config->config.route_private_data_size, new_size, *new); if (ret < 0) { - wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, config, *new); + wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, config->config.route_private_data_alignment, *new); *new = NULL; } else { if (parent_event != NULL) { @@ -1054,7 +1058,7 @@ static wolfsentry_errcode_t wolfsentry_route_new_by_exports( WOLFSENTRY_ERROR_RETURN(SYS_RESOURCE_FAILED); ret = wolfsentry_route_init_by_exports(parent_event, route_exports, config->config.route_private_data_size, new_size, *new); if (ret < 0) { - wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, config, *new); + wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, config->config.route_private_data_alignment, *new); *new = NULL; } else { if (parent_event != NULL) { @@ -1174,7 +1178,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_route_clone( #ifdef WOLFSENTRY_THREADSAFE thread, #endif - config, *new_route); + config->config.route_private_data_alignment, *new_route); WOLFSENTRY_ERROR_RERETURN(ret); } WOLFSENTRY_REFCOUNT_INCREMENT((*new_route)->parent_event->header.refcount, ret); @@ -1330,7 +1334,7 @@ static wolfsentry_errcode_t wolfsentry_route_insert_1( if (route_to_insert->flags & WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD) { if ((route_table->last_af_wildcard_route == NULL) || - (wolfsentry_route_key_cmp_1(route_to_insert, route_table->last_af_wildcard_route, 0 /* match_wildcards_p */, NULL /* inexact_matches */) < 0)) + (wolfsentry_route_key_cmp_1(route_to_insert, route_table->last_af_wildcard_route, 0 /* match_wildcards_p */, NULL /* inexact_matches */) > 0)) { route_table->last_af_wildcard_route = route_to_insert; } @@ -2162,10 +2166,19 @@ static wolfsentry_errcode_t wolfsentry_route_delete_0( wolfsentry_route_update_flags_1(route, WOLFSENTRY_ROUTE_FLAG_NONE, WOLFSENTRY_ROUTE_FLAG_IN_TABLE, &flags_before, &flags_after); } - if ((ret = wolfsentry_table_ent_delete_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &route->header)) < 0) { - wolfsentry_route_flags_t flags_before, flags_after; - wolfsentry_route_update_flags_1(route, WOLFSENTRY_ROUTE_FLAG_IN_TABLE, WOLFSENTRY_ROUTE_FLAG_NONE, &flags_before, &flags_after); - WOLFSENTRY_ERROR_RERETURN(ret); + /* snapshot linked-list neighbor before delete_1 nullifies prev/next. */ + { + struct wolfsentry_route *prev_route = (struct wolfsentry_route *)route->header.prev; + if ((ret = wolfsentry_table_ent_delete_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &route->header)) < 0) { + wolfsentry_route_flags_t flags_before, flags_after; + wolfsentry_route_update_flags_1(route, WOLFSENTRY_ROUTE_FLAG_IN_TABLE, WOLFSENTRY_ROUTE_FLAG_NONE, &flags_before, &flags_after); + WOLFSENTRY_ERROR_RERETURN(ret); + } + if (route_table->last_af_wildcard_route == route) { + while (prev_route && ! (prev_route->flags & WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD)) + prev_route = (struct wolfsentry_route *)prev_route->header.prev; + route_table->last_af_wildcard_route = prev_route; + } } #ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING @@ -2191,9 +2204,6 @@ static wolfsentry_errcode_t wolfsentry_route_delete_0( WOLFSENTRY_WARN("wolfsentry_action_list_dispatch for wolfsentry_route_delete_0 returned " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); } - if (route_table->last_af_wildcard_route == route) - route_table->last_af_wildcard_route = (struct wolfsentry_route *)route->header.prev; - { wolfsentry_priority_t effective_priority = route->parent_event ? route->parent_event->priority : 0; if (effective_priority == route_table->highest_priority_route_in_table) { @@ -3904,7 +3914,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_address( WOLFSENTRY_RETURN_OK; } - if (sa_family == WOLFSENTRY_AF_LINK) { + if (sa_family == WOLFSENTRY_AF_LINK || sa_family == WOLFSENTRY_AF_LINK64) { unsigned int i; if ((addr_bits >> 3) * 3 > (size_t)*buflen) WOLFSENTRY_ERROR_RETURN(BUFFER_TOO_SMALL); @@ -4431,7 +4441,7 @@ static wolfsentry_errcode_t wolfsentry_route_render_address(WOLFSENTRY_CONTEXT_A WOLFSENTRY_RETURN_OK; } - if (sa_family == WOLFSENTRY_AF_LINK) { + if (sa_family == WOLFSENTRY_AF_LINK || sa_family == WOLFSENTRY_AF_LINK64) { unsigned int i; for (i=0; i < (addr_bits >> 3); ++i) { if (fprintf(f, "%s%02x", i ? ":" : "", (unsigned int)addr[i]) < 0) From ae172316c12b9b772927e941098e541795bf6dd5 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 21 Apr 2026 15:32:11 +0000 Subject: [PATCH 4/6] correct route dispatch defaults and validation --- src/routes.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/routes.c b/src/routes.c index 7b1ae25..330d362 100644 --- a/src/routes.c +++ b/src/routes.c @@ -1991,6 +1991,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_table_default_policy_set( { if (WOLFSENTRY_MASKOUT_BITS(default_policy, WOLFSENTRY_ROUTE_DEFAULT_POLICY_MASK) != WOLFSENTRY_ACTION_RES_NONE) WOLFSENTRY_ERROR_RETURN(INVALID_ARG); + if ((default_policy != WOLFSENTRY_ACTION_RES_NONE) && + (! WOLFSENTRY_MASKIN_BITS(default_policy, WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_REJECT))) + WOLFSENTRY_ERROR_RETURN(INVALID_ARG); WOLFSENTRY_MUTEX_OR_RETURN(); table->default_policy = default_policy; if (table == wolfsentry->routes) @@ -2500,7 +2503,9 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0( (void)ret; } - if (! (current_rule_route_flags & WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS)) { + if ((! (current_rule_route_flags & WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS)) && + (config->config.max_connection_count > 0)) + { if (*action_results & WOLFSENTRY_ACTION_RES_CONNECT) { if (rule_route->meta.connection_count >= config->config.max_connection_count) { *action_results |= WOLFSENTRY_ACTION_RES_REJECT; From 54bab86d78c528d98db0482bade2ed6edfbd4b6a Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 21 Apr 2026 14:47:07 +0000 Subject: [PATCH 5/6] test penaltybox precedence over greenlist --- tests/unittests.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/unittests.c b/tests/unittests.c index 1817b60..815ed50 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -2124,6 +2124,42 @@ static int test_static_routes(void) { &inexact_matches, &route_ref)); + /* regression test: a route that is both PENALTYBOXED and GREENLISTED must + * reject (penaltybox takes precedence), not accept and reject together. + */ + { + wolfsentry_route_flags_t flags_before, flags_after; + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_update_flags( + WOLFSENTRY_CONTEXT_ARGS_OUT, + route_ref, + WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED, + WOLFSENTRY_ROUTE_FLAG_NONE, + &flags_before, + &flags_after, + &action_results)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(flags_after, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(flags_after, WOLFSENTRY_ROUTE_FLAG_GREENLISTED)); + + WOLFSENTRY_CLEAR_ALL_BITS(action_results); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &remote.sa, &local.sa, flags, + NULL /* event_label */, 0 /* event_label_len */, + NULL /* caller_arg */, + &route_id, &inexact_matches, &action_results)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_update_flags( + WOLFSENTRY_CONTEXT_ARGS_OUT, + route_ref, + WOLFSENTRY_ROUTE_FLAG_NONE, + WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED, + &flags_before, + &flags_after, + &action_results)); + } + { int old_derogatory_count; From 6047c6eb5291316b01cdd9f72a5382655839c9d9 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 21 Apr 2026 16:16:24 +0000 Subject: [PATCH 6/6] add tests for fenrir fixes --- tests/unittests.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/unittests.c b/tests/unittests.c index 815ed50..8f346ad 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -1573,6 +1573,16 @@ static int test_static_routes(void) { WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_NONE)); + /* non-decisional default_policy values (STOP/ERROR alone) must be rejected. */ + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INVALID_ARG, + wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_STOP)); + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INVALID_ARG, + wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ERROR)); + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INVALID_ARG, + wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_STOP | WOLFSENTRY_ACTION_RES_ERROR)); WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results)); @@ -2747,6 +2757,70 @@ static int test_static_routes(void) { &action_results)); } + /* max_connection_count == 0 means "no limit": CONNECT dispatches must not + * be rejected regardless of how many times we fire them. + */ + { + struct wolfsentry_eventconfig nolimit_config = config; + wolfsentry_ent_id_t nolimit_route_id; + wolfsentry_route_flags_t nolimit_flags; + unsigned int i; + + nolimit_config.max_connection_count = 0; + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "nolimit-conn-test", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 0 /* priority */, + &nolimit_config, + WOLFSENTRY_EVENT_FLAG_NONE, + NULL /* id */)); + + WOLFSENTRY_CLEAR_ALL_BITS(nolimit_flags); + WOLFSENTRY_SET_BITS(nolimit_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN + | WOLFSENTRY_ROUTE_FLAG_GREENLISTED); + memcpy(remote.sa.addr, "\7\10\11\12", sizeof remote.addr_buf); + memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + &remote.sa, &local.sa, nolimit_flags, + "nolimit-conn-test", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &nolimit_route_id, &action_results)); + + /* 20 iterations covers the "unlimited" no-reject behavior; the + * UINT16_MAX saturation path is covered by + * WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY_BY_ONE itself, not + * exercised here to avoid a 65535-iteration CI cost. + */ + for (i = 0; i < 20; ++i) { + WOLFSENTRY_CLEAR_ALL_BITS(action_results); + WOLFSENTRY_SET_BITS(action_results, WOLFSENTRY_ACTION_RES_CONNECT); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &remote.sa, &local.sa, nolimit_flags, + NULL /* event_label */, 0 /* event_label_len */, + NULL /* caller_arg */, + &id, &inexact_matches, &action_results)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + } + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + nolimit_route_id, + NULL /* event_label */, 0 /* event_label_len */, + &action_results)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "nolimit-conn-test", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &action_results)); + } + printf("all subtests succeeded -- %u distinct ents inserted and deleted.\n",wolfsentry->mk_id_cb_state.id_counter); WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); @@ -3703,6 +3777,46 @@ static int test_user_values(void) { } +#ifdef WOLFSENTRY_HAVE_JSON_DOM + /* strings rendered as JSON must escape quotes and backslashes. */ + { + static const char raw[] = "a\"b\\c"; + const struct wolfsentry_kv_pair *kv_exports; + char render_buf[64]; + int render_buf_space = (int)sizeof render_buf; + + memset(render_buf, 0, sizeof render_buf); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "json_escape_string", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + raw, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 0)); + { + const char *value = NULL; + int value_len = -1; + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_get_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "json_escape_string", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &value, &value_len, &kv_ref)); + } + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT)); + 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, render_buf, &render_buf_space)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + WOLFSENTRY_EXIT_ON_FALSE(strstr(render_buf, "\\\"") != NULL); + WOLFSENTRY_EXIT_ON_FALSE(strstr(render_buf, "\\\\") != NULL); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_release_record(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_ref)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "json_escape_string", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + } +#endif /* WOLFSENTRY_HAVE_JSON_DOM */ + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( BAD_VALUE, wolfsentry_user_value_store_string( @@ -4086,6 +4200,25 @@ static int test_user_addr_families(void) { family_number, &bits)); WOLFSENTRY_EXIT_ON_FALSE(bits == 48); + + /* LINK64 must roundtrip through pton → ntop → verify label. */ + { + struct wolfsentry_addr_family_bynumber *addr_family = NULL; + const char *family_name = NULL; + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_pton( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "LINK64", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &family_number)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_ntop( + WOLFSENTRY_CONTEXT_ARGS_OUT, + family_number, + &addr_family, + &family_name)); + WOLFSENTRY_EXIT_ON_FALSE((family_name != NULL) && (! strcmp(family_name, "LINK64"))); + if (addr_family) + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, addr_family, &action_results)); + } } #endif /* WOLFSENTRY_PROTOCOL_NAMES */