Very similar to https://github.com/apache/apr/pull/73
I'm inclined to just close this as "by design". However it seems I'm not able to close a PR in GitHub, I only have "comment". Is this just me? Maybe that is because I've changed by GitHub username. Cheers, Daniel Den tis 23 juni 2026 kl 04:10 skrev orbisai0security (via GitHub) < [email protected]>: > > orbisai0security opened a new pull request, #16: > URL: https://github.com/apache/apr-util/pull/16 > > ## Summary > Fix critical severity security issue in `buffer/apr_buffer.c`. > > ## Vulnerability > | Field | Value | > |-------|-------| > | **ID** | V-001 | > | **Severity** | CRITICAL | > | **Scanner** | multi_agent_ai | > | **Rule** | `V-001` | > | **File** | `buffer/apr_buffer.c:240` | > | **Assessment** | Confirmed exploitable | > > **Description**: Multiple memcpy operations in apr_buffer.c copy data > without validating that the destination buffer has sufficient capacity. The > code assumes the allocated buffer is large enough but doesn't verify the > size parameter against destination buffer bounds. > > ## Evidence > > **Exploitation scenario**: An attacker who can control the 'size' > parameter or the source data passed to apr_buffer_arraydup() or other > buffer functions can cause buffer overflow. > > **Scanner confirmation**: multi_agent_ai rule `V-001` flagged this > pattern. > > **Production code**: This file is in the production codebase, not > test-only code. > > ## Threat Model Context > > This is a local CLI tool - exploitation requires the attacker to > control command-line arguments or input files. > > ## Changes > - Security fix applied > > > **Note**: The following lines in the same file use a similar pattern > and may also need review: `buffer/apr_buffer.c:255`, > `buffer/apr_buffer.c:299`, `buffer/apr_buffer.c:385`, > `buffer/apr_buffer.c:390`, `buffer/apr_buffer.c:395` > > ## Verification > - [x] Build passes > - [x] Scanner re-scan confirms fix > - [x] LLM code review passed > > ## Security Invariant > > **Property**: The security boundary is maintained under adversarial > input > > <details> > <summary>Regression test</summary> > > ```c > #include <check.h> > #include <stdlib.h> > #include <string.h> > #include "buffer/apr_buffer.h" > > START_TEST(test_apr_buffer_arraydup_bounds_check) > { > // Invariant: apr_buffer_arraydup must not write beyond allocated > destination buffer bounds > // regardless of input size values > > // Payloads: exploit case (size causing overflow), boundary case > (zero size), valid input > struct { > apr_size_t size; > int zero_terminated; > int nelts; > const char *description; > } test_cases[] = { > {SIZE_MAX, 1, 2, "exploit: size + zero_terminated causes > overflow"}, > {0, 0, 1, "boundary: zero size"}, > {1024, 0, 3, "valid: normal operation"} > }; > > int num_cases = sizeof(test_cases) / sizeof(test_cases[0]); > > for (int i = 0; i < num_cases; i++) { > // Create source buffer array > apr_buffer_t *src_array = malloc(test_cases[i].nelts * > sizeof(apr_buffer_t)); > ck_assert_ptr_nonnull(src_array); > > // Initialize source buffers with test data > for (int j = 0; j < test_cases[i].nelts; j++) { > src_array[j].size = test_cases[i].size; > src_array[j].zero_terminated = > test_cases[i].zero_terminated; > > // Allocate source memory if size > 0 > if (test_cases[i].size > 0) { > src_array[j].d.mem = malloc(test_cases[i].size); > ck_assert_ptr_nonnull(src_array[j].d.mem); > memset(src_array[j].d.mem, 'A', test_cases[i].size); > } else { > src_array[j].d.mem = NULL; > } > } > > // Test the actual function > apr_buffer_t *dst_array = NULL; > apr_status_t result = apr_buffer_arraydup(&dst_array, > src_array, > > (apr_buffer_alloc)malloc, NULL, > test_cases[i].nelts); > > // Property: Function must either succeed with valid buffers or > fail gracefully > // without writing beyond allocated memory bounds > if (result == APR_SUCCESS) { > ck_assert_ptr_nonnull(dst_array); > > // Verify each destination buffer was properly allocated > for (int j = 0; j < test_cases[i].nelts; j++) { > if (test_cases[i].size + test_cases[i].zero_terminated > > 0) { > ck_assert_ptr_nonnull(dst_array[j].d.mem); > } > ck_assert_uint_eq(dst_array[j].size, > test_cases[i].size); > ck_assert_int_eq(dst_array[j].zero_terminated, > test_cases[i].zero_terminated); > } > > // Clean up destination > for (int j = 0; j < test_cases[i].nelts; j++) { > if (dst_array[j].d.mem) { > free(dst_array[j].d.mem); > } > } > free(dst_array); > } else { > // If function failed, ensure no partial writes corrupted > memory > ck_assert_ptr_null(dst_array); > } > > // Clean up source > for (int j = 0; j < test_cases[i].nelts; j++) { > if (src_array[j].d.mem) { > free(src_array[j].d.mem); > } > } > free(src_array); > } > } > END_TEST > > Suite *security_suite(void) > { > Suite *s; > TCase *tc_core; > > s = suite_create("Security"); > tc_core = tcase_create("Core"); > > tcase_add_test(tc_core, test_apr_buffer_arraydup_bounds_check); > suite_add_tcase(s, tc_core); > > return s; > } > > int main(void) > { > int number_failed; > Suite *s; > SRunner *sr; > > s = security_suite(); > sr = srunner_create(s); > > srunner_run_all(sr, CK_NORMAL); > number_failed = srunner_ntests_failed(sr); > srunner_free(sr); > > return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; > } > ``` > > </details> > > This test guards against regressions — it's useful independent of the > code change above. > > --- > *Automated security fix by [OrbisAI Security](https://orbisappsec.com)* > > > > -- > This is an automated message from the Apache Git Service. > To respond to the message, please log on to GitHub and use the > URL above to go to the specific comment. > > To unsubscribe, e-mail: [email protected] > > For queries about this service, please contact Infrastructure at: > [email protected] > >
