Skip to content

[mod_dialplan_xml] Keep regex=all ${var} $1 captures to fix issue#3141 - #3151

Open
walterfan wants to merge 3 commits into
signalwire:masterfrom
walterfan:fix/mod_dialplan-xml-regex-all-capture
Open

walterfan wants to merge 3 commits into
signalwire:masterfrom
walterfan:fix/mod_dialplan-xml-regex-all-capture

Conversation

@walterfan

Copy link
Copy Markdown

Problem

Fixes #3141

  • Affected version / commit: 1.11.2-release and current master (parse_exten in mod_dialplan_xml.c)
  • Environment: Debian 12 x86_64 (reporter); reproduced here with the in-tree FST binary (no live SIP)
  • Symptom: inside <condition regex="all">, a <regex field="${var}"> that matches still leaves $1 empty in the condition actions. The same expression works as a classic single <condition>, or when field is a bare caller-profile name.

Reproduction:

1. Dialplan with regex="all", empty ${did_number} =~ /^$/, and
   ${sip_h_Diversion} =~ /sip:1(\d+)@.+$/
2. INVITE with Diversion: sip:17001234567@192.0.2.1;reason=unconditional
3. Action log("transfer to $1") prints "transfer to " instead of
   "transfer to 7001234567"

Root cause

switch_regex_perform() stores PCRE2 match offsets into the subject string. In the regex="all" loop, ${var} is expanded into heap field_expanded, then on a capturing match the code did save_field_data = strdup(field_data) and switch_safe_free(field_expanded) while save_match_data still pointed at the original buffer. Later switch_perform_substitution() called pcre2_substring_get_bynumber() on that dangling subject.

The strdup only served strlen(field_data) for buffer sizing; it did not rebind PCRE2's subject pointer.

Solution

If the subject is the expanded heap buffer, transfer ownership to save_field_data (field_expanded = NULL) so the later free of field_expanded does not run. Profile fields (no $) still strdup as before; those subjects live in the caller profile.

Rejected: PCRE2_COPY_MATCHED_SUBJECT in switch_regex_perform — extra copy on every match site, larger blast radius.

Impact and risk

  • Behaviour change for existing users: regex="all|any|xor" actions that used $1 with field="${var}" now get the capture instead of an empty string. Match/fail of the condition itself is unchanged.
  • ABI / API / config compatibility: unchanged
  • Performance impact: none measured (avoids a strdup of the same string)

Testing

  • Built mod_dialplan_xml and tests/unit/switch_dialplan_xml
  • Reproduced the failure before the patch (Action log(transfer to ), 1/2 failing)
  • Verified the failure is gone after the patch (transfer to 7001234567)
  • Added a regression test: tests/unit/switch_dialplan_xml.c
    • regex_all_var_field_keeps_capture
    • classic_var_field_keeps_capture
    • regex_all_second_regex_fail_skips_action
    • regex_all_did_already_set_skips_action
    • classic_var_field_mismatch_skips_action
  • Ran ./tests/unit/switch_dialplan_xmlPASSED (5/5)
  • ASan: not run in this session

Checklist

  • One logical change; no unrelated formatting, renames, or refactors
  • Commit messages follow this project's [module] summary convention
  • Sign-off / CLA: not required (recent history almost never uses DCO)
  • Docs/changelog: not required by this project for this class of fix

Walter Fan and others added 3 commits September 9, 2026 22:16
Inside regex="all", field="${var}" saved PCRE2 match_data then
freed the expanded subject, so later $1 substitution was empty.

Transfer field_expanded ownership to save_field_data instead of
strdup+free. Add FST coverage for issue 3141 and mismatch paths.

Fixes signalwire#3141

Co-authored-by: Cursor <cursoragent@cursor.com>
@walterfan

Copy link
Copy Markdown
Author

@andywolk, do you know who can review this PR?

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.

[mod_dialplan_xml] Capture groups from <regex field="${var}"> inside regex="all" are empty (use-after-free of PCRE2 subject)

1 participant