• While browsing a supercached page that should have been generated by WP Super Cache, I saw what appeared to be a half-written page — while some of the page was present in the browser, the source was basically just cut off half way through.

    A reload fixed it, and it’s possible that it may have been just a broken page download or something. However, looking at the source code, it appears that this could happen due to the way WP Super Cache works.

    In wp-cache-phase2.php, it fopen()s the supercache file for writing, then uses fputs() to write the data. However, I don’t believe fputs() is atomic — in other words, it appears to be possible for other clients (apache) reading the same file to see a half-written file while fputs is in progress.

    The PHP documentation for fputs() says it’s identical to fwrite(), which has this note:

    “If handle was fopen()ed in append mode, fwrite()s are atomic (unless the size of string exceeds the filesystem’s block size, on some platforms, and as long as the file is on a local filesystem).”

    This strongly implies that writes are *not* atomic if the file wasn’t opened in append mode, which is the case here.

    The fix for this would seem to be to fopen/fputs/fclose a temporary file, then rename() it afterwards. If you’d like a patch, let me know and I’ll be glad to send one.

    — Robert L Mathews, Tiger Technologies

Viewing 7 replies - 1 through 7 (of 7 total)
  • Thanks Robert. I think it was probably only a browser issue as the script does either a file lock, or semaphore lock to guarantee atomic writes.

    If you see this again can you check if the actual file on your server is broken? That would help a lot.

    Thread Starter tigertech

    (@tigertech)

    donncha wrote:

    the script does either a file lock, or semaphore lock to guarantee atomic writes.

    Well, the flock/semaphore guarantees exclusive writes, but not atomic ones, unless I’m misunderstanding.

    In other words, the flock/semaphore makes sure that only one WP Super Cache process can be writing to the file at once, which is good. But it doesn’t prevent another process — in particular, Apache — from reading from the file while WP Super Cache has half-written it. Apache’s mod_rewrite doesn’t check the flock or the semaphore.

    Now that I look at it closely, the problem is not just that fputs() isn’t atomic. There is actually a zero-byte supercached file present on the disk as soon as the WP Super Cache fopen() line creates it, and Apache needs to be prevented from reading that file until after the fputs() returns…. but there’s nothing to prevent Apache from from doing so.

    To test that this is a real problem, I shoved a “sleep(30)” line after the fopen() and before the fputs(). Sure enough, that causes an error: if a second request arrives between the fopen() and fputs(), Apache’s mod_rewrite finds the empty file with the “-f” operator, and returns that empty file as the result of the second request. I was actually able to get blank pages to appear in my browser. (If you’re testing this yourself, note that some browsers don’t show zero-length blank pages properly — they just spin for a while without refreshing the page. I had to use Galeon, ApacheBench, or telnet to satisfy myself that it really was a blank page.)

    I should emphasize that the sleep isn’t causing the problem — it’s just making it much more likely to happen for any given request by lengthening the crucial interval. The problem must happen occasionally in the normal course of events; it’s just rare.

    I can probably supply a patch for this when I get a little more free time over the next couple of days….

    I just patched wp-cache-phase2.php to write to a temp filename. Go to the “Other Versions” page and grab the development version to give it a whirl but it seems to work ok on ocaoimh.ie right now.

    Thread Starter tigertech

    (@tigertech)

    I tried the test version, and it appears to work with no problems. I also manually reviewed the changed code, and it looks correct.

    Thanks very much for your hard work!

    Thread Starter tigertech

    (@tigertech)

    One extra followup comment: As noted in another thread, rename() doesn’t work on Windows if the target file already exists.

    If several WordPress processes or threads are rebuilding the cached file simultaneously, the cached file could well already exist when the later ones try the rename. On Unix-y systems, the later ones will simply replace the earlier one. On Windows, though, the rename will fail, leaving the temp file in the directory.

    The temporary file will get cleaned up eventually if necessary, but it seems that it would be better to make later files replace the earlier ones. In most cases they’ll be identical, but if two comments were posted within a couple of seconds, and the rebuild takes more than a couple of seconds, the later one could contain newer comments.

    This simple patch does that:

    https://www.tigertech.net/patches/wp-supercache-rename-unlink.patch

    Thread Starter tigertech

    (@tigertech)

    As yet another followup, I just noticed a potential issue with this.

    Before this change, WP Super Cache created files in the supercache directory with mode 0644 (at least on my machine).

    Now it’s using PHP’s tempnam() function to create the files, which always uses mode 0600.

    Depending on how Apache is accessing the cached files, this may cause problems for some people — Apache won’t be able to access the files any more if it was doing so based on group or world read access.

    To exactly mimic the old behavior and therefore work on the widest possible variety of systems, it needs something like this after the tempnam() call in wp_cache_ob_callback():

    chmod( $tmp_cache_filename, 0666 & ~umask());

    My apologies for not noticing this before — I should have done.

    I have been having issues with 403 and 500.
    This is what happens in my case.
    I have installed www.remarpro.com version 2.7.1.
    When I try to post a comment without being logged in, I get the 403 server error message i.e
    ” 403 – Forbidden: Access is denied.
    You do not have permission to view this directory or page using the credentials that you supplied”
    or the 500 server error message i.e
    ” 500 – Internal server error.
    There is a problem with the resource you are looking for, and it cannot be displayed”.
    I guess this is for the wp-comments-post.php file, because on click of submit on the comment, it goes to the wp-comments-post.php.
    And the strange thing is I don’t get it the first time I post the comment without being logged in but I get it from the second time.

    1) My wordpress is installed on godaddy and i enabled anonymous authentication but that didn’t work.
    2) I tried the .htaccess file – which seemed to have resolved 403 errors during log -in but in vain.
    3) I tried to fix it at the code level but couldn’t do it.

    Could you please tell me where to find the wp-cache-phase2.php file, from the discussion it appears that the changes need to be made to this file.
    my blog url : https://www.collegegoalsundayflorida.org/wordpress/

    I would be grateful to anyone who could help me with this.
    Thank you.

Viewing 7 replies - 1 through 7 (of 7 total)
  • The topic ‘WP Super Cache and half-written cache files’ is closed to new replies.