From 32c1f20abce2eea53f06ef32feae541ec71d2cd3 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 14:46:27 -0700 Subject: [PATCH 1/4] F-4564 - https://fenrir.wolfssl.com/finding/4564 - Initialize track-peer insert results and merge them only on success --- src/action_builtins.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/action_builtins.c b/src/action_builtins.c index 19cdf25..f9884cf 100644 --- a/src/action_builtins.c +++ b/src/action_builtins.c @@ -47,7 +47,7 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer( wolfsentry_addr_bits_t rule_local_addr_len, rule_remote_addr_len; struct wolfsentry_route_exports target_exports; wolfsentry_errcode_t ret; - wolfsentry_action_res_t insert_action_results; + wolfsentry_action_res_t insert_action_results = WOLFSENTRY_ACTION_RES_NONE; (void)action; (void)handler_arg; @@ -117,14 +117,18 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer( WOLFSENTRY_RERETURN_IF_ERROR(ret); /* can't pass action_results directly to _route_insert -- it would get zeroed. */ - WOLFSENTRY_WARN_ON_FAILURE( - wolfsentry_route_insert_by_exports_into_table( - WOLFSENTRY_CONTEXT_ARGS_OUT, - route_table, - NULL /* void *caller_arg*/, - &target_exports, - NULL /* id */, - &insert_action_results)); + ret = wolfsentry_route_insert_by_exports_into_table( + WOLFSENTRY_CONTEXT_ARGS_OUT, + route_table, + NULL /* void *caller_arg*/, + &target_exports, + NULL /* id */, + &insert_action_results); + WOLFSENTRY_WARN_ON_FAILURE(ret); + /* no route survives a failed insert, so drop INSERTED (it would suppress generator + * accounting); other bits are the chain's decisions and are NONE on early failure. */ + if (ret < 0) + WOLFSENTRY_CLEAR_BITS(insert_action_results, WOLFSENTRY_ACTION_RES_INSERTED); *action_results |= insert_action_results; /* copying over the bits is crucial to inhibit double-accounting for * connection_count, derogatory_count, and commendable_count. */ From 4fa9aaa9240b8f7557568d9b0b4307f9614a3dbc Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 14:46:28 -0700 Subject: [PATCH 2/4] F-2443 - https://fenrir.wolfssl.com/finding/2443 - Refuse track-peer routes that would never be purged --- src/action_builtins.c | 26 +++- tests/unittests.c | 270 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+), 5 deletions(-) diff --git a/src/action_builtins.c b/src/action_builtins.c index f9884cf..61cfcef 100644 --- a/src/action_builtins.c +++ b/src/action_builtins.c @@ -44,6 +44,8 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer( { wolfsentry_route_flags_t rule_flags; const struct wolfsentry_event *rule_parent_event = NULL, *rule_aux_event = NULL; + const struct wolfsentry_event *ephemeral_parent_event = NULL; + const struct wolfsentry_eventconfig_internal *ephemeral_config; wolfsentry_addr_bits_t rule_local_addr_len, rule_remote_addr_len; struct wolfsentry_route_exports target_exports; wolfsentry_errcode_t ret; @@ -99,17 +101,31 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer( rule_parent_event = wolfsentry_route_parent_event(rule_route); if (rule_parent_event) rule_aux_event = wolfsentry_event_get_aux_event(rule_parent_event); - if (rule_aux_event) { - target_exports.parent_event_label = wolfsentry_event_get_label(rule_aux_event); - target_exports.parent_event_label_len = WOLFSENTRY_LENGTH_NULL_TERMINATED; - } else if (rule_parent_event) { - target_exports.parent_event_label = wolfsentry_event_get_label(rule_parent_event); + if (rule_aux_event) + ephemeral_parent_event = rule_aux_event; + else + ephemeral_parent_event = rule_parent_event; + + if (ephemeral_parent_event != NULL) { + target_exports.parent_event_label = wolfsentry_event_get_label(ephemeral_parent_event); target_exports.parent_event_label_len = WOLFSENTRY_LENGTH_NULL_TERMINATED; } else { target_exports.parent_event_label = NULL; target_exports.parent_event_label_len = 0; } + if ((ephemeral_parent_event != NULL) && (ephemeral_parent_event->config != NULL)) + ephemeral_config = ephemeral_parent_event->config; + else + ephemeral_config = &wolfsentry->config; + + /* an ephemeral route with no purge time escapes both the idle purge and + * max_purgeable_routes, letting a remote peer grow the table without bound. + * skip the insert but return OK so the rest of the match chain still runs. + */ + if (ephemeral_config->config.route_idle_time_for_purge <= 0) + WOLFSENTRY_RETURN_OK; + target_exports.private_data = NULL; target_exports.private_data_size = 0; diff --git a/tests/unittests.c b/tests/unittests.c index 7a75686..6942754 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3469,6 +3469,271 @@ static int test_dynamic_rules(void) { WOLFSENTRY_RETURN_OK; } +static wolfsentry_errcode_t test_dynamic_count_main_routes( + WOLFSENTRY_CONTEXT_ARGS_IN, + wolfsentry_hitcount_t *n_routes) +{ + wolfsentry_errcode_t ret; + struct wolfsentry_route_table *main_routes; + struct wolfsentry_cursor *cursor; + struct wolfsentry_route *route; + + *n_routes = 0; + + ret = wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + + ret = wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes); + if (ret >= 0) { + ret = wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor); + if (ret >= 0) { + for (ret = wolfsentry_route_table_iterate_current(main_routes, cursor, &route); + ret >= 0; + ret = wolfsentry_route_table_iterate_next(main_routes, cursor, &route)) + { + ++*n_routes; + } + ret = wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor); + } + } + + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + WOLFSENTRY_ERROR_RERETURN(ret); +} + +static wolfsentry_errcode_t test_track_peer_sentinel_action( + WOLFSENTRY_CONTEXT_ARGS_IN, + const struct wolfsentry_action *action, + void *handler_context, + void *caller_arg, + const struct wolfsentry_event *event, + wolfsentry_action_type_t action_type, + const struct wolfsentry_route *target_route, + struct wolfsentry_route_table *route_table, + struct wolfsentry_route *rule_route, + wolfsentry_action_res_t *action_results) +{ + WOLFSENTRY_CONTEXT_ARGS_NOT_USED; + (void)action; + (void)caller_arg; + (void)event; + (void)action_type; + (void)target_route; + (void)route_table; + (void)rule_route; + (void)action_results; + + ++*(int *)handler_context; + + WOLFSENTRY_RETURN_OK; +} + +static int test_track_peer_ephemeral_route_bound(void) { + struct wolfsentry_context *wolfsentry; + struct wolfsentry_eventconfig aux_config; + int sentinel_hits = 0; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } remote, local; + wolfsentry_route_flags_t rule_flags = WOLFSENTRY_ROUTE_FLAG_NONE; + wolfsentry_route_flags_t dispatch_flags = WOLFSENTRY_ROUTE_FLAG_NONE; + wolfsentry_route_flags_t inexact_matches; + wolfsentry_action_res_t action_results; + wolfsentry_ent_id_t id; + wolfsentry_hitcount_t n_routes; + int i; + +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 }; +#else + static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 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_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_generator", + -1 /* label_len */, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + /* an aux event with no purge time would make every tracked peer a permanent route. */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_unpurgeable", + -1 /* label_len */, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &aux_config)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &aux_config.route_idle_time_for_purge)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_purgeable", + -1 /* label_len */, + 10, + &aux_config, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_generator", + -1, + WOLFSENTRY_ACTION_TYPE_MATCH, + WOLFSENTRY_BUILTIN_LABEL_PREFIX "track-peer-v1", + -1)); + + /* a refused insert must not stop the rest of the match chain. */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_sentinel", + -1, + WOLFSENTRY_ACTION_FLAG_NONE, + test_track_peer_sentinel_action, + &sentinel_hits, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_generator", + -1, + WOLFSENTRY_ACTION_TYPE_MATCH, + "track_peer_sentinel", + -1)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_generator", + -1, + "track_peer_unpurgeable", + -1)); + + remote.sa.sa_family = local.sa.sa_family = AF_INET; + remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP; + remote.sa.sa_port = 0; + local.sa.sa_port = 443; + remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE; + remote.sa.interface = local.sa.interface = 1; + memset(remote.sa.addr, 0, sizeof remote.addr_buf); + memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf); + + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN); + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD); + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD); + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + &remote.sa, + &local.sa, + rule_flags, + "track_peer_generator", + -1, + &id, + &action_results)); + + WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_count_main_routes(WOLFSENTRY_CONTEXT_ARGS_OUT, &n_routes)); + WOLFSENTRY_EXIT_ON_FALSE(n_routes == 1); + + WOLFSENTRY_SET_BITS(dispatch_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN); + + for (i = 1; i <= 3; ++i) { + remote.sa.sa_port = (wolfsentry_port_t)(1000 + i); + remote.sa.addr[0] = 10; + remote.sa.addr[1] = 0; + remote.sa.addr[2] = 0; + remote.sa.addr[3] = (byte)i; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &remote.sa, + &local.sa, + dispatch_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_INSERTED)); + } + + WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_count_main_routes(WOLFSENTRY_CONTEXT_ARGS_OUT, &n_routes)); + WOLFSENTRY_EXIT_ON_FALSE(n_routes == 1); + WOLFSENTRY_EXIT_ON_FALSE(sentinel_hits == 3); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_generator", + -1, + "track_peer_purgeable", + -1)); + + for (i = 1; i <= 3; ++i) { + remote.sa.sa_port = (wolfsentry_port_t)(2000 + i); + remote.sa.addr[0] = 10; + remote.sa.addr[1] = 0; + remote.sa.addr[2] = 1; + remote.sa.addr[3] = (byte)i; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &remote.sa, + &local.sa, + dispatch_flags, + NULL /* event_label */, + 0 /* event_label_len */, + NULL /* caller_arg */, + &id, + &inexact_matches, + &action_results)); + + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED)); + } + + WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_count_main_routes(WOLFSENTRY_CONTEXT_ARGS_OUT, &n_routes)); + WOLFSENTRY_EXIT_ON_FALSE(n_routes == 4); + WOLFSENTRY_EXIT_ON_FALSE(sentinel_hits == 6); + + 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_DYNAMIC_RULES */ #ifdef TEST_USER_VALUES @@ -5749,6 +6014,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_dynamic_rules failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_track_peer_ephemeral_route_bound(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_track_peer_ephemeral_route_bound failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From e263f578c4b769af7d14f4f47c5c414d2c32f508 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 14:46:29 -0700 Subject: [PATCH 3/4] F-2466 - https://fenrir.wolfssl.com/finding/2466 - Count the connection that spawns a track-peer route on that route --- src/action_builtins.c | 47 +++++- tests/unittests.c | 357 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 402 insertions(+), 2 deletions(-) diff --git a/src/action_builtins.c b/src/action_builtins.c index 61cfcef..9f58813 100644 --- a/src/action_builtins.c +++ b/src/action_builtins.c @@ -24,6 +24,43 @@ #define WOLFSENTRY_SOURCE_ID WOLFSENTRY_SOURCE_ID_ACTION_BUILTINS_C +/* the public insert wrappers zero action_results before the route table sees + * them, so the CONNECT that spawns a tracking route is counted here instead, + * on exactly the route that was just inserted. caller must hold the lock. + */ +static wolfsentry_errcode_t wolfsentry_builtin_track_peer_count_connect( + WOLFSENTRY_CONTEXT_ARGS_IN, + wolfsentry_ent_id_t route_id, + const struct wolfsentry_eventconfig_internal *config) +{ + wolfsentry_errcode_t ret; + struct wolfsentry_route *route = NULL; + uint16_t new_count; + + ret = wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, route_id, (struct wolfsentry_table_ent_header **)&route); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + if (route->header.parent_table == NULL) + WOLFSENTRY_ERROR_RETURN(INTERNAL_CHECK_FATAL); + if (route->header.parent_table->ent_type != WOLFSENTRY_OBJECT_TYPE_ROUTE) + WOLFSENTRY_ERROR_RETURN(WRONG_OBJECT); + + if (WOLFSENTRY_CHECK_BITS(WOLFSENTRY_ATOMIC_LOAD(route->flags), WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS)) + WOLFSENTRY_RETURN_OK; + + /* dispatch counts connections only under a limit, so match it: an unlimited route must stay at zero. */ + if (config->config.max_connection_count == 0) + WOLFSENTRY_RETURN_OK; + + if (WOLFSENTRY_ATOMIC_LOAD(route->meta.connection_count) >= config->config.max_connection_count) + WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE); + + WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY_BY_ONE(route->meta.connection_count, new_count); + if (new_count == 0) + WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED); + + WOLFSENTRY_RETURN_OK; +} + /* this simple action creates a new tracking rule, either as a fallthrough or as * a side effect. it can be used to open a pinhole route for bidirectional * traffic, or to track derogatory events on a locally bound port (e.g. crypto @@ -50,6 +87,7 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer( struct wolfsentry_route_exports target_exports; wolfsentry_errcode_t ret; wolfsentry_action_res_t insert_action_results = WOLFSENTRY_ACTION_RES_NONE; + wolfsentry_ent_id_t new_route_id = WOLFSENTRY_ENT_ID_NONE; (void)action; (void)handler_arg; @@ -138,13 +176,18 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer( route_table, NULL /* void *caller_arg*/, &target_exports, - NULL /* id */, + &new_route_id, &insert_action_results); WOLFSENTRY_WARN_ON_FAILURE(ret); /* no route survives a failed insert, so drop INSERTED (it would suppress generator * accounting); other bits are the chain's decisions and are NONE on early failure. */ - if (ret < 0) + if (ret < 0) { WOLFSENTRY_CLEAR_BITS(insert_action_results, WOLFSENTRY_ACTION_RES_INSERTED); + } else if (WOLFSENTRY_CHECK_BITS(insert_action_results, WOLFSENTRY_ACTION_RES_INSERTED) && + WOLFSENTRY_CHECK_BITS(*action_results, WOLFSENTRY_ACTION_RES_CONNECT)) + { + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_builtin_track_peer_count_connect(WOLFSENTRY_CONTEXT_ARGS_OUT, new_route_id, ephemeral_config)); + } *action_results |= insert_action_results; /* copying over the bits is crucial to inhibit double-accounting for * connection_count, derogatory_count, and commendable_count. */ diff --git a/tests/unittests.c b/tests/unittests.c index 6942754..b32d4d8 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3734,6 +3734,358 @@ static int test_track_peer_ephemeral_route_bound(void) { WOLFSENTRY_RETURN_OK; } +/* find the non-wildcard route for the peer whose remote address ends in remote_last_byte. */ +static wolfsentry_errcode_t test_dynamic_ephemeral_route_exports( + WOLFSENTRY_CONTEXT_ARGS_IN, + byte remote_last_byte, + struct wolfsentry_route_exports *exports_out) +{ + wolfsentry_errcode_t ret; + struct wolfsentry_route_table *main_routes; + struct wolfsentry_cursor *cursor; + struct wolfsentry_route *route; + struct wolfsentry_route_exports exports; + int found = 0; + + ret = wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + + ret = wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes); + if (ret >= 0) { + ret = wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor); + if (ret >= 0) { + for (ret = wolfsentry_route_table_iterate_current(main_routes, cursor, &route); + ret >= 0; + ret = wolfsentry_route_table_iterate_next(main_routes, cursor, &route)) + { + ret = wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, route, &exports); + if (ret < 0) + break; + if ((! WOLFSENTRY_CHECK_BITS(exports.flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD)) && + (exports.remote.addr_len == 4 * BITS_PER_BYTE) && + (exports.remote_address[3] == remote_last_byte)) + { + *exports_out = exports; + found = 1; + break; + } + } + if (ret >= 0 || WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND)) + ret = wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor); + else + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor)); + } + } + + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + if ((ret >= 0) && (! found)) + ret = WOLFSENTRY_ERROR_ENCODE(ITEM_NOT_FOUND); + + WOLFSENTRY_ERROR_RERETURN(ret); +} + +static int test_track_peer_connect_count(void) { + struct wolfsentry_context *wolfsentry; + struct wolfsentry_eventconfig limit_config, nocount_config, nolimit_config; + struct wolfsentry_route_exports ephemeral_exports; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } remote, local; + wolfsentry_route_flags_t rule_flags = WOLFSENTRY_ROUTE_FLAG_NONE; + wolfsentry_route_flags_t dispatch_flags = WOLFSENTRY_ROUTE_FLAG_NONE; + wolfsentry_route_flags_t inexact_matches; + wolfsentry_action_res_t action_results; + wolfsentry_ent_id_t id; + +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 }; +#else + static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 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_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_generator", + -1 /* label_len */, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &limit_config)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &limit_config.route_idle_time_for_purge)); + limit_config.max_connection_count = 1; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_limit", + -1 /* label_len */, + 10, + &limit_config, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &nocount_config)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &nocount_config.route_idle_time_for_purge)); + nocount_config.max_connection_count = 1; + nocount_config.route_flags_to_add_on_insert = WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_nocount", + -1 /* label_len */, + 10, + &nocount_config, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_generator", + -1, + WOLFSENTRY_ACTION_TYPE_MATCH, + WOLFSENTRY_BUILTIN_LABEL_PREFIX "track-peer-v1", + -1)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_generator", + -1, + "track_peer_cc_limit", + -1)); + + remote.sa.sa_family = local.sa.sa_family = AF_INET; + remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP; + remote.sa.sa_port = 0; + local.sa.sa_port = 443; + remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE; + remote.sa.interface = local.sa.interface = 1; + memset(remote.sa.addr, 0, sizeof remote.addr_buf); + memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf); + + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN); + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD); + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD); + WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + &remote.sa, + &local.sa, + rule_flags, + "track_peer_cc_generator", + -1, + &id, + &action_results)); + + WOLFSENTRY_SET_BITS(dispatch_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN); + + remote.sa.sa_port = 1234; + memcpy(remote.sa.addr, "\12\0\2\1", sizeof remote.addr_buf); + + /* the CONNECT that spawns the tracking route must be counted on that route. */ + 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, + dispatch_flags, + NULL /* event_label */, + 0 /* event_label_len */, + NULL /* caller_arg */, + &id, + &inexact_matches, + &action_results)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + + WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports)); + WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 1); + + /* with a per-peer limit of 1, a second concurrent CONNECT from the same peer is rejected. */ + 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, + dispatch_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_INSERTED)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + + WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports)); + WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 1); + + /* the matching DISCONNECT brings the count back to zero. */ + action_results = WOLFSENTRY_ACTION_RES_DISCONNECT; + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &remote.sa, + &local.sa, + dispatch_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(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports)); + WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 0); + + /* and the peer can connect again. */ + 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, + dispatch_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(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports)); + WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 1); + + /* a tracking route flagged dont-count-current-connections stays at zero. */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_generator", + -1, + "track_peer_cc_nocount", + -1)); + + remote.sa.sa_port = 2345; + memcpy(remote.sa.addr, "\12\0\3\2", sizeof remote.addr_buf); + + 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, + dispatch_flags, + NULL /* event_label */, + 0 /* event_label_len */, + NULL /* caller_arg */, + &id, + &inexact_matches, + &action_results)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + + WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 2, &ephemeral_exports)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(ephemeral_exports.flags, WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS)); + WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 0); + + /* with the unlimited setting dispatch never counts connections, so the + * spawning CONNECT must not leave a count a later DISCONNECT can't undo. + */ + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &nolimit_config)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &nolimit_config.route_idle_time_for_purge)); + nolimit_config.max_connection_count = 0; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_nolimit", + -1 /* label_len */, + 10, + &nolimit_config, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "track_peer_cc_generator", + -1, + "track_peer_cc_nolimit", + -1)); + + remote.sa.sa_port = 3456; + memcpy(remote.sa.addr, "\12\0\4\3", sizeof remote.addr_buf); + + 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, + dispatch_flags, + NULL /* event_label */, + 0 /* event_label_len */, + NULL /* caller_arg */, + &id, + &inexact_matches, + &action_results)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + + WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 3, &ephemeral_exports)); + WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 0); + + action_results = WOLFSENTRY_ACTION_RES_DISCONNECT; + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &remote.sa, + &local.sa, + dispatch_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(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 3, &ephemeral_exports)); + WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 0); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + #endif /* TEST_DYNAMIC_RULES */ #ifdef TEST_USER_VALUES @@ -6019,6 +6371,11 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_track_peer_ephemeral_route_bound failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + ret = test_track_peer_connect_count(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_track_peer_connect_count failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From f0160909bf512db40287885c5214444e2849241e Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 13:46:27 -0700 Subject: [PATCH 4/4] F-2443 - https://fenrir.wolfssl.com/finding/2443 - Set track-peer test addresses through a full-width buffer for C89 builds --- tests/unittests.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/unittests.c b/tests/unittests.c index b32d4d8..79dc15a 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3544,6 +3544,7 @@ static int test_track_peer_ephemeral_route_bound(void) { wolfsentry_ent_id_t id; wolfsentry_hitcount_t n_routes; int i; + byte addr[4]; #ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 }; @@ -3667,10 +3668,11 @@ static int test_track_peer_ephemeral_route_bound(void) { for (i = 1; i <= 3; ++i) { remote.sa.sa_port = (wolfsentry_port_t)(1000 + i); - remote.sa.addr[0] = 10; - remote.sa.addr[1] = 0; - remote.sa.addr[2] = 0; - remote.sa.addr[3] = (byte)i; + addr[0] = 10; + addr[1] = 0; + addr[2] = 0; + addr[3] = (byte)i; + memcpy(remote.sa.addr, addr, sizeof addr); WOLFSENTRY_EXIT_ON_FAILURE( wolfsentry_route_event_dispatch( @@ -3702,10 +3704,11 @@ static int test_track_peer_ephemeral_route_bound(void) { for (i = 1; i <= 3; ++i) { remote.sa.sa_port = (wolfsentry_port_t)(2000 + i); - remote.sa.addr[0] = 10; - remote.sa.addr[1] = 0; - remote.sa.addr[2] = 1; - remote.sa.addr[3] = (byte)i; + addr[0] = 10; + addr[1] = 0; + addr[2] = 1; + addr[3] = (byte)i; + memcpy(remote.sa.addr, addr, sizeof addr); WOLFSENTRY_EXIT_ON_FAILURE( wolfsentry_route_event_dispatch(