php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73885 Fatal Error Unable to write base address C:\windows\\ZendOPcache.MemoryBase@...
Submitted: 2017-01-07 00:09 UTC Modified: 2017-02-12 21:09 UTC
From: caaguado at xcentra dot com Assigned: ab (profile)
Status: Not a bug Package: opcache
PHP Version: 7.1.0 OS: Windows 7 to 10
Private report: No CVE-ID: None
 [2017-01-07 00:09 UTC] caaguado at xcentra dot com
Description:
------------
This is a follow-up bug report to #72623 (https://bugs.php.net/bug.php?id=72623)
for PHP 7.1.0 on Microsoft Windows 7 through to Windows 10.

Whenever php-cgi.exe is called from within Apache 2.4.25 as CGI (non Apache
module version of PHP 7.1.0), the following error trace is recorded in
OpCache's error log:

Fri Jan  6 22:16:06 2017 (8828): Warning C:\windows\\ZendOPcache.MemoryBase@USERNAME@92497dc5fe29db25b36d9610a381dbc0
Fri Jan  6 22:16:06 2017 (8828): Fatal Error Unable to write base address

Right after this, Apache immediately returns a "500 Internal Server Error"
to the user's browser and records the following trace into its own error log:

[Fri Jan 06 22:16:06.076457 2017] [cgi:error] [pid 5084:tid 1684] [client 127.0.0.1:58366] End of script output before headers: php-cgi.exe

The error occurs just calling a simple PHP file only containing a call to
PHP function "phpinfo();"

Looking at the PHP 7.1.0 source code, the problem is at file/line:
ext/opcache/shared_alloc_win32.c:319
when trying to fopen(mmap_base_file, "w");

The mmap_base_file variable used in fopen is returned from get_mmap_base_file().
This function calls in its turn Windows API function GetTempPath() in file/line:
ext/opcache/shared_alloc_win32.c:103

It turns out that in PHP 7.1.0 the GetTempPath() Windows API call __ALWAYS__
returns "C:\WINDOWS" instead of the Windows system temporary folder (usually
C:\WINDOWS\TEMP) or the user's temporary folder.

Since C:\WINDOWS is a system protected directory, write access to the
ZendOPcache.MemoryBase@USERNAME@... file containing OpCache's write base address
fails, __UNLESS__ Apache (and so PHP 7.1.0) is called from a Windows Command
Prompt window with elevated Administrator permissions (in which case the
ZendOPcache.MemoryBase@USERNAME@... file is successfully created and PHP 7.1.0
works fine).

The reason why the GetTempPath() Windows API call returns "C:\WINDOWS" could
not be determined (is any hook or any other new API call proxying mechanism
implemented in PHP 7.1.0 and not in previous PHP versions??).

I've noticed that bug report #73060 (https://bugs.php.net/bug.php?id=73060)
proposes a patch to get rid altogether of the ZendOPcache.MemoryBase@USERNAME@
file. I would suggest that this patch is considered for implementation if
deemed feasible.

Checks performed:
=================
- The fault can be reproduced both with the 32-bit version of Apache Lounge
  and Apache Haus Apache 2.4.25, available here:

  http://www.apachelounge.com/download/
  http://www.apachehaus.com/cgi-bin/download.plx#APACHE24VC14

