From ddf3b708203fdc1f99e1e6913a31d840a98172cf Mon Sep 17 00:00:00 2001 From: OrbisAI Security Date: Mon, 18 May 2026 11:40:35 +0530 Subject: [PATCH] apr_buffer_cpy: return NULL on allocation failure apr_buffer_cpy() passes the result of the user-supplied alloc callback directly to memcpy() without checking for NULL. If the allocator fails, this is undefined behaviour (memcpy with NULL source or destination). Add a NULL check after alloc(), returning NULL to the caller. This is consistent with apr_buffer_arraydup() which already returns APR_ENOMEM on allocation failure. Update the apr_buffer_cpy() documentation to reflect that NULL may be returned. Add a test exercising the allocation failure path. Co-Authored-By: Claude Opus 4.6 --- buffer/apr_buffer.c | 3 +++ include/apr_buffer.h | 2 +- test/testbuffer.c | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/buffer/apr_buffer.c b/buffer/apr_buffer.c index 4efd1ef75d..a76c39d16f 100644 --- a/buffer/apr_buffer.c +++ b/buffer/apr_buffer.c @@ -297,6 +297,9 @@ APR_DECLARE(apr_buffer_t *) apr_buffer_cpy(apr_buffer_t *dst, apr_size_t size = src->size + src->zero_terminated; void *mem = alloc(ctx, size); + if (!mem) { + return NULL; + } memcpy(mem, src->d.mem, size); dst->zero_terminated = src->zero_terminated; diff --git a/include/apr_buffer.h b/include/apr_buffer.h index 711c772776..4f54b248f9 100644 --- a/include/apr_buffer.h +++ b/include/apr_buffer.h @@ -354,7 +354,7 @@ APR_DECLARE(apr_status_t) apr_buffer_dup(apr_buffer_t **out, * @param src The second buffer * @param alloc The function callback to allocate memory for the buffer * @param ctx The context for the callback - * @return Returns dst. + * @return Returns dst, or NULL if alloc is provided and returns NULL. */ APR_DECLARE(apr_buffer_t *) apr_buffer_cpy(apr_buffer_t *dst, const apr_buffer_t *src, diff --git a/test/testbuffer.c b/test/testbuffer.c index fd4d7edc32..e129d61c2f 100644 --- a/test/testbuffer.c +++ b/test/testbuffer.c @@ -265,6 +265,22 @@ static void test_compare_buffers(abts_case *tc, void *data) apr_pool_destroy(pool); } +static void *test_failing_alloc(void *ctx, apr_size_t size) +{ + return NULL; +} + +static void test_buffer_cpy_alloc_failure(abts_case *tc, void *data) +{ + apr_buffer_t src, dst; + + apr_buffer_str_set(&src, "test", APR_BUFFER_STRING); + memset(&dst, 0, sizeof(dst)); + + ABTS_ASSERT(tc, "apr_buffer_cpy returns NULL on alloc failure", + apr_buffer_cpy(&dst, &src, test_failing_alloc, NULL) == NULL); +} + abts_suite *testbuffer(abts_suite *suite) { suite = ADD_SUITE(suite); @@ -274,6 +290,7 @@ abts_suite *testbuffer(abts_suite *suite) abts_run_test(suite, test_null_buffer, NULL); abts_run_test(suite, test_buffers, NULL); abts_run_test(suite, test_compare_buffers, NULL); + abts_run_test(suite, test_buffer_cpy_alloc_failure, NULL); return suite; }