Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,12 @@ avifResult avifRWStreamWriteChars(avifRWStream * stream, const char * chars, siz
avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags, avifBoxMarker * marker)
{
assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
if (marker) {
*marker = stream->offset;
}
avifBoxMarker oldOffset = stream->offset;
size_t headerSize = sizeof(uint32_t) + 4 /* size of type */;
if (version != -1) {
headerSize += 4;
}
AVIF_CHECKERR(contentSize <= UINT32_MAX - headerSize, AVIF_RESULT_INVALID_ARGUMENT);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avifRWStreamWriteFullBox() and the related avifRWStreamWriteBox() function are internal functions, so they don't need to check for invalid input arguments as thoroughly as a public API function. But it is fine to hold them to a higher standard.

I audited the callers of avifRWStreamWriteFullBox() and avifRWStreamWriteBox(). They all pass AVIF_BOX_SIZE_TBD as the contentSize argument, with only two exceptions: sizeof(uint16_t) and 0.


AVIF_CHECKRES(makeRoom(stream, headerSize));
memset(stream->raw->data + stream->offset, 0, headerSize);
Expand All @@ -416,6 +415,9 @@ avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, si
stream->raw->data[stream->offset + 10] = (uint8_t)((flags >> 8) & 0xff);
stream->raw->data[stream->offset + 11] = (uint8_t)((flags >> 0) & 0xff);
}
if (marker) {
*marker = oldOffset;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jmestwa-coder: The original location of this if statement is also correct; it only has the minor downside that the output parameter *marker is set even when the function fails. If we want to set the output parameter *marker only when the function succeeds, I would prefer we do this as soon as we know the function cannot fail, before we use stream->offset, because it will be clearer that we save the old value of stream->offset before it is modified.

stream->offset += headerSize;

return AVIF_RESULT_OK;
Expand Down
36 changes: 36 additions & 0 deletions tests/gtest/avifstreamtest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,42 @@ TEST(StreamTest, WriteBitsLimit) {
AVIF_RESULT_INVALID_ARGUMENT);
}

TEST(StreamTest, WriteBoxSizeLimit) {
testutil::AvifRwData rw_data;
avifRWStream rw_stream;
avifRWStreamStart(&rw_stream, &rw_data);
const char box_type[] = "type";
avifBoxMarker marker = 123;

EXPECT_EQ(
avifRWStreamWriteBox(
&rw_stream, box_type,
std::numeric_limits<uint32_t>::max() - sizeof(uint32_t) - 4, &marker),
AVIF_RESULT_OK);
EXPECT_EQ(marker, size_t{0});
EXPECT_EQ(avifRWStreamOffset(&rw_stream), sizeof(uint32_t) + 4);

EXPECT_EQ(
avifRWStreamWriteBox(
&rw_stream, box_type,
std::numeric_limits<uint32_t>::max() - sizeof(uint32_t) - 3, &marker),
AVIF_RESULT_INVALID_ARGUMENT);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The code below assumes that avifRWStreamWriteBox() does not modify the stream's offset when it fails. This is too strong a requirement because we stop using a stream as soon as there is an error. Tests should avoid "over-specifying" the expected behavior.

We can allow this, but let's add the following EXPECT_EQ, similar to the EXPECT_EQ at line 239:

  EXPECT_EQ(avifRWStreamOffset(&rw_stream), sizeof(uint32_t) + 4);


EXPECT_EQ(avifRWStreamWriteFullBox(
&rw_stream, box_type,
std::numeric_limits<uint32_t>::max() - sizeof(uint32_t) - 8,
/*version=*/0, /*flags=*/0, &marker),
AVIF_RESULT_OK);
EXPECT_EQ(marker, sizeof(uint32_t) + 4);
EXPECT_EQ(avifRWStreamOffset(&rw_stream), 2 * (sizeof(uint32_t) + 4) + 4);

EXPECT_EQ(avifRWStreamWriteFullBox(
&rw_stream, box_type,
std::numeric_limits<uint32_t>::max() - sizeof(uint32_t) - 7,
/*version=*/0, /*flags=*/0, &marker),
AVIF_RESULT_INVALID_ARGUMENT);
}

// Test the overflow checks in the makeRoom() function in src/stream.c.
TEST(StreamTest, OverflowChecksInMakeRoom) {
testutil::AvifRwData rw_data;
Expand Down