From 77dd4c19507f88b9ec670da1551acb4bdf376aac Mon Sep 17 00:00:00 2001 From: RAJATH MOHARE Date: Wed, 28 Oct 2026 15:36:52 +0530 Subject: [PATCH] nul-terminate bin/ext output in msgpack_object_print_buffer msgpack_object_bin_print_buffer copied printable bytes with memcpy and never wrote a terminator, unlike the snprintf calls around it. When a bin or ext payload filled the buffer, msgpack_object_print_buffer returned 0 with no NUL anywhere in the buffer, so a caller printing it with %s (as example/user_buffer_unpack.c does) read past the end. Stop one byte short and terminate the buffer, matching snprintf truncation. Return values are unchanged. --- src/objectc.c | 12 ++++++++---- test/msgpack_c.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/objectc.c b/src/objectc.c index c33ab2b9b..46d063e04 100644 --- a/src/objectc.c +++ b/src/objectc.c @@ -337,11 +337,15 @@ static int msgpack_object_bin_print_buffer(char *buffer, size_t buffer_size, con if (ptr[i] == '"') { MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\\\""); } else if (isprint((unsigned char)ptr[i])) { - if (aux_buffer_size > 0) { - memcpy(aux_buffer, ptr + i, 1); - aux_buffer = aux_buffer + 1; - aux_buffer_size = aux_buffer_size - 1; + // keep room for the terminator, as snprintf does + if (aux_buffer_size <= 1) { + if (aux_buffer_size == 1) { *aux_buffer = '\0'; } + return 0; } + memcpy(aux_buffer, ptr + i, 1); + aux_buffer = aux_buffer + 1; + aux_buffer_size = aux_buffer_size - 1; + *aux_buffer = '\0'; } else { MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\\x%02x", (unsigned char)ptr[i]); } diff --git a/test/msgpack_c.cpp b/test/msgpack_c.cpp index 572441b01..8bfb230cd 100644 --- a/test/msgpack_c.cpp +++ b/test/msgpack_c.cpp @@ -1651,6 +1651,32 @@ TEST(MSGPACKC, object_bin_print_buffer_overflow) { EXPECT_STREQ("\"test\"", buffer); } +TEST(MSGPACKC, object_bin_print_buffer_overflow_terminated) { + msgpack_object obj; + obj.type = MSGPACK_OBJECT_BIN; + obj.via.bin.ptr = "test"; + obj.via.bin.size = 4; + char buffer[7]; + + for (size_t size = 1; size < sizeof(buffer); ++size) { + memset(buffer, 'x', sizeof(buffer)); + EXPECT_EQ(0, msgpack_object_print_buffer(buffer, size, obj)); + EXPECT_TRUE(memchr(buffer, '\0', size) != NULL); + } + + obj.type = MSGPACK_OBJECT_EXT; + obj.via.ext.type = 1; + obj.via.ext.ptr = "test"; + obj.via.ext.size = 4; + char ext_buffer[15]; + + for (size_t size = 1; size < sizeof(ext_buffer); ++size) { + memset(ext_buffer, 'x', sizeof(ext_buffer)); + EXPECT_EQ(0, msgpack_object_print_buffer(ext_buffer, size, obj)); + EXPECT_TRUE(memchr(ext_buffer, '\0', size) != NULL); + } +} + TEST(MSGPACKC, init_msgpack_obj_nil) { msgpack_object obj; msgpack_object_init_nil(&obj);