diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 6b17f083c125..671d5d5cd15f 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -1408,9 +1408,8 @@ static void php_cli_server_request_ctor(php_cli_server_request *req) /* {{{ */ req->query_string = NULL; req->query_string_len = 0; zend_hash_init(&req->headers, 0, NULL, cli_header_value_dtor, 1); - /* No destructor is registered as the value pointed by is the same as for &req->headers */ GC_MAKE_PERSISTENT_LOCAL(&req->headers); - zend_hash_init(&req->headers_original_case, 0, NULL, NULL, 1); + zend_hash_init(&req->headers_original_case, 0, NULL, cli_header_value_dtor, 1); GC_MAKE_PERSISTENT_LOCAL(&req->headers_original_case); req->content = NULL; req->content_len = 0; @@ -1701,16 +1700,22 @@ static void php_cli_server_client_save_header(php_cli_server_client *client) } else { zend_string *curval = Z_STR_P(entry); zend_string *newval = zend_string_safe_alloc(1, ZSTR_LEN(curval), ZSTR_LEN(client->current_header_value) + 2, /* persistent */true); + GC_MAKE_PERSISTENT_LOCAL(newval); memcpy(ZSTR_VAL(newval), ZSTR_VAL(curval), ZSTR_LEN(curval)); memcpy(ZSTR_VAL(newval) + ZSTR_LEN(curval), ", ", 2); memcpy(ZSTR_VAL(newval) + ZSTR_LEN(curval) + 2, ZSTR_VAL(client->current_header_value), ZSTR_LEN(client->current_header_value) + 1); ZVAL_STR(&tmp, newval); + zend_string_release_ex(client->current_header_value, /* persistent */ true); } /* Add/Update the wrapped zend_string to the HashTable */ zend_hash_update(&client->request.headers, lc_header_name, &tmp); + /* Each table owns a reference to the new value. The old value can outlive the update + * above, as a repeat spelled differently keeps the first spelling's entry in + * headers_original_case. */ + zend_string_addref(Z_STR(tmp)); zend_hash_update(&client->request.headers_original_case, client->current_header_name, &tmp); zend_string_release_ex(lc_header_name, /* persistent */ true); diff --git a/sapi/cli/tests/gh16137_duplicate_headers.phpt b/sapi/cli/tests/gh16137_duplicate_headers.phpt new file mode 100644 index 000000000000..5fdc149de37b --- /dev/null +++ b/sapi/cli/tests/gh16137_duplicate_headers.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-16137: CLI server handling of repeated request headers +--SKIPIF-- + +--FILE-- + ['header' => [ + 'X-Foo: a', + 'X-Foo: b', + 'Cookie: Foo', + 'cOOKIE: Bar', + 'Set-Cookie: first', + 'set-cookie: second', +]]]); +echo file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS, false, $ctx); +echo file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS); +?> +--EXPECT-- +array(5) { + ["X-Foo"]=> + string(4) "a, b" + ["Cookie"]=> + string(3) "Foo" + ["cOOKIE"]=> + string(8) "Foo, Bar" + ["Set-Cookie"]=> + string(5) "first" + ["set-cookie"]=> + string(6) "second" +} +array(0) { +}