fix: free leaked vorbis_info/vorbis_comment on recursive error unwind#126
Open
SongTonyLi wants to merge 1 commit intoxiph:mainfrom
Open
fix: free leaked vorbis_info/vorbis_comment on recursive error unwind#126SongTonyLi wants to merge 1 commit intoxiph:mainfrom
SongTonyLi wants to merge 1 commit intoxiph:mainfrom
Conversation
Fixes xiph#125. _bisect_forward_serialno() parses intermediate link headers into local temporaries (vorbis_info, vorbis_comment, next_serialno_list) via _fetch_headers(). When a deeper recursive call fails, the early return exits without freeing those locals — leaking ~27 KB per invocation on crafted multi-stream Ogg files. Two-site fix in lib/vorbisfile.c: 1. _bisect_forward_serialno() recursive error path: add vorbis_info_clear/vorbis_comment_clear/_ogg_free before return. 2. _fetch_headers() bail path: free *serialno_list when present.
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
Fixes #125.
_bisect_forward_serialno()parses intermediate link headers into local temporaries (vorbis_info,vorbis_comment,next_serialno_list) via_fetch_headers(). When a deeper recursive call fails, the early return at line 582 exits without freeing those locals. Ownership was never transferred tovf->vi[m+1]/vf->vc[m+1], soov_clear()can't reclaim them. This leaks ~27 KB per invocation on crafted multi-stream Ogg files.Root cause
Call chain:
ov_open_callbacks()->_ov_open2()->_open_seekable2()->_bisect_forward_serialno().I audited all six return sites in
_bisect_forward_serialno()(lines 548, 551, 569, 572, 582, 598). Five are safe: three fire before_fetch_headers()allocates anything, one fires when_fetch_headers()itself fails (bail_header cleans up), and the success path transfers ownership then frees the list. Line 582 is the sole leak edge: it fires after a successful_fetch_headers()filled the locals but before ownership transfers at lines 590-591.Forward instrumentation confirmed this: at
m=0(serialno 8738), the successful parse yields live heap pointers invi.codec_setup,vc.vendor,vc.user_comments, andnext_serialno_list. The deeperm=1parse then fails withret=-133, and unwind atm=0shows those same pointers leaked.Fix
Two-site patch in
lib/vorbisfile.c:_bisect_forward_serialno()recursive error path: addsvorbis_info_clear(&vi),vorbis_comment_clear(&vc), and_ogg_free(next_serialno_list)before returning the error code._fetch_headers()bail path: frees*serialno_listand zeroes*serialno_nwhen present, before clearingvi/vc.Why this approach
I looked at three possible fix sites:
_bisect_forward_serialno()(went with this one): the function owns the locals until explicit transfer, so this is the only place that can actually reach them on recursive failure._fetch_headers()bail cleanup only: doesn't work because bail only runs when_fetch_headers()itself fails, not when a later recursive call fails after a successful header parse.ov_clear()/_ov_open2()teardown: doesn't work either since the leaked objects are stack locals that never got assigned tovf->vi[m+1]/vf->vc[m+1], soov_clear()has no way to reach them.Verification
Tested with an ASan+LeakSanitizer-instrumented build:
SUMMARY: AddressSanitizer: 26929 byte(s) leaked in 89 allocation(s).Reproducer: