kuzaxak created an issue (kamailio/kamailio#4807)

### Description

Concurrent dispatcher reloads can corrupt shared memory when multiple worker
processes rebuild the dispatcher destination table.

The dispatcher module keeps two destination-set slots in shared memory
(`ds_lists[0]` and `ds_lists[1]`) and publishes the active slot through
`ds_crt_idx`/`ds_next_idx`. In `ds_load_list()` and `ds_load_db()`
(`src/modules/dispatcher/dispatch.c`), the reload path checks that no reload is
in progress, selects the inactive slot, destroys that slot, repopulates it, and
then publishes it (condensed):

```c
if((*ds_crt_idx) != (*ds_next_idx)) {
    LM_WARN("load command already generated, aborting reload...\n");
    return 0;
}

/* ... open the list file / query the database ... */

*ds_next_idx = (*ds_crt_idx + 1) % 2;
ds_avl_destroy(&ds_lists[*ds_next_idx]);

/* add_dest2list(), reindex_dests(), etc. */

*ds_crt_idx = *ds_next_idx;
```

That check is not atomic with the later `*ds_next_idx` write. With multiple
workers, two workers can both observe `*ds_crt_idx == *ds_next_idx`, both select
the same inactive slot, and both destroy/rebuild the same shared-memory AVL
tree. Depending on timing, q_malloc reports either explicit double-free lines or
allocator fragment corruption followed by a worker crash.

The same unprotected rebuild pattern is also present in `ds_add_dst()` and
`ds_remove_dst()`, which select and destroy the inactive slot without the
`ds_crt_idx == ds_next_idx` guard.

Expected behavior: concurrent reload/add/remove requests should not corrupt
shared memory. At most one dispatcher table rebuild should be active at a time,
or later callers should return without touching the inactive slot.

The same unguarded sequence is present on both branches checked:

- `master`: `b3a8384d9d10343317b3df83c595f5cfe570d14a`
- `6.0`: `5709418e6a47768e4f729e44530722bc42fedf1c`

### Troubleshooting

#### Reproduction


A self-contained reproducer archive is attached to this issue:
`kamailio-dispatcher-reload-race-repro-with-pcap.zip`.

It contains the runnable repro harness and captured evidence.

[kamailio-dispatcher-reload-race-repro-with-pcap.zip](https://github.com/user-attachments/files/29594512/kamailio-dispatcher-reload-race-repro-with-pcap.zip)

To reproduce from the attached archive:

```sh
unzip kamailio-dispatcher-reload-race-repro-with-pcap.zip -d 
kamailio-dispatcher-reload-race-repro
cd kamailio-dispatcher-reload-race-repro
./run.sh --duration 25 --senders 32
```

The repro:

- builds unpatched `master` from `b3a8384d9d10343317b3df83c595f5cfe570d14a`
- uses a file-based dispatcher list with 4000 destinations (widens the
  destroy/rebuild window)
- uses `children=16` (more workers reloading concurrently)
- sets `modparam("dispatcher", "reload_delta", 0)` (see note below)
- starts Kamailio with `-x q_malloc` (debug allocator that detects double-free
  and overwritten fragments)
- calls `ds_reload()` from `request_route` on every received OPTIONS request
- sends OPTIONS from 32 parallel UDP senders for 25 seconds

This setup only makes the race window easy to hit within seconds; it does not
create the race. In particular, `reload_delta` does not close the window: the
gate in `ds_reload()` (the `LM_ERR("ongoing reload")` check on
`*ds_rpc_reload_time` in dispatcher.c) is itself a non-atomic check-then-set on
shared memory, so with the default `reload_delta` of 5 seconds two workers can
still pass it simultaneously — just as they can simultaneously pass the
`ds_crt_idx == ds_next_idx` guard shown above.

Observed on unpatched `master`:

```text
kam container state       : kam exited 139
double-free lines         : 0  (q_malloc 'freeing already freed pointer')
shm corruption BUG lines  : 2  (q_malloc 'fragm ... overwritten')
worker signal/core lines  : 2
reload-guard aborts       : 16  (WARN 'load command already generated')
flooder rc                : 0
```

(`exited 139` is 128+11, i.e. SIGSEGV; `flooder rc` is the exit code of the
traffic generator.)

A separate run from the same harness hit the explicit q_malloc double-free
detector:

```text
double-free lines         : 4
shm corruption BUG lines  : 1
worker signal/core lines  : 2
reload-guard aborts       : 3876
```

The existing `ds_crt_idx == ds_next_idx` guard does fire (16 and 3876
`reload-guard aborts` above), but it cannot prevent the corruption because the
check and the later index write are not atomic.


#### Debugging Data

The core below is from the main process: after child process 8 exited with
SIGSEGV (see Log Messages), the main process crashed during shutdown cleanup
while destroying the already-corrupted dispatcher table. Output of `bt full`
and `thread apply all bt` from `logs/coredump-bt-unpatched.txt`:

```text
warning: Can't open file /dev/zero (deleted) during file-backed mapping note 
processing
[New LWP 1]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
Core was generated by `/usr/local/sbin/kamailio -DD -E -x q_malloc -m 128 -M 16 
-w /cores -f /etc/kamailio/kam.cfg'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000aaaae11bb3b0 in qm_status (qmp=0xffffb48c0000) at 
core/mem/q_malloc.c:992

warning: 992    core/mem/q_malloc.c: No such file or directory
#0  0x0000aaaae11bb3b0 in qm_status (qmp=0xffffb48c0000) at 
core/mem/q_malloc.c:992
        qm = 0xffffb48c0000
        f = 0x8
        i = 4170
        j = 1
        h = 2060
        unused = 0
        memlog = 4
        mem_summary = 12
        __func__ = "qm_status"
        __llevel = <optimized out>
        __kld = <optimized out>
#1  0x0000aaaae11adf20 in qm_debug_check_frag (qm=0xffffb48c0000, 
f=0xffffb67152a0, file=0xffffbc939618 "dispatcher: dispatch.c", line=5141, 
efile=0xaaaae1401fb0 "core/mem/q_malloc.c", eline=569) at 
core/mem/q_malloc.c:146
        p = 0xbb5f1b6a0
        __func__ = "qm_debug_check_frag"
#2  0x0000aaaae11b26fc in qm_free (qmp=0xffffb48c0000, p=0xffffb67152e0, 
file=0xffffbc939618 "dispatcher: dispatch.c", func=0xffffbc943668 <__func__.1> 
"ds_avl_destroy", line=5141, mname=0xffffbc939608 "dispatcher") at 
core/mem/q_malloc.c:569
        qm = 0xffffb48c0000
        f = 0xffffb67152a0
        size = 176
        next = 0xffffbdc31430 <_IO_2_1_stderr_>
        prev = 0xaaaae11c024c <qm_shm_free+92>
        __func__ = "qm_free"
