From 53f4fbb1b4f185da8b57a9f0ab521106a1cd4bdc Mon Sep 17 00:00:00 2001 From: Koda Reef Date: Sun, 22 Mar 2026 22:07:34 +0000 Subject: [PATCH 1/2] Add integer overflow checks to URL escape allocation functions ap_escape_path_segment, ap_os_escape_path, and ap_escape_urlencoded allocate output buffers using 3 * strlen(input) + constant without checking for overflow. On platforms where size_t is 32-bit, large inputs cause the multiplication to wrap, resulting in undersized allocation. The HTML escape function ap_escape_html2 in the same file already has overflow protection (abort on overflow). Apply the same pattern to the three URL escape functions for consistency. --- server/util.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/server/util.c b/server/util.c index bca2636fd2b..97abdd7c278 100644 --- a/server/util.c +++ b/server/util.c @@ -2073,7 +2073,11 @@ AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment) { - return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment); + apr_size_t len = strlen(segment); + if (len > (APR_SIZE_MAX - 1) / 3) { + abort(); + } + return ap_escape_path_segment_buffer(apr_palloc(p, 3 * len + 1), segment); } AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial) @@ -2082,11 +2086,19 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia * Allocate another +1 to allow the caller to add a trailing '/' (see * comment in 'ap_sub_req_lookup_dirent') */ - char *copy = apr_palloc(p, 3 * strlen(path) + 3 + 1); - const unsigned char *s = (const unsigned char *)path; - unsigned char *d = (unsigned char *)copy; + apr_size_t len = strlen(path); + char *copy; + const unsigned char *s; + unsigned char *d; unsigned c; + if (len > (APR_SIZE_MAX - 4) / 3) { + abort(); + } + copy = apr_palloc(p, 3 * len + 3 + 1); + s = (const unsigned char *)path; + d = (unsigned char *)copy; + if (!partial) { const char *colon = ap_strchr_c(path, ':'); const char *slash = ap_strchr_c(path, '/'); @@ -2133,7 +2145,11 @@ AP_DECLARE(char *) ap_escape_urlencoded_buffer(char *copy, const char *buffer) AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer) { - return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * strlen(buffer) + 1), buffer); + apr_size_t len = strlen(buffer); + if (len > (APR_SIZE_MAX - 1) / 3) { + abort(); + } + return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * len + 1), buffer); } /* ap_escape_uri is now a macro for os_escape_path */ From 70b48377600e65acce0bd67bfb28dab7a0e996ba Mon Sep 17 00:00:00 2001 From: Koda Reef Date: Sun, 22 Mar 2026 22:30:28 +0000 Subject: [PATCH 2/2] Add integer overflow checks to apreq parameter and cookie allocation apreq_param_make(), apreq_param_decode(), apreq_param_encode(), and apreq_cookie_make() compute allocation sizes as nlen + vlen + constant without checking for overflow. On platforms where size_t is 32-bit, large parameter values can cause the sum to wrap, resulting in undersized allocation. Add overflow checks before each allocation, returning NULL or APR_ENOMEM on overflow. The URL-encode path (apreq_param_encode) also checks the 3 * (nlen + dlen) multiplication. --- server/apreq_cookie.c | 5 +++++ server/apreq_param.c | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/server/apreq_cookie.c b/server/apreq_cookie.c index 53ae16f64d6..46dcde17619 100644 --- a/server/apreq_cookie.c +++ b/server/apreq_cookie.c @@ -140,6 +140,11 @@ APREQ_DECLARE(apreq_cookie_t *) apreq_cookie_make(apr_pool_t *p, apreq_cookie_t *c; apreq_value_t *v; + if (nlen > APR_SIZE_MAX - sizeof *c - 1 + || nlen + sizeof *c + 1 > APR_SIZE_MAX - vlen) { + return NULL; + } + c = apr_palloc(p, nlen + vlen + 1 + sizeof *c); if (c == NULL) diff --git a/server/apreq_param.c b/server/apreq_param.c index 83e185b595a..23a8e3ef891 100644 --- a/server/apreq_param.c +++ b/server/apreq_param.c @@ -35,6 +35,11 @@ APREQ_DECLARE(apreq_param_t *) apreq_param_make(apr_pool_t *p, apreq_param_t *param; apreq_value_t *v; + if (nlen > APR_SIZE_MAX - sizeof *param - 1 + || nlen + sizeof *param + 1 > APR_SIZE_MAX - vlen) { + return NULL; + } + param = apr_palloc(p, nlen + vlen + 1 + sizeof *param); if (param == NULL) @@ -76,6 +81,12 @@ APREQ_DECLARE(apr_status_t) apreq_param_decode(apreq_param_t **param, return APR_EBADARG; } + if (nlen > APR_SIZE_MAX - sizeof *p - 1 + || nlen + sizeof *p + 1 > APR_SIZE_MAX - vlen) { + *param = NULL; + return APR_ENOMEM; + } + p = apr_palloc(pool, nlen + vlen + 1 + sizeof *p); p->info = NULL; p->upload = NULL; @@ -130,7 +141,15 @@ APREQ_DECLARE(char *) apreq_param_encode(apr_pool_t *pool, { apr_size_t dlen; char *data; - data = apr_palloc(pool, 3 * (param->v.nlen + param->v.dlen) + 2); + { + apr_size_t sum = param->v.nlen; + if (sum > APR_SIZE_MAX - param->v.dlen) + return NULL; + sum += param->v.dlen; + if (sum > (APR_SIZE_MAX - 2) / 3) + return NULL; + data = apr_palloc(pool, 3 * sum + 2); + } dlen = apreq_encode(data, param->v.name, param->v.nlen); data[dlen++] = '='; dlen += apreq_encode(data + dlen, param->v.data, param->v.dlen);