Skip to content

Commit 8ba10b8

Browse files
committed
Fix ASLR related invalid opline handler issues
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.
1 parent 2420678 commit 8ba10b8

File tree

1 file changed

+32
-7
lines changed

1 file changed

+32
-7
lines changed

ext/opcache/shared_alloc_win32.c

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "ZendAccelerator.h"
2323
#include "zend_shared_alloc.h"
2424
#include "zend_accelerator_util_funcs.h"
25+
#include "zend_execute.h"
2526
#include "tsrm_win32.h"
2627
#include "win32/winutil.h"
2728
#include <winbase.h>
@@ -133,6 +134,8 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
133134
char *mmap_base_file = get_mmap_base_file();
134135
FILE *fp = fopen(mmap_base_file, "r");
135136
MEMORY_BASIC_INFORMATION info;
137+
void *execute_ex_base;
138+
int execute_ex_moved;
136139

137140
if (!fp) {
138141
err = GetLastError();
@@ -148,15 +151,25 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
148151
fclose(fp);
149152
return ALLOC_FAILURE;
150153
}
154+
if (!fscanf(fp, "%p", &execute_ex_base)) {
155+
err = GetLastError();
156+
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read execute_ex base address", err);
157+
*error_in="read execute_ex base";
158+
fclose(fp);
159+
return ALLOC_FAILURE;
160+
}
151161
fclose(fp);
152162

153163
if (0 > win32_utime(mmap_base_file, NULL)) {
154164
err = GetLastError();
155165
zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
156166
}
157167

158-
/* Check if the requested address space is free */
159-
if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
168+
execute_ex_moved = (void *)execute_ex != execute_ex_base;
169+
170+
/* Check if execute_ex is at the same address and if the requested address space is free */
171+
if (execute_ex_moved ||
172+
VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
160173
info.State != MEM_FREE ||
161174
info.RegionSize < requested_size) {
162175
#if ENABLE_FILE_CACHE_FALLBACK
@@ -165,8 +178,13 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
165178

166179
wanted_mb_save = (size_t)wanted_mapping_base;
167180

168-
err = ERROR_INVALID_ADDRESS;
169-
zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
181+
if (execute_ex_moved) {
182+
err = ERROR_INVALID_ADDRESS;
183+
zend_win_error_message(ACCEL_LOG_WARNING, "Opcode handlers are unusable due to ASLR (fall-back to file cache)", err);
184+
} else {
185+
err = ERROR_INVALID_ADDRESS;
186+
zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
187+
}
170188

171189
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));
172190
/* Map only part of SHM to have access opcache shared globals */
@@ -181,10 +199,15 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
181199
return ALLOC_FALLBACK;
182200
}
183201
#endif
184-
err = ERROR_INVALID_ADDRESS;
185-
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);
202+
if (execute_ex_moved) {
203+
err = ERROR_INVALID_ADDRESS;
204+
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);
205+
} else {
206+
err = ERROR_INVALID_ADDRESS;
207+
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);
208+
}
186209
return ALLOC_FAILURE;
187-
}
210+
}
188211

189212
mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
190213

@@ -315,6 +338,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
315338
return ALLOC_FAILURE;
316339
} else {
317340
char *mmap_base_file = get_mmap_base_file();
341+
void *execute_ex_base = (void *)execute_ex;
318342
FILE *fp = fopen(mmap_base_file, "w");
319343
if (!fp) {
320344
err = GetLastError();
@@ -324,6 +348,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
324348
return ALLOC_FAILURE;
325349
}
326350
fprintf(fp, "%p\n", mapping_base);
351+
fprintf(fp, "%p\n", execute_ex_base);
327352
fclose(fp);
328353
}
329354

0 commit comments

Comments
 (0)