#3  0x0000aaaae11c023c in qm_shm_free (qmp=0xffffb48c0000, p=0xffffb67152e0, 
file=0xffffbc939618 "dispatcher: dispatch.c", func=0xffffbc943668 <__func__.1> 
"ds_avl_destroy", line=5141, mname=0xffffbc939608 "dispatcher") at 
core/mem/q_malloc.c:1602
No locals.
#4  0x0000ffffbc9178a0 in ds_avl_destroy (node_ptr=0xffffb5a19518) at 
dispatch.c:5141
        node = 0xffffb5f09410
        dest = 0x0
        i = 2
        __func__ = "ds_avl_destroy"
#5  0x0000ffffbc917754 in ds_avl_destroy (node_ptr=0xffffb49b1db8) at 
dispatch.c:5128
        node = 0xffffb5a19040
        dest = 0x0
        i = 1
        __func__ = "ds_avl_destroy"
#6  0x0000ffffbc917754 in ds_avl_destroy (node_ptr=0xffffb495eda0) at 
dispatch.c:5128
        node = 0xffffb49b18e0
        dest = 0x0
        i = 1
        __func__ = "ds_avl_destroy"
#7  0x0000ffffbc8ddb18 in ds_destroy_list () at dispatch.c:1731
        __func__ = "ds_destroy_list"
#8  0x0000ffffbc921184 in destroy () at dispatcher.c:589
        __func__ = "destroy"
