php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79398 Different behavior of flock and include on PHP 7.3 and PHP 7.4
Submitted: 2020-03-20 13:59 UTC Modified: 2021-07-05 13:18 UTC
From: praszywka dot adam at gmail dot com Assigned: cmb (profile)
Status: Not a bug Package: *General Issues
PHP Version: 7.4.4 OS: Windows 10 1909
Private report: No CVE-ID: None
 [2020-03-20 13:59 UTC] praszywka dot adam at gmail dot com
Description:
------------
PHP 7.3 allows to include previously locked file. PHP 7.4 throws Notice.

Test script:
---------------
<?php

$cachePath = 'file.php';

$lock = fopen($cachePath, 'w');
chmod($cachePath, 0666 & ~umask());

$res = flock($lock, LOCK_EX | LOCK_NB, $wouldBlock);

include $cachePath;

echo 'OK';


Expected result:
----------------
$ C:/Program\ Files/php-7.3.5-nts-Win32-VC15-x64/php.exe test2.php
OK


Actual result:
--------------
$ C:/Program\ Files/php-7.4.4-nts-Win32-VC15-x64/php.exe test2.php
PHP Notice:  include(): read of 4096 bytes failed with errno=13 Permission denied in C:\Users\praszywa\Downloads\test\test2.php on line 10
PHP Stack trace:
PHP   1. {main}() C:\Users\praszywa\Downloads\test\test2.php:0

Notice: include(): read of 4096 bytes failed with errno=13 Permission denied in C:\Users\praszywa\Downloads\test\test2.php on line 10

Call Stack:
    0.0002     393720   1. {main}() C:\Users\praszywa\Downloads\test\test2.php:0

PHP Warning:  include(): Failed opening 'file.php' for inclusion (include_path='.;C:\php\pear') in C:\Users\praszywa\Downloads\test\test2.php on line 10
PHP Stack trace:
PHP   1. {main}() C:\Users\praszywa\Downloads\test\test2.php:0

Warning: include(): Failed opening 'file.php' for inclusion (include_path='.;C:\php\pear') in C:\Users\praszywa\Downloads\test\test2.php on line 10

Call Stack:
    0.0002     393720   1. {main}() C:\Users\praszywa\Downloads\test\test2.php:0

OK

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-03-21 15:20 UTC] cmb@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: cmb
 [2020-03-21 15:20 UTC] cmb@php.net
> PHP 7.3 allows to include previously locked file.

I'm confused, since opening the file in 'w' mode immediately
truncates it.  Why would you include an empty file in the first
place?
 [2020-03-21 21:34 UTC] praszywka dot adam at gmail dot com
-Status: Feedback +Status: Assigned
 [2020-03-21 21:34 UTC] praszywka dot adam at gmail dot com
That's the way how Symfony is currently creating cache.

Topic was stared here: https://github.com/symfony/symfony/issues/36132

It's not a standard way of using flock and include so I reported it as a incompatibility between PHP < 7.{0,1,2,3} and 7.4.

That problem should be fixed in Symfony or PHP - it should be decided where.
 [2020-03-21 22:07 UTC] cmb@php.net
-Status: Assigned +Status: Open
 [2020-03-22 17:02 UTC] cmb@php.net
-Status: Assigned +Status: Not a bug
 [2020-03-22 17:02 UTC] cmb@php.net
Okay, better reproducer:

<?php
$lock = fopen($cachePath, 'w');
fwrite($lock, '<?php echo "test\n";');
flock($lock, LOCK_EX);
include $cachePath;
?>

Anyhow, the behavioral change has been triggered by lexing no
longer using mmap()[1]; however, the new behavior is now in line
with other file functions, e.g.

<?php
$lock = fopen($cachePath, 'w');
fwrite($lock, '<?php echo "test\n";');
flock($lock, LOCK_EX);
echo file_get_contents($cachePath);
?>

On Windows, this produces no output (PHP 7.3 and 7.4).  On Linux,
this usually prints the contents of the file.  The relevant
difference is that flock() uses advisory locking on Linux by
default, while it is always mandatory on Windows.

So a reasonable fix for Symfony would be using a separate lock file,
like suggested by Nicolas[2].

[1] <http://git.php.net/?p=php-src.git;a=commit;h=5161cebe28cca36fa7f7989b5a799290a3f1eb6a>
[2] <https://github.com/symfony/symfony/issues/36132#issuecomment-601708409>
 [2021-07-04 03:01 UTC] dallas at ekkysoftware dot com
This is a bug. Everything works in PHP7.3, but this issue shows up in PHP7.4.

require(): read of 25405 bytes failed with errno=13 Permission denied

On Windows there are no OS permissions to block the read, and in any case a permissions issue should block the file open and not the file read. Also the '25405' is the correct filesize for the included file, which shows the system can get stat the file, the path is correct and with readable permissions.
 [2021-07-04 04:03 UTC] dallas at ekkysoftware dot com
Just to follow that up, I have flock($file), then require($file). The flock is preventing the require from reading the file, even though I am the same process.
 [2021-07-05 13:18 UTC] cmb@php.net
This is how LockFileEx[1] behaves:

| If the locking process opens the file a second time, it cannot
| access the specified region through this second handle until it
| unlocks the region.
|
| Locking a portion of a file for exclusive access denies all
| other processes both read and write access to the specified region
| of the file.

There is nothing PHP can do about that.

The fact that this worked prior to PHP 7.4.0 is related to using
mmap:

| Locking a region of a file does not prevent reading from a
| mapped file view.

But that mmap caused other issues, so had been changed, and like I
said, include/require now behave more like other file functions.

The flock() man page[2] needs more info regarding the Windows
peculiarities.

[1] <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex>
[2] <https://www.php.net/flock>
 
PHP Copyright © 2001-2022 The PHP Group
All rights reserved.
Last updated: Fri May 20 17:03:35 2022 UTC