From 246d959d9fa7d67fca1ee41217b24baaef5caeb1 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Sun, 22 Mar 2026 22:38:28 -0700 Subject: [PATCH 1/6] Fix memory leaks in test_http_hdr_print_and_copy_aux Add missing HTTPHdr::destroy() calls for hdr and new_hdr on early return paths. marshal_hdr is intentionally not destroyed because it holds a reference to a stack-allocated TestRefCountObj. --- src/proxy/hdrs/unit_tests/test_Hdrs.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/proxy/hdrs/unit_tests/test_Hdrs.cc b/src/proxy/hdrs/unit_tests/test_Hdrs.cc index fef0166d499..5aec8b7bbd9 100644 --- a/src/proxy/hdrs/unit_tests/test_Hdrs.cc +++ b/src/proxy/hdrs/unit_tests/test_Hdrs.cc @@ -207,6 +207,7 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon if (err == ParseResult::ERROR) { std::printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum); + req_hdr.destroy(); return (0); } http_parser_clear(&parser); @@ -229,6 +230,8 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon if (err == ParseResult::ERROR) { printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum); + req_hdr.destroy(); + resp_hdr.destroy(); return (0); } @@ -412,6 +415,7 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if (err == ParseResult::ERROR) { std::printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum); + hdr.destroy(); return (0); } @@ -438,6 +442,9 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if ((prt_ret != 1) || (cpy_ret != 1)) { std::printf("FAILED: (test #%d) couldn't print req hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret); + hdr.destroy(); + new_hdr.destroy(); + return (0); } @@ -449,6 +456,9 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(request_tgt)), request_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); + hdr.destroy(); + new_hdr.destroy(); + return (0); } @@ -459,6 +469,9 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(request_tgt)), request_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); + hdr.destroy(); + new_hdr.destroy(); + return (0); } @@ -483,6 +496,7 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if (err == ParseResult::ERROR) { std::printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum); + hdr.destroy(); return (0); } @@ -501,6 +515,8 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if ((prt_ret != 1) || (cpy_ret != 1)) { std::printf("FAILED: (test #%d) couldn't print rsp hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret); + hdr.destroy(); + new_hdr.destroy(); return (0); } @@ -511,6 +527,8 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(response_tgt)), response_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); + hdr.destroy(); + new_hdr.destroy(); return (0); } @@ -521,6 +539,8 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(response_tgt)), response_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); + hdr.destroy(); + new_hdr.destroy(); return (0); } From ff8179083c111bc51ea48e4ea7e60f416e6e9f1f Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Sun, 22 Mar 2026 22:38:34 -0700 Subject: [PATCH 2/6] Remove stale entries from regression LSAN suppression file Remove 5 entries for code that no longer exists: - RegressionTest_SDK_API_TSHttpConnectIntercept (removed) - RegressionTest_SDK_API_TSHttpConnectServerIntercept (removed) - make_log_host (removed with Lua support) - RegressionTest_Cache_vol (converted to Catch2) - RegressionTest_Hdrs (converted to Catch2) --- ci/asan_leak_suppression/regression.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ci/asan_leak_suppression/regression.txt b/ci/asan_leak_suppression/regression.txt index b3dfcf83631..81b35af1b08 100644 --- a/ci/asan_leak_suppression/regression.txt +++ b/ci/asan_leak_suppression/regression.txt @@ -1,8 +1,5 @@ leak:RegressionTest_PARENTSELECTION leak:ParentConfig::reconfigure -leak:RegressionTest_SDK_API_TSHttpConnectIntercept -leak:RegressionTest_SDK_API_TSHttpConnectServerIntercept -leak:make_log_host leak:ReRegressionSM::clone leak:RegressionTest_ram_cache leak:RegressionTest_HttpTransact_is_request_valid @@ -11,9 +8,7 @@ leak:MakeTextLogFormat leak:RegressionTest_HttpTransact_handle_trace_and_options_requests leak:CRYPTO_malloc leak:RegressionTest_SDK_API_TSMgmtGet -leak:RegressionTest_Cache_vol leak:RegressionTest_SDK_API_TSCache -leak:RegressionTest_Hdrs leak:RegressionTest_SDK_API_TSPortDescriptor leak:RegressionTest_HostDBProcessor leak:RegressionTest_DNS From de60a8e229fd88b6986f807d2ca3a9208dedc7d3 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Sun, 22 Mar 2026 22:38:41 -0700 Subject: [PATCH 3/6] Remove obsolete entries from unit test LSAN suppression file Remove 5 entries for obsolete libraries: - libcrypto.so.1.1: OpenSSL 1.1 specific, test cleanup fixed - CRYPTO_malloc/CRYPTO_realloc: OpenSSL 1.0.2 (minimum is 1.1.1) - ConsCell: symbol no longer exists in codebase - pcre_jit_stack_alloc: PCRE replaced with PCRE2 Keep test_http_hdr_print_and_copy_aux suppression with updated comment explaining why marshal_hdr is intentionally not destroyed. --- ci/asan_leak_suppression/unit_tests.txt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ci/asan_leak_suppression/unit_tests.txt b/ci/asan_leak_suppression/unit_tests.txt index da34e83c615..a553604f1bf 100644 --- a/ci/asan_leak_suppression/unit_tests.txt +++ b/ci/asan_leak_suppression/unit_tests.txt @@ -1,10 +1,4 @@ -# leaks in test_X509HostnameValidator -leak:libcrypto.so.1.1 -# for OpenSSL 1.0.2: -leak:CRYPTO_malloc -leak:CRYPTO_realloc -leak:ConsCell -# PR#10295 -leak:pcre_jit_stack_alloc -# PR#10541 +# marshal_hdr in test_http_hdr_print_and_copy_aux is intentionally +# not destroyed because it holds a reference to a stack-allocated +# TestRefCountObj whose free() override calls exit(1). leak:test_http_hdr_print_and_copy_aux From b7ed6d37d05dfe4fcab320d78bab094e63196739 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Thu, 26 Mar 2026 22:15:54 -0700 Subject: [PATCH 4/6] Restore TSHttpConnect suppression entries (still leaking in generate_request) --- ci/asan_leak_suppression/regression.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/asan_leak_suppression/regression.txt b/ci/asan_leak_suppression/regression.txt index 81b35af1b08..3bcd60f553a 100644 --- a/ci/asan_leak_suppression/regression.txt +++ b/ci/asan_leak_suppression/regression.txt @@ -1,5 +1,7 @@ leak:RegressionTest_PARENTSELECTION leak:ParentConfig::reconfigure +leak:RegressionTest_SDK_API_TSHttpConnectIntercept +leak:RegressionTest_SDK_API_TSHttpConnectServerIntercept leak:ReRegressionSM::clone leak:RegressionTest_ram_cache leak:RegressionTest_HttpTransact_is_request_valid From 587787e055c6a64fc733f0496e5d08dbc6ae05ae Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Mon, 30 Mar 2026 14:44:14 -0700 Subject: [PATCH 5/6] Use ts::PostScript for HTTPHdr cleanup in test helpers Replace manual destroy() calls on every error path with ts::PostScript scope guards. HdrHeapSDKHandle::destroy() is idempotent (checks m_heap before acting), so the mid-function destroy/re-create cycle in test_http_hdr_print_and_copy_aux is safe alongside the guard. --- src/proxy/hdrs/unit_tests/test_Hdrs.cc | 46 ++++++++------------------ 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/src/proxy/hdrs/unit_tests/test_Hdrs.cc b/src/proxy/hdrs/unit_tests/test_Hdrs.cc index 5aec8b7bbd9..4221299afd7 100644 --- a/src/proxy/hdrs/unit_tests/test_Hdrs.cc +++ b/src/proxy/hdrs/unit_tests/test_Hdrs.cc @@ -183,6 +183,13 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon HTTPHdr copy1; HTTPHdr copy2; + ts::PostScript cleanup([&]() -> void { + req_hdr.destroy(); + resp_hdr.destroy(); + copy1.destroy(); + copy2.destroy(); + }); + HTTPParser parser; const char *start; const char *end; @@ -207,7 +214,6 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon if (err == ParseResult::ERROR) { std::printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum); - req_hdr.destroy(); return (0); } http_parser_clear(&parser); @@ -230,8 +236,6 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon if (err == ParseResult::ERROR) { printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum); - req_hdr.destroy(); - resp_hdr.destroy(); return (0); } @@ -263,16 +267,8 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon copy2.copy(&req_hdr); comp_str = comp_http_hdr(&req_hdr, ©2); - if (comp_str) { - goto done; - } done: - req_hdr.destroy(); - resp_hdr.destroy(); - copy1.destroy(); - copy2.destroy(); - if (comp_str) { printf("FAILED: (test #%d) copy & compare: %s\n", testnum, comp_str); printf("REQ:\n[%.*s]\n", static_cast(strlen(request)), request); @@ -382,10 +378,16 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r { ParseResult err; HTTPHdr hdr; + HTTPHdr new_hdr; HTTPParser parser; const char *start; const char *end; + ts::PostScript cleanup([&]() -> void { + hdr.destroy(); + new_hdr.destroy(); + }); + char prt_buf[2048]; int prt_bufsize = sizeof(prt_buf); int prt_bufindex, prt_dumpoffset, prt_ret; @@ -415,12 +417,11 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if (err == ParseResult::ERROR) { std::printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum); - hdr.destroy(); return (0); } /*** (2) copy the request header ***/ - HTTPHdr new_hdr, marshal_hdr; + HTTPHdr marshal_hdr; TestRefCountObj ref; // Pretend to pin this object with a refcount. @@ -442,9 +443,6 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if ((prt_ret != 1) || (cpy_ret != 1)) { std::printf("FAILED: (test #%d) couldn't print req hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret); - hdr.destroy(); - new_hdr.destroy(); - return (0); } @@ -456,9 +454,6 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(request_tgt)), request_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); - hdr.destroy(); - new_hdr.destroy(); - return (0); } @@ -469,9 +464,6 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(request_tgt)), request_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); - hdr.destroy(); - new_hdr.destroy(); - return (0); } @@ -496,7 +488,6 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if (err == ParseResult::ERROR) { std::printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum); - hdr.destroy(); return (0); } @@ -515,8 +506,6 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r if ((prt_ret != 1) || (cpy_ret != 1)) { std::printf("FAILED: (test #%d) couldn't print rsp hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret); - hdr.destroy(); - new_hdr.destroy(); return (0); } @@ -527,8 +516,6 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(response_tgt)), response_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); - hdr.destroy(); - new_hdr.destroy(); return (0); } @@ -539,14 +526,9 @@ test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *r std::printf("TARGET :\n[%.*s]\n", static_cast(strlen(response_tgt)), response_tgt); std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf); std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf); - hdr.destroy(); - new_hdr.destroy(); return (0); } - hdr.destroy(); - new_hdr.destroy(); - if (test_http_hdr_copy_over_aux(testnum, request, response) == 0) { return 0; } From fab42dc6cc51b05082a82b8bd222c7592c0efdb3 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Mon, 30 Mar 2026 14:47:57 -0700 Subject: [PATCH 6/6] Remove goto/done pattern from test_http_hdr_copy_over_aux Now that PostScript handles cleanup, replace goto with if-not-error chaining for the comparison steps. --- src/proxy/hdrs/unit_tests/test_Hdrs.cc | 30 +++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/proxy/hdrs/unit_tests/test_Hdrs.cc b/src/proxy/hdrs/unit_tests/test_Hdrs.cc index 4221299afd7..598e4e61894 100644 --- a/src/proxy/hdrs/unit_tests/test_Hdrs.cc +++ b/src/proxy/hdrs/unit_tests/test_Hdrs.cc @@ -243,15 +243,11 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon copy1.create(HTTPType::REQUEST); copy1.copy(&req_hdr); comp_str = comp_http_hdr(&req_hdr, ©1); - if (comp_str) { - goto done; - } - copy2.create(HTTPType::RESPONSE); - copy2.copy(&resp_hdr); - comp_str = comp_http_hdr(&resp_hdr, ©2); - if (comp_str) { - goto done; + if (!comp_str) { + copy2.create(HTTPType::RESPONSE); + copy2.copy(&resp_hdr); + comp_str = comp_http_hdr(&resp_hdr, ©2); } // The APIs for copying headers uses memcpy() which can be unsafe for @@ -259,24 +255,24 @@ test_http_hdr_copy_over_aux(int testnum, const char *request, const char *respon // created in the first place honestly, since nothing else does this. /*** (4) Gender bending copying ***/ - copy1.copy(&resp_hdr); - comp_str = comp_http_hdr(&resp_hdr, ©1); - if (comp_str) { - goto done; + if (!comp_str) { + copy1.copy(&resp_hdr); + comp_str = comp_http_hdr(&resp_hdr, ©1); } - copy2.copy(&req_hdr); - comp_str = comp_http_hdr(&req_hdr, ©2); + if (!comp_str) { + copy2.copy(&req_hdr); + comp_str = comp_http_hdr(&req_hdr, ©2); + } -done: if (comp_str) { printf("FAILED: (test #%d) copy & compare: %s\n", testnum, comp_str); printf("REQ:\n[%.*s]\n", static_cast(strlen(request)), request); printf("RESP :\n[%.*s]\n", static_cast(strlen(response)), response); return (0); - } else { - return (1); } + + return (1); } int