From 2bdce60c73d1a5d4ebd650fe62cca83c3158b7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Luis=20Aracil=20Boned?= Date: Mon, 6 Jul 2026 00:30:40 +0200 Subject: [PATCH] su_timer: skip an expired timer with no callback instead of aborting su_timer_expire() asserted that a set, expired timer always has a wakeup callback. Under load a stale/racy or invalidly-set timer can reach expiry with a NULL callback (e.g. a concurrent su_timer_reset); there is nothing valid to call, but assert(f) abort()s the whole process. Skip such a timer gracefully: log it, mark it not-running (sut_running = reset, sut_arg = NULL) and continue with the next timer, rather than aborting. Timers that do have a callback are unaffected. Fixes #251, #255. --- libsofia-sip-ua/su/su_timer.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libsofia-sip-ua/su/su_timer.c b/libsofia-sip-ua/su/su_timer.c index 5569767dc..e758461cd 100644 --- a/libsofia-sip-ua/su/su_timer.c +++ b/libsofia-sip-ua/su/su_timer.c @@ -560,7 +560,17 @@ int su_timer_expire(su_timer_queue_t * const timers, timers_remove(timers[0], 1); f = t->sut_wakeup; t->sut_wakeup = NULL; - assert(f); + if (!f) { + /* A set timer reached expiry with no callback (a stale/racy entry, e.g. + * a concurrent su_timer_reset, or an invalidly-set timer). There is + * nothing valid to call, so mark it not-running and skip it rather than + * aborting the process (this was an assert(f)). */ + SU_DEBUG_3(("su_timer_expire: timer %p expired with no callback, skipping\n", + (void *)t)); + t->sut_running = reset; + t->sut_arg = NULL; + continue; + } if (t->sut_running == run_at_intervals) { while (t->sut_running == run_at_intervals &&