From e913d782fe622d89e5b7b92f007e411e923fa945 Mon Sep 17 00:00:00 2001 From: Hiroshi Hatake Date: Fri, 24 Jul 2026 16:54:19 +0900 Subject: [PATCH 1/3] tests: runtime: out_chronicle: Prevent resource races on checking results Signed-off-by: Hiroshi Hatake --- tests/runtime/out_chronicle.c | 255 ++++++++++++++++++++++------------ 1 file changed, 168 insertions(+), 87 deletions(-) diff --git a/tests/runtime/out_chronicle.c b/tests/runtime/out_chronicle.c index 22eeb616ca8..6443d3182c9 100644 --- a/tests/runtime/out_chronicle.c +++ b/tests/runtime/out_chronicle.c @@ -6,26 +6,75 @@ pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER; int num_invoked = 0; +static const char *callback_error = NULL; + static int get_output_invoked() { int ret; + + pthread_mutex_lock(&result_mutex); + ret = num_invoked; + pthread_mutex_unlock(&result_mutex); + + return ret; +} + +static int increment_output_invoked() +{ + int ret; + pthread_mutex_lock(&result_mutex); + num_invoked++; ret = num_invoked; pthread_mutex_unlock(&result_mutex); return ret; } -static void set_output_invoked(int num) +static void set_callback_error(const char *message) { pthread_mutex_lock(&result_mutex); - num_invoked = num; + if (callback_error == NULL) { + callback_error = message; + } pthread_mutex_unlock(&result_mutex); } static void clear_output_invoked() { - set_output_invoked(0); + pthread_mutex_lock(&result_mutex); + num_invoked = 0; + callback_error = NULL; + pthread_mutex_unlock(&result_mutex); +} + +static void check_callback_error() +{ + const char *message; + + pthread_mutex_lock(&result_mutex); + message = callback_error; + pthread_mutex_unlock(&result_mutex); + + if (!TEST_CHECK(message == NULL)) { + TEST_MSG("%s", message); + } +} + +static void stop_and_check(flb_ctx_t *ctx, int expected_invocations) +{ + int invocations; + + flb_stop(ctx); + + invocations = get_output_invoked(); + if (!TEST_CHECK(invocations == expected_invocations)) { + TEST_MSG("got %d formatter callbacks, expected %d", + invocations, expected_invocations); + } + check_callback_error(); + + flb_destroy(ctx); } static void cb_check_format_no_log_key(void *ctx, int ffd, @@ -35,33 +84,38 @@ static void cb_check_format_no_log_key(void *ctx, int ffd, char *out_json = res_data; char *p; - set_output_invoked(1); + if (res_ret != 0 || out_json == NULL) { + set_callback_error("formatter returned an error or no output"); + flb_sds_destroy(res_data); + return; + } p = strstr(out_json, "\"customer_id\":\"test-customer\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected customer_id not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected customer_id was not found"); } p = strstr(out_json, "\"log_type\":\"TEST_LOG\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected log_type not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected log_type was not found"); } p = strstr(out_json, "\"entries\":["); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Entries array not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("entries array was not found"); } p = strstr(out_json, "\"log_text\":\"{\\\"message\\\":\\\"hello world\\\"}\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected log_text not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected log_text was not found"); } p = strstr(out_json, "\"ts_rfc3339\":"); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected ts_rfc3339 key not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected ts_rfc3339 key was not found"); } + increment_output_invoked(); flb_sds_destroy(res_data); } @@ -76,16 +130,21 @@ static void cb_check_format_with_log_key(void *ctx, int ffd, return; } - set_output_invoked(1); + if (res_ret != 0) { + set_callback_error("formatter returned an error"); + } p = strstr(out_json, "\"log_text\":\"This is the target message.\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected log_text with specific value not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected log_text value was not found"); } p = strstr(out_json, "other_key"); - TEST_CHECK(p == NULL); + if (p != NULL) { + set_callback_error("unexpected other_key was found"); + } + increment_output_invoked(); flb_sds_destroy(res_data); } @@ -96,18 +155,23 @@ static void cb_check_format_multiple_records(void *ctx, int ffd, char *out_json = res_data; char *p1, *p2; - set_output_invoked(1); + if (res_ret != 0 || out_json == NULL) { + set_callback_error("formatter returned an error or no output"); + flb_sds_destroy(res_data); + return; + } p1 = strstr(out_json, "\"log_text\":\"{\\\"message\\\":\\\"record one\\\"}\""); - if (!TEST_CHECK(p1 != NULL)) { - TEST_MSG("First record not found. Got: %s", out_json); + if (p1 == NULL) { + set_callback_error("first record was not found"); } p2 = strstr(out_json, "\"log_text\":\"{\\\"message\\\":\\\"record two\\\"}\""); - if (!TEST_CHECK(p2 != NULL)) { - TEST_MSG("Second record not found. Got: %s", out_json); + if (p2 == NULL) { + set_callback_error("second record was not found"); } + increment_output_invoked(); flb_sds_destroy(res_data); } @@ -118,16 +182,23 @@ static void cb_check_format_partially_succeeded_records(void *ctx, int ffd, char *out_json = res_data; char *p1, *p2; - set_output_invoked(1); + if (res_ret != 0 || out_json == NULL) { + set_callback_error("formatter returned an error or no output"); + flb_sds_destroy(res_data); + return; + } p1 = strstr(out_json, "\"log_text\":\"record one\""); - if (!TEST_CHECK(p1 != NULL)) { - TEST_MSG("Expected log_text with specific value not found. Got: %s", out_json); + if (p1 == NULL) { + set_callback_error("expected log_text value was not found"); } p2 = strstr(out_json, "\"test\""); - TEST_CHECK(p2 == NULL); + if (p2 != NULL) { + set_callback_error("unexpected test field was found"); + } + increment_output_invoked(); flb_sds_destroy(res_data); } @@ -138,38 +209,43 @@ static void cb_check_format_namespace_and_labels(void *ctx, int ffd, char *out_json = res_data; char *p; - set_output_invoked(1); + if (res_ret != 0 || out_json == NULL) { + set_callback_error("formatter returned an error or no output"); + flb_sds_destroy(res_data); + return; + } p = strstr(out_json, "\"namespace\":\"tenant-a\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected namespace not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected namespace was not found"); } p = strstr(out_json, "\"labels\":["); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected labels array not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected labels array was not found"); } p = strstr(out_json, "\"key\":\"env\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected static label key not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected static label key was not found"); } p = strstr(out_json, "\"value\":\"production\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected static label value not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected static label value was not found"); } p = strstr(out_json, "\"key\":\"cluster_name\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected dynamic label key not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected dynamic label key was not found"); } p = strstr(out_json, "\"value\":\"blue\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected dynamic label value not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected dynamic label value was not found"); } + increment_output_invoked(); flb_sds_destroy(res_data); } @@ -180,21 +256,28 @@ static void cb_check_format_namespace_fallback_and_missing_label(void *ctx, int char *out_json = res_data; char *p; - set_output_invoked(1); + if (res_ret != 0 || out_json == NULL) { + set_callback_error("formatter returned an error or no output"); + flb_sds_destroy(res_data); + return; + } p = strstr(out_json, "\"namespace\":\"fallback-namespace\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected fallback namespace not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected fallback namespace was not found"); } p = strstr(out_json, "\"key\":\"missing\""); - TEST_CHECK(p == NULL); + if (p != NULL) { + set_callback_error("unexpected missing label was found"); + } p = strstr(out_json, "\"log_text\":\"{\\\"message\\\":\\\"hello world\\\"}\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected log_text not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected log_text was not found"); } + increment_output_invoked(); flb_sds_destroy(res_data); } @@ -206,50 +289,64 @@ static void cb_check_format_split_on_metadata_change(void *ctx, int ffd, char *p; int invocation; - invocation = get_output_invoked() + 1; - set_output_invoked(invocation); + if (res_ret != 0 || out_json == NULL) { + set_callback_error("formatter returned an error or no output"); + flb_sds_destroy(res_data); + return; + } + + invocation = increment_output_invoked(); if (invocation == 1) { p = strstr(out_json, "\"namespace\":\"tenant-a\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected first namespace not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected first namespace was not found"); } p = strstr(out_json, "\"value\":\"blue\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected first dynamic label value not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected first dynamic label value was not found"); } p = strstr(out_json, "\"log_text\":\"{\\\"message\\\":\\\"record one\\\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected first record not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected first record was not found"); } p = strstr(out_json, "tenant-b"); - TEST_CHECK(p == NULL); + if (p != NULL) { + set_callback_error("unexpected second namespace was found"); + } p = strstr(out_json, "green"); - TEST_CHECK(p == NULL); + if (p != NULL) { + set_callback_error("unexpected second dynamic label value was found"); + } p = strstr(out_json, "record two"); - TEST_CHECK(p == NULL); + if (p != NULL) { + set_callback_error("unexpected second record was found"); + } } else if (invocation == 2) { p = strstr(out_json, "\"namespace\":\"tenant-b\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected second namespace not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected second namespace was not found"); } p = strstr(out_json, "\"value\":\"green\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected second dynamic label value not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected second dynamic label value was not found"); } p = strstr(out_json, "\"log_text\":\"{\\\"message\\\":\\\"record two\\\""); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG("Expected second record not found. Got: %s", out_json); + if (p == NULL) { + set_callback_error("expected second record was not found"); } } + else { + set_callback_error("formatter was invoked more than twice"); + } flb_sds_destroy(res_data); } @@ -284,9 +381,7 @@ void test_format_no_log_key() sleep(1); - TEST_CHECK(get_output_invoked() == 1); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 1); } void test_format_with_log_key_found() @@ -322,9 +417,7 @@ void test_format_with_log_key_found() sleep(1); - TEST_CHECK(get_output_invoked() == 1); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 1); } void test_format_with_log_key_not_found() @@ -358,9 +451,7 @@ void test_format_with_log_key_not_found() sleep(1); - TEST_CHECK(get_output_invoked() == 0); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 0); } @@ -399,9 +490,7 @@ void test_format_multiple_records() sleep(1); - TEST_CHECK(get_output_invoked() == 1); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 1); } void test_format_partially_suceeded_records() @@ -440,9 +529,7 @@ void test_format_partially_suceeded_records() sleep(1); - TEST_CHECK(get_output_invoked() == 1); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 1); } void test_format_namespace_and_labels() @@ -483,9 +570,7 @@ void test_format_namespace_and_labels() sleep(1); - TEST_CHECK(get_output_invoked() == 1); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 1); } void test_format_namespace_fallback_and_missing_label() @@ -524,9 +609,7 @@ void test_format_namespace_fallback_and_missing_label() sleep(1); - TEST_CHECK(get_output_invoked() == 1); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 1); } void test_format_split_on_metadata_change() @@ -573,9 +656,7 @@ void test_format_split_on_metadata_change() sleep(1); - TEST_CHECK(get_output_invoked() == 1); - flb_stop(ctx); - flb_destroy(ctx); + stop_and_check(ctx, 1); } From 49be100e0397b40e16012a5e4c9fd80f9c495a1f Mon Sep 17 00:00:00 2001 From: Hiroshi Hatake Date: Fri, 24 Jul 2026 16:56:52 +0900 Subject: [PATCH 2/3] tests: runtime: out_http: Prevent resource races on checking results Signed-off-by: Hiroshi Hatake --- tests/runtime/out_http.c | 99 ++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/tests/runtime/out_http.c b/tests/runtime/out_http.c index 5a17861d979..70cfb835aaa 100644 --- a/tests/runtime/out_http.c +++ b/tests/runtime/out_http.c @@ -36,9 +36,12 @@ struct test_ctx { pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER; int num_output = 0; +static const char *callback_error = NULL; + static int get_output_num() { int ret; + pthread_mutex_lock(&result_mutex); ret = num_output; pthread_mutex_unlock(&result_mutex); @@ -46,16 +49,48 @@ static int get_output_num() return ret; } -static void set_output_num(int num) +static void increment_output_num() +{ + pthread_mutex_lock(&result_mutex); + num_output++; + pthread_mutex_unlock(&result_mutex); +} + +static void add_output_num(int num) +{ + pthread_mutex_lock(&result_mutex); + num_output += num; + pthread_mutex_unlock(&result_mutex); +} + +static void set_callback_error(const char *message) { pthread_mutex_lock(&result_mutex); - num_output = num; + if (callback_error == NULL) { + callback_error = message; + } pthread_mutex_unlock(&result_mutex); } static void clear_output_num() { - set_output_num(0); + pthread_mutex_lock(&result_mutex); + num_output = 0; + callback_error = NULL; + pthread_mutex_unlock(&result_mutex); +} + +static void check_callback_error() +{ + const char *message; + + pthread_mutex_lock(&result_mutex); + message = callback_error; + pthread_mutex_unlock(&result_mutex); + + if (!TEST_CHECK(message == NULL)) { + TEST_MSG("%s", message); + } } struct str_list { @@ -69,38 +104,32 @@ static void cb_check_str_list(void *ctx, int ffd, int res_ret, { char *p; flb_sds_t out_line = res_data; - int num = get_output_num(); size_t i; struct str_list *l = (struct str_list *)data; - if (!TEST_CHECK(res_data != NULL)) { - TEST_MSG("res_data is NULL"); + if (res_data == NULL) { + set_callback_error("formatter returned no output"); return; } - if (!TEST_CHECK(l != NULL)) { - TEST_MSG("l is NULL"); + if (l == NULL) { + set_callback_error("formatter callback data is NULL"); flb_sds_destroy(out_line); return; } - if(!TEST_CHECK(res_ret == 0)) { - TEST_MSG("callback ret=%d", res_ret); - } - if (!TEST_CHECK(res_data != NULL)) { - TEST_MSG("res_data is NULL"); - flb_sds_destroy(out_line); - return; + if (res_ret != 0) { + set_callback_error("formatter returned an error"); } - for (i=0; isize; i++) { + for (i = 0; i < l->size; i++) { p = strstr(out_line, l->lists[i]); - if (!TEST_CHECK(p != NULL)) { - TEST_MSG(" Got :%s\n expect:%s", out_line, l->lists[i]); + if (p == NULL) { + set_callback_error("formatter output did not contain an expected string"); } } - set_output_num(num+1); + increment_output_num(); flb_sds_destroy(out_line); } @@ -177,18 +206,22 @@ static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret, int i_map; int map_size; int i_list; - int num = get_output_num(); + int matches = 0; - if (!TEST_CHECK(res_data != NULL)) { - TEST_MSG("res_data is NULL"); + if (res_data == NULL) { + set_callback_error("formatter returned no output"); return; } - if (!TEST_CHECK(data != NULL)) { - flb_error("data is NULL"); + if (data == NULL) { + set_callback_error("formatter callback data is NULL"); return; } + if (res_ret != 0) { + set_callback_error("formatter returned an error"); + } + /* Iterate each item array and apply rules */ msgpack_unpacked_init(&result); while (msgpack_unpack_next(&result, res_data, res_size, &off) == MSGPACK_UNPACK_SUCCESS) { @@ -197,18 +230,18 @@ static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret, msgpack_object_print(stdout, obj); */ if (obj.type != MSGPACK_OBJECT_ARRAY || obj.via.array.size != 2) { - flb_error("array error. type = %d", obj.type); + set_callback_error("formatter output contained an invalid record"); continue; } obj = obj.via.array.ptr[1]; if (obj.type != MSGPACK_OBJECT_MAP) { - flb_error("map error. type = %d", obj.type); + set_callback_error("formatter output record was not a map"); continue; } map_size = obj.via.map.size; for (i_map=0; i_mapsize/2; i_list++) { @@ -216,16 +249,14 @@ static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret, obj.via.map.ptr[i_map].key) == 0 && msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]), obj.via.map.ptr[i_map].val) == 0) { - num++; + matches++; } } } } - set_output_num(num); + add_output_num(matches); msgpack_unpacked_destroy(&result); - - return ; } static struct test_ctx *test_ctx_create() @@ -267,6 +298,7 @@ static void test_ctx_destroy(struct test_ctx *ctx) sleep(1); flb_stop(ctx->flb); + check_callback_error(); flb_destroy(ctx->flb); flb_free(ctx); } @@ -1047,11 +1079,8 @@ void flb_test_json_date_format_java_sql_timestamp() int callback_test(void* data, size_t size, void* cb_data) { - int num; - if (size > 0) { - num = get_output_num(); - set_output_num(num+1); + increment_output_num(); } return 0; } From 3cefa0960adefc52a258d036ca85be6ea5b00527 Mon Sep 17 00:00:00 2001 From: Hiroshi Hatake Date: Fri, 24 Jul 2026 17:30:17 +0900 Subject: [PATCH 3/3] tests: runtime: Show errors on unpacking failures Signed-off-by: Hiroshi Hatake --- tests/runtime/out_http.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/runtime/out_http.c b/tests/runtime/out_http.c index 70cfb835aaa..8a8c2d592e8 100644 --- a/tests/runtime/out_http.c +++ b/tests/runtime/out_http.c @@ -207,6 +207,7 @@ static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret, int map_size; int i_list; int matches = 0; + msgpack_unpack_return unpack_result = MSGPACK_UNPACK_CONTINUE; if (res_data == NULL) { set_callback_error("formatter returned no output"); @@ -224,7 +225,12 @@ static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret, /* Iterate each item array and apply rules */ msgpack_unpacked_init(&result); - while (msgpack_unpack_next(&result, res_data, res_size, &off) == MSGPACK_UNPACK_SUCCESS) { + while (off < res_size) { + unpack_result = msgpack_unpack_next(&result, res_data, res_size, &off); + if (unpack_result != MSGPACK_UNPACK_SUCCESS) { + break; + } + obj = result.data; /* msgpack_object_print(stdout, obj); @@ -255,7 +261,23 @@ static void cb_check_msgpack_kv(void *ctx, int ffd, int res_ret, } } - add_output_num(matches); + if (unpack_result == MSGPACK_UNPACK_PARSE_ERROR) { + set_callback_error("formatter output contained invalid MessagePack"); + } + else if (unpack_result == MSGPACK_UNPACK_NOMEM_ERROR) { + set_callback_error("could not unpack formatter output"); + } + else if (unpack_result == MSGPACK_UNPACK_CONTINUE && res_size != 0) { + set_callback_error("formatter output contained incomplete MessagePack"); + } + else if (unpack_result != MSGPACK_UNPACK_SUCCESS && + unpack_result != MSGPACK_UNPACK_CONTINUE) { + set_callback_error("formatter output returned an unexpected unpack result"); + } + else { + add_output_num(matches); + } + msgpack_unpacked_destroy(&result); }