Skip to content

Commit 5e64ead

Browse files
committed
Fix bug #64539: FPM status - query_string not properly JSON encoded
Closes GH-11050
1 parent e8a836e commit 5e64ead

File tree

5 files changed

+90
-12
lines changed

5 files changed

+90
-12
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ PHP NEWS
1717
- FPM:
1818
. Fixed bug GH-10461 (PHP-FPM segfault due to after free usage of
1919
child->ev_std(out|err)). (Jakub Zelenka)
20+
. Fixed bug #64539 (FPM status page: query_string not properly JSON encoded).
21+
(Jakub Zelenka)
2022

2123
- Hash:
2224
. Fixed bug GH-11180 (hash_file() appears to be restricted to 3 arguments).

sapi/fpm/fpm/fpm_status.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
#include "fpm_atomic.h"
1414
#include "fpm_conf.h"
1515
#include "fpm_php.h"
16-
#include <ext/standard/html.h>
16+
#include "ext/standard/html.h"
17+
#include "ext/json/php_json.h"
1718

1819
static char *fpm_status_uri = NULL;
1920
static char *fpm_status_ping_uri = NULL;
@@ -140,7 +141,8 @@ int fpm_status_handle_request(void) /* {{{ */
140141
struct fpm_scoreboard_proc_s *proc;
141142
char *buffer, *time_format, time_buffer[64];
142143
time_t now_epoch;
143-
int full, encode, has_start_time;
144+
int full, has_start_time;
145+
bool encode_html, encode_json;
144146
char *short_syntax, *short_post;
145147
char *full_pre, *full_syntax, *full_post, *full_separator;
146148
zend_string *_GET_str;
@@ -175,7 +177,8 @@ int fpm_status_handle_request(void) /* {{{ */
175177
full = (fpm_php_get_string_from_table(_GET_str, "full") != NULL);
176178
short_syntax = short_post = NULL;
177179
full_separator = full_pre = full_syntax = full_post = NULL;
178-
encode = 0;
180+
encode_html = false;
181+
encode_json = false;
179182
has_start_time = 1;
180183

181184
scoreboard_p = fpm_scoreboard_get();
@@ -218,7 +221,7 @@ int fpm_status_handle_request(void) /* {{{ */
218221
if (fpm_php_get_string_from_table(_GET_str, "html")) {
219222
sapi_add_header_ex(ZEND_STRL("Content-Type: text/html"), 1, 1);
220223
time_format = "%d/%b/%Y:%H:%M:%S %z";
221-
encode = 1;
224+
encode_html = true;
222225

223226
short_syntax =
224227
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
@@ -287,7 +290,7 @@ int fpm_status_handle_request(void) /* {{{ */
287290
} else if (fpm_php_get_string_from_table(_GET_str, "xml")) {
288291
sapi_add_header_ex(ZEND_STRL("Content-Type: text/xml"), 1, 1);
289292
time_format = "%s";
290-
encode = 1;
293+
encode_html = true;
291294

292295
short_syntax =
293296
"<?xml version=\"1.0\" ?>\n"
@@ -336,6 +339,8 @@ int fpm_status_handle_request(void) /* {{{ */
336339
sapi_add_header_ex(ZEND_STRL("Content-Type: application/json"), 1, 1);
337340
time_format = "%s";
338341

342+
encode_json = true;
343+
339344
short_syntax =
340345
"{"
341346
"\"pool\":\"%s\","
@@ -549,11 +554,24 @@ int fpm_status_handle_request(void) /* {{{ */
549554
query_string = NULL;
550555
tmp_query_string = NULL;
551556
if (proc->query_string[0] != '\0') {
552-
if (!encode) {
553-
query_string = proc->query_string;
557+
if (encode_html) {
558+
tmp_query_string = php_escape_html_entities_ex(
559+
(const unsigned char *) proc->query_string,
560+
strlen(proc->query_string), 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT,
561+
NULL, /* double_encode */ 1, /* quiet */ 0);
562+
} else if (encode_json) {
563+
tmp_query_string = php_json_encode_string(proc->query_string,
564+
strlen(proc->query_string), PHP_JSON_INVALID_UTF8_IGNORE);
554565
} else {
555-
tmp_query_string = php_escape_html_entities_ex((const unsigned char *) proc->query_string, strlen(proc->query_string), 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, NULL, /* double_encode */ 1, /* quiet */ 0);
566+
query_string = proc->query_string;
567+
}
568+
if (tmp_query_string) {
556569
query_string = ZSTR_VAL(tmp_query_string);
570+
/* remove quotes around the string */
571+
if (encode_json && ZSTR_LEN(tmp_query_string) >= 2) {
572+
query_string[ZSTR_LEN(tmp_query_string) - 1] = '\0';
573+
++query_string;
574+
}
557575
}
558576
}
559577

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
FPM: bug64539 - status json format escaping
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc"; ?>
6+
--FILE--
7+
<?php
8+
9+
require_once "tester.inc";
10+
11+
$cfg = <<<EOT
12+
[global]
13+
error_log = {{FILE:LOG}}
14+
[unconfined]
15+
listen = {{ADDR}}
16+
pm = static
17+
pm.max_children = 2
18+
pm.status_path = /status
19+
catch_workers_output = yes
20+
EOT;
21+
22+
$code = <<<EOT
23+
<?php
24+
usleep(200000);
25+
EOT;
26+
27+
$tester = new FPM\Tester($cfg, $code);
28+
$tester->start();
29+
$tester->expectLogStartNotices();
30+
$responses = $tester
31+
->multiRequest([
32+
['query' => 'a=b"c'],
33+
['uri' => '/status', 'query' => 'full&json', 'delay' => 100000],
34+
]);
35+
$data = json_decode($responses[1]->getBody('application/json'), true);
36+
var_dump(explode('?', $data['processes'][0]['request uri'])[1]);
37+
$tester->terminate();
38+
$tester->expectLogTerminatingNotices();
39+
$tester->close();
40+
41+
?>
42+
Done
43+
--EXPECT--
44+
string(5) "a=b"c"
45+
Done
46+
--CLEAN--
47+
<?php
48+
require_once "tester.inc";
49+
FPM\Tester::clean();
50+
?>

sapi/fpm/tests/response.inc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,18 +192,22 @@ class Response
192192

193193
/**
194194
* Print raw body.
195+
*
196+
* @param string $contentType Expect body to have specified content type.
195197
*/
196-
public function dumpBody()
198+
public function dumpBody(string $contentType = 'text/html')
197199
{
198-
var_dump($this->getBody());
200+
var_dump($this->getBody($contentType));
199201
}
200202

201203
/**
202204
* Print raw body.
205+
*
206+
* @param string $contentType Expect body to have specified content type.
203207
*/
204-
public function printBody()
208+
public function printBody(string $contentType = 'text/html')
205209
{
206-
echo $this->getBody() . "\n";
210+
echo $this->getBody($contentType) . "\n";
207211
}
208212

209213
/**

sapi/fpm/tests/tester.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,10 @@ class Tester
795795
$requestData['uri'] ?? null
796796
);
797797

798+
if (isset($requestData['delay'])) {
799+
usleep($requestData['delay']);
800+
}
801+
798802
return [
799803
'client' => $client,
800804
'requestId' => $client->async_request($params, false),

0 commit comments

Comments
 (0)