Skip to content

Commit

Permalink
Fix size 0 handling in avifParse() (#2152)
Browse files Browse the repository at this point in the history
header.size in avifParse is not the raw value that is found in
the input stream. It is calculated in avifROStreamReadBoxHeaderPartial.

So when handling size == 0 in avifParse(), we cannot use header.size
since it may be 0 in legit cases (where the box itself is empty).

This PR adds a new member to avifBoxHeader which stores the actual
raw size found in the input stream and uses that in avifParse().

Also handle the isSizeZeroBox in avifPeekCompatibleFileType.
  • Loading branch information
vigneshvg committed May 6, 2024
1 parent caf027a commit d715425
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 9 deletions.
5 changes: 5 additions & 0 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,11 @@ typedef size_t avifBoxMarker;

typedef struct avifBoxHeader
{
// If set to AVIF_TRUE, it means that the box goes on until the end of the
// stream. So, |size| must be set to the number of bytes left in the input
// stream. If set to AVIF_FALSE, |size| indicates the size of the box in
// bytes, excluding the box header.
avifBool isSizeZeroBox;
// Size of the box in bytes, excluding the box header.
size_t size;

Expand Down
18 changes: 9 additions & 9 deletions src/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -3983,8 +3983,8 @@ static avifResult avifParse(avifDecoder * decoder)
#endif
boxOffset = parseOffset;
size_t sizeToRead;
if (header.size == 0) {
// A size of 0 means the box body goes till the end of the file.
if (header.isSizeZeroBox) {
// The box body goes till the end of the file.
if (decoder->io->sizeHint != 0 && decoder->io->sizeHint - parseOffset < SIZE_MAX) {
sizeToRead = decoder->io->sizeHint - parseOffset;
} else {
Expand All @@ -3997,18 +3997,18 @@ static avifResult avifParse(avifDecoder * decoder)
if (readResult != AVIF_RESULT_OK) {
return readResult;
}
if (header.size == 0) {
if (header.isSizeZeroBox) {
header.size = boxContents.size;
} else if (boxContents.size != header.size) {
// A truncated box, bail out
return AVIF_RESULT_TRUNCATED_DATA;
}
} else if (header.size > (UINT64_MAX - parseOffset)) {
return AVIF_RESULT_BMFF_PARSE_FAILED;
} else if (header.size == 0) {
} else if (header.isSizeZeroBox) {
// An unknown top level box with size 0 was found. If we reach here it means we haven't completed parsing successfully
// since there are no further boxes left.
return AVIF_RESULT_BMFF_PARSE_FAILED;
} else if (header.size > (UINT64_MAX - parseOffset)) {
return AVIF_RESULT_BMFF_PARSE_FAILED;
}
parseOffset += header.size;

Expand Down Expand Up @@ -4114,9 +4114,9 @@ avifBool avifPeekCompatibleFileType(const avifROData * input)
if (!avifROStreamReadBoxHeaderPartial(&s, &header, /*topLevel=*/AVIF_TRUE) || memcmp(header.type, "ftyp", 4)) {
return AVIF_FALSE;
}
if (header.size == 0) {
// The size of the 'ftyp' box is 0, which means it goes till the end of the file.
// Either there is no brand requiring anything in the file but a FileTypebox (so not AVIF), or it is invalid.
if (header.isSizeZeroBox) {
// The ftyp box goes on till the end of the file. Either there is no brand requiring anything in the file but a
// FileTypebox (so not AVIF), or it is invalid.
return AVIF_FALSE;
}
AVIF_CHECK(avifROStreamHasBytesLeft(&s, header.size));
Expand Down
2 changes: 2 additions & 0 deletions src/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ avifBool avifROStreamReadBoxHeaderPartial(avifROStream * stream, avifBoxHeader *
// Otherwise size could be set to avifROStreamRemainingBytes(stream) + (stream->offset - startOffset) right now.

// Wait for avifIOReadFunc() to return AVIF_RESULT_OK.
header->isSizeZeroBox = AVIF_TRUE;
header->size = 0;
return AVIF_TRUE;
}
Expand All @@ -286,6 +287,7 @@ avifBool avifROStreamReadBoxHeaderPartial(avifROStream * stream, avifBoxHeader *
avifDiagnosticsPrintf(stream->diag, "%s: Header size overflow check failure", stream->diagContext);
return AVIF_FALSE;
}
header->isSizeZeroBox = AVIF_FALSE;
header->size = (size_t)(size - bytesRead);
return AVIF_TRUE;
}
Expand Down

0 comments on commit d715425

Please sign in to comment.