Skip to content

tm: fix $rd reversion after chained async() resume#3821

Open
NormB wants to merge 1 commit intoOpenSIPS:masterfrom
NormB:fix/tm-async-rd-reversion
Open

tm: fix $rd reversion after chained async() resume#3821
NormB wants to merge 1 commit intoOpenSIPS:masterfrom
NormB:fix/tm-async-rd-reversion

Conversation

@NormB
Copy link
Member

@NormB NormB commented Feb 12, 2026

Summary

When async() is called from a resume route (chained async), the FL_TM_FAKE_REQ check
in t_handle_async() prevents update_cloned_msg_from_msg() from saving message state
to the transaction's shm clone. This causes $rd, $du, and other msg fields modified
in the resume route to revert to their previous values when the next async operation
completes through the reactor path.

The bug is intermittent because it only manifests when async goes through the reactor
(slow endpoint). When the transfer completes during curl's busy-wait loop (ASYNC_SYNC),
the resume route runs inline with the same pkg memory msg pointer and the bug is masked.

Fixes #3676

Root Cause

In modules/tm/async.c line 446, the condition:

if ((t->uas.request) && (route_type==REQUEST_ROUTE) && ((msg->msg_flags & FL_TM_FAKE_REQ) == 0))
    update_cloned_msg_from_msg( t->uas.request, msg);

The FL_TM_FAKE_REQ check was originally an optimization to avoid redundantly updating
the shm clone from a message reconstructed from that same clone. However, when async()
is called from a resume route, the resume route's route_type is restored to
REQUEST_ROUTE (via swap_route_type at line 194), but the msg is a faked_req with
FL_TM_FAKE_REQ set. The check blocks the update, so any $rd/$du/flag/lump changes
made in the resume route are lost when the next async completes through the reactor.

Fix

Remove the FL_TM_FAKE_REQ guard:

if ((t->uas.request) && (route_type==REQUEST_ROUTE))
    update_cloned_msg_from_msg( t->uas.request, msg);

This is safe because update_cloned_msg_from_msg() already handles faked_req sources
correctly:

  • Lump memory: defers freeing old shm lumps when FL_TM_FAKE_REQ is set
    (sip_msg.c:1325-1336), and free_faked_req() completes the deferred free
    (t_msgbuilder.h:385-393)
  • URI fields: REALLOC/COPY macros work identically on pkg memory from either
    original or faked_req sources
  • Body: independent pkg copy in faked_req, unaffected by shm re-clone
  • No use-after-free: faked_req retains valid references to old shm lumps
  • No double-free: free_faked_req only frees lumps that differ from current clone
  • No leak: old shm lumps freed by free_faked_req, not leaked

The only affected code path is REQUEST_ROUTE with a faked_req (chained async from
REQUEST_ROUTE). FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE are unaffected.

Test Results

Tested on OpenSIPS 4.0.0-dev compiled from source.

Core tests:

  • Single async from REQUEST_ROUTE: PASS (no regression)
  • Double async with slow endpoint (reactor path): PASS ($rd preserved)
  • Double async with fast endpoint (ASYNC_SYNC path): PASS ($rd preserved)

Edge cases (all PASS):

  • Triple-chained async (3 reactor hops)
  • $du preservation across double async
  • Mixed slow/fast async chain
  • Multiple $rd changes in single resume before next async
  • $rd set back to original incoming value in resume
  • Named flags set/reset/add across chained async
  • Fast triple async (ASYNC_SYNC path)

Concurrency stress:

  • 20 concurrent double-async requests: 20/20 PASS
  • 50 concurrent double-async requests: 50/50 PASS
  • Zero crashes, zero segfaults, zero memory errors across all 4 worker processes

When async() is called from a resume route (chained async), the
FL_TM_FAKE_REQ check in t_handle_async() prevents
update_cloned_msg_from_msg() from saving message state to the
transaction's shm clone. This causes $rd, $du, and other msg fields
modified in the resume route to revert to their previous values when
the next async operation completes through the reactor path.

Remove the FL_TM_FAKE_REQ guard from the conditional. The
update_cloned_msg_from_msg() function already handles faked_req
sources correctly: it defers freeing old shm lumps (lines 1325-1336
of sip_msg.c), and free_faked_req() completes the deferred cleanup.

Fixes OpenSIPS#3676
@NormB NormB requested a review from bogdan-iancu February 12, 2026 02:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] (v3.4.12) contents of $rd variable changing during async(rest_get())

1 participant