Free the previous publication ETag before storing a new one - #340
Open
alvm wants to merge 1 commit into
Open
Conversation
An application that refreshes a publication by handing back the ETag
it received - the documented way to use PUBLISH - leaks one sip_etag_t
per refresh, for the whole life of the handle.
nua_publish_client_init() stores the application-supplied
SIPTAG_IF_MATCH into the publish usage with sip_etag_dup() but never
releases the tag that is already there. Every other assignment to
pu_etag does: the 2xx handler in nua_publish_client_response() and the
412 path in nua_publish_client_check_restart() both su_free() first
(nua_publish.c:381, 403-404), and nua_publish_usage_remove() frees on
teardown (line 115).
The claim turns on whether the usage can already hold an ETag when
this runs, and it can: nua_dialog_usage_add() returns the usage that
is already there rather than creating a second one ("Already exists"
branch, nua_dialog.c), so on every refresh after the first pu_etag is
non-NULL and the block it points at is lost.
The block is allocated from nh_home and therefore stays reachable
through the su_home block table, so no leak checker reports it; it
shows up only as heap growth proportional to the publication refresh
rate. Measured with massif on a driver sending exactly 2.00 If-Match
publishes per second: 99.5% of all heap growth attributed to
sip_etag_dup, at 138.0 B/s with an interquartile range of
138.0-138.0 - exactly 2.00 blocks/s at 69 B per sip_etag_t, one block
per operation.
su_free() ignores a NULL pointer, so the first PUBLISH on a fresh
usage is unaffected. The duplicate is made before the old tag is
released, so a failed allocation leaves the usage holding the tag it
already had, and the source can never alias the block being freed.
The code has never been touched since it was written: `git log -L
305,320:libsofia-sip-ua/nua/nua_publish.c` returns 044ac9d of
2007-04-15 as the last commit to reach it.
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.
An application that refreshes a publication by handing back the ETag it received — the documented way to use PUBLISH — leaks one
sip_etag_tper refresh, for the whole life of the handle.nua_publish_client_init()stores the application-suppliedSIPTAG_IF_MATCHinto the publish usage withsip_etag_dup()but never releases the tag that is already there. Every other assignment topu_etagdoes: the 2xx handler innua_publish_client_response()and the 412 path innua_publish_client_check_restart()bothsu_free()first (nua_publish.c:381,403-404), andnua_publish_usage_remove()frees on teardown (line 115).The claim turns on whether the usage can already hold an ETag when this runs, and it can:
nua_dialog_usage_add()returns the usage that is already there rather than creating a second one ("Already exists" branch innua_dialog.c), so on every refresh after the firstpu_etagis non-NULL and the block it points at is lost.The block is allocated from
nh_homeand therefore stays reachable through thesu_homeblock table, so no leak checker reports it — valgrind and the sanitizers see a live allocation, because it is one. It shows up only as heap growth proportional to the publication refresh rate. Measured with massif on a driver sending exactly 2.00 If-Match publishes per second: 99.5 % of all heap growth attributed tosip_etag_dup, at 138.0 B/s with an interquartile range of 138.0–138.0 — exactly 2.00 blocks/s at 69 B persip_etag_t, one block per operation.The duplicate is made before the old tag is released, so a failed allocation leaves the usage holding the tag it already had, and the source can never alias the block being freed.
su_free()ignores NULL, so the first PUBLISH on a fresh usage is unaffected.The code has never been touched since it was written:
git log -L 305,320:libsofia-sip-ua/nua/nua_publish.creturns044ac9dof 2007-04-15 as the last commit to reach it.No unit test, and why
The scaffolding for this path is
check_nua, which drives a whole stack; a test there is a considerably larger job than the four-line fix, and I would rather offer it separately than hold this up. I see from #282 that you ask contributors for tests, so I want to be explicit rather than quiet about it — the companion PR #341 for theAuthentication-Infononce leak does come with one.Verification
Built and checked in a clean
debian:trixiecontainer (gcc 14.2.0, autoconf 2.72, automake 1.17, glibc 2.41):autogen.sh,configure, full build of libsofia,make checkinlibsofia-sip-ua/iptsecpasses.nua_publish.ccompiles to the same 9 warnings with-Wall -Wextrabefore and after the patch, so this change adds none.The fix has also been running in production on two instances for several days at the time of writing, in a deployment where PUBLISH refreshes are the dominant traffic.