@@ -118,6 +118,7 @@ static bool check_has_header(const char *headers, const char *header) {
118118typedef struct _php_stream_http_response_header_info {
119119 php_stream_filter * transfer_encoding ;
120120 size_t file_size ;
121+ bool error ;
121122 bool follow_location ;
122123 char location [HTTP_HEADER_BLOCK_SIZE ];
123124} php_stream_http_response_header_info ;
@@ -127,6 +128,7 @@ static void php_stream_http_response_header_info_init(
127128{
128129 header_info -> transfer_encoding = NULL ;
129130 header_info -> file_size = 0 ;
131+ header_info -> error = false;
130132 header_info -> follow_location = 1 ;
131133 header_info -> location [0 ] = '\0' ;
132134}
@@ -164,10 +166,11 @@ static bool php_stream_http_response_header_trim(char *http_header_line,
164166/* Process folding headers of the current line and if there are none, parse last full response
165167 * header line. It returns NULL if the last header is finished, otherwise it returns updated
166168 * last header line. */
167- static zend_string * php_stream_http_response_headers_parse (php_stream * stream ,
168- php_stream_context * context , int options , zend_string * last_header_line_str ,
169- char * header_line , size_t * header_line_length , int response_code ,
170- zval * response_header , php_stream_http_response_header_info * header_info )
169+ static zend_string * php_stream_http_response_headers_parse (php_stream_wrapper * wrapper ,
170+ php_stream * stream , php_stream_context * context , int options ,
171+ zend_string * last_header_line_str , char * header_line , size_t * header_line_length ,
172+ int response_code , zval * response_header ,
173+ php_stream_http_response_header_info * header_info )
171174{
172175 char * last_header_line = ZSTR_VAL (last_header_line_str );
173176 size_t last_header_line_length = ZSTR_LEN (last_header_line_str );
@@ -209,6 +212,19 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
209212 /* Find header separator position. */
210213 char * last_header_value = memchr (last_header_line , ':' , last_header_line_length );
211214 if (last_header_value ) {
215+ /* Verify there is no space in header name */
216+ char * last_header_name = last_header_line + 1 ;
217+ while (last_header_name < last_header_value ) {
218+ if (* last_header_name == ' ' || * last_header_name == '\t' ) {
219+ header_info -> error = true;
220+ php_stream_wrapper_log_error (wrapper , options ,
221+ "HTTP invalid response format (space in header name)!" );
222+ zend_string_efree (last_header_line_str );
223+ return NULL ;
224+ }
225+ ++ last_header_name ;
226+ }
227+
212228 last_header_value ++ ; /* Skip ':'. */
213229
214230 /* Strip leading whitespace. */
@@ -217,9 +233,12 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
217233 last_header_value ++ ;
218234 }
219235 } else {
220- /* There is no colon. Set the value to the end of the header line, which is effectively
221- * an empty string. */
222- last_header_value = last_header_line_end ;
236+ /* There is no colon which means invalid response so error. */
237+ header_info -> error = true;
238+ php_stream_wrapper_log_error (wrapper , options ,
239+ "HTTP invalid response format (no colon in header line)!" );
240+ zend_string_efree (last_header_line_str );
241+ return NULL ;
223242 }
224243
225244 bool store_header = true;
@@ -926,10 +945,16 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
926945
927946 if (last_header_line_str != NULL ) {
928947 /* Parse last header line. */
929- last_header_line_str = php_stream_http_response_headers_parse (stream , context ,
930- options , last_header_line_str , http_header_line , & http_header_line_length ,
931- response_code , response_header , & header_info );
932- if (last_header_line_str != NULL ) {
948+ last_header_line_str = php_stream_http_response_headers_parse (wrapper , stream ,
949+ context , options , last_header_line_str , http_header_line ,
950+ & http_header_line_length , response_code , response_header , & header_info );
951+ if (EXPECTED (last_header_line_str == NULL )) {
952+ if (UNEXPECTED (header_info .error )) {
953+ php_stream_close (stream );
954+ stream = NULL ;
955+ goto out ;
956+ }
957+ } else {
933958 /* Folding header present so continue. */
934959 continue ;
935960 }
@@ -959,8 +984,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
959984
960985 /* If the stream was closed early, we still want to process the last line to keep BC. */
961986 if (last_header_line_str != NULL ) {
962- php_stream_http_response_headers_parse (stream , context , options , last_header_line_str ,
963- NULL , NULL , response_code , response_header , & header_info );
987+ php_stream_http_response_headers_parse (wrapper , stream , context , options ,
988+ last_header_line_str , NULL , NULL , response_code , response_header , & header_info );
964989 }
965990
966991 if (!reqok || (header_info .location [0 ] != '\0' && header_info .follow_location )) {
0 commit comments