#9  0x0000aaaae108be20 in destroy_modules () at core/sr_module.c:895
        t = 0xffffbcb869e0
        foo = 0xffffbcb862a0
        __func__ = "destroy_modules"
#10 0x0000aaaae0db1d2c in cleanup (show_status=1) at main.c:606
        memlog = 1747626
        __func__ = "cleanup"
#11 0x0000aaaae0db3e74 in shutdown_children (sig=15, show_status=1) at 
main.c:754
        __func__ = "shutdown_children"
#12 0x0000aaaae0db7a38 in handle_sigs () at main.c:854
        chld = 0
        chld_status = 139
        any_chld_stopped = 1
        memlog = 65535
        __func__ = "handle_sigs"
#13 0x0000aaaae0dc8370 in main_loop () at main.c:2094
        i = 16
        pid = 27
        si = 0x0
        sx = 0xaaaae0dd4290 <main+47504>
        si_desc = "udp receiver child=15 
sock=0.0.0.0:5060\000`\3409\341\252\252\000\000(\2518\341\252\252\000\000\340 
4\327\377\377\000\000\340 4\327\377\377\000\000\260 
4\327\377\377\000\000\320\377\377\377\200\377\377\377\340[\270\274\377\377\000\000\340
 4\327\377\377\000\000\340 4\327\377\377\000\000\260 
4\327\377\377\000\000\320\377\377\377\200\377\377\377"
        nrprocs = 16
        woneinit = 1
        agfound = 1
        __func__ = "main_loop"
#14 0x0000aaaae0dd42d8 in main (argc=13, argv=0xffffd7342508) at main.c:3439
        cfg_stream = 0xaaab1d895380
        c = -1
        r = 0
        tmp = 0xffffd7342f52 ""
        tmp_len = 0
        port = 5060
        proto = 0
        aproto = 0
        ahost = 0x0
        socket_name = 0x0
        aport = 0
        listen_field_count = 0
        listen_fields = {0xffffbdd1a740 "\300\256\321\275\377\377", 
0xffffbdd25000 <_rtld_global> "@f\322\275\377\377", 0xaaaae149f500 
"\314\374\332\340\252\252"}
        options = 0xaaaae1387fe0 
":f:cm:M:dVIhEeb:B:l:L:n:vKrRDTN:W:w:t:u:g:P:G:SQ:O:a:A:x:X:Y:"
        ret = -1
        debug_save = 0
        debug_flag = 0
        dont_fork_cnt = 2
        n_lst = 0xaaaae0daec00
        p = 0xffffbdd1a740 "\300\256\321\275\377\377"
        tbuf = 0xaaaae0dae558 "\020\004r"
        tbuf_tmp = 0x0
        st = {st_dev = 0, st_ino = 0, st_mode = 0, st_nlink = 0, st_uid = 0, 
st_gid = 0, st_rdev = 0, __pad1 = 0, st_size = 0, st_blksize = 0, __pad2 = 0, 
st_blocks = 0, st_atim = {tv_sec = 0, tv_nsec = 0}, st_mtim = {tv_sec = 0, 
tv_nsec = 0}, st_ctim = {tv_sec = 0, tv_nsec = 0}, __glibc_reserved = {0, 0}}
        l1 = 128
        lim = {rlim_cur = 1048576, rlim_max = 1048576}
        option_index = 0
        long_options = {{name = 0xaaaae138a930 "help", has_arg = 0, flag = 0x0, 
val = 104}, {name = 0xaaaae1385218 "version", has_arg = 0, flag = 0x0, val = 
118}, {name = 0xaaaae138a938 "alias", has_arg = 1, flag = 0x0, val = 1024}, 
{name = 0xaaaae138a940 "domain", has_arg = 1, flag = 0x0, val = 1024}, {name = 
0xaaaae138a948 "subst", has_arg = 1, flag = 0x0, val = 1025}, {name = 
0xaaaae138a950 "substdef", has_arg = 1, flag = 0x0, val = 1026}, {name = 
0xaaaae138a960 "substdefs", has_arg = 1, flag = 0x0, val = 1027}, {name = 
0xaaaae138a970 "server-id", has_arg = 1, flag = 0x0, val = 1028}, {name = 
0xaaaae138a980 "loadmodule", has_arg = 1, flag = 0x0, val = 1029}, {name = 
0xaaaae138a990 "modparam", has_arg = 1, flag = 0x0, val = 1030}, {name = 
0xaaaae138a9a0 "log-engine", has_arg = 1, flag = 0x0, val = 1031}, {name = 
0xaaaae138a9b0 "debug", has_arg = 1, flag = 0x0, val = 1032}, {name = 
0xaaaae138a9b8 "cfg-print", has_arg = 0, flag = 0x0, val = 1033}, {name = 
0xaaaae138a9c8 "atexit", has_arg = 1, flag = 0x0, val = 1034}, {name = 
0xaaaae138a9d0 "all-errors", has_arg = 0, flag = 0x0, val = 1035}, {name = 
0xaaaae138a9e0 "iuid", has_arg = 1, flag = 0x0, val = 1036}, {name = 0x0, 
has_arg = 0, flag = 0x0, val = 0}}
        __func__ = "main"

==== all threads ====

Thread 1 (Thread 0xffffbdd1a020 (LWP 1)):
#0  0x0000aaaae11bb3b0 in qm_status (qmp=0xffffb48c0000) at 
core/mem/q_malloc.c:992
#1  0x0000aaaae11adf20 in qm_debug_check_frag (qm=0xffffb48c0000, 
f=0xffffb67152a0, file=0xffffbc939618 "dispatcher: dispatch.c", line=5141, 
efile=0xaaaae1401fb0 "core/mem/q_malloc.c", eline=569) at 
core/mem/q_malloc.c:146
#2  0x0000aaaae11b26fc in qm_free (qmp=0xffffb48c0000, p=0xffffb67152e0, 
file=0xffffbc939618 "dispatcher: dispatch.c", func=0xffffbc943668 <__func__.1> 
"ds_avl_destroy", line=5141, mname=0xffffbc939608 "dispatcher") at 
core/mem/q_malloc.c:569
#3  0x0000aaaae11c023c in qm_shm_free (qmp=0xffffb48c0000, p=0xffffb67152e0, 
file=0xffffbc939618 "dispatcher: dispatch.c", func=0xffffbc943668 <__func__.1> 
"ds_avl_destroy", line=5141, mname=0xffffbc939608 "dispatcher") at 
core/mem/q_malloc.c:1602
#4  0x0000ffffbc9178a0 in ds_avl_destroy (node_ptr=0xffffb5a19518) at 
dispatch.c:5141
#5  0x0000ffffbc917754 in ds_avl_destroy (node_ptr=0xffffb49b1db8) at 
dispatch.c:5128
#6  0x0000ffffbc917754 in ds_avl_destroy (node_ptr=0xffffb495eda0) at 
dispatch.c:5128
#7  0x0000ffffbc8ddb18 in ds_destroy_list () at dispatch.c:1731
#8  0x0000ffffbc921184 in destroy () at dispatcher.c:589
#9  0x0000aaaae108be20 in destroy_modules () at core/sr_module.c:895
#10 0x0000aaaae0db1d2c in cleanup (show_status=1) at main.c:606
#11 0x0000aaaae0db3e74 in shutdown_children (sig=15, show_status=1) at 
main.c:754
#12 0x0000aaaae0db7a38 in handle_sigs () at main.c:854
#13 0x0000aaaae0dc8370 in main_loop () at main.c:2094
#14 0x0000aaaae0dd42d8 in main (argc=13, argv=0xffffd7342508) at main.c:3439
```

#### Log Messages


Allocator corruption and crash from `logs/kam-unpatched.log`:

```text
CRITICAL: <core> [core/mem/q_malloc.c:130]: qm_debug_check_frag(): BUG: qm: 
fragm. 0xffffb68bc530 (address 0xffffb68bc570) beginning overwritten (0)! 
Memory allocator was called from dispatcher: dispatch.c:1201. Fragment marked 
by (null):0. Exec from core/mem/q_malloc.c:439.
ALERT: <core> [main.c:824]: handle_sigs(): child process 8 exited by a signal 11
ALERT: <core> [main.c:828]: handle_sigs(): core was generated
CRITICAL: <core> [core/mem/q_malloc.c:140]: qm_debug_check_frag(): BUG: qm: 
fragm. 0xffffb67152a0 (address 0xffffb67152e0) end overwritten (0, 20)! Memory 
allocator was called from dispatcher: dispatch.c:5141. Fragment marked by 
dispatcher: dispatch.c:1201. Exec from core/mem/q_malloc.c:569.
```

Explicit double-free form from another run of the same harness
(`logs/kam-unpatched-doublefree.log`):

```text
CRITICAL: <core> [core/mem/q_malloc.c:578]: qm_free(): BUG: freeing already 
freed pointer (0xffff99c9e870), called from dispatcher: dispatch.c: 
ds_avl_destroy(5136), first free dispatcher: dispatch.c: ds_set_attrs(383) - 
ignoring
CRITICAL: <core> [core/mem/q_malloc.c:578]: qm_free(): BUG: freeing already 
freed pointer (0xffff99c9bda0), called from dispatcher: dispatch.c: 
ds_avl_destroy(5132), first free dispatcher: dispatch.c: pack_dest(647) - 
ignoring
CRITICAL: <core> [core/mem/q_malloc.c:578]: qm_free(): BUG: freeing already 
freed pointer (0xffff99c99360), called from dispatcher: dispatch.c: 
ds_avl_destroy(5132), first free dispatcher: dispatch.c: ds_avl_destroy(5132) - 
ignoring
CRITICAL: <core> [core/mem/q_malloc.c:152]: qm_debug_check_frag(): BUG: qm: 
prev. fragm. tail overwritten(736e6172743b3036, 
7064753d74726f70)[0xffff99c9bd60:0xffff99c9bda0]! Memory allocator was called 
from dispatcher: dispatch.c:5132. Fragment marked by dispatcher: 
dispatch.c:647. Exec from core/mem/q_malloc.c:569.
ALERT: <core> [main.c:824]: handle_sigs(): child process 16 exited by a signal 6
ALERT: <core> [main.c:828]: handle_sigs(): core was generated
```

