Fix unchecked integer overflow in encoder UV plane allocation#3233
Fix unchecked integer overflow in encoder UV plane allocation#3233jortles wants to merge 2 commits into
Conversation
Add pre-multiplication overflow checks to codec_avm.c and codec_svt.c for UV plane size computations, consistent with the existing safe pattern in avifImageAllocatePlanes() (avif.c:435-441). Also add an overflow check in avifImageCopyProperties() before the numProperties allocation, consistent with avifImagePushProperty() (avif.c:387). Without these checks, crafted image dimensions can silently wrap the uint32_t multiplication, leading to undersized allocations. Signed-off-by: Anthony Hurtado <amhurtado@pm.me>
y-guyon
left a comment
There was a problem hiding this comment.
Thank you for your interest in libavif.
| // Allocate the U plane if necessary. | ||
| if (!avmImageAllocated) { | ||
| uint32_t channelSize = avifImageUsesU16(image) ? 2 : 1; | ||
| if (monoUVWidth > UINT32_MAX / channelSize) { |
There was a problem hiding this comment.
monoUVWidth is always below UINT32_MAX / channelSize because channelSize is at most 2 and monoUVWidth = (image->width + 1) >> 1.
However I wonder if there should be a check for the image->width + 1 part. Maybe avm_codec_enc_init() fails earlier in case of a huge dimension, but that could change in the future.
There was a problem hiding this comment.
Good catch — you're right that monoUVWidth can never exceed UINT32_MAX / channelSize given the current math. I've removed that unreachable check and added a guard for the image->width + 1 overflow instead, which is the actual risk. Same change applied to codec_svt.c.
| return AVIF_RESULT_INVALID_ARGUMENT; | ||
| } | ||
| uint32_t monoUVRowBytes = channelSize * monoUVWidth; | ||
| if (monoUVHeight > PTRDIFF_MAX / monoUVRowBytes) { |
There was a problem hiding this comment.
I see these checks were inspired from:
Lines 433 to 440 in d8b4e04
But PTRDIFF_MAX makes little sense in both places in my opinion. It should be SIZE_MAX right?
There was a problem hiding this comment.
Agreed — SIZE_MAX is the correct bound here. Changed in both codec_avm.c and codec_svt.c.
- Add UINT32_MAX dimension guard before (width+1)>>1 to prevent the +1 overflow, as suggested by reviewer - Remove unreachable monoUVWidth > UINT32_MAX/channelSize check (provably safe given the dimension guard) - Change PTRDIFF_MAX to SIZE_MAX for size_t allocation bound Signed-off-by: Anthony Hurtado <amhurtado@pm.me>
| // monochrome. Manually set UV planes to 0.5. | ||
|
|
||
| // avmImage is always 420 when we're monochrome | ||
| if (image->width == UINT32_MAX || image->height == UINT32_MAX) { |
There was a problem hiding this comment.
Maybe image->width should be cast to size_t instead of compared against UINT32_MAX? It does not matter much for such huge dimensions I guess.
There was a problem hiding this comment.
Yes, we should allow UINT32_MAX.
As Yannis suggested, we can cast image->width to a larger type such as uint64_t. (size_t is 32 bits in 32-bit systems.) We use this approach elsewhere.
Another approach is to replace (image->width + 1) >> 1 with image->width / 2 + image->width % 2, or (since image->width >= 1) 1 + (image->width - 1) / 2, or some other similar trick.
Summary
codec_avm.candcodec_svt.cfor UV plane size computations, consistent with the existing safe pattern inavifImageAllocatePlanes()(avif.c:435-441)avifImageCopyProperties()beforenumPropertiesallocation, consistent withavifImagePushProperty()(avif.c:387)Details
Three sites compute allocation sizes via unchecked integer multiplication, unlike their parallel functions which validate before multiplying:
codec_avm.c:919 —
channelSize * monoUVWidth(uint32_t) andmonoUVHeight * monoUVRowBytes(size_t) used foravifAlloc()without overflow checks. The parallel code inavifImageAllocatePlanes()checkswidth > UINT32_MAX / channelSizeandheight > PTRDIFF_MAX / fullRowBytesbefore the same operations.codec_svt.c:286 —
uvWidth * bytesPerPixel(uint32_t) anduvHeight * uvRowBytes(size_t) computed without pre-multiplication validation. The existing post-hoc check (uvSize > UINT32_MAX / 2) cannot detect a wrap that already occurred.avif.c:237 —
numProperties * sizeof(...)passed toavifAlloc()without an overflow guard.avifImagePushProperty()in the same file already checksnumProperties < SIZE_MAX / sizeof(avifImageItemProperty)before an identical allocation.Test plan
cmake --build . --target avif_obj(zero warnings)