diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index effff75e00f2..b5059ddf5400 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -760,12 +760,14 @@ static inline zend_long safe_shift_left(zend_long n, zend_long s) { } static inline bool shift_left_overflows(zend_long n, zend_long s) { - /* This considers shifts that shift in the sign bit to be overflowing as well */ - if (n >= 0) { - return s >= SIZEOF_ZEND_LONG * 8 - 1 || safe_shift_left(n, s) < n; - } else { - return s >= SIZEOF_ZEND_LONG * 8 || safe_shift_left(n, s) > n; + if (UNEXPECTED((zend_ulong)s >= SIZEOF_ZEND_LONG * 8)) { + return true; + } + if (UNEXPECTED(s == SIZEOF_ZEND_LONG * 8 - 1)) { + return n != 0 && n != -1; } + zend_long shifted = (zend_long)((zend_ulong)n << (zend_ulong)s); + return (shifted >> s) != n; } /* If b does not divide a exactly, return the two adjacent values between which the real result diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index b293ee1a51dc..61754ceb3c1a 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -1034,19 +1034,21 @@ static zend_always_inline void zend_mm_chunk_init(zend_mm_heap *heap, zend_mm_ch static zend_always_inline zend_mm_chunk *zend_mm_encode_cached_chunk(const zend_mm_heap *heap, const void *holder, const zend_mm_chunk *next) { #ifdef WORDS_BIGENDIAN - return (zend_mm_chunk*)((uintptr_t)next ^ heap->shadow_key ^ (uintptr_t)holder); + uintptr_t p = (uintptr_t)next; #else - return (zend_mm_chunk*)(BSWAPPTR((uintptr_t)next) ^ heap->shadow_key ^ (uintptr_t)holder); + uintptr_t p = BSWAPPTR((uintptr_t)next); #endif + /* ARX (Addition-XOR) non-linear mixing prevents linear algebraic shadow_key recovery */ + return (zend_mm_chunk*)((p + heap->shadow_key) ^ (uintptr_t)holder); } static zend_always_inline zend_mm_chunk *zend_mm_decode_cached_chunk_key(uintptr_t key, const void *holder, const zend_mm_chunk *encoded) { -#ifdef WORDS_BIGENDIAN - zend_mm_chunk *next = (zend_mm_chunk*)((uintptr_t)encoded ^ key ^ (uintptr_t)holder); -#else - zend_mm_chunk *next = (zend_mm_chunk*)(BSWAPPTR((uintptr_t)encoded ^ key ^ (uintptr_t)holder)); + uintptr_t p = ((uintptr_t)encoded ^ (uintptr_t)holder) - key; +#ifndef WORDS_BIGENDIAN + p = BSWAPPTR(p); #endif + zend_mm_chunk *next = (zend_mm_chunk*)p; ZEND_MM_CHECK(ZEND_MM_ALIGNED_OFFSET(next, ZEND_MM_CHUNK_SIZE) == 0, "zend_mm_heap corrupted"); return next; diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index cd7c3c512daf..9d31845efdc0 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -421,7 +421,10 @@ static zend_always_inline gc_root_buffer* gc_decompress(zend_refcounted *ref, ui while (1) { idx += GC_MAX_UNCOMPRESSED; - ZEND_ASSERT(idx < GC_G(first_unused)); + if (UNEXPECTED(idx >= GC_G(first_unused))) { + zend_error_noreturn(E_CORE_ERROR, "GC root buffer corruption detected: unable to resolve compressed address"); + return NULL; + } root = GC_IDX2PTR(idx); if (GC_GET_PTR(root->ref) == ref) { return root; diff --git a/main/streams/memory.c b/main/streams/memory.c index c43ddca579b6..751b79948cb2 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -52,8 +52,12 @@ static ssize_t php_stream_memory_write(php_stream *stream, const char *buf, size if (ms->mode & TEMP_STREAM_APPEND) { ms->fpos = data_len; } - if (ms->fpos + count > data_len) { - zend_string *grown = zend_string_frealloc(ms->data, ms->fpos + count, 0, /* fallible */ true); + if (UNEXPECTED(count > SIZE_MAX - ms->fpos)) { + return (ssize_t) -1; + } + size_t new_pos = ms->fpos + count; + if (new_pos > data_len) { + zend_string *grown = zend_string_frealloc(ms->data, new_pos, 0, /* fallible */ true); if (UNEXPECTED(!grown)) { return (ssize_t) -1; } @@ -69,7 +73,7 @@ static ssize_t php_stream_memory_write(php_stream *stream, const char *buf, size ZEND_ASSERT(buf != NULL); memcpy(ZSTR_VAL(ms->data) + ms->fpos, (char*) buf, count); ZSTR_VAL(ms->data)[ZSTR_LEN(ms->data)] = '\0'; - ms->fpos += count; + ms->fpos = new_pos; } return count; } @@ -142,9 +146,13 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe return 0; } } else { + if (UNEXPECTED((size_t)offset > SIZE_MAX - ms->fpos)) { + *newoffs = -1; + return -1; + } stream->eof = 0; stream->fatal_error = 0; - ms->fpos = ms->fpos + offset; + ms->fpos += (size_t)offset; *newoffs = ms->fpos; return 0; } @@ -154,7 +162,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe *newoffs = -1; return -1; } else { - ms->fpos = offset; + ms->fpos = (size_t)offset; *newoffs = ms->fpos; stream->eof = 0; stream->fatal_error = 0; @@ -162,7 +170,11 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe } case SEEK_END: if (offset > 0) { - ms->fpos = ZSTR_LEN(ms->data) + offset; + if (UNEXPECTED((size_t)offset > SIZE_MAX - ZSTR_LEN(ms->data))) { + *newoffs = -1; + return -1; + } + ms->fpos = ZSTR_LEN(ms->data) + (size_t)offset; *newoffs = ms->fpos; stream->eof = 0; stream->fatal_error = 0; diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 8c4b43f6ce83..ec18f3dda699 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -612,6 +612,9 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ } else { convert_to_long(&retval); didwrite = Z_LVAL(retval); + if (didwrite < 0) { + didwrite = -1; + } } /* don't allow strange buffer overruns due to bogus return */