root/ext/opcache/shared_alloc_win32.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. zend_win_error_message
  2. create_name_with_username
  3. get_mmap_base_file
  4. zend_shared_alloc_create_lock
  5. zend_shared_alloc_lock_win32
  6. zend_shared_alloc_unlock_win32
  7. zend_shared_alloc_reattach
  8. create_segments
  9. detach_segment
  10. segment_type_size

   1 /*
   2    +----------------------------------------------------------------------+
   3    | Zend OPcache                                                         |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1998-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors: Andi Gutmans <andi@zend.com>                                |
  16    |          Zeev Suraski <zeev@zend.com>                                |
  17    |          Stanislav Malyshev <stas@zend.com>                          |
  18    |          Dmitry Stogov <dmitry@zend.com>                             |
  19    +----------------------------------------------------------------------+
  20 */
  21 
  22 #include "ZendAccelerator.h"
  23 #include "zend_shared_alloc.h"
  24 #include "zend_accelerator_util_funcs.h"
  25 #include <winbase.h>
  26 #include <process.h>
  27 #include <LMCONS.H>
  28 
  29 #define ACCEL_FILEMAP_NAME "ZendOPcache.SharedMemoryArea"
  30 #define ACCEL_MUTEX_NAME "ZendOPcache.SharedMemoryMutex"
  31 #define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000
  32 #define ACCEL_FILEMAP_BASE "ZendOPcache.MemoryBase"
  33 #define ACCEL_EVENT_SOURCE "Zend OPcache"
  34 
  35 static HANDLE memfile = NULL, memory_mutex = NULL;
  36 static void *mapping_base;
  37 
  38 #define MAX_MAP_RETRIES 25
  39 
  40 static void zend_win_error_message(int type, char *msg, int err)
  41 {
  42         LPVOID lpMsgBuf;
  43         HANDLE h;
  44         char *ev_msgs[2];
  45 
  46         FormatMessage(
  47                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
  48                 FORMAT_MESSAGE_FROM_SYSTEM |
  49                 FORMAT_MESSAGE_IGNORE_INSERTS,
  50                 NULL,
  51                 err,
  52                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  53                 (LPTSTR) &lpMsgBuf,
  54                 0,
  55                 NULL
  56         );
  57 
  58         h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE));
  59         ev_msgs[0] = msg;
  60         ev_msgs[1] = lpMsgBuf;
  61         ReportEvent(h,                            // event log handle
  62             EVENTLOG_ERROR_TYPE,  // event type
  63             0,                    // category zero
  64             err,                                  // event identifier
  65             NULL,                 // no user security identifier
  66             2,                    // one substitution string
  67             0,                    // no data
  68             ev_msgs,              // pointer to string array
  69             NULL);                // pointer to data
  70         DeregisterEventSource(h);
  71 
  72         LocalFree( lpMsgBuf );
  73 
  74         zend_accel_error(type, msg);
  75 }
  76 
  77 static char *create_name_with_username(char *name)
  78 {
  79         static char newname[MAXPATHLEN + UNLEN + 4 + 1 + 32];
  80         char uname[UNLEN + 1];
  81         DWORD unsize = UNLEN;
  82 
  83         GetUserName(uname, &unsize);
  84         snprintf(newname, sizeof(newname) - 1, "%s@%s@%.32s", name, uname, ZCG(system_id));
  85         return newname;
  86 }
  87 
  88 static char *get_mmap_base_file(void)
  89 {
  90         static char windir[MAXPATHLEN+UNLEN + 3 + sizeof("\\\\@") + 1 + 32];
  91         char uname[UNLEN + 1];
  92         DWORD unsize = UNLEN;
  93         int l;
  94 
  95         GetTempPath(MAXPATHLEN, windir);
  96         GetUserName(uname, &unsize);
  97         l = strlen(windir);
  98         snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%s@%.32s", ACCEL_FILEMAP_BASE, uname, ZCG(system_id));
  99         return windir;
 100 }
 101 
 102 void zend_shared_alloc_create_lock(void)
 103 {
 104         memory_mutex = CreateMutex(NULL, FALSE, create_name_with_username(ACCEL_MUTEX_NAME));
 105         if (!memory_mutex) {
 106                 zend_accel_error(ACCEL_LOG_FATAL, "Cannot create mutex");
 107                 return;
 108         }
 109         ReleaseMutex(memory_mutex);
 110 }
 111 
 112 void zend_shared_alloc_lock_win32(void)
 113 {
 114         DWORD waitRes = WaitForSingleObject(memory_mutex, INFINITE);
 115 
 116         if (waitRes == WAIT_FAILED) {
 117                 zend_accel_error(ACCEL_LOG_ERROR, "Cannot lock mutex");
 118         }
 119 }
 120 
 121 void zend_shared_alloc_unlock_win32(void)
 122 {
 123         ReleaseMutex(memory_mutex);
 124 }
 125 
 126 static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
 127 {
 128         int err;
 129         void *wanted_mapping_base;
 130         char *mmap_base_file = get_mmap_base_file();
 131         FILE *fp = fopen(mmap_base_file, "r");
 132         MEMORY_BASIC_INFORMATION info;
 133 
 134         err = GetLastError();
 135         if (!fp) {
 136                 zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
 137                 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err);
 138                 *error_in="fopen";
 139                 return ALLOC_FAILURE;
 140         }
 141         if (!fscanf(fp, "%p", &wanted_mapping_base)) {
 142                 err = GetLastError();
 143                 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err);
 144                 *error_in="read mapping base";
 145                 fclose(fp);
 146                 return ALLOC_FAILURE;
 147         }
 148         fclose(fp);
 149         /* Check if the requested address space is free */
 150         if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
 151             info.State != MEM_FREE ||
 152             info.RegionSize < requested_size) {
 153 #if ENABLE_FILE_CACHE_FALLBACK
 154                 if (ZCG(accel_directives).file_cache && ZCG(accel_directives).file_cache_fallback) {
 155                         size_t pre_size, wanted_mb_save;
 156 
 157                         wanted_mb_save = (size_t)wanted_mapping_base;
 158 
 159                         err = ERROR_INVALID_ADDRESS;
 160                         zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
 161 
 162                         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));
 163                         /* Map only part of SHM to have access opcache shared globals */
 164                         mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, pre_size + ZEND_ALIGNED_SIZE(sizeof(zend_accel_shared_globals)), NULL);
 165                         if (mapping_base == NULL) {
 166                                 err = GetLastError();
 167                                 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to opcache shared globals", err);
 168                                 return ALLOC_FAILURE;
 169                         }
 170                         accel_shared_globals = (zend_accel_shared_globals *)((char *)((zend_smm_shared_globals *)mapping_base)->app_shared_globals + ((char *)mapping_base - (char *)wanted_mb_save));
 171 
 172                         /* Make this process to use file-cache only */
 173                         ZCG(accel_directives).file_cache_only = 1;
 174 
 175                         return ALLOC_FALLBACK;
 176                 }
 177 #endif
 178             err = ERROR_INVALID_ADDRESS;
 179                 zend_win_error_message(ACCEL_LOG_FATAL, "Base address marks unusable memory region. Please setup opcache.file_cache and opcache.file_cache_callback directives for more convenient Opcache usage", err);
 180                 return ALLOC_FAILURE;
 181         }
 182 
 183         mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
 184         err = GetLastError();
 185 
 186         if (mapping_base == NULL) {
 187                 if (err == ERROR_INVALID_ADDRESS) {
 188                         zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
 189                         return ALLOC_FAILURE;
 190                 }
 191                 return ALLOC_FAIL_MAPPING;
 192         }
 193         smm_shared_globals = (zend_smm_shared_globals *) mapping_base;
 194 
 195         return SUCCESSFULLY_REATTACHED;
 196 }
 197 
 198 static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
 199 {
 200         int err, ret;
 201         zend_shared_segment *shared_segment;
 202         int map_retries = 0;
 203         void *default_mapping_base_set[] = { 0, 0 };
 204         /* TODO:
 205           improve fixed addresses on x64. It still makes no sense to do it as Windows addresses are virtual per se and can or should be randomized anyway
 206           through Address Space Layout Radomization (ASLR). We can still let the OS do its job and be sure that each process gets the same address if
 207           desired. Not done yet, @zend refused but did not remember the exact reason, pls add info here if one of you know why :)
 208         */
 209 #if defined(_WIN64)
 210         void *vista_mapping_base_set[] = { (void *) 0x0000100000000000, (void *) 0x0000200000000000, (void *) 0x0000300000000000, (void *) 0x0000700000000000, 0 };
 211 #else
 212         void *vista_mapping_base_set[] = { (void *) 0x20000000, (void *) 0x21000000, (void *) 0x30000000, (void *) 0x31000000, (void *) 0x50000000, 0 };
 213 #endif
 214         void **wanted_mapping_base = default_mapping_base_set;
 215 
 216         zend_shared_alloc_lock_win32();
 217         /* Mapping retries: When Apache2 restarts, the parent process startup routine
 218            can be called before the child process is killed. In this case, the map will fail
 219            and we have to sleep some time (until the child releases the mapping object) and retry.*/
 220         do {
 221                 memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME));
 222                 err = GetLastError();
 223                 if (memfile == NULL) {
 224                         break;
 225                 }
 226 
 227                 ret =  zend_shared_alloc_reattach(requested_size, error_in);
 228                 err = GetLastError();
 229                 if (ret == ALLOC_FAIL_MAPPING) {
 230                         /* Mapping failed, wait for mapping object to get freed and retry */
 231                         CloseHandle(memfile);
 232                         memfile = NULL;
 233                         if (++map_retries >= MAX_MAP_RETRIES) {
 234                                 break;
 235                         }
 236                         zend_shared_alloc_unlock_win32();
 237                         Sleep(1000 * (map_retries + 1));
 238                         zend_shared_alloc_lock_win32();
 239                 } else {
 240                         zend_shared_alloc_unlock_win32();
 241                         return ret;
 242                 }
 243         } while (1);
 244 
 245         if (map_retries == MAX_MAP_RETRIES) {
 246                 zend_shared_alloc_unlock_win32();
 247                 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open file mapping", err);
 248                 *error_in = "OpenFileMapping";
 249                 return ALLOC_FAILURE;
 250         }
 251 
 252         /* creating segment here */
 253         *shared_segments_count = 1;
 254         *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *));
 255         if (!*shared_segments_p) {
 256                 zend_shared_alloc_unlock_win32();
 257                 zend_win_error_message(ACCEL_LOG_FATAL, "calloc() failed", GetLastError());
 258                 *error_in = "calloc";
 259                 return ALLOC_FAILURE;
 260         }
 261         shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *));
 262         (*shared_segments_p)[0] = shared_segment;
 263 
 264         memfile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, requested_size,
 265                                                                 create_name_with_username(ACCEL_FILEMAP_NAME));
 266         err = GetLastError();
 267         if (memfile == NULL) {
 268                 zend_shared_alloc_unlock_win32();
 269                 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create file mapping", err);
 270                 *error_in = "CreateFileMapping";
 271                 return ALLOC_FAILURE;
 272         }
 273 
 274         /* Starting from windows Vista, heap randomization occurs which might cause our mapping base to
 275            be taken (fail to map). So under Vista, we try to map into a hard coded predefined addresses
 276            in high memory. */
 277         if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) {
 278                 wanted_mapping_base = vista_mapping_base_set;
 279         } else {
 280                 char *s = ZCG(accel_directives).mmap_base;
 281 
 282                 /* skip leading 0x, %p assumes hexdeciaml format anyway */
 283                 if (*s == '0' && *(s + 1) == 'x') {
 284                         s += 2;
 285                 }
 286                 if (sscanf(s, "%p", &default_mapping_base_set[0]) != 1) {
 287                         zend_shared_alloc_unlock_win32();
 288                         zend_win_error_message(ACCEL_LOG_FATAL, "Bad mapping address specified in opcache.mmap_base", err);
 289                         return ALLOC_FAILURE;
 290                 }
 291         }
 292 
 293         do {
 294                 shared_segment->p = mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, *wanted_mapping_base);
 295                 if (*wanted_mapping_base == NULL) { /* Auto address (NULL) is the last option on the array */
 296                         break;
 297                 }
 298                 wanted_mapping_base++;
 299         } while (!mapping_base);
 300 
 301         err = GetLastError();
 302         if (mapping_base == NULL) {
 303                 zend_shared_alloc_unlock_win32();
 304                 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create view for file mapping", err);
 305                 *error_in = "MapViewOfFile";
 306                 return ALLOC_FAILURE;
 307         } else {
 308                 char *mmap_base_file = get_mmap_base_file();
 309                 FILE *fp = fopen(mmap_base_file, "w");
 310                 err = GetLastError();
 311                 if (!fp) {
 312                         zend_shared_alloc_unlock_win32();
 313                         zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
 314                         zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err);
 315                         return ALLOC_FAILURE;
 316                 }
 317                 fprintf(fp, "%p\n", mapping_base);
 318                 fclose(fp);
 319         }
 320 
 321         shared_segment->pos = 0;
 322         shared_segment->size = requested_size;
 323 
 324         zend_shared_alloc_unlock_win32();
 325 
 326         return ALLOC_SUCCESS;
 327 }
 328 
 329 static int detach_segment(zend_shared_segment *shared_segment)
 330 {
 331         zend_shared_alloc_lock_win32();
 332         if (mapping_base) {
 333                 UnmapViewOfFile(mapping_base);
 334                 mapping_base = NULL;
 335         }
 336         CloseHandle(memfile);
 337         memfile = NULL;
 338         zend_shared_alloc_unlock_win32();
 339         CloseHandle(memory_mutex);
 340         memory_mutex = NULL;
 341         return 0;
 342 }
 343 
 344 static size_t segment_type_size(void)
 345 {
 346         return sizeof(zend_shared_segment);
 347 }
 348 
 349 zend_shared_memory_handlers zend_alloc_win32_handlers = {
 350         create_segments,
 351         detach_segment,
 352         segment_type_size
 353 };

/* [<][>][^][v][top][bottom][index][help] */