From a79d65ba15be2866bb381d119f31b13cbb62b964 Mon Sep 17 00:00:00 2001 From: George McIntyre Date: Wed, 8 Jul 2026 21:19:18 -0700 Subject: [PATCH] testmon: cancelDuringEvent() --- test/testmon.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/testmon.cpp b/test/testmon.cpp index e7f7f2d..20875f9 100644 --- a/test/testmon.cpp +++ b/test/testmon.cpp @@ -128,6 +128,39 @@ struct BasicTest { testOk1(!done->wait(1.1)); } + void cancelDuringEvent() + { + testShow()<<__func__; + + mbox.open(initial); + serv.start(); + + // A heap-allocated captured value the callback reads AFTER cancelling. + // If the functor (and this capture) were freed by the in-call cancel, + // touching `canary` would be a use-after-free (caught under ASan). + auto canary(std::make_shared("alive")); + auto fired(std::make_shared()); + auto ok(std::make_shared>(false)); + + std::shared_ptr self; + self = cli.monitor("mailbox") + .maskConnected(true) + .maskDisconnected(true) + .event([canary, fired, ok](client::Subscription& s) { + // Re-entrant cancel on this same loop: move-destroys `event`. + s.cancel(); + // Touch the capture after the cancel; must still be valid. + ok->store(*canary == "alive"); + fired->signal(); + }) + .exec(); + + post(1); + + testOk1(fired->wait(5.0)); + testOk1(ok->load()); + } + void badRequest() { testShow()<<__func__; @@ -381,13 +414,14 @@ struct TestReconn : public BasicTest MAIN(testmon) { - testPlan(43); + testPlan(45); testSetup(); try{ logger_config_env(); BasicTest().orphan(); BasicTest().cancel(); BasicTest().asyncCancel(); + BasicTest().cancelDuringEvent(); BasicTest().badRequest(); BasicTest().testNoMark(); TestLifeCycle().testBasic(true);