Fixes #4503.
### Problem
Transactions leak when `drop;` executes in a `branch_route` and modules
like `pua_dialoginfo` are loaded. The leaked transactions persist
indefinitely in shared memory — no final reply is sent, no timer fires,
and `wait_handler()` never frees the cell.
### Cause
`set_kr()` accumulates flags with `|=`, but the first cleanup check in
`t_unref()` uses exact equality:
```c
if(unlikely(kr == REQ_ERR_DELAYED)) { // fails when other flags are set
```
When `pua_dialoginfo` processes an INVITE, it calls `dialog_publish_multi()`
inline during `DLGCB_CREATED`, which goes through `pua_send_publish()` →
`tmb.t_request()` → `t_uac_prepare()` → `set_kr(REQ_FWDED)`. If the
INVITE's `branch_route` then calls `drop;`, `t_relay_to()` ORs in
`REQ_ERR_DELAYED`, making `_tm_kr = REQ_FWDED | REQ_ERR_DELAYED` (17).
In `t_unref()`:
- Check 1: `17 == 16` → false
- Check 2: `17 == 0` → false
- Check 3: `(17 & 16)` true, but `(17 & ~(4|2|16|1))` = 0 → false
All three checks fail. The transaction is orphaned.
This affects any module that calls `t_request()` or `t_uac_prepare()` inline
during INVITE processing — `pua_dialoginfo` is one common example.
### Fix
Change the check from exact equality to a bitwise test:
```c
- if(unlikely(kr == REQ_ERR_DELAYED)) {
+ if(unlikely(kr & REQ_ERR_DELAYED)) {
```
The delayed-reply handler now fires whenever `REQ_ERR_DELAYED` is present,
regardless of other flags accumulated by `set_kr()`.
This is safe because:
- `REQ_ERR_DELAYED` is only set in `t_relay_to()` when `t_forward_nonack()`
fails and the delayed-reply path is chosen — it is never set spuriously.
- `kill_transaction()` already handles the case where a final reply was
previously sent (`FL_FINAL_REPLY` check → `t_release_transaction()`).
The existing check-3 fallback (lines 2077-2084) becomes unreachable after
this change but is harmless to leave in place.
### Workaround
On 6.1+ where `delayed_reply` is a runtime modparam, setting
`modparam("tm", "delayed_reply", 0)` avoids the vulnerable
code path.
On ≤ 6.0.x, `TM_DELAYED_REPLY` is a compile-time `#define` (always on)
and there is no workaround — only this patch fixes the leak.
### Test results
Ten-scenario matrix across 6.0.3, 6.1.0, and 6.2.0-dev0 master. 1000
INVITEs per run, `fr_timer=5000`, `branch_route` with unconditional
`drop;`. Contamination triggered naturally by loading `dialog` + `pua` +
`pua_dialoginfo` — no source modification beyond this patch.
| # | Version | Patch | `delayed_reply` | current | 5xx | SHM delta | Verdict |
|---|---------|-------|:---------------:|--------:|----:|----------:|---------|
| 1 | 6.0.3 | no | x | 431 | 0 | +7,150,272 | **LEAK** |
| 2 | 6.0.3 | yes | x | 0 | 353 | 0 | OK |
| 3 | 6.1.0 | no | 1 | 431 | 0 | +7,287,200 | **LEAK** |
| 4 | 6.1.0 | no | 0 | 0 | 375 | 0 | OK |
| 5 | 6.1.0 | yes | 1 | 0 | 407 | 0 | OK |
| 6 | 6.1.0 | yes | 0 | 0 | 336 | 0 | OK |
| 7 | 6.2.0-dev0 | no | 1 | 446 | 0 | +7,770,288 | **LEAK** |
| 8 | 6.2.0-dev0 | no | 0 | 0 | 461 | 0 | OK |
| 9 | 6.2.0-dev0 | yes | 1 | 0 | 448 | 0 | OK |
| 10 | 6.2.0-dev0 | yes | 0 | 0 | 448 | 0 | OK |
`delayed_reply` = x: not available as a modparam (compile-time, always on).
`current`: transactions remaining in hash table after test.
`5xx`: 500 replies sent by the delayed-reply handler (nonzero = cleanup ran).
---
*Note: AI tooling was used to assist with the code analysis, reproduction,
and test automation for this issue. All diagnosis, testing, and review was
supervised by @NormB — the AI did not operate autonomously. The root cause
identification, fix selection, and validation were guided and verified at
each step.*
You can view, comment on, or merge this pull request online at:
https://github.com/kamailio/kamailio/pull/4644
-- Commit Summary --
* tm: use bitwise check for REQ_ERR_DELAYED in t_unref()
-- File Changes --
M src/modules/tm/t_lookup.c (2)
-- Patch Links --
https://github.com/kamailio/kamailio/pull/4644.patch
https://github.com/kamailio/kamailio/pull/4644.diff
--
Reply to this email directly or view it on GitHub:
https://github.com/kamailio/kamailio/pull/4644
You are receiving this because you are subscribed to this thread.
Message ID: <kamailio/kamailio/pull/[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!