From 5e4e06ca31210614dbbdf2550df5711ef0c0423e Mon Sep 17 00:00:00 2001 From: Yuma Kakei Date: Mon, 26 Oct 2026 14:55:07 +0900 Subject: [PATCH] Fix JIT fuzzers never enabling the JIT Since the default of opcache.jit changed from "tracing" to "disable" (c16ad918ba3, RFC jit_config_defaults), JIT_G(enabled) is false after startup and zend_jit_config() rejects every runtime change of opcache.jit. The function-jit and tracing-jit fuzzers turn the JIT on at runtime without checking the result, so since PHP 8.4 they have only been running the VM. The OSS-Fuzz coverage report of 2026-10-24 shows 0.04% line coverage (40/103166) for ext/opcache/jit/. Start with opcache.jit=off, which keeps the JIT available, and abort if switching the JIT mode fails, so that this cannot regress silently. --- sapi/fuzzer/fuzzer-execute-common.h | 9 +++++++++ sapi/fuzzer/fuzzer-function-jit.c | 8 ++++---- sapi/fuzzer/fuzzer-tracing-jit.c | 8 ++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/sapi/fuzzer/fuzzer-execute-common.h b/sapi/fuzzer/fuzzer-execute-common.h index 4febd88fafbe..7fb9ef1598df 100644 --- a/sapi/fuzzer/fuzzer-execute-common.h +++ b/sapi/fuzzer/fuzzer-execute-common.h @@ -146,3 +146,12 @@ ZEND_ATTRIBUTE_UNUSED static void opcache_invalidate(void) { zval_ptr_dtor(&retval); EG(exception) = exception; } + +ZEND_ATTRIBUTE_UNUSED static void set_jit_option(zend_string *jit_option, const char *value) { + if (zend_alter_ini_entry_chars( + jit_option, value, strlen(value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { + /* Fail loudly instead of silently fuzzing without the JIT. */ + fprintf(stderr, "Failed to set opcache.jit=%s\n", value); + abort(); + } +} diff --git a/sapi/fuzzer/fuzzer-function-jit.c b/sapi/fuzzer/fuzzer-function-jit.c index d949995f8a23..7b3b18db12b6 100644 --- a/sapi/fuzzer/fuzzer-function-jit.c +++ b/sapi/fuzzer/fuzzer-function-jit.c @@ -27,15 +27,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { * we bail out here, as the JIT code may loop infinitely. */ steps_left = MAX_STEPS; bailed_out = false; - zend_alter_ini_entry_chars( - jit_option, "off", sizeof("off")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + set_jit_option(jit_option, "off"); fuzzer_do_request_from_buffer( FILE_NAME, (const char *) Data, Size, /* execute */ 1, opcache_invalidate); if (!bailed_out) { steps_left = MAX_STEPS; - zend_alter_ini_entry_chars(jit_option, - "function", sizeof("function")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + set_jit_option(jit_option, "function"); zend_execute_ex = orig_execute_ex; fuzzer_do_request_from_buffer( FILE_NAME, (const char *) Data, Size, /* execute */ 1, opcache_invalidate); @@ -50,6 +48,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { int LLVMFuzzerInitialize(int *argc, char ***argv) { char ini_buf[512]; snprintf(ini_buf, sizeof(ini_buf), + /* The default opcache.jit=disable does not allow enabling the JIT at runtime. */ + "opcache.jit=off\n" "opcache.validate_timestamps=0\n" "opcache.file_update_protection=0\n" "opcache.jit_buffer_size=128M\n" diff --git a/sapi/fuzzer/fuzzer-tracing-jit.c b/sapi/fuzzer/fuzzer-tracing-jit.c index e15f5b4bac51..d56913222394 100644 --- a/sapi/fuzzer/fuzzer-tracing-jit.c +++ b/sapi/fuzzer/fuzzer-tracing-jit.c @@ -27,15 +27,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { * we bail out here, as the JIT code may loop infinitely. */ steps_left = MAX_STEPS; bailed_out = false; - zend_alter_ini_entry_chars( - jit_option, "off", sizeof("off")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + set_jit_option(jit_option, "off"); fuzzer_do_request_from_buffer( FILE_NAME, (const char *) Data, Size, /* execute */ 1, opcache_invalidate); if (!bailed_out) { steps_left = MAX_STEPS; - zend_alter_ini_entry_chars(jit_option, - "tracing", sizeof("tracing")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + set_jit_option(jit_option, "tracing"); zend_execute_ex = orig_execute_ex; /* Trace & compile */ fuzzer_do_request_from_buffer( @@ -54,6 +52,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { int LLVMFuzzerInitialize(int *argc, char ***argv) { char ini_buf[512]; snprintf(ini_buf, sizeof(ini_buf), + /* The default opcache.jit=disable does not allow enabling the JIT at runtime. */ + "opcache.jit=off\n" "opcache.validate_timestamps=0\n" "opcache.file_update_protection=0\n" "opcache.memory_consumption=1024\n"