[wp-trac] [WordPress Trac] #31767: insert_with_markers() is not atomic, leading to corrupted .htaccess updates under race conditions
WordPress Trac
noreply at wordpress.org
Wed Mar 25 21:25:06 UTC 2015
#31767: insert_with_markers() is not atomic, leading to corrupted .htaccess updates
under race conditions
---------------------------+-----------------------------
Reporter: tigertech | Owner:
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Rewrite Rules | Version: 4.1.1
Severity: normal | Keywords:
Focuses: |
---------------------------+-----------------------------
My company has occasionally seen corrupted .htaccess files on WordPress
sites we host. When it happens, there appears to be a part of the
.htaccess file that has been deleted out of the middle.
I believe it happens when two different processes simultaneously call the
WordPress insert_with_markers() function in "wp-admin/includes/misc.php".
The function does not atomically rewrite the file, and the second process
can see a half-rewritten copy of the first one. This can be triggered by
plugins that use flush_rewrite_rules() to rewrite the file if two such
requests occur simultaneously. I've been able to reproduce this behavior
with a simple test case:
Create a file called "corruption.php" in the top level of a WordPress
site:
{{{
<?php
require( dirname( __FILE__ ) . '/wp-blog-header.php' );
require_once( dirname( __FILE__ ) . '/wp-admin/includes/misc.php' );
$array = array();
for($i = 1; $i <= 100; $i++) { $array[$i] = "Test line $i pid " .
getmypid(); }
for($i = 1; $i <= 5000; $i++) { insert_with_markers( dirname( __FILE__ )
. '/corruption.test', 'CorruptionTest', $array ); }
print("done");
}}}
Load <http://www.example.com/corruption.php> from a single browser
connection. The resulting "corruption.test" file should look fine.
Then load <http://www.example.com/corruption.php> from multiple browser
connections simultaneously. This simulates two WordPress HTTP requests
writing the file simultaneously. In my testing, "corruption.test" is
corrupted much of the time. (Try it repeatedly if it doesn't appear
corrupted at first.)
This happens because insert_with_markers() does the following:
1) Reads the existing file with "file( $filename )"
2) Truncates the existing file with "fopen( $filename, 'w' )"
3) Appends to the file one line at a time in a loop with "fwrite()"
4) Calls "fclose()"
However, if a second copy of insert_with_markers() executes step 1 while
the first copy is in the middle of the step 3 loop, it will load an
incomplete copy of the existing file, and the written copy will be
corrupted.
To avoid this problem, insert_with_markers() could use PHP's "tmpfile()"
to write to a temporary file, then rename it after closing it.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/31767>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list