Skip to content

Commit 3bbe277

Browse files
committed
dispatcher: Fix rare crash with 'pvar_algo_pattern' (part 2)
Commit fe1a50d was a quick-fix attempt, but the race condition was still there, and continuing to cause rare crashes during 'ds_reload' if using the 'pvar_algo_pattern' feature. Long story short: pv_get_spec_value() on SHM-allocated structs has a high risk of running into concurrency issues, depending on the spec in use. Here, the issue happened to be with the $stat(xxx) algo pattern, where 2 or more SIP workers performed concurrent READ/WRITE on it, leading to a crash. Many thanks to Eric Tamme from Five9 for reporting & testing! (cherry picked from commit 063435d)
1 parent a3574eb commit 3bbe277

3 files changed

Lines changed: 34 additions & 3 deletions

File tree

modules/dispatcher/dispatch.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,11 @@ int add_dest2list(int id, str uri, const struct socket_info *sock, str *comsock,
368368
}
369369
}
370370

371+
if (!lock_init(&dp->wlock)) {
372+
LM_ERR("failed to init lock\n");
373+
goto err;
374+
}
375+
371376
/*
372377
* search the proper place based on priority
373378
* put them in reverse order, since they will be reindexed
@@ -643,6 +648,8 @@ int ds_pvar_algo(struct sip_msg *msg, ds_set_p set, ds_dest_p **sorted_set,
643648
}
644649

645650
for (i = 0, cnt = 0; i < set->nr - (ds_use_default?1:0); i++) {
651+
int locked = 0;
652+
646653
if ( !dst_is_active(set->dlist[i]) ) {
647654
/* move to the end of the list */
648655
sset[end_idx--] = &set->dlist[i];
@@ -657,15 +664,38 @@ int ds_pvar_algo(struct sip_msg *msg, ds_set_p set, ds_dest_p **sorted_set,
657664
set->dlist[i].uri.len, set->dlist[i].uri.s);
658665
continue;
659666
}
660-
set->dlist[i].param = (void *)param;
667+
668+
/* concurrent access -- avoid SHM leak */
669+
lock_get(&set->dlist[i].wlock);
670+
if (set->dlist[i].param) {
671+
shm_free(param);
672+
param = (ds_pvar_param_p)set->dlist[i].param;
673+
} else {
674+
set->dlist[i].param = (void *)param;
675+
}
676+
lock_release(&set->dlist[i].wlock);
661677
} else {
662678
param = (ds_pvar_param_p)set->dlist[i].param;
663679
}
680+
681+
/* until the underlying (stat *) struct is created, we cannot
682+
* perform READ/WRITE against this pv_spec_t concurrently */
683+
if (!pv_has_dname(&param->pvar)) {
684+
locked = 1;
685+
lock_get(&set->dlist[i].wlock);
686+
}
687+
664688
if (pv_get_spec_value(msg, &param->pvar, &val) < 0) {
689+
if (locked)
690+
lock_release(&set->dlist[i].wlock);
665691
LM_ERR("cannot get spec value for spec %.*s\n",
666692
set->dlist[i].uri.len, set->dlist[i].uri.s);
667693
continue;
668694
}
695+
696+
if (locked)
697+
lock_release(&set->dlist[i].wlock);
698+
669699
if (!(val.flags & PV_VAL_NULL)) {
670700
if (!(val.flags & PV_VAL_INT)) {
671701
/* last attempt to retrieve value */

modules/dispatcher/dispatch.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ typedef struct _ds_dest
6767
str attrs;
6868
str script_attrs;
6969
str description;
70-
int flags;
70+
int flags; /* e.g. DS_INACTIVE_DST, etc. */
7171
unsigned short weight; /* dynamic weight - may change at runtime */
7272
unsigned short rr_count; /* times it was chosen in a row for weighted round-robin */
7373
unsigned short running_weight;
@@ -83,6 +83,7 @@ typedef struct _ds_dest
8383
void *param;
8484
int route_algo_value;
8585
fs_evs *fs_sock;
86+
gen_lock_t wlock; /* serialize any concurrent R/W on this dest */
8687
struct _ds_dest *next;
8788
} ds_dest_t, *ds_dest_p;
8889

modules/statistics/statistics.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,8 +806,8 @@ static inline int get_stat_name(struct sip_msg* msg, pv_name_t *name,
806806
//shm_free(name->u.isname.name.s.s);
807807
name->u.isname.name.s.s = NULL;
808808
name->u.isname.name.s.len = 0;
809-
name->type = PV_NAME_PVAR;
810809
name->u.dname = (void*)*stat;
810+
name->type = PV_NAME_PVAR;
811811
}
812812
} else {
813813
/* stat already found ! */

0 commit comments

Comments
 (0)