From d320c977470bf88835e8e0f65a4510d87fc5303c Mon Sep 17 00:00:00 2001 From: Clemens Kirchgatterer Date: Mon, 16 Mar 2026 09:22:31 +0100 Subject: [PATCH 1/2] Fix possible crash in UdpContext host mocking. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I saw this crash in UdpContext.h happen after some hours of regular NTP requests.... ``` [17:38:51] ADE: voltage spike: phase A, factor=0.81, cycles=1 (mock) ClientContext::connect: ::connect(): Connection refused (mock) ClientContext::connect: ::connect(): Connection refused (mock) ClientContext::connect: ::connect(): Connection refused (mock) ClientContext::connect: ::connect(): Connection refused [17:41:14] ADE: voltage spike: phase A, factor=0.86, cycles=2 (mock) ClientContext::connect: ::connect(): Connection refused (mock) ClientContext::connect: ::connect(): Connection refused (mock) ClientContext::connect: ::connect(): Connection refused (mock) ClientContext::connect: ::connect(): Connection refused [17:43:49] ADE: voltage spike: phase B, factor=1.12, cycles=3 (mock) ClientContext::connect: ::connect(): Connection refused (mock) =====> UdpServer port: 2390 <===== [17:44:31] NTP: waiting for response from '81.169.233.252' timed out Program received signal SIGSEGV, Segmentation fault. 0x000055555556848c in std::_Function_base::_M_empty() const () (gdb) where #0 0x000055555556848c in std::_Function_base::_M_empty() const () #1 0x00005555555685be in std::function::operator bool() const () #2 0x00005555555684b8 in UdpContext::mock_cb() () #3 0x00005555555683af in check_incoming_udp() () #4 0x0000555555567fa6 in main () ``` I tracked it down to UdpContext::mock_cb() which is calling a std::function callback that's been destroyed thus used-after-free. I believe what happens is: 1. UdpContext::unref() calls delete this when refcount hits 0 (line 56) 2. The destructor (~UdpContext(), line 47) is empty — it never calls register_udp(_sock, nullptr) to remove itself from the global udps map 3. check_incoming_udp() iterates udps, finds the dangling pointer, calls mock_cb() on freed memory 4. _on_rx (a destroyed std::function) → segfault in _M_empty() The disconnect() method already does the right thing (lines 78-84: closes socket + unregisters), but it's never called before the object is destroyed via unref(). The fix is to make the destructor clean up properly. I tested intensely, no crashes since. --- tests/host/common/include/UdpContext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/host/common/include/UdpContext.h b/tests/host/common/include/UdpContext.h index e8ae9461e9..1a4f790453 100644 --- a/tests/host/common/include/UdpContext.h +++ b/tests/host/common/include/UdpContext.h @@ -44,7 +44,7 @@ class UdpContext _sock = mockUDPSocket(); } - ~UdpContext() { } + ~UdpContext() { disconnect(); } void ref() { From ee9b98e1d27afbd566b8133f20ea15a93a95f3a2 Mon Sep 17 00:00:00 2001 From: Clemens Kirchgatterer Date: Mon, 16 Mar 2026 10:11:09 +0100 Subject: [PATCH 2/2] Format destructor for UdpContext for clarity Fixes code-style CI. --- tests/host/common/include/UdpContext.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/host/common/include/UdpContext.h b/tests/host/common/include/UdpContext.h index 1a4f790453..020407dbe9 100644 --- a/tests/host/common/include/UdpContext.h +++ b/tests/host/common/include/UdpContext.h @@ -44,7 +44,10 @@ class UdpContext _sock = mockUDPSocket(); } - ~UdpContext() { disconnect(); } + ~UdpContext() + { + disconnect(); + } void ref() {