Fix #80849: HTTP Status header truncation

While truncating the contents of a header is okay, we must never omit
the trailing CRLF.

Closes GH-7238.
This commit is contained in:
Christoph M. Becker 2021-07-15 17:50:44 +02:00
parent a942cf5b02
commit a054ef2aad
No known key found for this signature in database
GPG Key ID: D66C9593118BCCB6
5 changed files with 63 additions and 8 deletions

3
NEWS
View File

@ -5,6 +5,9 @@ PHP NEWS
- Core: - Core:
. Fixed bug #72595 (php_output_handler_append illegal write access). (cmb) . Fixed bug #72595 (php_output_handler_append illegal write access). (cmb)
- CGI:
. Fixed bug #80849 (HTTP Status header truncation). (cmb)
- Standard: - Standard:
. Fixed bug #72146 (Integer overflow on substr_replace). (cmb) . Fixed bug #72146 (Integer overflow on substr_replace). (cmb)

View File

@ -387,7 +387,7 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers)
if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) { if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
char *s; char *s;
len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line); len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s", SG(sapi_headers).http_status_line);
if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) { if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
response_status = atoi((s + 1)); response_status = atoi((s + 1));
} }
@ -404,7 +404,7 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers)
(s - SG(sapi_headers).http_status_line) >= 5 && (s - SG(sapi_headers).http_status_line) >= 5 &&
strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0 strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
) { ) {
len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s); len = slprintf(buf, sizeof(buf), "Status:%s", s);
response_status = atoi((s + 1)); response_status = atoi((s + 1));
} else { } else {
h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos); h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
@ -427,9 +427,9 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers)
err++; err++;
} }
if (err->str) { if (err->str) {
len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->str); len = slprintf(buf, sizeof(buf), "Status: %d %s", SG(sapi_headers).http_response_code, err->str);
} else { } else {
len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code); len = slprintf(buf, sizeof(buf), "Status: %d", SG(sapi_headers).http_response_code);
} }
} }
} }
@ -437,6 +437,7 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers)
if (!has_status) { if (!has_status) {
PHPWRITE_H(buf, len); PHPWRITE_H(buf, len);
PHPWRITE_H("\r\n", 2);
ignore_status = 1; ignore_status = 1;
} }
} }

View File

@ -0,0 +1,10 @@
--TEST--
Bug #80849 (HTTP Status header truncation)
--CGI--
--FILE--
<?php
header('HTTP/1.1 201 ' . str_repeat('A', 1014), true);
?>
--EXPECTHEADERS--
Status: 201 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
--EXPECT--

View File

@ -328,7 +328,7 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) { if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
char *s; char *s;
len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line); len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s", SG(sapi_headers).http_status_line);
if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) { if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
response_status = atoi((s + 1)); response_status = atoi((s + 1));
} }
@ -345,7 +345,7 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
(s - SG(sapi_headers).http_status_line) >= 5 && (s - SG(sapi_headers).http_status_line) >= 5 &&
strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0 strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
) { ) {
len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s); len = slprintf(buf, sizeof(buf), "Status:%s", s);
response_status = atoi((s + 1)); response_status = atoi((s + 1));
} else { } else {
h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos); h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
@ -368,9 +368,9 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
err++; err++;
} }
if (err->str) { if (err->str) {
len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->str); len = slprintf(buf, sizeof(buf), "Status: %d %s", SG(sapi_headers).http_response_code, err->str);
} else { } else {
len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code); len = slprintf(buf, sizeof(buf), "Status: %d", SG(sapi_headers).http_response_code);
} }
} }
} }
@ -378,6 +378,7 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
if (!has_status) { if (!has_status) {
PHPWRITE_H(buf, len); PHPWRITE_H(buf, len);
PHPWRITE_H("\r\n", 2);
ignore_status = 1; ignore_status = 1;
} }
} }

View File

@ -0,0 +1,40 @@
--TEST--
Bug #80849 (HTTP Status header truncation)
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$code = <<<EOT
<?php
header('HTTP/1.1 201 ' . str_repeat('A', 1014), true);
EOT;
$tester = new FPM\Tester($cfg, $code);
$tester->start();
$tester->expectLogStartNotices();
$tester
->request()
->expectHeader('Status', '201 ' . str_repeat('A', 1011));
$tester->terminate();
$tester->close();
?>
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
--EXPECT--