From a00da17312a38217c5e24714b24c52ddc3d3bd83 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 8 Nov 2026 19:30:20 -0500 Subject: [PATCH] ext/opcache: Fix JIT code emitted past the buffer end on 32-bit ir_emit_code() and ir_emit_exitgroup() round code_buffer->pos up to 16 bytes before checking the remaining space. On 32-bit builds the JIT buffer end is only 8-byte aligned, so a pos in the last 8 bytes yields an entry past the end, the unsigned size check wraps, and the code is encoded out of bounds. ext/opcache/tests/jit/gh25165.phpt crashes this way on the nightly x86 lanes. Reject an aligned entry beyond the end; the aarch64 emitters get the same guard. --- ext/opcache/jit/ir/ir_aarch64.dasc | 6 +++--- ext/opcache/jit/ir/ir_x86.dasc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index c3e88e46e3b4..28a0efb5ca67 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -6914,7 +6914,7 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) if (ctx->code_buffer) { entry = ctx->code_buffer->pos; entry = (void*)IR_ALIGNED_SIZE(((size_t)(entry)), 16); - if (size > (size_t)((char*)ctx->code_buffer->end - (char*)entry)) { + if ((char*)entry > (char*)ctx->code_buffer->end || size > (size_t)((char*)ctx->code_buffer->end - (char*)entry)) { dasm_free(&data.dasm_state); ctx->data = NULL; ctx->status = IR_ERROR_CODE_MEM_OVERFLOW; @@ -7057,7 +7057,7 @@ const void *ir_emit_exitgroup(uint32_t first_exit_point, uint32_t exit_points_pe entry = code_buffer->pos; entry = (void*)IR_ALIGNED_SIZE(((size_t)(entry)), 16); - if (size > (size_t)((char*)code_buffer->end - (char*)entry)) { + if ((char*)entry > (char*)code_buffer->end || size > (size_t)((char*)code_buffer->end - (char*)entry)) { dasm_free(&dasm_state); return NULL; } @@ -7227,7 +7227,7 @@ void *ir_emit_thunk(ir_code_buffer *code_buffer, void *addr, size_t *size_ptr) entry = code_buffer->pos; entry = (void*)IR_ALIGNED_SIZE(((size_t)(entry)), 4); - if (size > (size_t)((char*)code_buffer->end - (char*)entry)) { + if ((char*)entry > (char*)code_buffer->end || size > (size_t)((char*)code_buffer->end - (char*)entry)) { dasm_free(&dasm_state); return NULL; } diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index dee5867108e6..b9e841c67dfb 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -12243,7 +12243,7 @@ next_block:; if (ctx->code_buffer) { entry = ctx->code_buffer->pos; entry = (void*)IR_ALIGNED_SIZE(((size_t)(entry)), 16); - if (size > (size_t)((char*)ctx->code_buffer->end - (char*)entry)) { + if ((char*)entry > (char*)ctx->code_buffer->end || size > (size_t)((char*)ctx->code_buffer->end - (char*)entry)) { dasm_free(&data.dasm_state); ctx->data = NULL; ctx->status = IR_ERROR_CODE_MEM_OVERFLOW; @@ -12383,7 +12383,7 @@ const void *ir_emit_exitgroup(uint32_t first_exit_point, uint32_t exit_points_pe entry = code_buffer->pos; entry = (void*)IR_ALIGNED_SIZE(((size_t)(entry)), 16); - if (size > (size_t)((char*)code_buffer->end - (char*)entry)) { + if ((char*)entry > (char*)code_buffer->end || size > (size_t)((char*)code_buffer->end - (char*)entry)) { dasm_free(&dasm_state); return NULL; }