httpd-38070
BUG link:
https://issues.apache.org/bugzilla/show_bug.cgi?id=38070
Version:
2.2.0
How to reproduce?
See symptom below:
Symptom:
Use Apache to serve a cgi script (perl) as below:
#!/usr/bin/perl
print "Status: 200\n";
print "Last-Modified: Tue, 15 Feb 2005 15:00:00 GMT\n";
print "Content-Type: text/html\n\n";
print "Hello world\n"
When browser access the this cgi program multiple times,
this script should always print “Hello world” on the browser.
However, in the incorrect execution:
1st time, everything is good.
2nd time, httpd returns status code 200 without BODY and logged
status 304 in accesslog. Blank screen is displayed.
Root cause:
The problem is that the script contains the following two lines:
print "Status: 200\n";
print "Last-Modified: Tue, 15 Feb 2005 15:00:00 GMT\n";
This would trick httpd in the 2nd time to think the script is ‘304’ and should return an empty response body.
Here is the logic in “ap_scan_script_header_err_core”, which is to parse the output of cgi script:
It meant to call “ap_meets_condition” only if “cgi_status” is not explicitly set in the header. Otherwise, if the “Status: XXX” is contained in the header, then we will set the cgi_status on later line “r->status=cgi_status=atoi(l)”.
So the key is to know:
1. the highlighted condition passed the check, and ap_meets_conditions is called, with ‘cond_status = 304’.
2. that the “cgi_status == HTTP_OK” is set at line X1.
patch:
--- httpd/httpd/branches/2.2.x/server/util_script.c 374894
+++ httpd/httpd/branches/2.2.x/server/util_script.c 374895
AP_DECLARE(int) ap_scan_script_header_err_core(... ...) {
@@ -405,7 +407,7 @@ AP_DECLARE(int) ap_scan_script_header_er
- int cgi_status = HTTP_OK;
+ int cgi_status = HTTP_UNSET;
apr_table_t *merge;
apr_table_t *cookie_table;
… …
while (1) {
… ...
@@ -466,7 +468,18 @@ AP_DECLARE(int) ap_scan_script_header_er
if (w[0] == '\0') {
int cond_status = OK;
- if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) {
+ /* PR#38070: This fails because it gets confused when a
+ * CGI Status header overrides ap_meets_conditions.
+ *
+ * We can fix that by dropping ap_meets_conditions when
+ * Status has been set. Since this is the only place
+ * cgi_status gets used, let's test it explicitly.
+ *
+ * The alternative would be to ignore CGI Status when
+ * ap_meets_conditions returns anything interesting.
+ * That would be safer wrt HTTP, but would break CGI.
+ */
+ if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) {
cond_status = ap_meets_conditions(r);
// ap_meets_conditions will set cond_status to ‘304’ the 2nd time,
// because the HTTP header contains “Last-Modified: a-past-date”.
// It should return cond_status ‘200’ since set explicitly in script!
…
480: return cond_status;
}
}
… ...
else if (!strcasecmp(w, "Status")) {
r->status = cgi_status = atoi(l);
// Here will set cgi_status -> HTTP_OK (200), from script output.
r->status_line = apr_pstrdup(r->pool, l);
}
}
}
No any message!
Can we anticipate the error?
Yes. They are performing input check on the httpd header.