@@ -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.
494499bool 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