Skip to content

Commit 39d04f1

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. (cherry picked from commit 8ba10b8)
1 parent 9083e17 commit 39d04f1

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ PHP NEWS
66
. Fixed bug #78787 (Segfault with trait overriding inherited private shadow
77
property). (Nikita)
88

9+
- OPcache:
10+
. Fixed potential ASLR related invalid opline handler issues. (cmb)
11+
912
- Standard:
1013
. Fixed bug #78759 (array_search in $GLOBALS). (Nikita)
1114

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 <winbase.h>
2728
#include <process.h>
@@ -144,6 +145,8 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
144145
char *mmap_base_file = get_mmap_base_file();
145146
FILE *fp = fopen(mmap_base_file, "r");
146147
MEMORY_BASIC_INFORMATION info;
148+
void *execute_ex_base;
149+
int execute_ex_moved;
147150

148151
if (!fp) {
149152
err = GetLastError();
@@ -159,15 +162,25 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
159162
fclose(fp);
160163
return ALLOC_FAILURE;
161164
}
165+
if (!fscanf(fp, "%p", &execute_ex_base)) {
166+
err = GetLastError();
167+
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read execute_ex base address", err);
168+
*error_in="read execute_ex base";
169+
fclose(fp);
170+
return ALLOC_FAILURE;
171+
}
162172
fclose(fp);
163173

164174
if (0 > win32_utime(mmap_base_file, NULL)) {
165175
err = GetLastError();
166176
zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
167177
}
168178

169-
/* Check if the requested address space is free */
170-
if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
179+
execute_ex_moved = (void *)execute_ex != execute_ex_base;
180+
181+
/* Check if execute_ex is at the same address and if the requested address space is free */
182+
if (execute_ex_moved ||
183+
VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
171184
info.State != MEM_FREE ||
172185
info.RegionSize < requested_size) {
173186
#if ENABLE_FILE_CACHE_FALLBACK
@@ -176,8 +189,13 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
176189

177190
wanted_mb_save = (size_t)wanted_mapping_base;
178191

179-
err = ERROR_INVALID_ADDRESS;
180-
zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
192+
if (execute_ex_moved) {
193+
err = ERROR_INVALID_ADDRESS;
194+
zend_win_error_message(ACCEL_LOG_WARNING, "Opcode handlers are unusable due to ASLR (fall-back to file cache)", err);
195+
} else {
196+
err = ERROR_INVALID_ADDRESS;
197+
zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
198+
}
181199

182200
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));
183201
/* 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)
192210
return ALLOC_FALLBACK;
193211
}
194212
#endif
195-
err = ERROR_INVALID_ADDRESS;
196-
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);
213+
if (execute_ex_moved) {
214+
err = ERROR_INVALID_ADDRESS;
215+
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);
216+
} else {
217+
err = ERROR_INVALID_ADDRESS;
218+
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);
219+
}
197220
return ALLOC_FAILURE;
198-
}
221+
}
199222

200223
mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
201224

@@ -326,6 +349,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
326349
return ALLOC_FAILURE;
327350
} else {
328351
char *mmap_base_file = get_mmap_base_file();
352+
void *execute_ex_base = (void *)execute_ex;
329353
FILE *fp = fopen(mmap_base_file, "w");
330354
if (!fp) {
331355
err = GetLastError();
@@ -335,6 +359,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
335359
return ALLOC_FAILURE;
336360
}
337361
fprintf(fp, "%p\n", mapping_base);
362+
fprintf(fp, "%p\n", execute_ex_base);
338363
fclose(fp);
339364
}
340365

0 commit comments

Comments
 (0)