Skip to content

Commit 2ea6424

Browse files
fix(prof): Do not call zend_jit_status() on affected versions (#3356)
* do not call `zend_jit_status()` on affected versions * upstream PR not yet merged
1 parent ed128c8 commit 2ea6424

1 file changed

Lines changed: 82 additions & 13 deletions

File tree

profiling/src/php_ffi.c

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -482,19 +482,40 @@ void ddog_php_opcache_init_handle() {
482482
}
483483
}
484484

485-
// This checks if the JIT actually has a buffer, if so, JIT is active, otherwise
486-
// JIT is inactive. This will only work after OPcache was initialized (after it
487-
// assigned its PHP.INI settings), so make sure to call this in RINIT.
485+
// Detects if JIT is enabled by checking OPcache settings.
488486
//
489-
// Attention: this will check for the `opcache.jit_buffer_size` setting as this
490-
// one is a PHP_INI_SYSTEM (can not be changed on a per directory basis). This
491-
// means that the `opcache.jit` setting could be `off` and this function would
492-
// still consider JIT to be enabled, as it could be enabled at anytime (runtime
493-
// and per directory settings).
487+
// This function uses two different detection methods based on PHP version:
488+
// 1. For PHP versions with the zend_jit_status() crash fix
489+
// - Calls zend_jit_status() directly to get accurate JIT state
490+
// 2. For PHP versions where zend_jit_status() can crash in Apache mod_php:
491+
// - Uses INI-based detection to avoid the crash
492+
// - Checks opcache.enable, opcache.enable_cli, opcache.jit_buffer_size, and opcache.jit
493+
//
494+
// The INI fallback may have false positives (e.g., if JIT is enabled via INI but disabled because
495+
// user opcode handlers are installed) but avoids false negatives and prevents crashes.
496+
//
497+
// Note: This function should be called in RINIT or later, after OPcache initialization.
498+
// Returns true if JIT is potentially active, false otherwise.
494499
bool ddog_php_jit_enabled() {
495-
bool jit = false;
500+
#if PHP_VERSION_ID < 80000
501+
// JIT was introduced in PHP 8.0
502+
return false;
503+
#else
504+
// No OPcache -> no JIT
505+
if (!opcache_handle) {
506+
return false;
507+
}
496508

497-
if (opcache_handle) {
509+
// Check if we can safely use zend_jit_status() based on PHP version
510+
bool can_use_zend_jit_status = false; // Upstream PR has not yet been merged
511+
// Most likely those will be the versions that will have the fix:
512+
// PHP_VERSION_ID >= 80500 || // PHP 8.5+
513+
// (PHP_VERSION_ID >= 80230 && PHP_VERSION_ID < 80300) || // PHP 8.2.30+
514+
// (PHP_VERSION_ID >= 80324 && PHP_VERSION_ID < 80400) || // PHP 8.3.24+
515+
// (PHP_VERSION_ID >= 80411 && PHP_VERSION_ID < 80500); // PHP 8.4.11+
516+
517+
if (can_use_zend_jit_status) {
518+
// Safe to use zend_jit_status() on these versions
498519
void (*zend_jit_status)(zval *ret) = DL_FETCH_SYMBOL(opcache_handle, "zend_jit_status");
499520
if (zend_jit_status == NULL) {
500521
zend_jit_status = DL_FETCH_SYMBOL(opcache_handle, "_zend_jit_status");
@@ -506,14 +527,62 @@ bool ddog_php_jit_enabled() {
506527

507528
zval *jit_stats = zend_hash_str_find(Z_ARR(jit_stats_arr), ZEND_STRL("jit"));
508529
zval *jit_buffer = zend_hash_str_find(Z_ARR_P(jit_stats), ZEND_STRL("buffer_size"));
509-
jit = Z_LVAL_P(jit_buffer) > 0; // JIT is active!
530+
bool jit = Z_LVAL_P(jit_buffer) > 0; // JIT is active!
510531

511532
zval_ptr_dtor(&jit_stats_arr);
533+
return jit;
512534
}
535+
// zend_jit_status() symbol not found despite having an OPcache handle, this is weird, but
536+
// let's fallback to INI based detection
537+
}
538+
539+
// For versions with the bug, use INI-based detection
540+
541+
zend_string *key = zend_string_init(ZEND_STRL("opcache.enable"), 0);
542+
zend_string *opcache_enable_str = zend_ini_get_value(key);
543+
zend_string_release(key);
544+
if (opcache_enable_str && !zend_ini_parse_bool(opcache_enable_str)) {
545+
return false;
513546
}
514-
return jit;
515-
}
516547

548+
// For CLI SAPI, also check opcache.enable_cli
549+
if (strcmp("cli", sapi_module.name) == 0) {
550+
key = zend_string_init(ZEND_STRL("opcache.enable_cli"), 0);
551+
zend_string *opcache_enable_cli_str = zend_ini_get_value(key);
552+
zend_string_release(key);
553+
if (!opcache_enable_cli_str || !zend_ini_parse_bool(opcache_enable_cli_str)) {
554+
return false;
555+
}
556+
}
557+
558+
// Check opcache.jit_buffer_size, no buffer -> no JIT
559+
char *buffer_size_str = zend_ini_string("opcache.jit_buffer_size", sizeof("opcache.jit_buffer_size") - 1, 0);
560+
if (!buffer_size_str || strlen(buffer_size_str) == 0 || strcmp(buffer_size_str, "0") == 0) {
561+
return false;
562+
}
563+
564+
// Parse buffer size, handle suffixes like K, M, G
565+
long buffer_size = ZEND_STRTOL(buffer_size_str, NULL, 10);
566+
if (buffer_size <= 0) {
567+
return false;
568+
}
569+
570+
// Finally check the opcache.jit setting
571+
char *jit_str = zend_ini_string("opcache.jit", sizeof("opcache.jit") - 1, 0);
572+
if (!jit_str || strlen(jit_str) == 0 ||
573+
strcmp(jit_str, "disable") == 0 ||
574+
strcmp(jit_str, "off") == 0 ||
575+
strcmp(jit_str, "0") == 0) {
576+
return false;
577+
}
578+
579+
// At this point:
580+
// - opcache is loaded and enabled
581+
// - buffer_size > 0 (JIT memory allocated)
582+
// - opcache.jit is truthy
583+
return true;
584+
#endif // PHP_VERSION_ID >= 80000
585+
}
517586

518587
#if PHP_VERSION_ID < 70200
519588
#define zend_parse_parameters_none_throw() \

0 commit comments

Comments
 (0)