rtp_relay: give the caller its own copy of the route engine's body - #4256
Open
Lt-Flash wants to merge 1 commit into
Open
rtp_relay: give the caller its own copy of the route engine's body#4256Lt-Flash wants to merge 1 commit into
Lt-Flash wants to merge 1 commit into
Conversation
rtp_relay_route_fill_body() returned the script's return value by assignment, but script_return_set() stores that value in a single pkg allocation with an inline buffer (v->val.rs.s = v->buf), so val.rs.s points 32 bytes into a block the core owns and frees itself. The callers of the offer/answer engine hooks take ownership of the body they get back: rtp_relay_reinvite() pkg_free()s it, and the reply path hands it to replace_lump_rpl() with LUMP_RPL_NODUP, which frees it too. The rtpengine engine already honours that contract, filling the body from bencode_dictionary_get_str_dup(). The route engine did not, so the first of those frees ran on an interior pointer and f_malloc read the fragment header from inside the allocation's own header, ending in a SIGSEGV in fm_remove_free(). check_double_free() cannot catch it: frag_seems_valid() only tests that f->pf lies inside the block, and here f->pf reads val.rs.s, which is the pointer being freed. Duplicate the value into the caller's str instead. Also drop the second rtp_relay_replace_body() in rtp_relay_route_offer(): the message body is already replaced inside rtp_relay_route_fill_body() using a copy of its own, and now that the caller owns the returned buffer, passing it to a lump as well would make the message free it a second time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix a crash in the
rtp_relayroute engine: the body produced by the script route was handed to the caller without a copy, while the caller takes ownership of it and frees it. Reported as #4255.Details
rtp_relay_route_fill_body()returned the script's return value by assignment:but
script_return_set()(route.c) stores that value in a single pkg allocation with an inline buffer:struct script_return_valueispv_value_t val(24) +next(8) +char buf[0], soval.rs.spoints 32 bytes into a block the core owns and releases itself inscript_return_free().The callers of the offer/answer engine hooks do take ownership of the body they get back:
rtp_relay_reinvite()callspkg_free(body->s)whenrelease_bodyis set - this is thertp_relay:updateMI path;replace_lump_rpl(..., LUMP_RPL_NODUP), andfree_lump_rpl()frees a NODUP text.The rtpengine engine already honours that contract: it fills the body from
bencode_dictionary_get_str_dup(), a real allocation base. The route engine did not, so the free ran on an interior pointer -f_mallocreads the fragment header fromp - sizeof(struct fm_frag), which lands inside the allocation's own header, and then follows it. With F_MALLOC that is a SIGSEGV infm_remove_free(), with Q_MALLOC an abort ("fragm ... beginning overwritten").check_double_free()cannot catch this one:frag_seems_valid()only tests thatf->pflies inside the block, and heref->pfreadsval.rs.s, which is the pointer being freed, so it is always in range.Solution
Duplicate the returned value into the caller's
strwithpkg_str_dup(), so what the caller frees is a real allocation base - the same contract the rtpengine engine already implements.Also drop the second
rtp_relay_replace_body(sess->msg, body)inrtp_relay_route_offer():rtp_relay_route_fill_body()has already replaced the message body using a copy of its own, anddel_lump()does not detect the overlapping deletion, so now that the caller owns the returned buffer, passing it to a lump as well would make the message free it a second time.Verified on master (
0a33489dc5), two prefixes from the same revision differing only by this patch. 20 calls established withrtp_relay_engage("route")and held, then onertp_relay:update:-a F_MALLOC-a Q_MALLOCfm_remove_free()In the patched runs the offer route ran 40 times (20 initial engagements + 20 re-INVITE offers), so the freeing path was exercised once per context.
Compatibility
No script or configuration change, and no change to the engine hook contract - this only brings the route engine in line with what the rtpengine engine already does.
One pre-existing problem this does not touch:
media_exchange_get_offer_sdp()overwrites the returned body on its success path without releasing it, so it leaks with any engine that returns an owned buffer (rtpengine today, route after this change). That belongs tomedia_exchangeand is left alone here to keep this fix minimal.Closing issues
closes #4255