From 883d00ef2beee08f650e87ba4db5465e8f690e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 20 Oct 2025 00:14:18 +0200 Subject: [PATCH 1/2] zend_long: Remove `ZEND_LTOA()` This macro is unsafe when the given buffer is too small, since `snprintf()` returns the *required* length of the string if it would fit. Thus unconditionally writing a NUL there might result in a out-of-bounds read. --- UPGRADING.INTERNALS | 2 ++ Zend/zend_long.h | 12 ------------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index fe2781e025687..6b8512401cf02 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -29,6 +29,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES . CHECK_ZVAL_NULL_PATH() and CHECK_NULL_PATH() have been removed, use zend_str_has_nul_byte(Z_STR_P(...)) and zend_char_has_nul_byte() respectively. + . ZEND_LTOA() has been removed, as it was unsafe. Directly use + ZEND_LONG_FMT with a function from the printf family. ======================== 2. Build system changes diff --git a/Zend/zend_long.h b/Zend/zend_long.h index 3796f1c5ababb..827a6535a2a83 100644 --- a/Zend/zend_long.h +++ b/Zend/zend_long.h @@ -61,7 +61,6 @@ typedef int32_t zend_off_t; # define ZEND_LONG_FMT_SPEC PRId64 # define ZEND_ULONG_FMT_SPEC PRIu64 # ifdef ZEND_WIN32 -# define ZEND_LTOA(i, s, len) _i64toa_s((i), (s), (len), 10) # define ZEND_ATOL(s) _atoi64((s)) # define ZEND_STRTOL(s0, s1, base) _strtoi64((s0), (s1), (base)) # define ZEND_STRTOUL(s0, s1, base) _strtoui64((s0), (s1), (base)) @@ -69,11 +68,6 @@ typedef int32_t zend_off_t; # define ZEND_STRTOUL_PTR _strtoui64 # define ZEND_ABS _abs64 # else -# define ZEND_LTOA(i, s, len) \ - do { \ - int st = snprintf((s), (len), ZEND_LONG_FMT, (i)); \ - (s)[st] = '\0'; \ - } while (0) # define ZEND_ATOL(s) atoll((s)) # define ZEND_STRTOL(s0, s1, base) strtoll((s0), (s1), (base)) # define ZEND_STRTOUL(s0, s1, base) strtoull((s0), (s1), (base)) @@ -90,14 +84,8 @@ typedef int32_t zend_off_t; # define ZEND_LONG_FMT_SPEC PRId32 # define ZEND_ULONG_FMT_SPEC PRIu32 # ifdef ZEND_WIN32 -# define ZEND_LTOA(i, s, len) _ltoa_s((i), (s), (len), 10) # define ZEND_ATOL(s) atol((s)) # else -# define ZEND_LTOA(i, s, len) \ - do { \ - int st = snprintf((s), (len), ZEND_LONG_FMT, (i)); \ - (s)[st] = '\0'; \ - } while (0) # define ZEND_ATOL(s) atol((s)) # endif # define ZEND_STRTOL_PTR strtol From 513080ab742c733b584431a71ca8d3d51170f211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 20 Oct 2025 00:21:08 +0200 Subject: [PATCH 2/2] zend_long: Remove `ZEND_LTOA_BUF_LEN` --- UPGRADING.INTERNALS | 5 +++-- Zend/zend_long.h | 3 --- ext/standard/hrtime.c | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 6b8512401cf02..d269bd107885e 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -29,8 +29,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES . CHECK_ZVAL_NULL_PATH() and CHECK_NULL_PATH() have been removed, use zend_str_has_nul_byte(Z_STR_P(...)) and zend_char_has_nul_byte() respectively. - . ZEND_LTOA() has been removed, as it was unsafe. Directly use - ZEND_LONG_FMT with a function from the printf family. + . ZEND_LTOA() (and ZEND_LTOA_BUF_LEN) has been removed, as it was + unsafe. Directly use ZEND_LONG_FMT with a function from the + printf family. ======================== 2. Build system changes diff --git a/Zend/zend_long.h b/Zend/zend_long.h index 827a6535a2a83..fef237701f3bd 100644 --- a/Zend/zend_long.h +++ b/Zend/zend_long.h @@ -51,9 +51,6 @@ typedef int32_t zend_off_t; #endif -/* Conversion macros. */ -#define ZEND_LTOA_BUF_LEN 65 - #ifdef ZEND_ENABLE_ZVAL_LONG64 # define ZEND_LONG_FMT "%" PRId64 # define ZEND_ULONG_FMT "%" PRIu64 diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c index 10853493b6390..652531bd3ed4f 100644 --- a/ext/standard/hrtime.c +++ b/ext/standard/hrtime.c @@ -31,9 +31,9 @@ } while (0) #endif #define PHP_RETURN_HRTIME(t) do { \ - char _a[ZEND_LTOA_BUF_LEN]; \ + char _a[65]; \ double _d; \ - HRTIME_U64A(t, _a, ZEND_LTOA_BUF_LEN); \ + HRTIME_U64A(t, _a, sizeof(_a)); \ _d = zend_strtod(_a, NULL); \ RETURN_DOUBLE(_d); \ } while (0)