[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

 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 ); }

 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

 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