Repository navigation
vp8: Fix integer overflow in vp8_yv12_realloc_frame_buffer - #13
Open
itsgaldoron wants to merge 1 commit into
Open
itsgaldoron wants to merge 1 commit into
itsgaldoron wants to merge 1 commit into
Conversation
The VP8 frame buffer allocator computed yplane_size, uvplane_size and frame_size in a signed int with no dimension or overflow check, so legal width/height/border values wrap the plane sizes (e.g. 100000x100000 with the default border is accepted with a 2134304256-byte allocation for a 15019206144-byte frame, and border = 1 << 29 with a 32x32 frame is accepted with a 1536-byte allocation and the plane pointers placed hundreds of MB past it). The wrapped values also feed the u_buffer and v_buffer pointer offsets. Compute the plane sizes and pointer offsets in uint64_t and add the limit checks the VP9 sibling in this file, vpx_realloc_frame_buffer(), already carries: a CONFIG_SIZE_LIMIT dimension bound, a VPX_MAX_ALLOCABLE_MEMORY / REF_FRAMES bound and a frame_size > SIZE_MAX fail-early. Validate width, height and border before the arithmetic (negative values are rejected; the magnitudes are bounded at INT_MAX / 4 so the remaining int stride math cannot overflow) and move the existing border & 0x1f check ahead of the allocation. Add test/yv12config_test.cc: the wrap cases must be rejected or sized at the exact 64-bit value, huge/negative borders and negative dimensions must be rejected, and normal frames must report the unchanged layout.
Author
|
libvpx reviews via Gerrit ( |
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.
The defect
vp8_yv12_realloc_frame_buffer()computes the plane sizes in a signedint:At dimensions and a border that fit the
intparameters these products wrap, and the wrapped values are used both for thevpx_memalign(32, frame_size)allocation and for theu_buffer/v_bufferplane pointer placement. The only validation isborder & 0x1f, which is applied after the allocation. Measured on the unpatched tree (x86-64, gcc 11 -O2), true sizes computed in 64-bit:yplane_sizeyplane_size(int32)frame_sizeframe_sizesum wraps a second time to a negative int on this build (undefined behavior — it can as well land in the accepted rows)size_t)y_bufferplaced atbuffer_alloc + 536,870,912border = 2^29is a multiple of 32 and passes the only existing check. In the accepted rows, any encode/decode/fill of the logical frame writes past the end of the allocation, and the plane pointers are computed from the same wrapped products. UBSan reports signed integer overflow at exactly these sites on the unpatched tree (yv12config.c:57yplane_size,:63uvplane_size,:64frame_size,:111/:113the plane pointer offsets).The VP9 allocator in the same file,
vpx_realloc_frame_buffer(), already does this arithmetic inuint64_tand carries the full guard set (CONFIG_SIZE_LIMITdimension bound,VPX_MAX_ALLOCABLE_MEMORY / REF_FRAMESbound,frame_size > SIZE_MAXfail-early, external-frame-size cast check). The VP8 path has none of it.The fix
Port the sibling's proven pattern to the VP8 path, minimal:
yplane_size,uvplane_size,frame_sizeinuint64_t, and compute the plane pointer offsets from the same 64-bit products (they had the same wrap);vpx_realloc_frame_buffer():#if CONFIG_SIZE_LIMITdimension bound,#if defined(VPX_MAX_ALLOCABLE_MEMORY)frame_size > VPX_MAX_ALLOCABLE_MEMORY / REF_FRAMES,#if UINT64_MAX > SIZE_MAXframe_size > SIZE_MAX, each failing with -1 exactly like the VP9 path;width/heightare rejected, andwidth,height,borderare bounded atINT_MAX / 4so the remainingintstride math (aligned_width + 2 * border + 31 <= 3 * (INT_MAX / 4) + 46 < INT_MAX) cannot overflow; negativeborderis rejected with the existing -3 code (it would place the plane pointers outside the buffer);border & 0x1fcheck (comment unchanged) ahead of the allocation instead of after it.Legal frames are byte-identical to before: same layout formula, same return codes on the allocation-failure paths,
vp8_yv12_de_alloc_frame_buffer()untouched. Measured after the patch (same probe): 65,536 x 65,536 reportsframe_size = 6,455,040,000, 46,341 x 46,341 reports3,232,781,568, 100,000 x 100,000 reports15,019,206,144(each either allocated at full size or failing cleanly invpx_memalign- never a wrapped size),border = 2^29is rejected with -3 before any allocation, and the two legal control rows (16x16, 16383x16383) report the same sizes as the unpatched build. UBSan reports no runtime errors on the patched tree.Regression test
New
test/yv12config_test.cc(registered intest/test.mkunder the unconditional whitebox tests), with the expected layout computed independently in 64-bit:Vp8NormalFrames- 16x16, 352x288, 16383x16383 withVP8BORDERINPIXELSmust still allocate and report the exact frame size and plane offsets. Passes before and after the change (behavior-neutral for legal input).Vp8WrappingSizesNotUndersized- the wrapping dimension rows must never be accepted with a wrapped size: either the call fails, or the buffer is the full 64-bit size at the exact plane offsets.Vp8OversizedBorderRejected-border = 1 << 29must be rejected outright.Vp8NegativeArgsRejected- negative width/height/border must be rejected.Unpatched tree (control build, same test file):
Patched tree:
The full
test_libvpxsuite (13,058 tests) was run on both the unpatched and the patched tree and the per-test results were diffed: the only differences are the three new failing tests turning green; everything else is identical. The environment's ~10,105 failures are missing-external-testdata artifacts (decode/encode corpus, y4m files) that reproduce identically on both trees - they are artifacts of the environment, not of this change.Honest boundary
vp8_yv12_alloc_frame_buffer()/vp8_yv12_realloc_frame_buffer()are internal (vpx_scale/yv12config.h, not an installedvpx/header), and every in-tree caller is bounded far below the wrapping range: the VP8 bitstream frame header carries width/height in 14 bits, the encoder config enforcesRANGE_CHECK(cfg, g_w, 1, 16383)/g_h, andCONFIG_SIZE_LIMITbounds the decode interface where enabled - at 16,383 the plane math tops out around 2.7e8. So this is not a demonstrated in-library overflow today; it is API-soundness hardening of an allocator whose sibling function in the same file already carries the full guard set, in the same spirit as pnggroup/libpng#967 (sizing arithmetic that wraps at legal inputs of the API).