#### SIP Traffic

The message content is not significant here — the flood consists of identical
plain OPTIONS requests and only the request rate matters. Captured traffic and
the generator script are in the attached repro archive:

```text
pcap/sip-unpatched.pcap          (full capture, unpatched run)
pcap/sip-unpatched-sample.pcap   (small sample capture, unpatched run)
pcap/sip-patched.pcap            (full capture, patched run)
stress/flood.py                  (traffic generator)
```

### Possible Solutions


Serialize the full dispatcher rebuild critical section with a shared dispatcher
lock. The lock should cover check/select/destroy/populate/publish in:

- `ds_load_list()`
- `ds_load_db()`
- `ds_add_dst()`
- `ds_remove_dst()`

I tested this with a dispatcher-global `gen_lock_t *ds_reload_lock`, allocated
in shared memory and released on every success/error return from those paths.
The patch does not lock the request-routing hot path; normal dispatch selection
continues to read the active slot as before. The tested patch is included in
the attached archive as `build/dispatcher-reload-lock.patch`. I can submit it 
as a pull request if the approach is acceptable.

```diff
--- a/src/modules/dispatcher/dispatch.c
+++ b/src/modules/dispatcher/dispatch.c
@@ -134,6 +134,7 @@ static ds_set_t **ds_lists = NULL;
 static int *ds_list_nr = NULL;
 static int *ds_crt_idx = NULL;
 static int *ds_next_idx = NULL;
+static gen_lock_t *ds_reload_lock = NULL;
 
 static ds_set_t *ds_strictest_node = NULL;
 static int ds_strictest_idx = 0;
@@ -315,6 +316,21 @@ int ds_init_data(void)
        ds_list_nr = p + 2;
        *ds_crt_idx = *ds_next_idx = 0;
 
+       ds_reload_lock = lock_alloc();
+       if(!ds_reload_lock) {
+               shm_free(p);
+               shm_free(ds_lists);
+               SHM_MEM_ERROR;
+               return -1;
+       }
+       if(lock_init(ds_reload_lock) == 0) {
+               lock_dealloc(ds_reload_lock);
+               shm_free(p);
+               shm_free(ds_lists);
+               LM_ERR("could not initialize reload lock\n");
+               return -1;
+       }
+
        return 0;
 }
 
@@ -1016,19 +1032,23 @@ int ds_load_list(char *lfile)
        str attrs;
        ds_latency_stats_t *latency_stats;
 
+       lock_get(ds_reload_lock);
        if((*ds_crt_idx) != (*ds_next_idx)) {
                LM_WARN("load command already generated, aborting reload...\n");
+               lock_release(ds_reload_lock);
                return 0;
        }
 
        if(lfile == NULL || strlen(lfile) <= 0) {
                LM_ERR("bad list file\n");
+               lock_release(ds_reload_lock);
                return -1;
        }
 
        f = fopen(lfile, "r");
        if(f == NULL) {
                LM_ERR("can't open list file [%s]\n", lfile);
+               lock_release(ds_reload_lock);
                return -1;
        }
 
@@ -1137,6 +1157,7 @@ int ds_load_list(char *lfile)
        /* Update list - should it be sync'ed? */
        _ds_list_nr = setn;
        *ds_crt_idx = *ds_next_idx;
+       lock_release(ds_reload_lock);
 
        LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
@@ -1148,6 +1169,7 @@ error:
                fclose(f);
        ds_avl_destroy(&ds_lists[*ds_next_idx]);
        *ds_next_idx = *ds_crt_idx;
+       lock_release(ds_reload_lock);
        return -1;
 }
 
@@ -1286,18 +1308,22 @@ int ds_load_db(void)
                }
        }
 
+       lock_get(ds_reload_lock);
        if((*ds_crt_idx) != (*ds_next_idx)) {
                LM_WARN("load command already generated, aborting reload...\n");
+               lock_release(ds_reload_lock);
                return 0;
        }
 
        if(ds_db_handle == NULL) {
                LM_ERR("invalid DB handler\n");
+               lock_release(ds_reload_lock);
                return -1;
        }
 
        if(ds_dbf.use_table(ds_db_handle, &ds_table_name) < 0) {
                LM_ERR("error in use_table\n");
+               lock_release(ds_reload_lock);
                return -1;
        }
 
@@ -1307,6 +1333,7 @@ int ds_load_db(void)
        if(ds_dbf.query(ds_db_handle, 0, 0, 0, query_cols, 0, nrcols, 0, &res)
                        < 0) {
                LM_ERR("error while querying database\n");
+               lock_release(ds_reload_lock);
                return -1;
        }
 
@@ -1395,6 +1422,7 @@ int ds_load_db(void)
        /* update data - should it be sync'ed? */
        _ds_list_nr = setn;
        *ds_crt_idx = *ds_next_idx;
+       lock_release(ds_reload_lock);
 
        LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
@@ -1408,6 +1436,7 @@ err2:
        ds_avl_destroy(&ds_lists[*ds_next_idx]);
        ds_dbf.free_result(ds_db_handle, res);
        *ds_next_idx = *ds_crt_idx;
+       lock_release(ds_reload_lock);
 
        return -1;
 }
@@ -1424,6 +1453,12 @@ int ds_destroy_list(void)
        if(ds_crt_idx)
                shm_free(ds_crt_idx);
 
+       if(ds_reload_lock) {
+               lock_destroy(ds_reload_lock);
+               lock_dealloc(ds_reload_lock);
+               ds_reload_lock = NULL;
+       }
+
        return 0;
 }
 
@@ -2890,6 +2925,7 @@ int ds_add_dst(int group, str *address, int flags, int 
priority, str *attrs)
 
        setn = _ds_list_nr;
 
+       lock_get(ds_reload_lock);
        *ds_next_idx = (*ds_crt_idx + 1) % 2;
        ds_avl_destroy(&ds_lists[*ds_next_idx]);
 
@@ -2914,6 +2950,7 @@ int ds_add_dst(int group, str *address, int flags, int 
priority, str *attrs)
 
        _ds_list_nr = setn;
        *ds_crt_idx = *ds_next_idx;
+       lock_release(ds_reload_lock);
 
        ds_log_sets();
        return 0;
@@ -2921,6 +2958,7 @@ int ds_add_dst(int group, str *address, int flags, int 
priority, str *attrs)
 error:
        ds_avl_destroy(&ds_lists[*ds_next_idx]);
        *ds_next_idx = *ds_crt_idx;
+       lock_release(ds_reload_lock);
        return -1;
 }
 
@@ -2972,6 +3010,7 @@ int ds_remove_dst(int group, str *address)
        filter_arg.dest = dp;
        filter_arg.setn = &setn;
 
+       lock_get(ds_reload_lock);
        *ds_next_idx = (*ds_crt_idx + 1) % 2;
        ds_avl_destroy(&ds_lists[*ds_next_idx]);
 
@@ -2985,6 +3024,7 @@ int ds_remove_dst(int group, str *address)
 
        _ds_list_nr = setn;
        *ds_crt_idx = *ds_next_idx;
+       lock_release(ds_reload_lock);
 
        ds_log_sets();
        return 0;
@@ -2992,6 +3032,7 @@ int ds_remove_dst(int group, str *address)
 error:
        ds_avl_destroy(&ds_lists[*ds_next_idx]);
        *ds_next_idx = *ds_crt_idx;
+       lock_release(ds_reload_lock);
        return -1;
 }
```


