Skip to content

Commit

Permalink
Fix ASLR related invalid opline handler issues
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cmb69 committed May 21, 2019
1 parent 2420678 commit 8ba10b8
Showing 1 changed file with 32 additions and 7 deletions.
39 changes: 32 additions & 7 deletions ext/opcache/shared_alloc_win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 "win32/winutil.h"
#include <winbase.h>
Expand Down Expand Up @@ -133,6 +134,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();
Expand All @@ -148,15 +151,25 @@ 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)) {
err = GetLastError();
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
Expand All @@ -165,8 +178,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 */
Expand All @@ -181,10 +199,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);

Expand Down Expand Up @@ -315,6 +338,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();
Expand All @@ -324,6 +348,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);
}

Expand Down

0 comments on commit 8ba10b8

Please sign in to comment.