From 9db0ff047560ef68173ed5c13254d66d28ab03ab Mon Sep 17 00:00:00 2001 From: ForgeBlue AI Date: Fri, 23 Oct 2026 21:55:05 +0330 Subject: [PATCH 1/2] Fix GH-24492: make userland base64_encode allocation fallible --- ext/standard/base64.c | 35 +++++++++++++++---- ext/standard/base64.h | 1 + .../tests/url/gh24492_base64_encode_oom.phpt | 18 ++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 ext/standard/tests/url/gh24492_base64_encode_oom.phpt diff --git a/ext/standard/base64.c b/ext/standard/base64.c index 8d4feee84975..375a031e4328 100644 --- a/ext/standard/base64.c +++ b/ext/standard/base64.c @@ -518,7 +518,11 @@ zend_string *php_base64_encode_avx512_vbmi(const unsigned char *str, size_t leng unsigned char *o; zend_string *result; - result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0); + result = zend_string_safe_falloc(((length + 2) / 3), 4 * sizeof(char), 0, 0, + (flags & PHP_BASE64_FALLIBLE) != 0); + if (UNEXPECTED(result == NULL)) { + return NULL; + } o = (unsigned char *)ZSTR_VAL(result); const __m512i shuffle_splitting = _mm512_setr_epi32( @@ -628,7 +632,11 @@ zend_string *php_base64_encode_avx512(const unsigned char *str, size_t length, z unsigned char *o; zend_string *result; - result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0); + result = zend_string_safe_falloc(((length + 2) / 3), 4 * sizeof(char), 0, 0, + (flags & PHP_BASE64_FALLIBLE) != 0); + if (UNEXPECTED(result == NULL)) { + return NULL; + } o = (unsigned char *)ZSTR_VAL(result); while (length > 63) { @@ -915,7 +923,11 @@ zend_string *php_base64_encode_ssse3(const unsigned char *str, size_t length, ze unsigned char *o; zend_string *result; - result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0); + result = zend_string_safe_falloc(((length + 2) / 3), 4 * sizeof(char), 0, 0, + (flags & PHP_BASE64_FALLIBLE) != 0); + if (UNEXPECTED(result == NULL)) { + return NULL; + } o = (unsigned char *)ZSTR_VAL(result); # if defined(ZEND_INTRIN_AVX2_NATIVE) || defined(ZEND_INTRIN_AVX2_RESOLVER) if (length > 31) { @@ -956,7 +968,11 @@ zend_string *php_base64_encode_ssse3(const unsigned char *str, size_t length, ze unsigned char *o; zend_string *result; - result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0); + result = zend_string_safe_falloc(((length + 2) / 3), 4 * sizeof(char), 0, 0, + (flags & PHP_BASE64_FALLIBLE) != 0); + if (UNEXPECTED(result == NULL)) { + return NULL; + } o = (unsigned char *)ZSTR_VAL(result); PHP_BASE64_ENCODE_SSSE3_LOOP; @@ -1192,7 +1208,11 @@ PHPAPI zend_string *php_base64_encode_ex(const unsigned char *str, size_t length unsigned char *p; zend_string *result; - result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0); + result = zend_string_safe_falloc(((length + 2) / 3), 4 * sizeof(char), 0, 0, + (flags & PHP_BASE64_FALLIBLE) != 0); + if (UNEXPECTED(result == NULL)) { + return NULL; + } p = (unsigned char *)ZSTR_VAL(result); p = php_base64_encode_impl(str, length, p, flags); @@ -1238,7 +1258,10 @@ PHP_FUNCTION(base64_encode) Z_PARAM_STRING(str, str_len) ZEND_PARSE_PARAMETERS_END(); - result = php_base64_encode((unsigned char*)str, str_len); + result = php_base64_encode_ex((unsigned char*)str, str_len, PHP_BASE64_FALLIBLE); + if (UNEXPECTED(result == NULL)) { + RETURN_OOM(); + } RETURN_STR(result); } /* }}} */ diff --git a/ext/standard/base64.h b/ext/standard/base64.h index 19ff0b60d252..eb46cd88fe11 100644 --- a/ext/standard/base64.h +++ b/ext/standard/base64.h @@ -63,6 +63,7 @@ PHP_MINIT_FUNCTION(base64_intrin); /* php_base64_encode_ex flags */ #define PHP_BASE64_NO_PADDING 1 +#define PHP_BASE64_FALLIBLE 2 /* Only PHP userland uses this fallible result contract */ PHPAPI extern zend_string *php_base64_encode_ex(const unsigned char *, size_t, zend_long flags); PHPAPI extern zend_string *php_base64_decode_ex(const unsigned char *, size_t, bool); diff --git a/ext/standard/tests/url/gh24492_base64_encode_oom.phpt b/ext/standard/tests/url/gh24492_base64_encode_oom.phpt new file mode 100644 index 000000000000..1c07ca2d9cea --- /dev/null +++ b/ext/standard/tests/url/gh24492_base64_encode_oom.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-24492: base64_encode() uses soft memory limit without hard-limit bailout +--INI-- +memory_limit=16M +memory_hard_limit_percent=200 +--FILE-- + +--EXPECTF-- +FB_INPUT_READY + +Fatal error: Allowed memory size of 16777216 bytes exhausted%s +FB_SHUTDOWN From b179816e47df86b6bcb350380016f1e01089f172 Mon Sep 17 00:00:00 2001 From: ForgeBlue AI Date: Fri, 23 Oct 2026 22:15:07 +0330 Subject: [PATCH 2/2] Keep GH-24492 Base64 allocation flag private to implementation --- ext/standard/base64.c | 3 +++ ext/standard/base64.h | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/standard/base64.c b/ext/standard/base64.c index 375a031e4328..60b70f74f740 100644 --- a/ext/standard/base64.c +++ b/ext/standard/base64.c @@ -20,6 +20,9 @@ #include "php.h" #include "base64.h" +/* Private to this translation unit; C API callers retain existing flags. */ +#define PHP_BASE64_FALLIBLE 2 + /* {{{ base64 tables */ static const char base64_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', diff --git a/ext/standard/base64.h b/ext/standard/base64.h index eb46cd88fe11..19ff0b60d252 100644 --- a/ext/standard/base64.h +++ b/ext/standard/base64.h @@ -63,7 +63,6 @@ PHP_MINIT_FUNCTION(base64_intrin); /* php_base64_encode_ex flags */ #define PHP_BASE64_NO_PADDING 1 -#define PHP_BASE64_FALLIBLE 2 /* Only PHP userland uses this fallible result contract */ PHPAPI extern zend_string *php_base64_encode_ex(const unsigned char *, size_t, zend_long flags); PHPAPI extern zend_string *php_base64_decode_ex(const unsigned char *, size_t, bool);