Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0alpha3

- GMP:
. Fixed GMP power and shift operators to reject GMP right operands outside
the unsigned long range instead of silently truncating them. (Weilin Du)

- ODBC:
. Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the
driver-reported display size). (iliaal)

- Opcache:
. Re-enable JIT for ZTS builds on Apple Silicon. (realFlowControl)

- PDO_ODBC:
. Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the
driver-reported display size). (iliaal)
Expand Down
3 changes: 3 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ PHP 8.6 UPGRADE NOTES
- mysqli
. Added new constant MYSQLI_OPT_COMPRESS.

- Opcache
. JIT is now supported for ZTS builds on Apple Silicon.

========================================
10. New Global Constants
========================================
Expand Down
46 changes: 30 additions & 16 deletions ext/gmp/gmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,17 @@ static zend_result binop_operator_helper(gmp_binary_op_t gmp_op, zval *return_va

typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong);

static void gmp_shift_operator_range_error(uint8_t opcode) {
zend_throw_error(
zend_ce_value_error, "%s must be between 0 and %lu",
opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
);
}

static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, uint8_t opcode) {
zend_long shift = 0;
gmp_ulong shift_ui = 0;
bool have_shift_ui = false;

if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
if (UNEXPECTED(!IS_GMP(op2))) {
Expand All @@ -349,31 +358,36 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
goto typeof_op_failure;
}
} else {
// TODO We shouldn't cast the GMP object to int here
shift = zval_get_long(op2);
mpz_ptr gmpnum_shift = GET_GMP_FROM_ZVAL(op2);
if (!mpz_fits_ulong_p(gmpnum_shift)) {
gmp_shift_operator_range_error(opcode);
return FAILURE;
}
shift_ui = (gmp_ulong) mpz_get_ui(gmpnum_shift);
have_shift_ui = true;
}
} else {
shift = Z_LVAL_P(op2);
}

if (shift < 0 || shift > ULONG_MAX) {
zend_throw_error(
zend_ce_value_error, "%s must be between 0 and %lu",
opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
);
return FAILURE;
} else {
mpz_ptr gmpnum_op, gmpnum_result;

if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
goto typeof_op_failure;
if (!have_shift_ui) {
if (shift < 0 || shift > ULONG_MAX) {
gmp_shift_operator_range_error(opcode);
return FAILURE;
}
shift_ui = (gmp_ulong) shift;
}

INIT_GMP_RETVAL(gmpnum_result);
op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
return SUCCESS;
mpz_ptr gmpnum_op, gmpnum_result;

if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
goto typeof_op_failure;
}

INIT_GMP_RETVAL(gmpnum_result);
op(gmpnum_result, gmpnum_op, shift_ui);
return SUCCESS;

