This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/qpid-proton.git
commit 2ce88c34e066bff2f97874b1db95a1fe1249c58b Author: Andrew Stitcher <[email protected]> AuthorDate: Tue Sep 15 14:07:33 2026 -0400 PROTON-2950: More robustness for pn_buffer - Tightened up one possible buffer wraparound case - Test for more robust pn_string --- c/src/core/buffer.c | 14 +++++++++----- c/tests/object_test.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/c/src/core/buffer.c b/c/src/core/buffer.c index 103d57c21..61a4a2c2c 100644 --- a/c/src/core/buffer.c +++ b/c/src/core/buffer.c @@ -117,21 +117,25 @@ int pn_buffer_ensure(pn_buffer_t *buf, size_t size) { if (pn_buffer_available(buf) >= size) return 0; - size_t old_capacity = buf->capacity; - size_t old_head = pni_buffer_head(buf); - bool wrapped = pni_buffer_wrapped(buf); - + // Reject the request before adding it to the size, so the sum can't wrap + if (size > 0x80000000ULL) { + return PN_OUT_OF_MEMORY; + } uint64_t needed = (uint64_t) buf->size + size; if (needed < 32) needed = 32; if (needed > 0x80000000ULL) { return PN_OUT_OF_MEMORY; } uint32_t new_capacity = pni_round_up_pow2((uint32_t) needed); - char* new_bytes = (char *) pni_mem_subreallocate(PN_CLASSCLASS(pn_buffer), buf, buf->bytes, new_capacity); if (!new_bytes) { return PN_OUT_OF_MEMORY; } + + size_t old_capacity = buf->capacity; + size_t old_head = pni_buffer_head(buf); + bool wrapped = pni_buffer_wrapped(buf); + buf->bytes = new_bytes; buf->capacity = new_capacity; diff --git a/c/tests/object_test.cpp b/c/tests/object_test.cpp index af0d5d5e8..60c7ffb4f 100644 --- a/c/tests/object_test.cpp +++ b/c/tests/object_test.cpp @@ -679,6 +679,56 @@ TEST_CASE("string_addf") { pn_free(str); } +TEST_CASE("string_setn_overflow") { + pn_string_t *str = pn_string("hello"); + CHECK(str); + + // A size that can't be represented is refused outright, without touching + // the string and without reading from bytes + for (size_t n : {(size_t)INT32_MAX + 1, SIZE_MAX - 1, SIZE_MAX}) { + CHECK(pn_string_setn(str, "world", n) != 0); + CHECK(pn_string_size(str) == 5); + CHECK_THAT("hello", Equals(pn_string_get(str))); + } + + // The refused growth must not have recorded a capacity that was never + // allocated - if it had, appending here would run off the heap block + CHECK(pn_string_addf(str, "%s", " world") == 0); + CHECK_THAT("hello world", Equals(pn_string_get(str))); + CHECK(pn_string_size(str) == 11); + + CHECK(pn_string_set(str, "goodbye") == 0); + CHECK_THAT("goodbye", Equals(pn_string_get(str))); + pn_free(str); +} + +TEST_CASE("string_addf_repeated") { + // Exercises the grow-and-retry loop in pn_string_vaddf + pn_string_t *str = pn_string(""); + CHECK(str); + for (int i = 0; i < 1000; i++) { + CHECK(pn_string_addf(str, "%s", "0123456789") == 0); + } + CHECK(pn_string_size(str) == 10000); + CHECK(strlen(pn_string_get(str)) == 10000); + pn_free(str); +} + +TEST_CASE("string_setn_sizes") { + // Round trip a range of sizes across the power-of-two growth boundaries + pn_string_t *str = pn_string(NULL); + CHECK(str); + std::string value; + for (size_t n = 0; n < 1100; n++) { + value.push_back('a' + (char)(n % 26)); + CHECK(pn_string_setn(str, value.data(), value.size()) == 0); + CHECK(pn_string_size(str) == value.size()); + CHECK(memcmp(pn_string_get(str), value.data(), value.size()) == 0); + CHECK(pn_string_get(str)[value.size()] == '\0'); + } + pn_free(str); +} + TEST_CASE("map_iteration") { int n = 5; pn_list_t *pairs = pn_list(PN_OBJECT, 2 * n); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
