Bug 39287 - Incorrect If-Modified-Since validation (due to synthetic mtime?)
Summary: Incorrect If-Modified-Since validation (due to synthetic mtime?)
Status: NEW
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: Core (show other bugs)
Version: 2.5-HEAD
Hardware: All FreeBSD
: P2 normal (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL: http://www.mnot.net/cgi-bin/apache-im...
Keywords: PatchAvailable
Depends on:
Reported: 2006-04-12 19:06 UTC by Mark Nottingham
Modified: 2015-03-10 03:21 UTC (History)
1 user (show)

Fix If-Modified-Since "now" with non-mtime-aware resources, add missing APR_DATE_BAD check. (1.92 KB, patch)
2012-08-15 15:05 UTC, Carsten Gaebler
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Mark Nottingham 2006-04-12 19:06:03 UTC
Apache 1.3 (and 2.0, as far as I can see) calculates the modified time used to compare the If-Modified-
Since request header with and determine validation like this (in http_protocol.c ap_meets_conditions);

    mtime = (r->mtime != 0) ? r->mtime : time(NULL);

Later, this time is used to figure out if a 304 Not Modified is sent;

        if ((ims >= mtime) && (ims <= r->request_time)) {
            return HTTP_NOT_MODIFIED;

The problem is that Apache fakes the mtime to the current time if it can't find r->mtime (e.g., if it's a 
CGI script). If a client sends an If-Modified-Since header, I believe there's a race condition whereby an 
IMS containing the current time will get a 304 Not Modified, even though the underlying resource has 
no concept of mtime (i.e., it doesn't have a Last-Modified).

This can be demonstrated against the following script (at http://www.mnot.net/cgi-bin/apache-

import os
print "Content-Type: text/plain"
print os.environ.get("HTTP_IF_MODIFIED_SINCE", "-")

using the following command-line for the client side;

~> URL="http://www.mnot.net/cgi-bin/apache-ims.cgi"; DATE=`curl -Is -X HEAD $URL | grep ^Date | 
cut -d ":" -f 2-`; echo "D-> $DATE"; curl -Is -H "If-Modified-Since: $DATE" $URL
D->  Wed, 12 Apr 2006 17:58:39 GMT
HTTP/1.1 304 Not Modified
Date: Wed, 12 Apr 2006 17:58:39 GMT
Server: Apache/1.3.29

As you can see, Apache incorrectly sends a 304 Not Modified when the If-Modified-Since request 
header is set to the current time. For a CGI script that changes on a sub-second basis (e.g., one used 
by AJAX), this reduces the granularity of responses to one second, confusing the implementor (because 
they don't expect a 304 Not Modified to ever be sent).

All of this would be largely a theoretical problem, because such a resource should never see an If-
Modified-Since request header, having not send a previous Last-Modified response header. However, 
Safari/OSX *does* send a If-Modified-Since request header based upon the Date response header when 
it doesn't find a Last-Modified response (a bug which I'm reporting to them now). 

Safari, of course, should fix themselves, but Apache is also somewhat broken here, as it should not 
default mtime to the current time (which may cause problems in other scenarios).

Note that while I see this behaviour on the mnot.net server (Apache 1.3.29), I've had trouble verifying it 
on other systems. I suspect that this is because it's a race condition, and therefore hard to reproduce; 
that said, it could be something on that particular server instance (it's a shared host, so I dont' have 
access to the source they've used). 

However, if I'm correct about the underlying reason, the code hasn't changed from then until HEAD, and 
is substantially the same in Apache 2.2.0.
Comment 1 Mark Nottingham 2006-04-12 19:12:58 UTC
BTW, there's nothing special about that script; I used the one I'm showing Apple's problem with, but it 
could easily be as simple as 

print "Content-Type: text/plain"
print "hi!"
Comment 2 Carsten Gaebler 2012-08-15 15:05:30 UTC
Created attachment 29237 [details]
Fix If-Modified-Since "now" with non-mtime-aware resources, add missing APR_DATE_BAD check.

No response within six years although the fix is easy:

Keep assuming that r->mtime == 0 means "now" but check for mtime == 0 explicitly instead of faking mtime = apr_time_now(). Note that this will break the If-Unmodified-Since check if the requested resource really has an mtime of 1970-01-01T00:00:00 (but how likely is that?).

A thorough fix would be to introduce a "time unset" constant as already stated in a comment by dgaudet.
Comment 3 Wim Lewis 2013-04-24 01:44:33 UTC
A quick test shows Mark Nottingham's test case still elicits the bug with httpd trunk (2.5.0-dev) r1470940.
Comment 4 Wim Lewis 2015-03-10 03:21:43 UTC
I just retested this and the test case still elicits the bug in 2.4.12, 2.4.13-dev (r1665365), and 2.5.0-dev (r1665394), running on OSX 10.9.5.

There's been enough code change that Carsten Gaebler's patch no longer applies cleanly, but it looks like the current code still has the same bug/behavior as the old code: if the resource has no last-modified date, it acts as if the last-modified date is apr_time_now().