- Monitoring for crashes with DebugDiag as per instructions in the "Generating
  backtrace, without compiler, on Win32" section, as well as with ProcDump v8.2
  (command procdump.exe -e -o -t -w php-cgi.exe, tool available here:
  https://technet.microsoft.com/en-us/sysinternals/dd996900.aspx), no such
  crashes appear.

- The following event is registered in Windows' Application Event log:

  Level	Date and Time	Source	Event ID	Task Category
  Error	2017-01-06 22:16:07	Zend OPcache	5	None
  "The description for Event ID 5 from source Zend OPcache cannot be found.
  Either the component that raises this event is not installed on your local
  computer or the installation is corrupted. You can install or repair the
  component on the local computer.

  If the event originated on another computer, the display information had to
  be saved with the event.

  The following information was included with the event: 

  Unable to write base address
  Access is denied.

- The fault happens only with PHP 7.1.0 and not e.g. with PHP 7.0.14. It occurs
  at least in Windows 10 64-bits, Windows 8.1 64 and 32 bits and Windows 7. It
  also occurs when attempting it in a VirtualBox image of Windows.

- The fault does NOT happen with Nginx 1.11.8 (which uses FastCGI instead of
  CGI. Nginx available here: http://nginx.org/en/download.html).

Test script:
---------------
1. Create a temporary test folder, for example: C:\Temp\P71bug.

2. Download Apache Lounge or Apache Haus Apache 2.4.25 and unzip it in subfolder
   C:\Temp\P71bug\bin\a24

3. Download PHP 7.1.0 from http://windows.php.net/download#php-7.1 and unzip it
   in subfolder C:\Temp\P71bug\bin\p71

4. Copy the Apache configuration file at http://bugs.xtack.org/httpd.conf
   to folder C:\Temp\P71bug\bin\a24\conf

5. Save the following PHP 7.1.0 configuration file available at
   http://bugs.xtack.org/phpini.txt as file name php.ini in folder
   C:\Temp\P71bug\bin\p71

6. Create an index.php file in folder C:\Temp\P71bug containing the following:
   <?php
   phpinfo();

7. Start Apache 2.4.25 up with command:
   .\bin\a24\bin\httpd.exe -X -f "C:\Temp\P71bug\bin\a24\conf\httpd.conf"

8. Open a browser window and point it to: http//127.0.0.1/index.php

9. The fault occurs and info is written to log files:
   - C:\Temp\P71bug\xtack_apache24_error.log
   - C:\Temp\P71bug\xtack_php71_opcache_error.log

10. If PHP 7.0.14 is unzipped to folder C:\Temp\P71bug\bin\p70 and the same
    php.ini file is copied to that very same folder, Apache can be started
    pointing httpd.conf to that folder, in order to see that PHP 7.0.14 works
    fine and the error doesn't happen.

Expected result:
----------------
PHP 7.1.0 successfully executed without errors from within Apache 2.4.25.

Actual result:
--------------
500 Internal Server Error, as per explanations given above.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-01-07 21:13 UTC] caaguado at xcentra dot com
I forgot to mention that changing either the system our user TEMP and TMP system variables to whatever values does make no difference to the fault, that is, the file is always attempted to be created at C:\WINDOWS.
 [2017-01-11 04:53 UTC] krakjoe@php.net
-Assigned To: +Assigned To: ab
 [2017-01-11 04:53 UTC] krakjoe@php.net
Assigning to you Anatol, can you take a quick look at this and reassign dmitry if necessary ?
 [2017-01-12 17:06 UTC] ab@php.net
Thanks for the detailed report. Same as in bug #72623, nothing is changed. The CGI/FCGI process is invoked by Apache and inherits its environment. GetTempPath() might be affected by that, too, or even by both the system and Apache. Not only Opcache, but anything using GetTempPath will be affected, fe sys_get_temp_dir() and others. The diff in 7.0 and 7.1 is likely about different environment, too, because otherwise it's same CRT and same call. Not sure there could be a way to affect these environments in Apache so it becomes effective, though the config options do exist.

Thanks.
 [2017-01-12 22:13 UTC] caaguado at xcentra dot com
Thanks Anatol, I see your point about the inherited environment and, actually, it's probably a good lead. What we know:

- As per #73060, the problem appeared in early PHP 7.1 alphas.
- Problem reproduceable in Apache 2.4.20 through to 2.4.25 in 2 different builds (Apache Lounge and Apache Haus).
- Nginx unaffected all this time.
- GetTempPath() used in a number of places along the codebase, yet only OpCache  seemingly affected.
- The later fact makes me think that if this is an environment "propagation" issue, the issue isn't related by the environment passed by Apache down to PHP, but from PHP to OpCache specifically (does this make sense altogether?).
- This seems reinforced by the fact that if the Windows TEMP/TMP variables are changed, the fault remains the same. So, whatever environment Apache passes, it is not well ultimately propagated to OpCache...

Anyway, the person who wrote #73060 has a relevant point here: If using a file is  cumbersome anyway, why not changing the implementation to avoid it (saving some few I/O in the process), handling everything in RAM (please kindly consider his/her patch).

Or, alternatively, why not implementing this with a new php.ini setting?

Finally, I tried to replace php_opcache.dll between 7.0 and 7.1 but since there's new functionality has been added in 7.1 and so the API has most likely changed, PHP complained that DLL entry points could not be found. So, I haven't been able to find out/discard whether the fault has been eventually introduced as "collateral damage" by any of OpCode's implementation changes in 7.1 (could you please maybe inestigate this a bit?).

How could we proceed? Is there anything I can do or test to help? Please kindly let me know 🙂

Thank you!
 [2017-01-13 01:16 UTC] ab@php.net
Thanks for the further feedback, @caaguado. It is not, that a similar issue were not reported earlier, I remember some already in the times of PHP 5.5. However it was always possible to solve this with the corresponding general setup. Furthermore, I recall it was tricky to reproduce it (and indeed newer was) on any environments i were able to debug on. So it is not 7.1, for one.

Regarding the functionality currently used, https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx . Please check the remarks section. Obviously Apache misses the environment, so then the last possibility is used to set tmp dir to c:\windows. If you were running it as FCGI, I would have linked you to the FcgidInitialEnv directive, to set the corresponding env var. Unfortunately, it's quite far from the days I used CGI pure last time :), so please lookup the corresponding Apache configuration yourself. AFAIR, Apache doesn't always automatically propagate the environment. I also guess, that mod_cgi is rarely touched nowadays, as the usage of CGI itself is rare. 

Frankly, in first place it makes not much sense to use shared memory for pure CGI. As soon as your server runs idle, you it'll lose all the shared memory cache. The first request after idle will have to populate the cache again and again. For CGI, more suitable were IMO using file cache only, which would be in any case persistent across requests, though some slower. Or even, why not FCGI? In 7.1 it can be even decoupled from the actual server through TCP, as PHP_FCGI_CHILDREN is supported.

Regarding the other ticket you linked - I gave feedback there already. The base address file is required only once at the process start. There is a lot of possibilities to run PHP, IMHO it would be inconvenient to fix issues by putting workarounds, if they are solvable by the proper DevOp operation otherwise. Of course there are workarounds, nothing is without it, but IMHO it should be more generic and not try to fix configuration issues, etc. I'd see this case as apparently a configuration issue of this kind, so not about touching the actual handling code. As mentioned, some similar API or case can be for sure met elsewhere in the core or also in the dependency libraries, which would still trace itself back to the configuration question.

Btw binaries from different PHP versions are ABI incompatible, that won't work per se. 

Thanks.
 [2017-02-06 21:04 UTC] caaguado at xcentra dot com
Sorry, but don't you think this is a bit too little to say about the problem? CGI or not, the fact is that PHP 7.1 seems to not deal well with this environment propagation in Apache, doesn't it?

Can you retrieve any recommended Apache configuration settings from the old PHP 5.5 bug report that you mentioned, both for CGI and/or FastCGI, so I can try troubleshooting a bit more for you?

Thank you!
 [2017-02-07 00:26 UTC] ab@php.net
Thanks for the further comment. Here's a quote from https://httpd.apache.org/docs/2.4/env.html

==============================
First, there are the environment variables controlled by the underlying operating system. These are set before the server starts. They can be used in expansions in configuration files, and can optionally be passed to CGI scripts and SSI using the PassEnv directive.
==============================

There is no PassEnv directive in your Apache config, this has hardly to do something with PHP. Either this or explicitly SetEnv will set the CGI vars. I haven't worked with CGI for edges, but specifically looked up this doc page for you :) Hopefully any similar tickets can be avoided if this one is found. Apache won't propagate the system environment, it is the default crossplatform behavior. An Apache distribution on Linux is pre configured and is integrated with the OS, so it is easy. Under Windows, having the documentation at hand is often necessary to achieve even simple things.

Thanks.
 [2017-02-12 19:34 UTC] ab@php.net
-Status: Assigned +Status: Not a bug
 [2017-02-12 19:34 UTC] ab@php.net
Closing, as it's clearly something about the configuration on the level upper to PHP.

Thanks.
 [2017-02-12 20:37 UTC] caaguado at xcentra dot com
Hi, close the ticket, but this is a PHP 7.1 bug... and a very poor way of closing the bug report. Thanks.
 [2017-02-12 21:09 UTC] ab@php.net
It is not a bug in PHP, please configure Apache to pass the tmp dir as it's documented on the linked documentation page.

Thanks.
 [2017-02-17 11:28 UTC] caaguado at xcentra dot com
Hi again,

I'm glad to report that, indeed, the fault can be worked around by adding the following to new entries to the beginning of Apache 2.4's configuration file:

SetEnv TEMP "C:\Users\<username>\AppData\Local\Temp"
SetEnv TMP "C:\Users\<username>\AppData\Local\Temp"

However, I now get a new crash on php7.dll when running phpMyAdmin 4.6.6  :-(

I will eventually report that new crash in a separate bug report.

Thanks, Carlos.
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Sun Nov 19 01:31:42 2017 UTC