typeof_op_failure: ;
/* Returning FAILURE without throwing an exception would emit the
* Unsupported operand types: GMP OP TypeOfOp2
Expand Down
33 changes: 33 additions & 0 deletions ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
GMP operator right operand rejects values outside the unsigned long range
--EXTENSIONS--
gmp
--FILE--
<?php
$too_large = gmp_init("18446744073709551616");

try {
var_dump(gmp_init(2) ** $too_large);
} catch (ValueError $e) {
echo $e->getMessage(), PHP_EOL;
}

try {
var_dump(gmp_init(2) << $too_large);
} catch (ValueError $e) {
echo $e->getMessage(), PHP_EOL;
}

try {
var_dump(gmp_init(2) >> $too_large);
} catch (ValueError $e) {
echo $e->getMessage(), PHP_EOL;
}

echo "Done\n";
?>
--EXPECTF--
Exponent must be between 0 and %d
Shift must be between 0 and %d
Shift must be between 0 and %d
Done
25 changes: 16 additions & 9 deletions ext/opcache/ZendAccelerator.c
Original file line number Diff line number Diff line change
Expand Up @@ -3259,8 +3259,9 @@ static zend_result accel_post_startup(void)
file_cache_only = ZCG(accel_directives).file_cache_only;
if (!file_cache_only) {
size_t shm_size = ZCG(accel_directives).memory_consumption;
#ifdef HAVE_JIT
size_t jit_size = 0;
#ifdef HAVE_JIT
size_t jit_buffer_size = 0;
bool reattached = false;

if (JIT_G(enabled) && JIT_G(buffer_size)
Expand All @@ -3272,15 +3273,16 @@ static zend_result accel_post_startup(void)
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can't get page size.");
abort();
}
jit_size = JIT_G(buffer_size);
jit_size = ZEND_MM_ALIGNED_SIZE_EX(jit_size, page_size);
jit_buffer_size = JIT_G(buffer_size);
jit_buffer_size = ZEND_MM_ALIGNED_SIZE_EX(jit_buffer_size, page_size);
# ifndef ZEND_JIT_USE_APPLE_MAP_JIT
jit_size = jit_buffer_size;
shm_size += jit_size;
# endif
}
#endif

switch (zend_shared_alloc_startup(shm_size, jit_size)) {
#else
switch (zend_shared_alloc_startup(shm_size, 0)) {
#endif
case ALLOC_SUCCESS:
if (zend_accel_init_shm() == FAILURE) {
accel_startup_ok = false;
Expand Down Expand Up @@ -3334,10 +3336,15 @@ static zend_result accel_post_startup(void)
if (JIT_G(buffer_size) == 0) {
JIT_G(enabled) = false;
JIT_G(on) = false;
} else if (!ZSMMG(reserved)) {
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Could not enable JIT: could not use reserved buffer!");
} else {
zend_jit_startup(ZSMMG(reserved), jit_size, reattached);
# ifdef ZEND_JIT_USE_APPLE_MAP_JIT
zend_jit_startup(NULL, jit_buffer_size, reattached);
# else
if (!ZSMMG(reserved)) {
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Could not enable JIT: could not use reserved buffer!");
}
zend_jit_startup(ZSMMG(reserved), jit_buffer_size, reattached);
# endif
zend_jit_startup_ok = true;
}
}
Expand Down
7 changes: 3 additions & 4 deletions ext/opcache/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [
PHP_OPCACHE_JIT=no
])

if test "$host_vendor" = "apple" && test "$host_cpu" = "aarch64" && test "$PHP_THREAD_SAFETY" = "yes"; then
AC_MSG_WARN([JIT not supported on Apple Silicon with ZTS])
PHP_OPCACHE_JIT=no
fi
AS_IF([test "$host_vendor" = "apple" && test "$host_cpu" = "aarch64" && test "$PHP_THREAD_SAFETY" = "yes"],
[AC_CHECK_FUNC([pthread_jit_write_protect_np], [],
[AC_MSG_ERROR([OPcache JIT on Apple Silicon with ZTS requires pthread_jit_write_protect_np()])])])
])

AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [
Expand Down
73 changes: 44 additions & 29 deletions ext/opcache/jit/zend_jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@

#include "jit/zend_jit_internal.h"

#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP
#ifdef ZEND_JIT_USE_APPLE_MAP_JIT
#include <mach/vm_inherit.h>
#include <pthread.h>
#include <sys/mman.h>
#endif

#ifdef ZTS
Expand Down Expand Up @@ -77,9 +79,6 @@ int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT];

const zend_op *zend_jit_halt_op = NULL;
const zend_op *zend_jit_interrupt_op = NULL;
#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP
static int zend_write_protect = 1;
#endif

static void *dasm_buf = NULL;
static void *dasm_end = NULL;
Expand Down Expand Up @@ -3517,29 +3516,26 @@ int zend_jit_script(zend_script *script)

void zend_jit_unprotect(void)
{
#ifdef HAVE_MPROTECT
#ifdef ZEND_JIT_USE_APPLE_MAP_JIT
pthread_jit_write_protect_np(0);
#elif defined(HAVE_MPROTECT)
if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
int opts = PROT_READ | PROT_WRITE;
#ifdef ZTS
#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP
if (zend_write_protect) {
pthread_jit_write_protect_np(0);
}
#endif
# ifdef ZTS
opts |= PROT_EXEC;
#endif
# endif
if (mprotect(dasm_buf, dasm_size, opts) != 0) {
fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
}
}
#elif defined(_WIN32)
if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
DWORD old, new;
#ifdef ZTS
# ifdef ZTS
new = PAGE_EXECUTE_READWRITE;
#else
# else
new = PAGE_READWRITE;
#endif
# endif
if (!VirtualProtect(dasm_buf, dasm_size, new, &old)) {
DWORD err = GetLastError();
char *msg = php_win32_error_to_msg(err);
Expand All @@ -3552,13 +3548,10 @@ void zend_jit_unprotect(void)

void zend_jit_protect(void)
{
#ifdef HAVE_MPROTECT
#ifdef ZEND_JIT_USE_APPLE_MAP_JIT
pthread_jit_write_protect_np(1);
#elif defined(HAVE_MPROTECT)
if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP
if (zend_write_protect) {
pthread_jit_write_protect_np(1);
}
#endif
if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) {
fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
}
Expand Down Expand Up @@ -3780,20 +3773,36 @@ void zend_jit_startup(void *buf, size_t size, bool reattached)
zend_jit_interrupt_op = zend_get_interrupt_op();
zend_jit_profile_counter_rid = zend_get_op_array_extension_handle(ACCELERATOR_PRODUCT_NAME);

#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP
zend_write_protect = pthread_jit_write_protect_supported_np();
#ifdef ZEND_JIT_USE_APPLE_MAP_JIT
buf = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
if (buf == MAP_FAILED) {
int error = errno;
zend_accel_error_noreturn(ACCEL_LOG_FATAL,
"Unable to allocate %zu bytes for JIT buffer using MAP_JIT: %s (%d)",
size, strerror(error), error);
}
if (minherit(buf, size, VM_INHERIT_SHARE) != 0) {
int error = errno;
munmap(buf, size);
zend_accel_error_noreturn(ACCEL_LOG_FATAL,
"Unable to share JIT buffer across fork using minherit(): %s (%d)",
strerror(error), error);
}
if (!pthread_jit_write_protect_supported_np()) {
munmap(buf, size);
zend_accel_error_noreturn(ACCEL_LOG_FATAL,
"Apple Silicon ZTS JIT requires pthread_jit_write_protect_np() support");
}
#endif

dasm_buf = buf;
dasm_size = size;
dasm_ptr = dasm_end = (void*)(((char*)dasm_buf) + size - sizeof(*dasm_ptr) * 2);

#ifdef HAVE_MPROTECT
#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP
if (zend_write_protect) {
pthread_jit_write_protect_np(1);
}
#endif
#ifdef ZEND_JIT_USE_APPLE_MAP_JIT
pthread_jit_write_protect_np(1);
#elif defined(HAVE_MPROTECT)
if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) {
if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
Expand Down Expand Up @@ -3874,6 +3883,12 @@ void zend_jit_shutdown(void)
zend_jit_trace_free_caches(&jit_globals);
#endif

#ifdef ZEND_JIT_USE_APPLE_MAP_JIT
if (dasm_buf != NULL) {
munmap(dasm_buf, dasm_size);
}
#endif

/* Reset global pointers to prevent use-after-free in `zend_jit_status()`
* after gracefully restarting Apache with mod_php, see:
* https://github.com/php/php-src/pull/19212 */
Expand Down
7 changes: 7 additions & 0 deletions ext/opcache/jit/zend_jit.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
#ifndef HAVE_JIT_H
#define HAVE_JIT_H

#if defined(__APPLE__) && defined(__aarch64__) && defined(ZTS)
# ifndef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP
# error "Apple Silicon ZTS JIT requires pthread_jit_write_protect_np()"
# endif
# define ZEND_JIT_USE_APPLE_MAP_JIT 1
#endif

#if defined(__x86_64__) || defined(i386) || defined(ZEND_WIN32)
# define ZEND_JIT_TARGET_X86 1
# define ZEND_JIT_TARGET_ARM64 0
Expand Down
Loading