php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76832 ZendOPcache.MemoryBase periodically deleted by the OS
Submitted: 2018-09-01 06:47 UTC Modified: 2018-09-03 09:31 UTC
Votes:1
Avg. Score:4.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: webmaster_20180824 at cubiclesoft dot com Assigned:
Status: Closed Package: opcache
PHP Version: 7.2.9 OS: Windows
Private report: No CVE-ID: None
 [2018-09-01 06:47 UTC] webmaster_20180824 at cubiclesoft dot com
Description:
------------
My most recent adventure with PHP started with the following seemingly innocuous message being emitted:

Fatal Error Unable to open base address file

There is exactly one place where this message is emitted in 'ext\opcache\shared_alloc_win32.c' in the zend_shared_alloc_reattach() function.  The function attempts to open a temporary file as read only.  If it fails to open the file, the message above is the emitted error and ALLOC_FAILURE is returned to the caller.

There is only one caller of the zend_shared_alloc_reattach() function, which is the create_segments() function.  The relevant code from create_segments() is:

	zend_shared_alloc_lock_win32();
	do {
		memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME));
		if (memfile == NULL) {
			err = GetLastError();
			break;
		}

		ret =  zend_shared_alloc_reattach(requested_size, error_in);
		if (ret == ALLOC_FAIL_MAPPING) {
			err = GetLastError();
			/* Mapping failed, wait for mapping object to get freed and retry */
			CloseHandle(memfile);
			memfile = NULL;
			if (++map_retries >= MAX_MAP_RETRIES) {
				break;
			}
			zend_shared_alloc_unlock_win32();
			Sleep(1000 * (map_retries + 1));
			zend_shared_alloc_lock_win32();
		} else {
			zend_shared_alloc_unlock_win32();
			return ret;
		}
	} while (1);

So what happens here is that if OpenFileMapping() succeeds (i.e. the memory map name already exists), then the call to zend_shared_alloc_reattach() assumes that the temporary file exists.

Unfortunately, Windows 10 Creators Update will periodically delete files and folders in the user's Temp folder while the OS is running without bothering to ask the user.  I've seen the behavior on brand new, fresh from shrink-wrapped retail box Win10 installs, which means it is part of the core product.  Regardless, the Temp directory is not a safe place to store files for more than a few hours on Windows.  Once the 'ZendOPcache.MemoryBase@...' file has been deleted, only first fully shutting down and then restarting the processes running with opcache will recreate the required file.

Obviously, disabling the opcache altogether will possibly work too.

Update:  After monitoring the Temp folder for a solid week with Process Monitor, I have finally found the culprit:  cleanmgr.exe.  Once I had an executable name to look for, I also quickly found the relevant Task Scheduler task under Microsoft -> Windows -> Disk Cleanup (along with also finding people online who really, really dislike cleanmgr.exe).  It claims it is just a "Maintenance task used by the system to launch a silent auto disk cleanup when running low on free disk space."  However, Process Monitor has officially caught it red-handed doing Very Bad Things(TM) to actively running software on a drive with over 100GB free.  The task is also lacking any triggers, so it looks to me like a very broken task in Task Scheduler that is found on all Windows 10 OSes.  On the plus side, it looks like files are only deleted after a week of inactivity, so performing a periodic touch() on the file might be a way to mitigate the issue.  Disabling, altering, or deleting the task in Task Scheduler will likely only work until the next major Windows Update cycle.  Microsoft has a very bad habit of re-enabling and re-creating disabled and deleted tasks and system services.


Test script:
---------------
Fastest method to replicate:

Start an Apache + mod_php instance with the opcache enabled.
Delete the 'ZendOPcache.MemoryBase' file from the Temp directory.
Run 'php-cgi.exe' from the command-line.



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-09-03 09:31 UTC] ab@php.net
Thanks for the report and analysis.

Indeed, touch()ing would solve an issue with some smart cleanup. Another solution could be to create a new shared memory area, which would temporarily duplicate things until the old processes did shut down. However that's a bit too complex, as potentially it might be too much.

The touch() solution is also not ideal, as if a cleanup tool is not smart, it could still delete the file. Solutions amount is quite limited in this case, touch() seems to be enough for now. I'm going to work towards this solution.

Thanks.
 [2018-09-03 13:12 UTC] ab@php.net
Automatic comment on behalf of ab
Revision: http://git.php.net/?p=php-src.git;a=commit;h=f26172f9343cf49d62bbefff355bb76d6adf7ebe
Log: Fixed bug #76832 ZendOPcache.MemoryBase periodically deleted by the OS
 [2018-09-03 13:12 UTC] ab@php.net
-Status: Open +Status: Closed
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Thu Oct 18 23:01:25 2018 UTC