### Additional Information

- [x] LLM/AI Assistants were involved in discovery, analysis or submitting of 
the issue
- [x] It happened in a production deployment
- [ ] It happened in a testing or development deployment
- [x] Testing or fuzzing tools were used to generate SIP traffic when it 
happened

Originally occurred in prod and was later reproduced locally.

  * **Kamailio Version** - output of `kamailio -v`

```text
version: kamailio 6.2.0-dev1 (aarch64/linux)
flags: USE_TCP, USE_TLS, USE_SCTP, TLS_HOOKS, USE_RAW_SOCKS, DISABLE_NAGLE, 
USE_MCAST, DNS_IP_HACK, SHM_MMAP, PKG_MALLOC, MEM_JOIN_FREE, Q_MALLOC, 
F_MALLOC, TLSF_MALLOC, DBG_SR_MEMORY, DBG_QM_MALLOC, DBG_F_MALLOC, 
DBG_TLSF_MALLOC, USE_FUTEX, FAST_LOCK-ADAPTIVE_WAIT, USE_DNS_CACHE, 
USE_DNS_FAILOVER, USE_NAPTR, USE_DST_BLOCKLIST, HAVE_RESOLV_RES, 
TLS_PTHREAD_MUTEX_SHARED
ADAPTIVE_WAIT_LOOPS 1024, MAX_RECV_BUFFER_SIZE 262144, MAX_SEND_BUFFER_SIZE 
262144, MAX_URI_SIZE 1024, BUF_SIZE 65535, DEFAULT PKG_SIZE 8MB
poll method support: poll, epoll_lt, epoll_et, sigio_rt, select.
id: unknown
compiled on 09:55:20 Jul  2 2026 with gcc 14.2.0
```

* **Operating System**:

```text
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
NAME="Debian GNU/Linux"
VERSION_ID="13"
VERSION="13 (trixie)"
VERSION_CODENAME=trixie
DEBIAN_VERSION_FULL=13.5
ID=debian
HOME_URL="https://www.debian.org/";
SUPPORT_URL="https://www.debian.org/support";
BUG_REPORT_URL="https://bugs.debian.org/";

Linux 4d913b2b0ae0 6.10.14-linuxkit #1 SMP Thu Aug 14 19:26:13 UTC 2025 aarch64 
GNU/Linux
```


-- 
Reply to this email directly or view it on GitHub:
https://github.com/kamailio/kamailio/issues/4807
You are receiving this because you are subscribed to this thread.

Message ID: <kamailio/kamailio/issues/[email protected]>
_______________________________________________
Kamailio - Development Mailing List -- [email protected]
To unsubscribe send an email to [email protected]
Important: keep the mailing list in the recipients, do not reply only to the 
sender!

Reply via email to