File php7-CVE-2025-1734.patch of Package php7.37963

From 0548c4c1756724a89ef8310709419b08aadb2b3b Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Sun, 19 Jan 2025 17:49:53 +0100
Subject: [PATCH] Fix GHSA-pcmh-g36c-qc44: http headers without colon
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The header line must contain colon otherwise it is invalid and it needs
to fail.

Reviewed-by: Tim Düsterhus <tim@tideways-gmbh.com>
---
 ext/standard/http_fopen_wrapper.c             | 51 ++++++++++++++-----
 ext/standard/tests/http/bug47021.phpt         | 22 ++++----
 ext/standard/tests/http/bug75535.phpt         |  4 +-
 .../tests/http/ghsa-pcmh-g36c-qc44-001.phpt   | 51 +++++++++++++++++++
 .../tests/http/ghsa-pcmh-g36c-qc44-002.phpt   | 51 +++++++++++++++++++
 5 files changed, 154 insertions(+), 25 deletions(-)
 create mode 100644 ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt
 create mode 100644 ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt

Index: php-7.4.33/ext/standard/http_fopen_wrapper.c
===================================================================
--- php-7.4.33.orig/ext/standard/http_fopen_wrapper.c
+++ php-7.4.33/ext/standard/http_fopen_wrapper.c
@@ -136,6 +136,7 @@ static zend_bool check_has_header(const
 typedef struct _php_stream_http_response_header_info {
 	php_stream_filter *transfer_encoding;
 	size_t file_size;
+	bool error;
 	bool follow_location;
 	char location[HTTP_HEADER_BLOCK_SIZE];
 } php_stream_http_response_header_info;
@@ -145,6 +146,7 @@ static void php_stream_http_response_hea
 {
 	header_info->transfer_encoding = NULL;
 	header_info->file_size = 0;
+	header_info->error = false;
 	header_info->follow_location = 1;
 	header_info->location[0] = '\0';
 }
@@ -182,10 +184,11 @@ static bool php_stream_http_response_hea
 /* Process folding headers of the current line and if there are none, parse last full response
  * header line. It returns NULL if the last header is finished, otherwise it returns updated
  * last header line.  */
-static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
-		php_stream_context *context, int options, zend_string *last_header_line_str,
-		char *header_line, size_t *header_line_length, int response_code,
-		zval *response_header, php_stream_http_response_header_info *header_info)
+static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *wrapper,
+		php_stream *stream, php_stream_context *context, int options,
+		zend_string *last_header_line_str, char *header_line, size_t *header_line_length,
+		int response_code, zval *response_header,
+		php_stream_http_response_header_info *header_info)
 {
 	char *last_header_line = ZSTR_VAL(last_header_line_str);
 	size_t last_header_line_length = ZSTR_LEN(last_header_line_str);
@@ -227,6 +230,19 @@ static zend_string *php_stream_http_resp
 	/* Find header separator position. */
 	char *last_header_value = memchr(last_header_line, ':', last_header_line_length);
 	if (last_header_value) {
+		/* Verify there is no space in header name */
+		char *last_header_name = last_header_line + 1;
+		while (last_header_name < last_header_value) {
+			if (*last_header_name == ' ' || *last_header_name == '\t') {
+				header_info->error = true;
+				php_stream_wrapper_log_error(wrapper, options,
+					"HTTP invalid response format (space in header name)!");
+				zend_string_efree(last_header_line_str);
+				return NULL;
+			}
+			++last_header_name;
+		}
+
 		last_header_value++; /* Skip ':'. */
 
 		/* Strip leading whitespace. */
@@ -235,9 +251,12 @@ static zend_string *php_stream_http_resp
 			last_header_value++;
 		}
 	} else {
-		/* There is no colon. Set the value to the end of the header line, which is effectively
-		 * an empty string. */
-		last_header_value = last_header_line_end;
+		/* There is no colon which means invalid response so error. */
+		header_info->error = true;
+		php_stream_wrapper_log_error(wrapper, options,
+				"HTTP invalid response format (no colon in header line)!");
+		zend_string_efree(last_header_line_str);
+		return NULL;
 	}
 
 	bool store_header = true;
@@ -943,10 +962,16 @@ finish:
 
                         if (last_header_line_str != NULL) {
                                 /* Parse last header line. */
-                                last_header_line_str = php_stream_http_response_headers_parse(stream, context,
-                                                options, last_header_line_str, http_header_line, &http_header_line_length,
-                                                response_code, response_header, &header_info);
-                                if (last_header_line_str != NULL) {
+				last_header_line_str = php_stream_http_response_headers_parse(wrapper, stream,
+						context, options, last_header_line_str, http_header_line,
+						&http_header_line_length, response_code, response_header, &header_info);
+				if (EXPECTED(last_header_line_str == NULL)) {
+					if (UNEXPECTED(header_info.error)) {
+						php_stream_close(stream);
+						stream = NULL;
+						goto out;
+					}
+				} else {
                                         /* Folding header present so continue. */
                                         continue;
                                 }
@@ -976,8 +1001,8 @@ finish:
 
        /* If the stream was closed early, we still want to process the last line to keep BC. */
        if (last_header_line_str != NULL) {
-               php_stream_http_response_headers_parse(stream, context, options, last_header_line_str,
-                               NULL, NULL, response_code, response_header, &header_info);
+		php_stream_http_response_headers_parse(wrapper, stream, context, options,
+				last_header_line_str, NULL, NULL, response_code, response_header, &header_info);
        }
 
        if (!reqok || (header_info.location[0] != '\0' && header_info.follow_location)) {
openSUSE Build Service is sponsored by