From cce34df92a33471959509778c98b67dc4b3d926d Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:45:36 -0700 Subject: [PATCH 01/14] F-5558 - https://fenrir.wolfssl.com/finding/5558 - Size the event lookup buffer for a maximum-length label and its terminator --- src/events.c | 2 +- tests/unittests.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/events.c b/src/events.c index 6d92a88..5234ed1 100644 --- a/src/events.c +++ b/src/events.c @@ -319,7 +319,7 @@ static wolfsentry_errcode_t wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_IN, c WOLFSENTRY_STACKBUF( struct wolfsentry_event, label, - WOLFSENTRY_MAX_LABEL_BYTES, + WOLFSENTRY_MAX_LABEL_BYTES + 1, target); struct wolfsentry_event *event_1 = &target.target; diff --git a/tests/unittests.c b/tests/unittests.c index 7a75686..518cf5e 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3469,6 +3469,80 @@ static int test_dynamic_rules(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_max_length_label(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id; + struct wolfsentry_event *event; + struct wolfsentry_eventconfig eventconfig; + char label[WOLFSENTRY_MAX_LABEL_BYTES + 1]; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + memset(label, 'm', WOLFSENTRY_MAX_LABEL_BYTES); + label[WOLFSENTRY_MAX_LABEL_BYTES] = 0; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &event)); + + WOLFSENTRY_EXIT_ON_FALSE(id == wolfsentry_get_object_id(event)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_drop_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + event, + NULL /* action_results */)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_MAX_LABEL_BYTES, + &eventconfig)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL /* action_results */)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + ITEM_NOT_FOUND, + wolfsentry_event_get_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &event)); + + 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 +5823,12 @@ 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_event_max_length_label(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_max_length_label failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From 22e0de466d7d3b5bfd67240d5220902b80087033 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:46:09 -0700 Subject: [PATCH 02/14] F-13883 - https://fenrir.wolfssl.com/finding/13883 - Cover maximum-length event labels across the event lookup APIs --- tests/unittests.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/tests/unittests.c b/tests/unittests.c index 518cf5e..7c88eb5 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3543,6 +3543,123 @@ static int test_event_max_length_label(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_max_length_label_lookups(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id; + struct wolfsentry_action_list_ent *cursor; + const char *action_label; + int action_label_len; + struct wolfsentry_eventconfig eventconfig; + int n_actions = 0; + char label[WOLFSENTRY_MAX_LABEL_BYTES + 1]; +#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), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + memset(label, 'n', WOLFSENTRY_MAX_LABEL_BYTES); + label[WOLFSENTRY_MAX_LABEL_BYTES] = 0; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "max-label-action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + wolfsentry_action_dummy_callback, + NULL /* handler_context */, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &eventconfig)); + + WOLFSENTRY_EXIT_ON_FALSE(eventconfig.route_private_data_size == config.route_private_data_size); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_TYPE_MATCH, + "max-label-action", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_list_start( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_TYPE_MATCH, + &cursor)); + + while (wolfsentry_event_action_list_next( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &cursor, + &action_label, + &action_label_len) >= 0) { + WOLFSENTRY_EXIT_ON_FALSE(strcmp(action_label, "max-label-action") == 0); + ++n_actions; + } + + WOLFSENTRY_EXIT_ON_FALSE(n_actions == 1); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_list_done( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &cursor)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_TYPE_MATCH, + "max-label-action", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + 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 @@ -5829,6 +5946,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_max_length_label failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_max_length_label_lookups(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_max_length_label_lookups failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From b4334c00b2c83d671a8fbacec38e3ce3397a0228 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:46:33 -0700 Subject: [PATCH 03/14] F-8758 - https://fenrir.wolfssl.com/finding/8758 - Set the event id output only after the event is inserted --- src/events.c | 4 ++-- tests/unittests.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/events.c b/src/events.c index 5234ed1..bcd5d30 100644 --- a/src/events.c +++ b/src/events.c @@ -289,12 +289,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_insert( wolfsentry_event_free(WOLFSENTRY_CONTEXT_ARGS_OUT, new); WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); } - if (id) - *id = new->header.id; if ((ret = wolfsentry_table_ent_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, &new->header, &wolfsentry->events->header, 1 /* unique_p */)) < 0) { wolfsentry_event_free(WOLFSENTRY_CONTEXT_ARGS_OUT, new); WOLFSENTRY_ERROR_UNLOCK_AND_RETURN_RECODED(ret); } + if (id) + *id = new->header.id; WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); } diff --git a/tests/unittests.c b/tests/unittests.c index 7c88eb5..277216d 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3660,6 +3660,52 @@ static int test_event_max_length_label_lookups(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_insert_failure_leaves_id_untouched(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id, id_before; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "id_out_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + id_before = id; + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + ITEM_ALREADY_PRESENT, + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "id_out_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FALSE(id == id_before); + + 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 @@ -5952,6 +5998,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_max_length_label_lookups failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_insert_failure_leaves_id_untouched(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_insert_failure_leaves_id_untouched failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From 90490ea430750dccadd7509b06dfe8e01499c4e0 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:47:00 -0700 Subject: [PATCH 04/14] F-11606 - https://fenrir.wolfssl.com/finding/11606 - Reject a NULL configuration in event config updates --- src/events.c | 3 ++ tests/unittests.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/events.c b/src/events.c index bcd5d30..0207ef3 100644 --- a/src/events.c +++ b/src/events.c @@ -360,6 +360,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_update_config(WOLFSENTRY_CO struct wolfsentry_event *event; wolfsentry_errcode_t ret; + if (config == NULL) + WOLFSENTRY_ERROR_RETURN(INVALID_ARG); + WOLFSENTRY_MUTEX_OR_RETURN(); ret = wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, label, label_len, &event); diff --git a/tests/unittests.c b/tests/unittests.c index 277216d..16850b3 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3706,6 +3706,70 @@ static int test_event_insert_failure_leaves_id_untouched(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_update_config_null(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id; + struct wolfsentry_eventconfig global_eventconfig, eventconfig; +#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_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "null_config_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "null_config_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &global_eventconfig)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INVALID_ARG, + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "null_config_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL /* config */)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "null_config_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &eventconfig)); + + WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == global_eventconfig.route_private_data_size) && + (eventconfig.route_private_data_alignment == global_eventconfig.route_private_data_alignment) && + (eventconfig.max_connection_count == global_eventconfig.max_connection_count)); + WOLFSENTRY_EXIT_ON_FALSE(eventconfig.max_connection_count == config.max_connection_count); + + 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 @@ -6004,6 +6068,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_insert_failure_leaves_id_untouched failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_update_config_null(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_update_config_null failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From 373aacf20032cfbe7d30a1fd6aa9af5af9472545 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:48:04 -0700 Subject: [PATCH 05/14] F-11619 - https://fenrir.wolfssl.com/finding/11619 - Reject route layout changes in event config updates once routes use the event --- src/events.c | 40 +++++-- tests/unittests.c | 250 ++++++++++++++++++++++++++++++++++++++++ wolfsentry/wolfsentry.h | 2 +- 3 files changed, 283 insertions(+), 9 deletions(-) diff --git a/src/events.c b/src/events.c index 0207ef3..55bfb6c 100644 --- a/src/events.c +++ b/src/events.c @@ -149,6 +149,10 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_event_clone_bare( memcpy(*new_event, src_event, new_size); WOLFSENTRY_TABLE_ENT_HEADER_RESET(**new_ent); + /* no route in a route-free clone can depend on the copied layout */ + if (WOLFSENTRY_CHECK_BITS(flags, WOLFSENTRY_CLONE_FLAG_NO_ROUTES)) + WOLFSENTRY_CLEAR_BITS((*new_event)->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT); + WOLFSENTRY_LIST_HEADER_RESET((*new_event)->post_action_list.header); WOLFSENTRY_LIST_HEADER_RESET((*new_event)->insert_action_list.header); WOLFSENTRY_LIST_HEADER_RESET((*new_event)->match_action_list.header); @@ -356,8 +360,22 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_config(WOLFSENTRY_CONTE WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); } +static int wolfsentry_event_has_routes(const struct wolfsentry_context *wolfsentry, const struct wolfsentry_event *event) { + const struct wolfsentry_table_ent_header *ent; + + if (WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT)) + return 1; + for (ent = wolfsentry_table_first(&wolfsentry->routes->header); ent != NULL; ent = ent->next) { + if (((const struct wolfsentry_route *)ent)->parent_event == event) + return 1; + } + return 0; +} + WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_update_config(WOLFSENTRY_CONTEXT_ARGS_IN, const char *label, int label_len, const struct wolfsentry_eventconfig *config) { struct wolfsentry_event *event; + const struct wolfsentry_eventconfig_internal *current_config; + struct wolfsentry_eventconfig_internal new_config; wolfsentry_errcode_t ret; if (config == NULL) @@ -368,18 +386,24 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_update_config(WOLFSENTRY_CO ret = wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, label, label_len, &event); WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); + ret = wolfsentry_eventconfig_load(config, &new_config); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); + + current_config = (event->config != NULL) ? event->config : &wolfsentry->config; + + /* existing routes were allocated, and are accessed and freed, using the event's current layout */ + if (wolfsentry_event_has_routes(wolfsentry, event) && + ((new_config.config.route_private_data_size != current_config->config.route_private_data_size) || + (new_config.config.route_private_data_alignment != current_config->config.route_private_data_alignment))) + WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(INCOMPATIBLE_STATE); + if (event->config == NULL) { if ((event->config = (struct wolfsentry_eventconfig_internal *)WOLFSENTRY_MALLOC(sizeof *event->config)) == NULL) WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(SYS_RESOURCE_FAILED); - if ((ret = wolfsentry_eventconfig_load(config, event->config)) < 0) { - WOLFSENTRY_FREE(event->config); - event->config = NULL; - } - WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); - } else { - ret = wolfsentry_eventconfig_load(config, event->config); - WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); } + memcpy(event->config, &new_config, sizeof *event->config); + + WOLFSENTRY_UNLOCK_AND_RETURN_OK; } WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_IN, const char *label, int label_len, struct wolfsentry_event **event) { diff --git a/tests/unittests.c b/tests/unittests.c index 16850b3..e965dd7 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -3770,6 +3770,250 @@ static int test_event_update_config_null(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_update_config_layout_with_routes(void) { + struct wolfsentry_context *wolfsentry, *ctx_clone; + wolfsentry_ent_id_t id; + wolfsentry_action_res_t action_results; + struct wolfsentry_eventconfig eventconfig; + wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } remote, local; +#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 }; + static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 }; + static const struct wolfsentry_eventconfig config2_more_connections = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 20 }; +#else + static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static const struct wolfsentry_eventconfig config2_more_connections = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 20, 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)); + + remote.sa.sa_family = local.sa.sa_family = AF_INET; + remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP; + remote.sa.sa_port = 12345; + 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; + memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf); + memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + &config2, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes_inherited", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_no_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + &config2, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + &remote.sa, + &local.sa, + flags, + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &id, + &action_results)); + + memcpy(remote.sa.addr, "\4\5\6\7", sizeof remote.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + &remote.sa, + &local.sa, + flags, + "owns_routes_inherited", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &id, + &action_results)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &eventconfig)); + + WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config2.route_private_data_size) && + (eventconfig.route_private_data_alignment == config2.route_private_data_alignment) && + (eventconfig.max_connection_count == config2.max_connection_count)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config2_more_connections)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &eventconfig)); + + WOLFSENTRY_EXIT_ON_FALSE(eventconfig.max_connection_count == config2_more_connections.max_connection_count); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes_inherited", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config2)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes_inherited", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout-insert-action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + wolfsentry_action_dummy_callback, + NULL /* handler_context */, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes_with_insert_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + &config2, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes_with_insert_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_TYPE_INSERT, + "layout-insert-action", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + memcpy(remote.sa.addr, "\10\11\12\13", sizeof remote.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + &remote.sa, + &local.sa, + flags, + "owns_routes_with_insert_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &id, + &action_results)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_routes_with_insert_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_no_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owns_no_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &eventconfig)); + + WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config.route_private_data_size) && + (eventconfig.route_private_data_alignment == config.route_private_data_alignment)); + + /* a clone without routes has no route using the old layout */ + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NO_ROUTES)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone), + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone))); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NONE)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone), + "owns_routes", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone))); + + 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 @@ -6074,6 +6318,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_update_config_null failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_update_config_layout_with_routes(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_update_config_layout_with_routes failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES diff --git a/wolfsentry/wolfsentry.h b/wolfsentry/wolfsentry.h index 225980b..ad0e30d 100644 --- a/wolfsentry/wolfsentry.h +++ b/wolfsentry/wolfsentry.h @@ -2801,7 +2801,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_config( \param label_len the length of the label \param config the updated configuration - \return WOLFSENTRY_IS_SUCCESS(ret) is true on success. + \return WOLFSENTRY_IS_SUCCESS(ret) is true on success. Once any route has used the event as its parent, changes to `route_private_data_size` or `route_private_data_alignment` are rejected with `INCOMPATIBLE_STATE`, even after that route is deleted, because a deleted route can still be held by a checked-out reference. \sa WOLFSENTRY_CONTEXT_ARGS_IN */ From ab8fc1ef13b3ee506b94f83e676b1d38c29dca40 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:48:42 -0700 Subject: [PATCH 06/14] F-11602 - https://fenrir.wolfssl.com/finding/11602 - Reject an event as its own auxiliary event --- src/events.c | 3 +- tests/unittests.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/events.c b/src/events.c index 55bfb6c..b035ad0 100644 --- a/src/events.c +++ b/src/events.c @@ -656,7 +656,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_set_aux_event( aux_event = NULL; goto out; } - if (WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT)) { + if ((aux_event == event) || + WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT)) { ret = WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE); goto out; } diff --git a/tests/unittests.c b/tests/unittests.c index e965dd7..4e67e06 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4014,6 +4014,70 @@ static int test_event_update_config_layout_with_routes(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_aux_event_self_reference(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id; + struct wolfsentry_event *event; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "self_aux_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "self_aux_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "self_aux_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "self_aux_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &event)); + + WOLFSENTRY_EXIT_ON_TRUE(wolfsentry_event_get_aux_event(event) != NULL); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(wolfsentry_event_get_flags(event), WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_drop_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + event, + NULL /* action_results */)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "self_aux_event", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL /* action_results */)); + + 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 @@ -6324,6 +6388,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_update_config_layout_with_routes failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_aux_event_self_reference(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_aux_event_self_reference failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From f0da0e6f6cd0f8fac43cb76b6023ad5c5fea6835 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:49:54 -0700 Subject: [PATCH 07/14] F-5566 - https://fenrir.wolfssl.com/finding/5566 - Clear the subevent mark when an event stops being any event's auxiliary event --- src/events.c | 67 +++++-- src/wolfsentry_internal.h | 1 + tests/unittests.c | 364 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 420 insertions(+), 12 deletions(-) diff --git a/src/events.c b/src/events.c index b035ad0..6e602e2 100644 --- a/src/events.c +++ b/src/events.c @@ -77,6 +77,37 @@ static wolfsentry_errcode_t wolfsentry_event_init_1(const char *label, int label WOLFSENTRY_RETURN_OK; } +/* callers hold the context lock, which is what the plain flag write relies on */ +static wolfsentry_errcode_t wolfsentry_event_aux_owner_add(struct wolfsentry_event *aux_event) { + wolfsentry_refcount_t owners; + + WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY_BY_ONE(aux_event->aux_owner_count, owners); + if (owners == 0) + WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED); + if (! WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)) + WOLFSENTRY_SET_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT); + WOLFSENTRY_RETURN_OK; +} + +/* may run without the context lock when a deleted owner is finally freed, so only the atomic count changes here; the mark is resynced under the lock */ +static wolfsentry_errcode_t wolfsentry_event_aux_owner_remove(struct wolfsentry_event *aux_event) { + wolfsentry_refcount_t owners; + + WOLFSENTRY_ATOMIC_DECREMENT_UNSIGNED_SAFELY_BY_ONE(aux_event->aux_owner_count, owners); + if (owners == MAX_UINT_OF(aux_event->aux_owner_count)) + WOLFSENTRY_ERROR_RETURN(INTERNAL_CHECK_FATAL); + WOLFSENTRY_RETURN_OK; +} + +static void wolfsentry_event_aux_owner_resync_mark(struct wolfsentry_event *event) { + if ((WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) == 0) && + WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)) + { + WOLFSENTRY_CLEAR_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT); + } + WOLFSENTRY_RETURN_VOID; +} + static void wolfsentry_event_free(WOLFSENTRY_CONTEXT_ARGS_IN, struct wolfsentry_event *event) { WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_delete_all(WOLFSENTRY_CONTEXT_ARGS_OUT, &event->post_action_list)); WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_delete_all(WOLFSENTRY_CONTEXT_ARGS_OUT, &event->insert_action_list)); @@ -161,6 +192,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_event_clone_bare( WOLFSENTRY_LIST_HEADER_RESET((*new_event)->decision_action_list.header); (*new_event)->aux_event = NULL; + (*new_event)->aux_owner_count = 0; + WOLFSENTRY_CLEAR_BITS((*new_event)->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT); if (src_event->config) { if (((*new_event)->config = WOLFSENTRY_MALLOC_1(dest_context->hpi.allocator, sizeof *(*new_event)->config)) == NULL) { @@ -261,6 +294,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_event_clone_resolve( } WOLFSENTRY_REFCOUNT_INCREMENT(new_event->aux_event->header.refcount, ret); WOLFSENTRY_RERETURN_IF_ERROR(ret); + ret = wolfsentry_event_aux_owner_add(new_event->aux_event); + WOLFSENTRY_RERETURN_IF_ERROR(ret); } WOLFSENTRY_RETURN_OK; @@ -445,6 +480,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_drop_reference(WOLFSENTRY_C if (refs_left > 0) WOLFSENTRY_RETURN_OK; if (event->aux_event) { + ret = wolfsentry_event_aux_owner_remove(event->aux_event); + WOLFSENTRY_RERETURN_IF_ERROR(ret); ret = wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event->aux_event, NULL); WOLFSENTRY_RERETURN_IF_ERROR(ret); event->aux_event = NULL; @@ -640,16 +677,18 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_set_aux_event( int aux_event_label_len) { wolfsentry_errcode_t ret; - struct wolfsentry_event *event, *aux_event = NULL; + struct wolfsentry_event *event, *aux_event = NULL, *old_aux_event; WOLFSENTRY_MUTEX_OR_RETURN(); ret = wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, event_label_len, &event); WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); - if (WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)) { + /* the owner count is authoritative; a mark left by an owner freed without the lock is cleared here */ + if (WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) != 0) { ret = WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE); goto out; } + wolfsentry_event_aux_owner_resync_mark(event); if (aux_event_label) { ret = wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux_event_label, aux_event_label_len, &aux_event); if (ret < 0) { @@ -664,22 +703,26 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_set_aux_event( } else aux_event = NULL; - if (event->aux_event) - WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event->aux_event, NULL /* action_results */)); + if (aux_event != NULL) { + ret = wolfsentry_event_aux_owner_add(aux_event); + if (ret < 0) + goto out; + } + + old_aux_event = event->aux_event; event->aux_event = aux_event; + if (old_aux_event != NULL) { + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_aux_owner_remove(old_aux_event)); + wolfsentry_event_aux_owner_resync_mark(old_aux_event); + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old_aux_event, NULL /* action_results */)); + } ret = WOLFSENTRY_ERROR_ENCODE(OK); out: WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event, NULL /* action_results */)); - if (aux_event) { - if (ret < 0) - WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux_event, NULL /* action_results */)); - else { - if (! WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)) - WOLFSENTRY_SET_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT); - } - } + if ((aux_event != NULL) && (ret < 0)) + WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux_event, NULL /* action_results */)); WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret); } diff --git a/src/wolfsentry_internal.h b/src/wolfsentry_internal.h index 484c591..014be0a 100644 --- a/src/wolfsentry_internal.h +++ b/src/wolfsentry_internal.h @@ -340,6 +340,7 @@ struct wolfsentry_event { * policy for the ephemeral route. */ + wolfsentry_refcount_t aux_owner_count; /* events whose aux_event is this one */ byte label_len; char label[WOLFSENTRY_FLEXIBLE_ARRAY_SIZE]; diff --git a/tests/unittests.c b/tests/unittests.c index 4e67e06..5b612fe 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4078,6 +4078,364 @@ static int test_event_aux_event_self_reference(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_aux_event_reuse_after_clear(void) { + struct wolfsentry_context *wolfsentry, *ctx_clone; + struct wolfsentry_event *deleted_owner; + struct wolfsentry_event *held_aux; + wolfsentry_ent_id_t id; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_a", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_d", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + /* a -> b, then clear: b is free to own an aux event of its own */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_a", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_a", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL, + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + /* c is now an aux event, so it cannot own one */ + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_d", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + /* replacing a's aux event releases the old one */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_a", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL, + 0)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_d", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_a", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_d", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_one", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "shared_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "other_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_one", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "shared_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "shared_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + /* owner_one is deleted from the table but stays alive, and still owns shared_aux */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_one", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &deleted_owner)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_delete( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_one", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL /* action_results */)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL, + 0)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "shared_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "other_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_drop_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + deleted_owner, + NULL /* action_results */)); + + /* freeing the deleted owner released shared_aux, which can now own an aux event */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "shared_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "other_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "held_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "held_target", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + /* an ordinary reference on the aux event is not ownership */ + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "held_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &held_aux)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "held_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "held_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "held_target", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL, + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "held_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "held_target", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_drop_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + held_aux, + NULL /* action_results */)); + + /* a clone rebuilds the aux ownership graph, independently of the source */ + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NONE)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone), + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone), + "aux_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL, + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone), + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "aux_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "owner_two", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone))); + + 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 @@ -6394,6 +6752,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_aux_event_self_reference failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_aux_event_reuse_after_clear(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_aux_event_reuse_after_clear failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From a03c1d456c8c06a6d745393ece89b606aceab475 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 12:50:21 -0700 Subject: [PATCH 08/14] F-3870 - https://fenrir.wolfssl.com/finding/3870 - Test that an event serving as an auxiliary event cannot have its own auxiliary event --- tests/unittests.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/unittests.c b/tests/unittests.c index 5b612fe..098ab1d 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4436,6 +4436,74 @@ static int test_event_aux_event_reuse_after_clear(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_aux_event_rejects_subevent_owner(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "subevent_a", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "subevent_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "subevent_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "subevent_a", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "subevent_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "subevent_b", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "subevent_c", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + 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 @@ -6758,6 +6826,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_aux_event_reuse_after_clear failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_aux_event_rejects_subevent_owner(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_aux_event_rejects_subevent_owner failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From 819e109336af27997ef700c6ece7f62dbe01fd3a Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 13:29:40 -0700 Subject: [PATCH 09/14] F-3871 - https://fenrir.wolfssl.com/finding/3871 - Test that a route parent event cannot be set as an auxiliary event --- tests/unittests.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/tests/unittests.c b/tests/unittests.c index 098ab1d..ecad1a8 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4504,6 +4504,83 @@ static int test_event_aux_event_rejects_subevent_owner(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_aux_event_rejects_parent_event(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id; + wolfsentry_action_res_t action_results; + wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } remote, local; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "route_parent", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "route_owner", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + remote.sa.sa_family = local.sa.sa_family = AF_INET; + remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP; + remote.sa.sa_port = 12345; + 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; + memcpy(remote.sa.addr, "\0\1\2\3", 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, + flags, + "route_parent", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &id, + &action_results)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "route_owner", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "route_parent", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + 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 @@ -6832,6 +6909,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_aux_event_rejects_subevent_owner failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_aux_event_rejects_parent_event(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_aux_event_rejects_parent_event failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From 386ace4d27551eb64116bed6f105a7572820763e Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 22 Oct 2026 13:30:58 -0700 Subject: [PATCH 10/14] F-10506 - https://fenrir.wolfssl.com/finding/10506 - Document the default event priority and test that omitted priority behaves as zero --- doc/json_configuration.md | 2 +- tests/unittests.c | 113 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/doc/json_configuration.md b/doc/json_configuration.md index 9aa1bc8..dcb0023 100644 --- a/doc/json_configuration.md +++ b/doc/json_configuration.md @@ -239,7 +239,7 @@ Each event is composed of the following elements, all of which are optional exce * `label` -- The name by which the event is identified. See the definition of `label` in the ABNF grammar below for permissible values. -* `priority` -- The priority of routes that have this event as their `parent-event` (see `routes` below). Lower number means higher priority. +* `priority` -- The priority of routes that have this event as their `parent-event` (see `routes` below). Lower number means higher priority. If omitted, the priority is 0, the highest priority. * `config` -- The configuration to associate with routes with this `parent-event`, as above for `config-update`. diff --git a/tests/unittests.c b/tests/unittests.c index ecad1a8..5c3e86e 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4581,6 +4581,111 @@ static int test_event_aux_event_rejects_parent_event(void) { WOLFSENTRY_RETURN_OK; } +#ifndef WOLFSENTRY_NO_JSON + +static int event_priority_check_winner(const char *priority_clause, int expect_accept) { + struct wolfsentry_context *wolfsentry; + wolfsentry_errcode_t ret; + wolfsentry_ent_id_t route_id; + wolfsentry_route_flags_t inexact_matches; + wolfsentry_action_res_t action_results; + char err_buf[512]; + char json[2048]; + int json_len; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } remote, local; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + json_len = snprintf( + json, sizeof json, + "{ \"wolfsentry-config-version\" : 1," + " \"events-insert\" : [" + " { \"label\" : \"first-event\" %s }," + " { \"label\" : \"second-event\", \"priority\" : 1 } ]," + " \"static-routes-insert\" : [" + " { \"parent-event\" : \"first-event\", \"direction-in\" : true," + " \"green-listed\" : true, \"penalty-boxed\" : false, \"family\" : %u," + " \"remote\" : { \"address\" : \"10.0.0.0\", \"prefix-bits\" : 8 }," + " \"local\" : { \"address\" : \"10.0.0.0\", \"prefix-bits\" : 8 } }," + " { \"parent-event\" : \"second-event\", \"direction-in\" : true," + " \"green-listed\" : false, \"penalty-boxed\" : true, \"family\" : %u," + " \"remote\" : { \"address\" : \"10.1.2.3\" }," + " \"local\" : { \"address\" : \"10.0.0.0\", \"prefix-bits\" : 8 } } ] }", + priority_clause, (unsigned int)AF_INET, (unsigned int)AF_INET); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + + ret = wolfsentry_config_json_oneshot( + WOLFSENTRY_CONTEXT_ARGS_OUT, + (unsigned char *)json, + (size_t)json_len, + WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, + err_buf, + sizeof err_buf); + if (ret < 0) + (void)fprintf(stderr, "%s\n", err_buf); + WOLFSENTRY_EXIT_ON_FAILURE(ret); + + memset(&remote, 0, sizeof remote); + memset(&local, 0, sizeof local); + remote.sa.sa_family = local.sa.sa_family = AF_INET; + remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP; + remote.sa.sa_port = 12345; + 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; + memcpy(remote.sa.addr, "\12\1\2\3", sizeof remote.addr_buf); + memcpy(local.sa.addr, "\12\11\11\11", sizeof local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch( + WOLFSENTRY_CONTEXT_ARGS_OUT, + &remote.sa, + &local.sa, + WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN, + NULL /* event_label */, + 0 /* event_label_len */, + NULL /* caller_arg */, + &route_id, + &inexact_matches, + &action_results)); + + if (expect_accept) { + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + } else { + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)); + } + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +static int test_event_default_priority(void) { + /* an omitted priority behaves as priority 0, the highest */ + WOLFSENTRY_EXIT_ON_FAILURE(event_priority_check_winner("", 1)); + WOLFSENTRY_EXIT_ON_FAILURE(event_priority_check_winner(", \"priority\" : 0", 1)); + WOLFSENTRY_EXIT_ON_FAILURE(event_priority_check_winner(", \"priority\" : 2", 0)); + + WOLFSENTRY_RETURN_OK; +} + +#endif /* !WOLFSENTRY_NO_JSON */ + #endif /* TEST_DYNAMIC_RULES */ #ifdef TEST_USER_VALUES @@ -6915,6 +7020,14 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_aux_event_rejects_parent_event failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + +#ifndef WOLFSENTRY_NO_JSON + ret = test_event_default_priority(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_default_priority failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } +#endif #endif #ifdef TEST_USER_VALUES From c755dece101bc6d30c45f3ac435da747ba9abbb3 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 09:04:04 -0700 Subject: [PATCH 11/14] F-13883 - https://fenrir.wolfssl.com/finding/13883 - Accept a maximum-length event label in the JSON loader --- src/json/load_config.c | 2 +- tests/unittests.c | 71 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index 732f17a..2246d8c 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -1100,7 +1100,7 @@ static wolfsentry_errcode_t handle_event_clause(struct wolfsentry_json_process_s } if ((jps->cur_depth == 3) && (type == JSON_STRING) && (! strcmp(jps->cur_keyname, "label"))) { - if (data_size >= sizeof jps->o_u_c.event.label) + if (data_size > sizeof jps->o_u_c.event.label) WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG); memcpy(jps->o_u_c.event.label, data, data_size); jps->o_u_c.event.label_len = (int)data_size; diff --git a/tests/unittests.c b/tests/unittests.c index 5c3e86e..cf1b1df 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4684,6 +4684,71 @@ static int test_event_default_priority(void) { WOLFSENTRY_RETURN_OK; } +static int test_json_event_max_length_label(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_errcode_t ret; + struct wolfsentry_eventconfig config; + char label[WOLFSENTRY_MAX_LABEL_BYTES + 1]; + char err_buf[512]; + char json[WOLFSENTRY_MAX_LABEL_BYTES + 128]; + int json_len; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + memset(label, 'L', sizeof label); + + json_len = snprintf( + json, sizeof json, + "{ \"wolfsentry-config-version\" : 1, \"events-insert\" : [ { \"label\" : \"%.*s\" } ] }", + (int)WOLFSENTRY_MAX_LABEL_BYTES, label); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + ret = wolfsentry_config_json_oneshot( + WOLFSENTRY_CONTEXT_ARGS_OUT, + (unsigned char *)json, + (size_t)json_len, + WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, + err_buf, + sizeof err_buf); + if (ret < 0) + (void)fprintf(stderr, "%s\n", err_buf); + WOLFSENTRY_EXIT_ON_FAILURE(ret); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_get_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + label, + (int)WOLFSENTRY_MAX_LABEL_BYTES, + &config)); + + json_len = snprintf( + json, sizeof json, + "{ \"wolfsentry-config-version\" : 1, \"events-insert\" : [ { \"label\" : \"%.*s\" } ] }", + (int)WOLFSENTRY_MAX_LABEL_BYTES + 1, label); + WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json)); + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + STRING_ARG_TOO_LONG, + wolfsentry_config_json_oneshot( + WOLFSENTRY_CONTEXT_ARGS_OUT, + (unsigned char *)json, + (size_t)json_len, + WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, + err_buf, + sizeof err_buf)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + #endif /* !WOLFSENTRY_NO_JSON */ #endif /* TEST_DYNAMIC_RULES */ @@ -7027,6 +7092,12 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_event_default_priority failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_json_event_max_length_label(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_json_event_max_length_label failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #endif From 2fe8155f809214bf1c89c6fd5000f67e5b634822 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 09:05:12 -0700 Subject: [PATCH 12/14] F-8758 - https://fenrir.wolfssl.com/finding/8758 - Set the action id output only after the action is inserted --- src/actions.c | 4 +-- tests/unittests.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/actions.c b/src/actions.c index 755c80e..21ae8e0 100644 --- a/src/actions.c +++ b/src/actions.c @@ -143,12 +143,12 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_insert_1( goto out; if ((ret = wolfsentry_id_allocate(WOLFSENTRY_CONTEXT_ARGS_OUT, &new->header)) < 0) goto out; - if (id) - *id = new->header.id; if ((ret = wolfsentry_table_ent_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, &new->header, &wolfsentry->actions->header, 1 /* unique_p */)) < 0) { ret = WOLFSENTRY_ERROR_RECODE(ret); goto out; } + if (id != NULL) + *id = new->header.id; ret = WOLFSENTRY_ERROR_ENCODE(OK); out: diff --git a/tests/unittests.c b/tests/unittests.c index cf1b1df..eb09cc2 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4751,6 +4751,77 @@ static int test_json_event_max_length_label(void) { #endif /* !WOLFSENTRY_NO_JSON */ +static wolfsentry_errcode_t test_action_insert_noop( + WOLFSENTRY_CONTEXT_ARGS_IN, + const struct wolfsentry_action *action, + void *handler_arg, + void *caller_arg, + const struct wolfsentry_event *trigger_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)handler_arg; + (void)caller_arg; + (void)trigger_event; + (void)action_type; + (void)target_route; + (void)route_table; + (void)rule_route; + (void)action_results; + WOLFSENTRY_RETURN_OK; +} + +static int test_action_insert_failure_leaves_id_untouched(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id, id_before; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "id_out_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action_insert_noop, + NULL /* handler_arg */, + &id)); + + id_before = id; + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + ITEM_ALREADY_PRESENT, + wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "id_out_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action_insert_noop, + NULL /* handler_arg */, + &id)); + + WOLFSENTRY_EXIT_ON_FALSE(id == id_before); + + 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 @@ -7099,6 +7170,12 @@ int main(int argc, char* argv[]) { err = 1; } #endif + + ret = test_action_insert_failure_leaves_id_untouched(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_action_insert_failure_leaves_id_untouched failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES From 593be8a17a22250f43d9b436cc28df0d20d4bf79 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 09:16:12 -0700 Subject: [PATCH 13/14] F-11619 - https://fenrir.wolfssl.com/finding/11619 - Treat aux-event use and held references as route ownership in the event layout guard --- src/events.c | 6 ++ tests/unittests.c | 202 ++++++++++++++++++++++++++++++++++++++++ wolfsentry/wolfsentry.h | 2 +- 3 files changed, 209 insertions(+), 1 deletion(-) diff --git a/src/events.c b/src/events.c index 6e602e2..435a7a2 100644 --- a/src/events.c +++ b/src/events.c @@ -400,6 +400,12 @@ static int wolfsentry_event_has_routes(const struct wolfsentry_context *wolfsent if (WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT)) return 1; + /* an aux event parents the ephemeral routes its owners generate */ + if (WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) != 0) + return 1; + /* beyond the table's own reference, each live route, deleted or not, holds one */ + if (WOLFSENTRY_ATOMIC_LOAD(event->header.refcount) > 1) + return 1; for (ent = wolfsentry_table_first(&wolfsentry->routes->header); ent != NULL; ent = ent->next) { if (((const struct wolfsentry_route *)ent)->parent_event == event) return 1; diff --git a/tests/unittests.c b/tests/unittests.c index eb09cc2..da273ae 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4822,6 +4822,196 @@ static int test_action_insert_failure_leaves_id_untouched(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_aux_event_layout_pinned(void) { + struct wolfsentry_context *wolfsentry; + 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 }; + static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 }; +#else + static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 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_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_aux_owner", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + NULL /* config */, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + &config, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_aux_owner", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "layout_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config2)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_set_aux_event( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_aux_owner", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + NULL /* aux_event_label */, + 0)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_aux", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config2)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + + WOLFSENTRY_RETURN_OK; +} + +static int test_event_layout_guard_held_deleted_route(void) { + struct wolfsentry_context *wolfsentry; + wolfsentry_ent_id_t id; + wolfsentry_action_res_t action_results; + struct wolfsentry_route *route = NULL; + wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } remote, local; +#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 }; + static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 }; +#else + static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 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_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_insert_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action_insert_noop, + NULL /* handler_arg */, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_held_parent", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 10, + &config, + WOLFSENTRY_EVENT_FLAG_NONE, + &id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_held_parent", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_TYPE_INSERT, + "layout_insert_action", + WOLFSENTRY_LENGTH_NULL_TERMINATED)); + + remote.sa.sa_family = local.sa.sa_family = AF_INET; + remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP; + remote.sa.sa_port = 12345; + 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; + memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf); + memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf); + + WOLFSENTRY_CLEAR_ALL_BITS(action_results); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert_and_check_out( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + &remote.sa, + &local.sa, + flags, + "layout_held_parent", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &route, + &action_results)); + + WOLFSENTRY_CLEAR_ALL_BITS(action_results); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_delete_by_id( + WOLFSENTRY_CONTEXT_ARGS_OUT, + NULL /* caller_arg */, + route->header.id, + NULL /* trigger_label */, + 0, + &action_results)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + INCOMPATIBLE_STATE, + wolfsentry_event_update_config( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "layout_held_parent", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + &config2)); + + WOLFSENTRY_CLEAR_ALL_BITS(action_results); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_drop_reference( + WOLFSENTRY_CONTEXT_ARGS_OUT, + route, + &action_results)); + + 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 @@ -7176,6 +7366,18 @@ int main(int argc, char* argv[]) { (void)fprintf(stderr, "test_action_insert_failure_leaves_id_untouched failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); err = 1; } + + ret = test_event_aux_event_layout_pinned(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_aux_event_layout_pinned failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } + + ret = test_event_layout_guard_held_deleted_route(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_layout_guard_held_deleted_route failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } #endif #ifdef TEST_USER_VALUES diff --git a/wolfsentry/wolfsentry.h b/wolfsentry/wolfsentry.h index ad0e30d..612a771 100644 --- a/wolfsentry/wolfsentry.h +++ b/wolfsentry/wolfsentry.h @@ -2801,7 +2801,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_config( \param label_len the length of the label \param config the updated configuration - \return WOLFSENTRY_IS_SUCCESS(ret) is true on success. Once any route has used the event as its parent, changes to `route_private_data_size` or `route_private_data_alignment` are rejected with `INCOMPATIBLE_STATE`, even after that route is deleted, because a deleted route can still be held by a checked-out reference. + \return WOLFSENTRY_IS_SUCCESS(ret) is true on success. Once any route has used the event as its parent, or while the event is another event's aux event or is otherwise referenced, changes to `route_private_data_size` or `route_private_data_alignment` are rejected with `INCOMPATIBLE_STATE`, even after that route is deleted, because a deleted route can still be held by a checked-out reference. \sa WOLFSENTRY_CONTEXT_ARGS_IN */ From f6aa0560ad618e822784c5cd207c2a5d157e5275 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 23 Oct 2026 18:23:07 -0700 Subject: [PATCH 14/14] F-5566 - https://fenrir.wolfssl.com/finding/5566 - Derive the reported subevent mark from the aux owner count --- src/events.c | 12 +++++++++++- tests/unittests.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/events.c b/src/events.c index 435a7a2..1c4c276 100644 --- a/src/events.c +++ b/src/events.c @@ -345,7 +345,17 @@ WOLFSENTRY_API const char *wolfsentry_event_get_label(const struct wolfsentry_ev WOLFSENTRY_API wolfsentry_event_flags_t wolfsentry_event_get_flags(const struct wolfsentry_event *event) { - return event ? event->flags : WOLFSENTRY_EVENT_FLAG_NONE; + wolfsentry_event_flags_t flags; + + if (event == NULL) + return WOLFSENTRY_EVENT_FLAG_NONE; + /* an owner freed without the context lock only drops the count, so derive the mark from it */ + flags = event->flags; + if (WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) > 0) + WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT); + else + WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT); + return flags; } WOLFSENTRY_API const struct wolfsentry_event *wolfsentry_event_get_aux_event(const struct wolfsentry_event *event) diff --git a/tests/unittests.c b/tests/unittests.c index da273ae..6d707c3 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -4436,6 +4436,37 @@ static int test_event_aux_event_reuse_after_clear(void) { WOLFSENTRY_RETURN_OK; } +static int test_event_aux_mark_cleared_on_owner_free(void) { + struct wolfsentry_context *wolfsentry; + struct wolfsentry_event *aux; + wolfsentry_ent_id_t id; + + WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + NULL /* default_config */, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_owner", WOLFSENTRY_LENGTH_NULL_TERMINATED, 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_aux", WOLFSENTRY_LENGTH_NULL_TERMINATED, 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_set_aux_event(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_owner", WOLFSENTRY_LENGTH_NULL_TERMINATED, "mark_aux", WOLFSENTRY_LENGTH_NULL_TERMINATED)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_aux", WOLFSENTRY_LENGTH_NULL_TERMINATED, &aux)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(wolfsentry_event_get_flags(aux), WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)); + + /* freeing the only owner leaves the aux event with no owners */ + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_owner", WOLFSENTRY_LENGTH_NULL_TERMINATED, NULL /* action_results */)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(wolfsentry_event_get_flags(aux), WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux, NULL /* action_results */)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); + WOLFSENTRY_RETURN_OK; +} + static int test_event_aux_event_rejects_subevent_owner(void) { struct wolfsentry_context *wolfsentry; wolfsentry_ent_id_t id; @@ -7335,6 +7366,12 @@ int main(int argc, char* argv[]) { err = 1; } + ret = test_event_aux_mark_cleared_on_owner_free(); + if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { + (void)fprintf(stderr, "test_event_aux_mark_cleared_on_owner_free failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + err = 1; + } + ret = test_event_aux_event_rejects_subevent_owner(); if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) { (void)fprintf(stderr, "test_event_aux_event_rejects_subevent_owner failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));