diff --git a/src/api/InkAPITest.cc b/src/api/InkAPITest.cc index 3ba3d903a96..7900878c543 100644 --- a/src/api/InkAPITest.cc +++ b/src/api/InkAPITest.cc @@ -7271,9 +7271,16 @@ struct ParentTest { ~ParentTest() { - synclient_txn_close(this->browser); - synclient_txn_delete(this->browser); - synserver_delete(this->os); + // A destructor is implicitly noexcept, so anything escaping the teardown + // calls would terminate the process without saying where it came from. + try { + synclient_txn_close(this->browser); + synclient_txn_delete(this->browser); + synserver_delete(this->os); + } catch (...) { + ink_abort("ParentTest teardown threw an exception"); + } + this->os = nullptr; this->magic = MAGIC_DEAD; } diff --git a/src/iocore/aio/test_AIO.cc b/src/iocore/aio/test_AIO.cc index 3ae808bbd17..fd7240d5916 100644 --- a/src/iocore/aio/test_AIO.cc +++ b/src/iocore/aio/test_AIO.cc @@ -29,6 +29,7 @@ #include "tscore/Layout.h" #include "tscore/TSSystemState.h" #include "tscore/Random.h" +#include #include #include #include @@ -452,9 +453,8 @@ class IOUringLoopTailHandler : public EThread::LoopTailHandler #endif -// coverity[exn_spec_violation] - called functions may throw but this is a test program -int -main(int argc, char *argv[]) +static int +run_test(int argc, char *argv[]) { int i; @@ -552,4 +552,20 @@ main(int argc, char *argv[]) sleep(1); #endif } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + try { + return run_test(argc, argv); + } catch (std::exception const &e) { + fprintf(stderr, "test_AIO aborted: %s\n", e.what()); + } catch (...) { + fprintf(stderr, "test_AIO aborted: unknown exception\n"); + } + + return 1; } diff --git a/src/iocore/cache/CacheTest.cc b/src/iocore/cache/CacheTest.cc index d171ac6b1fc..33cbfc08e69 100644 --- a/src/iocore/cache/CacheTest.cc +++ b/src/iocore/cache/CacheTest.cc @@ -47,6 +47,9 @@ CacheTestSM::CacheTestSM(const CacheTestSM &ao) : RegressionSM(ao) SET_HANDLER(&CacheTestSM::event_handler); } +// Coverity resolves ink_assert() to the throwing _ink_assert() defined by +// eventsystem/unit_tests/test_MIOBufferWriter.cc, not the one linked here. +// coverity[UNCAUGHT_EXCEPT:FALSE] CacheTestSM::~CacheTestSM() { ink_assert(!cache_action); diff --git a/src/iocore/cache/unit_tests/CacheTestHandler.h b/src/iocore/cache/unit_tests/CacheTestHandler.h index d4351a13246..58b08fe95ba 100644 --- a/src/iocore/cache/unit_tests/CacheTestHandler.h +++ b/src/iocore/cache/unit_tests/CacheTestHandler.h @@ -37,6 +37,9 @@ class CacheTestBase; struct TestContChain : public Continuation { TestContChain(); + // EThread::schedule() asserts; Coverity resolves _ink_assert() to the + // throwing definition in eventsystem/unit_tests/test_MIOBufferWriter.cc. + // coverity[UNCAUGHT_EXCEPT:FALSE] virtual ~TestContChain() { this->next_test(); } void diff --git a/src/iocore/cache/unit_tests/main.h b/src/iocore/cache/unit_tests/main.h index c43df36e20b..bdb59ec48f4 100644 --- a/src/iocore/cache/unit_tests/main.h +++ b/src/iocore/cache/unit_tests/main.h @@ -188,6 +188,9 @@ class CacheWriteTest : public CacheTestBase build_hdrs(this->info, url); } + // free_MIOBuffer() and HTTPInfo::destroy() assert; Coverity resolves + // _ink_assert() to the throwing definition in test_MIOBufferWriter.cc. + // coverity[UNCAUGHT_EXCEPT:FALSE] ~CacheWriteTest() override { if (this->_write_buffer) { @@ -255,6 +258,9 @@ class CacheReadTest : public CacheTestBase build_hdrs(this->info, url); } + // free_MIOBuffer() and HTTPInfo::destroy() assert; Coverity resolves + // _ink_assert() to the throwing definition in test_MIOBufferWriter.cc. + // coverity[UNCAUGHT_EXCEPT:FALSE] ~CacheReadTest() override { if (this->_read_buffer) { diff --git a/src/iocore/eventsystem/unit_tests/test_Lock.cc b/src/iocore/eventsystem/unit_tests/test_Lock.cc index 2194d059382..b42c80b8d60 100644 --- a/src/iocore/eventsystem/unit_tests/test_Lock.cc +++ b/src/iocore/eventsystem/unit_tests/test_Lock.cc @@ -25,6 +25,7 @@ #include "inkevent_test_fixtures.h" #include +#include #include @@ -48,10 +49,16 @@ class HoldOnEThread : public Continuation // In case of an exception in a thread that would have set release, we set // it here in order to unfreeze any threads that may be waiting on done. + // A destructor is implicitly noexcept, so anything escaping the teardown + // would terminate the test binary without saying where it came from. ~HoldOnEThread() { - this->cancel_callback(); - this->wait_for_callback_finish(); + try { + this->cancel_callback(); + this->wait_for_callback_finish(); + } catch (...) { + ink_abort("HoldOnEThread teardown threw an exception"); + } } bool diff --git a/src/iocore/hostdb/unit_tests/test_RefCountCache.cc b/src/iocore/hostdb/unit_tests/test_RefCountCache.cc index 4be77b6c5a5..a28b97a5b8e 100644 --- a/src/iocore/hostdb/unit_tests/test_RefCountCache.cc +++ b/src/iocore/hostdb/unit_tests/test_RefCountCache.cc @@ -25,6 +25,8 @@ #include "iocore/eventsystem/EventSystem.h" #include "tscore/Layout.h" #include "iocore/utils/diags.i" +#include +#include #include // TODO: add tests with expiry_time @@ -260,7 +262,15 @@ test() int main() { - int ret = test(); + int ret = 1; + + try { + ret = test(); + } catch (std::exception const &e) { + fprintf(stderr, "test_RefCountCache aborted: %s\n", e.what()); + } catch (...) { + fprintf(stderr, "test_RefCountCache aborted: unknown exception\n"); + } for (const auto item : ExampleStruct::items_freed) { printf("really freeing: %p\n", item); diff --git a/src/iocore/net/NetVCTest.cc b/src/iocore/net/NetVCTest.cc index 9c436157e9e..235f75443ea 100644 --- a/src/iocore/net/NetVCTest.cc +++ b/src/iocore/net/NetVCTest.cc @@ -38,6 +38,7 @@ #include "iocore/net/NetVConnection.h" #include "P_NetVCTest.h" #include "tscore/ink_atomic.h" +#include "tscore/ink_error.h" // Each test requires two definition entries. One for the passive // side of the connection and one for the active side @@ -88,18 +89,24 @@ NetVCTest::~NetVCTest() { mutex = nullptr; - if (read_buffer) { - Dbg(dbg_ctl, "Freeing read MIOBuffer with %d blocks on %s", read_buffer->max_block_count(), - (test_cont_type == NET_VC_TEST_ACTIVE) ? "Active" : "Passive"); - free_MIOBuffer(read_buffer); - read_buffer = nullptr; - } + // A destructor is implicitly noexcept, so anything escaping the buffer + // teardown would terminate the process without saying where it came from. + try { + if (read_buffer) { + Dbg(dbg_ctl, "Freeing read MIOBuffer with %d blocks on %s", read_buffer->max_block_count(), + (test_cont_type == NET_VC_TEST_ACTIVE) ? "Active" : "Passive"); + free_MIOBuffer(read_buffer); + read_buffer = nullptr; + } - if (write_buffer) { - Dbg(dbg_ctl, "Freeing write MIOBuffer with %d blocks on %s", write_buffer->max_block_count(), - (test_cont_type == NET_VC_TEST_ACTIVE) ? "Active" : "Passive"); - free_MIOBuffer(write_buffer); - write_buffer = nullptr; + if (write_buffer) { + Dbg(dbg_ctl, "Freeing write MIOBuffer with %d blocks on %s", write_buffer->max_block_count(), + (test_cont_type == NET_VC_TEST_ACTIVE) ? "Active" : "Passive"); + free_MIOBuffer(write_buffer); + write_buffer = nullptr; + } + } catch (...) { + ink_abort("NetVCTest teardown threw an exception"); } } diff --git a/src/proxy/http/remap/unit-tests/test_RemapRulesYaml.cc b/src/proxy/http/remap/unit-tests/test_RemapRulesYaml.cc index 513982370c4..0183f06f51a 100644 --- a/src/proxy/http/remap/unit-tests/test_RemapRulesYaml.cc +++ b/src/proxy/http/remap/unit-tests/test_RemapRulesYaml.cc @@ -145,6 +145,9 @@ struct EasyURL { url.create(heap); url.parse(s); } + // THREAD_FREE() reaches thread_freeup(), which asserts; Coverity resolves + // _ink_assert() to the throwing definition in test_MIOBufferWriter.cc. + // coverity[UNCAUGHT_EXCEPT:FALSE] ~EasyURL() { heap->destroy(); } }; diff --git a/src/proxy/http2/test_HPACK.cc b/src/proxy/http2/test_HPACK.cc index cde31b7c13f..2785b0d1e4b 100644 --- a/src/proxy/http2/test_HPACK.cc +++ b/src/proxy/http2/test_HPACK.cc @@ -27,6 +27,7 @@ #include "iocore/eventsystem/Thread.h" #include #include +#include #include #include #include @@ -393,30 +394,38 @@ REGRESSION_TEST(HPACK_Encoding)(RegressionTest *t, int /* atype ATS_UNUSED */, i int main(int argc, const char **argv) { - auto &version = AppVersionInfo::setup_version("test_HPACK"); - process_args(&version, argument_descriptions, countof(argument_descriptions), argv); + try { + auto &version = AppVersionInfo::setup_version("test_HPACK"); + process_args(&version, argument_descriptions, countof(argument_descriptions), argv); - ink_freelist_init_ops(cmd_disable_freelist, cmd_disable_pfreelist); + ink_freelist_init_ops(cmd_disable_freelist, cmd_disable_pfreelist); - if (*cmd_input_dir) { - input_dir = cmd_input_dir; - if (input_dir.back() != '/') { - input_dir += '/'; + if (*cmd_input_dir) { + input_dir = cmd_input_dir; + if (input_dir.back() != '/') { + input_dir += '/'; + } } - } - if (*cmd_output_dir) { - output_dir = cmd_output_dir; - if (output_dir.back() != '/') { - output_dir += '/'; + if (*cmd_output_dir) { + output_dir = cmd_output_dir; + if (output_dir.back() != '/') { + output_dir += '/'; + } } - } - Thread *main_thread = new EThread; - main_thread->set_specific(); - url_init(); - mime_init(); - http_init(); - prepare(); - int status = RegressionTest::main(argc, argv, REGRESSION_TEST_QUICK); - return status; + Thread *main_thread = new EThread; + main_thread->set_specific(); + url_init(); + mime_init(); + http_init(); + prepare(); + int status = RegressionTest::main(argc, argv, REGRESSION_TEST_QUICK); + return status; + } catch (const std::exception &e) { + cerr << "test_HPACK: unhandled exception: " << e.what() << endl; + return 2; + } catch (...) { + cerr << "test_HPACK: unhandled exception of unknown type" << endl; + return 2; + } } diff --git a/src/records/unit_tests/unit_test_main.cc b/src/records/unit_tests/unit_test_main.cc index e03d219efce..5aba4f0a935 100644 --- a/src/records/unit_tests/unit_test_main.cc +++ b/src/records/unit_tests/unit_test_main.cc @@ -18,6 +18,9 @@ the License. */ +#include +#include +#include #include #include #include "tscore/ink_resolver.h" @@ -34,17 +37,28 @@ extern void ts_session_protocol_well_known_name_indices_init(); int main(int argc, char *argv[]) { - // Set the global diags variable - Layout::create(); // RecProcess will fail if Layout is not created. - DiagsPtr::set(new CatchDiags); - RecProcessInit(); - // Global data initialization needed for the unit tests. - ts_session_protocol_well_known_name_indices_init(); - // Cheat for ts_host_res_global_init as there's no records.config to check for non-default. - host_res_default_preference_order = HOST_RES_DEFAULT_PREFERENCE_ORDER; - int result = Catch::Session().run(argc, argv); - - // global clean-up... - - return result; + // The global initialization below runs outside of any Catch2 assertion, so an exception thrown + // there would otherwise escape main and abort without a diagnostic. + try { + // Set the global diags variable + Layout::create(); // RecProcess will fail if Layout is not created. + DiagsPtr::set(new CatchDiags); + RecProcessInit(); + // Global data initialization needed for the unit tests. + ts_session_protocol_well_known_name_indices_init(); + // Cheat for ts_host_res_global_init as there's no records.config to check for non-default. + host_res_default_preference_order = HOST_RES_DEFAULT_PREFERENCE_ORDER; + + int result = Catch::Session().run(argc, argv); + + // global clean-up... + + return result; + } catch (std::exception const &ex) { + std::fprintf(stderr, "test_records aborted: %s\n", ex.what()); + } catch (...) { + std::fprintf(stderr, "test_records aborted: unknown exception\n"); + } + + return EXIT_FAILURE; }