]> granicus.if.org Git - php/commitdiff
Fix ASLR related invalid opline handler issues
authorChristoph M. Becker <cmbecker69@gmx.de>
Tue, 12 Nov 2019 15:12:59 +0000 (16:12 +0100)
committerChristoph M. Becker <cmbecker69@gmx.de>
Fri, 15 Nov 2019 08:46:58 +0000 (09:46 +0100)
Opcache stores `opline->handler`s in shared memory.  These pointers are
invalid, if the main PHP DLL is loaded at another base address due to
ASLR.  We therefore store the address of `execute_ex` in the mmap base
file, and check on startup whether it matches its current address.  If
not, we fall back on the file cache if enabled, and bail out otherwise.

This still does not address cases where the opline handler is located
inside of another DLL (e.g. for some profilers, debuggers), but there
seems to be no general solution for now.

(cherry picked from commit 8ba10b8fbc020dc225d3b19d8f088f1351a3e304)

NEWS
ext/opcache/shared_alloc_win32.c

diff --git a/NEWS b/NEWS
index 8d540d620a199d19e94d210c48b5018f14fab4e6..61b7f4b7817bdb23ca955e8d04f882e30c80c6ad 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,9 @@ PHP                                                                        NEWS
   . Fixed bug #78787 (Segfault with trait overriding inherited private shadow
     property). (Nikita)
 
+- OPcache:
+  . Fixed potential ASLR related invalid opline handler issues. (cmb)
+
 - Standard:
   . Fixed bug #78759 (array_search in $GLOBALS). (Nikita)
 
index ca60c9f5f0781a26182ddff8ed608be23206dbfd..a4cfc5d9c6d9a4e722fa052a1abc726edac3bd70 100644 (file)
@@ -22,6 +22,7 @@
 #include "ZendAccelerator.h"
 #include "zend_shared_alloc.h"
 #include "zend_accelerator_util_funcs.h"
+#include "zend_execute.h"
 #include "tsrm_win32.h"
 #include <winbase.h>
 #include <process.h>
@@ -144,6 +145,8 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
        char *mmap_base_file = get_mmap_base_file();
        FILE *fp = fopen(mmap_base_file, "r");
        MEMORY_BASIC_INFORMATION info;
+       void *execute_ex_base;
+       int execute_ex_moved;
 
        if (!fp) {
                err = GetLastError();
@@ -159,6 +162,13 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
                fclose(fp);
                return ALLOC_FAILURE;
        }
+       if (!fscanf(fp, "%p", &execute_ex_base)) {
+               err = GetLastError();
+               zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read execute_ex base address", err);
+               *error_in="read execute_ex base";
+               fclose(fp);
+               return ALLOC_FAILURE;
+       }
        fclose(fp);
 
        if (0 > win32_utime(mmap_base_file, NULL)) {
@@ -166,8 +176,11 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
                zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
        }
 
-       /* Check if the requested address space is free */
-       if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
+       execute_ex_moved = (void *)execute_ex != execute_ex_base;
+
+       /* Check if execute_ex is at the same address and if the requested address space is free */
+       if (execute_ex_moved ||
+           VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
            info.State != MEM_FREE ||
            info.RegionSize < requested_size) {
 #if ENABLE_FILE_CACHE_FALLBACK
@@ -176,8 +189,13 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
 
                        wanted_mb_save = (size_t)wanted_mapping_base;
 
-                       err = ERROR_INVALID_ADDRESS;
-                       zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
+                       if (execute_ex_moved) {
+                               err = ERROR_INVALID_ADDRESS;
+                               zend_win_error_message(ACCEL_LOG_WARNING, "Opcode handlers are unusable due to ASLR (fall-back to file cache)", err);
+                       } else {
+                               err = ERROR_INVALID_ADDRESS;
+                               zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
+                       }
 
                        pre_size = ZEND_ALIGNED_SIZE(sizeof(zend_smm_shared_globals)) + ZEND_ALIGNED_SIZE(sizeof(zend_shared_segment)) + ZEND_ALIGNED_SIZE(sizeof(void *)) + ZEND_ALIGNED_SIZE(sizeof(int));
                        /* Map only part of SHM to have access opcache shared globals */
@@ -192,10 +210,15 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
                        return ALLOC_FALLBACK;
                }
 #endif
-           err = ERROR_INVALID_ADDRESS;
-               zend_win_error_message(ACCEL_LOG_FATAL, "Base address marks unusable memory region. Please setup opcache.file_cache and opcache.file_cache_fallback directives for more convenient Opcache usage", err);
+               if (execute_ex_moved) {
+                       err = ERROR_INVALID_ADDRESS;
+                       zend_win_error_message(ACCEL_LOG_FATAL, "Opcode handlers are unusable due to ASLR. Please setup opcache.file_cache and opcache.file_cache_fallback directives for more convenient Opcache usage", err);
+               } else {
+                       err = ERROR_INVALID_ADDRESS;
+                       zend_win_error_message(ACCEL_LOG_FATAL, "Base address marks unusable memory region. Please setup opcache.file_cache and opcache.file_cache_fallback directives for more convenient Opcache usage", err);
+               }
                return ALLOC_FAILURE;
-       }
+       }
 
        mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
 
@@ -326,6 +349,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
                return ALLOC_FAILURE;
        } else {
                char *mmap_base_file = get_mmap_base_file();
+               void *execute_ex_base = (void *)execute_ex;
                FILE *fp = fopen(mmap_base_file, "w");
                if (!fp) {
                        err = GetLastError();
@@ -335,6 +359,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
                        return ALLOC_FAILURE;
                }
                fprintf(fp, "%p\n", mapping_base);
+               fprintf(fp, "%p\n", execute_ex_base);
                fclose(fp);
        }