From 93d22097a50644bbb741933a648ea4d8e1e6bfa5 Mon Sep 17 00:00:00 2001 From: pedrobtz Date: Wed, 21 Oct 2026 23:49:45 +0200 Subject: [PATCH] fix(ipc): Reject message sizes that overflow the header size A message size near INT32_MAX overflowed decoder->header_size_bytes when the 4- or 8-byte prefix was added to it. Check it before adding. Co-Authored-By: Claude Opus 5.5 --- src/nanoarrow/ipc/decoder.c | 11 ++++++++-- src/nanoarrow/ipc/decoder_test.cc | 34 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/nanoarrow/ipc/decoder.c b/src/nanoarrow/ipc/decoder.c index 80e53ec8d..c7ebc6860 100644 --- a/src/nanoarrow/ipc/decoder.c +++ b/src/nanoarrow/ipc/decoder.c @@ -1612,8 +1612,6 @@ static inline int ArrowIpcDecoderReadHeaderPrefix(struct ArrowIpcDecoder* decode length = ArrowIpcReadInt32LE(data_mut, swap_endian); *prefix_size_bytes = sizeof(continuation) + sizeof(length); } - decoder->header_size_bytes = *prefix_size_bytes + length; - if (length < 0) { ArrowErrorSet(error, "Expected message size > 0 but found message size of %" PRId32 " bytes", @@ -1621,6 +1619,15 @@ static inline int ArrowIpcDecoderReadHeaderPrefix(struct ArrowIpcDecoder* decode return EINVAL; } + // The prefix and the message size must together fit in header_size_bytes + if (length > INT32_MAX - *prefix_size_bytes) { + ArrowErrorSet(error, "Expected message size <= %" PRId32 " but found %" PRId32, + INT32_MAX - *prefix_size_bytes, length); + return EINVAL; + } + + decoder->header_size_bytes = *prefix_size_bytes + length; + if (length == 0) { ArrowErrorSet(error, "End of Arrow stream"); return ENODATA; diff --git a/src/nanoarrow/ipc/decoder_test.cc b/src/nanoarrow/ipc/decoder_test.cc index 8f89acc6f..0d351ba6d 100644 --- a/src/nanoarrow/ipc/decoder_test.cc +++ b/src/nanoarrow/ipc/decoder_test.cc @@ -371,6 +371,40 @@ TEST(NanoarrowIpcTest, NanoarrowIpcCheckHeader) { ArrowIpcDecoderReset(&decoder); } +TEST(NanoarrowIpcTest, NanoarrowIpcCheckHeaderSizeOverflow) { + struct ArrowIpcDecoder decoder; + struct ArrowError error; + + uint32_t int32_max_le = INT32_MAX; + uint32_t pre_015_max_le = INT32_MAX - 3; + if (ArrowIpcSystemEndianness() == NANOARROW_IPC_ENDIANNESS_BIG) { + int32_max_le = bswap32(int32_max_le); + pre_015_max_le = bswap32(pre_015_max_le); + } + + int32_t prefix_size_bytes; + ArrowIpcDecoderInit(&decoder); + + // A message size whose sum with the 8-byte prefix overflows int32_t + uint32_t continued[] = {0xFFFFFFFF, int32_max_le}; + struct ArrowBufferView data; + data.data.as_uint32 = continued; + data.size_bytes = sizeof(continued); + EXPECT_EQ(ArrowIpcDecoderPeekHeader(&decoder, data, &prefix_size_bytes, &error), + EINVAL); + EXPECT_STREQ(error.message, "Expected message size <= 2147483639 but found 2147483647"); + + // A pre-0.15 message with only the 4-byte length prefix + uint32_t legacy[] = {pre_015_max_le, 0}; + data.data.as_uint32 = legacy; + data.size_bytes = sizeof(legacy); + EXPECT_EQ(ArrowIpcDecoderPeekHeader(&decoder, data, &prefix_size_bytes, &error), + EINVAL); + EXPECT_STREQ(error.message, "Expected message size <= 2147483643 but found 2147483644"); + + ArrowIpcDecoderReset(&decoder); +} + TEST(NanoarrowIpcTest, NanoarrowIpcPeekSimpleSchema) { struct ArrowIpcDecoder decoder; struct ArrowError error;