From a9970b948b378bf6ba9f2ebb0331b62cda9ef686 Mon Sep 17 00:00:00 2001 From: laconicman Date: Tue, 6 Oct 2026 16:42:45 +0300 Subject: [PATCH 1/2] fix(pjsua): honour the retry interval when it is below the jitter schedule_reregistration() randomises the retry delay by reg_retry_random_interval. For an interval below it, the else branch cleared delay.sec before using it in the modulo, so delay.sec * 1000 was always 0 and the delay was a uniform 0 .. reg_retry_random_interval whatever the interval was: 1 and 9 behaved alike, and 9 averaged shorter than 10. Compute the delay before clearing delay.sec, so it is uniform in 0 .. interval + reg_retry_random_interval. That is what the code from #1802 (e9268d219) wrote, it matches the other branch at interval == reg_retry_random_interval, and it grows with the interval. An interval of 0, including the default reg_first_retry_interval, is unchanged. The documented "interval +/- reg_retry_random_interval" cannot apply below the jitter, where the lower end would be negative. Say in the pjsua and pjsua2 comments what range applies there. --- pjsip/include/pjsua-lib/pjsua.h | 6 +++++- pjsip/include/pjsua2/account.hpp | 6 +++++- pjsip/src/pjsua-lib/pjsua_acc.c | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 3525cf1529..d20e329715 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -5172,7 +5172,11 @@ typedef struct pjsua_acc_config * This is useful to avoid all clients re-registering at the same time. * For example, if the registration retry interval is set to 100 seconds * and this is set to 10 seconds, the actual registration retry interval - * will be in the range of 90 to 110 seconds. + * will be in the range of 90 to 110 seconds. If the retry interval is + * shorter than this value, it cannot be reduced by that much, and the + * actual interval will be in the range of 0 to the retry interval plus + * this value instead: for example 0 to 15 seconds for a retry interval + * of 5 seconds. * * Default: 10 */ diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index acf974679b..c2bce5a1ae 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -145,7 +145,11 @@ struct AccountRegConfig : public PersistentObject * This is useful to avoid all clients re-registering at the same time. * For example, if the registration retry interval is set to 100 seconds * and this is set to 10 seconds, the actual registration retry interval - * will be in the range of 90 to 110 seconds. + * will be in the range of 90 to 110 seconds. If the retry interval is + * shorter than this value, it cannot be reduced by that much, and the + * actual interval will be in the range of 0 to the retry interval plus + * this value instead: for example 0 to 15 seconds for a retry interval + * of 5 seconds. * * See also \a retryIntervalSec and \a firstRetryIntervalSec settings. * diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 6d6e8f4552..2f4e1b333e 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -5527,8 +5527,8 @@ static void schedule_reregistration(pjsua_acc *acc) if (delay.sec >= (long)acc->cfg.reg_retry_random_interval) { delay.msec = -rand_ms + (pj_rand() % (rand_ms * 2)); } else { - delay.sec = 0; delay.msec = (pj_rand() % (delay.sec * 1000 + rand_ms)); + delay.sec = 0; } } pj_time_val_normalize(&delay); From 0e1ad2802c1c828b4831aadcdec7f03235fefb1f Mon Sep 17 00:00:00 2001 From: laconicman Date: Wed, 7 Oct 2026 13:32:26 +0300 Subject: [PATCH 2/2] fix(pjsua): guard the retry delay against huge intervals, fix comments Where long is 32-bit, a reg_retry_interval or reg_first_retry_interval too large for a long makes delay.sec negative. It then takes the branch below reg_retry_random_interval, whose divisor now depends on delay.sec, and the divisor can become 0 or overflow. Clamp a negative delay.sec to 0 there, which gives such a value the same 0 .. reg_retry_random_interval delay as before. Also correct the comments: the lower bound of the randomized range is clamped at 0, and the first retry since the last successful registration waits reg_first_retry_interval rather than happening immediately. Point the retry interval docs at reg_retry_random_interval for the resulting range, and use the pjsua2 field names in AccountRegConfig. --- pjsip/include/pjsua-lib/pjsua.h | 12 ++++++------ pjsip/include/pjsua2/account.hpp | 22 +++++++++++----------- pjsip/src/pjsua-lib/pjsua_acc.c | 17 +++++++++++++---- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index d20e329715..d87f828451 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -5144,9 +5144,9 @@ typedef struct pjsua_acc_config * 6xx (global failure), and failure caused by transport problem. * For registration retry caused by transport failure, the first retry * will be done after \a reg_first_retry_interval seconds instead. - * Note that the interval will be randomized slightly by some seconds - * (specified in \a reg_retry_random_interval) to avoid all clients - * re-registering at the same time. + * Note that the interval will be randomized by + * \a reg_retry_random_interval to avoid all clients re-registering at + * the same time; see there for the resulting range. * * See also \a reg_first_retry_interval setting. * @@ -5157,9 +5157,9 @@ typedef struct pjsua_acc_config /** * This specifies the interval for the first registration retry. The * registration retry is explained in \a reg_retry_interval. Note that - * the value here will also be randomized by some seconds (specified - * in \a reg_retry_random_interval) to avoid all clients re-registering - * at the same time. + * the value here will also be randomized by + * \a reg_retry_random_interval to avoid all clients re-registering at + * the same time; see there for the resulting range. * * Default: 0 */ diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp index c2bce5a1ae..0bab33fc8c 100644 --- a/pjsip/include/pjsua2/account.hpp +++ b/pjsip/include/pjsua2/account.hpp @@ -114,9 +114,9 @@ struct AccountRegConfig : public PersistentObject * disable auto re-registration. Note that if the registration retry * occurs because of transport failure, the first retry will be done * after \a firstRetryIntervalSec seconds instead. Also note that - * the interval will be randomized slightly by some seconds (specified - * in \a reg_retry_random_interval) to avoid all clients re-registering - * at the same time. + * the interval will be randomized by \a randomRetryIntervalSec to + * avoid all clients re-registering at the same time; see there for + * the resulting range. * * See also \a firstRetryIntervalSec and \a randomRetryIntervalSec * settings. @@ -128,9 +128,9 @@ struct AccountRegConfig : public PersistentObject /** * This specifies the interval for the first registration retry. The * registration retry is explained in \a retryIntervalSec. Note that - * the value here will also be randomized by some seconds (specified - * in \a reg_retry_random_interval) to avoid all clients re-registering - * at the same time. + * the value here will also be randomized by \a randomRetryIntervalSec + * to avoid all clients re-registering at the same time; see there for + * the resulting range. * * See also \a retryIntervalSec and \a randomRetryIntervalSec settings. * @@ -141,15 +141,15 @@ struct AccountRegConfig : public PersistentObject /** * This specifies maximum randomized value to be added/substracted * to/from the registration retry interval specified in \a - * reg_retry_interval and \a reg_first_retry_interval, in second. + * retryIntervalSec and \a firstRetryIntervalSec, in second. * This is useful to avoid all clients re-registering at the same time. * For example, if the registration retry interval is set to 100 seconds * and this is set to 10 seconds, the actual registration retry interval * will be in the range of 90 to 110 seconds. If the retry interval is - * shorter than this value, it cannot be reduced by that much, and the - * actual interval will be in the range of 0 to the retry interval plus - * this value instead: for example 0 to 15 seconds for a retry interval - * of 5 seconds. + * shorter than \a randomRetryIntervalSec, it cannot be reduced by that + * much, and the actual interval will be in the range of 0 to the retry + * interval plus \a randomRetryIntervalSec instead: for example 0 to 15 + * seconds for a retry interval of 5 seconds. * * See also \a retryIntervalSec and \a firstRetryIntervalSec settings. * diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 2f4e1b333e..8bb163d8ed 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -5467,8 +5467,9 @@ static void auto_rereg_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) } -/* Schedule reregistration for specified account. Note that the first - * re-registration after a registration failure will be done immediately. +/* Schedule reregistration for specified account. Until a registration + * succeeds again, the first retry waits reg_first_retry_interval and later + * ones reg_retry_interval, both randomized by reg_retry_random_interval. * Also note that this function should be called within PJSUA mutex. */ static void schedule_reregistration(pjsua_acc *acc) @@ -5516,17 +5517,25 @@ static void schedule_reregistration(pjsua_acc *acc) acc->auto_rereg.timer.cb = &auto_rereg_timer_cb; acc->auto_rereg.timer.user_data = acc; - /* Reregistration attempt. The first attempt will be done immediately. */ + /* Reregistration attempt. The first one since the last successful + * registration waits reg_first_retry_interval. + */ delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : acc->cfg.reg_first_retry_interval; delay.msec = 0; - /* Randomize interval by +/- reg_retry_random_interval, if configured */ + /* Randomize interval by +/- reg_retry_random_interval, if configured. + * The lower bound is clamped at 0: an interval shorter than that gives + * a delay between 0 and the interval plus reg_retry_random_interval. + */ if (acc->cfg.reg_retry_random_interval) { long rand_ms = acc->cfg.reg_retry_random_interval * 1000; if (delay.sec >= (long)acc->cfg.reg_retry_random_interval) { delay.msec = -rand_ms + (pj_rand() % (rand_ms * 2)); } else { + /* A huge unsigned interval may not fit in a long */ + if (delay.sec < 0) + delay.sec = 0; delay.msec = (pj_rand() % (delay.sec * 1000 + rand_ms)); delay.sec = 0; }