NormB left a comment (kamailio/kamailio#4629)
## Analysis & Fix for #4629 — DTMF Event SIGSEGV in rtpengine Module
I was able to reproduce this crash and have a proposed fix. Sharing the full
analysis below in case it's helpful.
### Root Cause
`rtpengine_raise_dtmf_event()` unconditionally dereferences all 10 `pv_spec_t*`
pointers via `->setf()` when processing incoming DTMF event JSON. These
pointers are only non-NULL if the corresponding `modparam` has been configured.
Six new DTMF pvars were added in 6.1:
| New in 6.1 | modparam |
|------------|----------|
| `dtmf_event_source_label_pvar` | `dtmf_event_source_label` |
| `dtmf_event_tags_pvar` | `dtmf_event_tags` |
| `dtmf_event_type_pvar` | `dtmf_event_type` |
| `dtmf_event_source_ip_pvar` | `dtmf_event_source_ip` |
| `dtmf_event_duration_pvar` | `dtmf_event_duration` |
| `dtmf_event_volume_pvar` | `dtmf_event_volume` |
When rtpengine sends a DTMF event containing any of these new fields (e.g.,
`source_label`) and the user hasn't configured the corresponding modparam, the
code dereferences a NULL `pv_spec_t*`:
```c
// rtpengine.c:1762 — dtmf_event_source_label_pvar is NULL
if(dtmf_event_source_label_pvar->setf(0,
&dtmf_event_source_label_pvar->pvp, (int)EQ_T, &pv_val) < 0) {
```
The `setf` function pointer sits at offset `0x10` (16 bytes) in `pv_spec_t` on
x86_64, so `NULL->setf` produces the `segfault at 10` seen in the report.
This is a regression from 5.8, where only 4 DTMF pvars existed and the new JSON
fields were simply ignored by the parser.
### Crash Signature Match
Our reproduction matches the reporter's crash:
| | Reporter | Reproduction |
|--|----------|--------------|
| **Fault address** | `segfault at 10` | `segfault at 10` |
| **Error code** | `error 4` (user-mode read) | SIGSEGV |
| **Module** | `rtpengine.so` | `rtpengine.so` |
| **Version** | `kamailio 6.1.1 (x86_64)` | `kamailio 6.1.1 (x86_64)` |
| **Frames in module** | 2 (`#0` + `#1`) | 2 (`rtpengine_raise_dtmf_event` →
`rtpengine_dtmf_events_loop`) |
| **Outer chain** | `init_child → main_loop → main` | `child_init → init_child
→ main_loop → main` |
The `segfault at 10` is distinctive — it's not a generic NULL deref at `0x0`,
but specifically the `setf` field at offset `0x10` in `pv_spec_t`, which
uniquely identifies this class of bug.
### Immediate Workaround
While waiting for a fix, adding the 6 new modparams to your configuration will
prevent the crash:
```cfg
modparam("rtpengine", "dtmf_event_source_label", "$var(dtmf_source_label)")
modparam("rtpengine", "dtmf_event_tags", "$var(dtmf_tags)")
modparam("rtpengine", "dtmf_event_type", "$var(dtmf_type)")
modparam("rtpengine", "dtmf_event_source_ip", "$var(dtmf_source_ip)")
modparam("rtpengine", "dtmf_event_duration", "$var(dtmf_duration)")
modparam("rtpengine", "dtmf_event_volume", "$var(dtmf_volume)")
```
This initializes the pvar pointers so they are non-NULL when the DTMF event
handler runs. No rebuild required.
### Reproduction
**Environment:** Debian 13 (trixie), x86_64, Kamailio built from source.
**Test config** — mimics a 5.8-era configuration with only the original 4 DTMF
pvars:
```cfg
#!KAMAILIO
debug=3
log_stderror=yes
fork=yes
children=1
auto_aliases=no
listen=udp:127.0.0.1:5060
mpath="/usr/local/kamailio-6.1/lib64/kamailio/modules/"
loadmodule "rtpengine.so"
loadmodule "pv.so"
loadmodule "xlog.so"
loadmodule "sl.so"
loadmodule "tm.so"
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:22222")
modparam("rtpengine", "dtmf_events_sock", "127.0.0.1:9999")
modparam("rtpengine", "dtmf_event_callid", "$var(dtmf_callid)")
modparam("rtpengine", "dtmf_event_source_tag", "$var(dtmf_source_tag)")
modparam("rtpengine", "dtmf_event_timestamp", "$var(dtmf_timestamp)")
modparam("rtpengine", "dtmf_event", "$var(dtmf_event)")
# The 6 new pvars are intentionally NOT configured
event_route[rtpengine:dtmf-event] {
xlog("L_INFO", "DTMF event received\n");
}
request_route {
sl_send_reply(200, "OK");
}
```
**Trigger** — send a DTMF event JSON containing any of the new fields:
```bash
echo -n
'{"callid":"test-call-123","source_tag":"tag1","timestamp":1709942400,"event":1,"source_label":"caller"}'
\
| nc -u -w1 127.0.0.1 9999
```
### Test Results
The same DTMF event JSON is used for tests 1–3 and 5–6 to isolate the variable
under test (the Kamailio version/patch):
```json
{"callid":"test-call-123","source_tag":"tag1","timestamp":1709942400,"event":1,"source_label":"caller"}
```
Tests 4 and 7 use a JSON payload with all 10 fields populated to exercise every
pvar path:
```json
{"callid":"test-call-123","source_tag":"tag1","timestamp":1709942400,"event":1,
"source_label":"caller","tags":["audio","video"],"type":1,
"source_ip":"10.0.0.1","duration":160,"volume":10}
```
**6.1 branch** (reporter's version):
| Test | Version | Config | Input | Result |
|------|---------|--------|-------|--------|
| 1 | **6.1.1 (unpatched)** | 4 original pvars | JSON with `source_label` |
**SIGSEGV** — core dump, `segfault at 10` |
| 2 | **5.8.8** | 4 original pvars | same JSON | **No crash** — field ignored
(no handler in 5.8) |
| 3 | **6.1.1 (patched)** | 4 original pvars | same JSON | **No crash** — field
skipped by NULL guard |
| 4 | **6.1.1 (patched)** | all 10 pvars | JSON with all 10 fields | **No
crash** — all fields populated correctly |
**master** (6.2.0-dev0, commit `91a95ec`):
| Test | Version | Config | Input | Result |
|------|---------|--------|-------|--------|
| 5 | **master (unpatched)** | 4 original pvars | JSON with `source_label` |
**SIGSEGV** — same crash as 6.1 |
| 6 | **master (patched)** | 4 original pvars | same JSON | **No crash** —
field skipped by NULL guard |
| 7 | **master (patched)** | all 10 pvars | JSON with all 10 fields | **No
crash** — all fields populated correctly |
### GDB Backtrace (unpatched 6.1.1)
```
Core was generated by `kamailio -f kamailio-dtmf-test.cfg -DD -E'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f5941c2905b in rtpengine_raise_dtmf_event (
buffer=0x7f593dbc6980
"{\"callid\":\"test-call-123\",\"source_tag\":\"tag1\",
\"timestamp\":1709942400,\"event\":1,\"source_label\":\"caller\"}", len=103)
at rtpengine.c:1762
1762 if(dtmf_event_source_label_pvar->setf(0,
#1 0x00007f5941c2672f in rtpengine_dtmf_events_loop () at rtpengine.c:1646
#2 0x00007f5941c3b345 in child_init (rank=0) at rtpengine.c:3058
#3 0x000055624ee9fbbc in init_mod_child (m=..., rank=0) at core/sr_module.c:934
...
#9 0x000055624ec3f978 in main_loop () at main.c:2034
```
### Proposed Fix
The patch wraps each of the 10 `->setf()` dereferences in
`rtpengine_raise_dtmf_event()` with a NULL check on the corresponding pvar
pointer. When a pvar is unconfigured, the JSON field is silently skipped. Shown
here abbreviated for readability — the full patch applies the same pattern to
all 10 fields:
```diff
--- a/src/modules/rtpengine/rtpengine.c
+++ b/src/modules/rtpengine/rtpengine.c
@@ -1691,14 +1691,16 @@
if(strcmp(it->string, "callid") == 0) {
- pv_value_t pv_val;
- pv_val.rs.s = it->valuestring;
- ...
- if(dtmf_event_callid_pvar->setf(
- 0, &dtmf_event_callid_pvar->pvp,
...) < 0) {
+ if(dtmf_event_callid_pvar) {
+ pv_value_t pv_val;
+ pv_val.rs.s = it->valuestring;
+ ...
+ if(dtmf_event_callid_pvar->setf(0,
+
&dtmf_event_callid_pvar->pvp, ...) < 0) {
+ }
}
} else if(strcmp(it->string, "source_label") == 0) {
- ...
- if(dtmf_event_source_label_pvar->setf(...) < 0) {
+ if(dtmf_event_source_label_pvar) {
+ ...
+ if(dtmf_event_source_label_pvar->setf(...) < 0)
{
+ }
}
// ... same pattern for all 10 fields ...
```
This is a minimal fix that addresses the immediate crash. The module
maintainers, being more familiar with the internals of the rtpengine module,
may prefer a different approach — for example, restructuring the function to
use a lookup table, or requiring certain pvars as mandatory when
`dtmf_events_sock` is configured. Happy to adjust the patch based on feedback.
Note that while the crash is triggered by the 6 new pvars added in 6.1, the
NULL guards are applied to all 10 for defense-in-depth, since the original 4
have the same vulnerability if a user configures `dtmf_events_sock` without any
pvars.
--
Reply to this email directly or view it on GitHub:
https://github.com/kamailio/kamailio/issues/4629#issuecomment-4028298442
You are receiving this because you are subscribed to this thread.
Message ID: <kamailio/kamailio/issues/4629/[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!