Skip to content

Commit 0bc6a66

Browse files
committed
Fix bug #77653 (operator displayed instead of the real error message)
1 parent 2a76e3a commit 0bc6a66

File tree

8 files changed

+202
-49
lines changed

8 files changed

+202
-49
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ PHP NEWS
2222
cmb)
2323
. Fixed bug #79271 (DOMDocumentType::$childNodes is NULL). (cmb)
2424

25+
- FPM:
26+
. Fixed bug #77653 (operator displayed instead of the real error message).
27+
(Jakub Zelenka)
28+
2529
- PCRE:
2630
. Fixed bug #79188 (Memory corruption in preg_replace/preg_replace_callback
2731
and unicode). (Nikita)

sapi/fpm/fpm/fpm_main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
8787
#include "fpm_request.h"
8888
#include "fpm_status.h"
8989
#include "fpm_signals.h"
90+
#include "fpm_stdio.h"
9091
#include "fpm_conf.h"
9192
#include "fpm_php.h"
9293
#include "fpm_log.h"
@@ -1968,6 +1969,8 @@ consult the installation file that came with this distribution, or visit \n\
19681969

19691970
php_request_shutdown((void *) 0);
19701971

1972+
fpm_stdio_flush_child();
1973+
19711974
requests++;
19721975
if (UNEXPECTED(max_requests && (requests == max_requests))) {
19731976
fcgi_request_set_keep(request, 0);

sapi/fpm/fpm/fpm_request.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#include "fpm_children.h"
1717
#include "fpm_scoreboard.h"
1818
#include "fpm_status.h"
19-
#include "fpm_stdio.h"
2019
#include "fpm_request.h"
2120
#include "fpm_log.h"
2221

@@ -200,7 +199,6 @@ void fpm_request_end(void) /* {{{ */
200199
#endif
201200
proc->memory = memory;
202201
fpm_scoreboard_proc_release(proc);
203-
fpm_stdio_flush_child();
204202
}
205203
/* }}} */
206204

sapi/fpm/fpm/fpm_stdio.c

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,11 @@ int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
106106
}
107107
/* }}} */
108108

109+
#define FPM_STDIO_CMD_FLUSH "\0fscf"
110+
109111
int fpm_stdio_flush_child() /* {{{ */
110112
{
111-
return write(STDERR_FILENO, "\0", 1);
113+
return write(STDERR_FILENO, FPM_STDIO_CMD_FLUSH, sizeof(FPM_STDIO_CMD_FLUSH));
112114
}
113115
/* }}} */
114116

@@ -120,10 +122,8 @@ static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg)
120122
struct fpm_child_s *child;
121123
int is_stdout;
122124
struct fpm_event_s *event;
123-
int fifo_in = 1, fifo_out = 1;
124-
int in_buf = 0;
125-
int read_fail = 0, finish_log_stream = 0, create_log_stream;
126-
int res;
125+
int in_buf = 0, cmd_pos = 0, pos, start;
126+
int read_fail = 0, create_log_stream;
127127
struct zlog_stream *log_stream;
128128

129129
if (!arg) {
@@ -162,49 +162,53 @@ static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg)
162162
}
163163
}
164164

165-
while (fifo_in || fifo_out) {
166-
if (fifo_in) {
167-
res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
168-
if (res <= 0) { /* no data */
169-
fifo_in = 0;
170-
if (res == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
171-
/* pipe is closed or error */
172-
read_fail = (res < 0) ? res : 1;
173-
}
174-
} else {
175-
in_buf += res;
176-
/* if buffer ends with \0, then the stream will be finished */
177-
if (!buf[in_buf - 1]) {
178-
finish_log_stream = 1;
179-
in_buf--;
180-
}
165+
while (1) {
166+
stdio_read:
167+
in_buf = read(fd, buf, max_buf_size - 1);
168+
if (in_buf <= 0) { /* no data */
169+
if (in_buf == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
170+
/* pipe is closed or error */
171+
read_fail = (in_buf < 0) ? in_buf : 1;
181172
}
173+
break;
182174
}
183-
184-
if (fifo_out) {
185-
if (in_buf == 0) {
186-
fifo_out = 0;
175+
start = 0;
176+
if (cmd_pos > 0) {
177+
if ((sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos) <= in_buf &&
178+
!memcmp(buf, &FPM_STDIO_CMD_FLUSH[cmd_pos], sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos)) {
179+
zlog_stream_finish(log_stream);
180+
start = cmd_pos;
187181
} else {
188-
char *nl;
189-
190-
nl = memchr(buf, '\n', in_buf);
191-
if (nl) {
192-
/* we should print each new line int the new message */
193-
int out_len = nl - buf;
194-
zlog_stream_str(log_stream, buf, out_len);
182+
zlog_stream_str(log_stream, &FPM_STDIO_CMD_FLUSH[0], cmd_pos);
183+
}
184+
cmd_pos = 0;
185+
}
186+
for (pos = start; pos < in_buf; pos++) {
187+
switch (buf[pos]) {
188+
case '\n':
189+
zlog_stream_str(log_stream, buf + start, pos - start);
195190
zlog_stream_finish(log_stream);
196-
/* skip new line */
197-
out_len++;
198-
/* move data in the buffer */
199-
memmove(buf, buf + out_len, in_buf - out_len);
200-
in_buf -= out_len;
201-
} else if (in_buf == max_buf_size - 1 || !fifo_in) {
202-
/* we should print if no more space in the buffer or no more data to come */
203-
zlog_stream_str(log_stream, buf, in_buf);
204-
in_buf = 0;
205-
}
191+
start = pos + 1;
192+
break;
193+
case '\0':
194+
if (pos + sizeof(FPM_STDIO_CMD_FLUSH) <= in_buf) {
195+
if (!memcmp(buf + pos, FPM_STDIO_CMD_FLUSH, sizeof(FPM_STDIO_CMD_FLUSH))) {
196+
zlog_stream_str(log_stream, buf + start, pos - start);
197+
zlog_stream_finish(log_stream);
198+
start = pos + sizeof(FPM_STDIO_CMD_FLUSH);
199+
pos = start - 1;
200+
}
201+
} else if (!memcmp(buf + pos, FPM_STDIO_CMD_FLUSH, in_buf - pos)) {
202+
cmd_pos = in_buf - pos;
203+
zlog_stream_str(log_stream, buf + start, pos - start);
204+
goto stdio_read;
205+
}
206+
break;
206207
}
207208
}
209+
if (start < pos) {
210+
zlog_stream_str(log_stream, buf + start, pos - start);
211+
}
208212
}
209213

210214
if (read_fail) {
@@ -225,8 +229,6 @@ static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg)
225229
close(child->fd_stderr);
226230
child->fd_stderr = -1;
227231
}
228-
} else if (finish_log_stream) {
229-
zlog_stream_finish(log_stream);
230232
}
231233
}
232234
/* }}} */

sapi/fpm/fpm/zlog.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -734,14 +734,16 @@ ssize_t zlog_stream_format(struct zlog_stream *stream, const char *fmt, ...) /*
734734

735735
ssize_t zlog_stream_str(struct zlog_stream *stream, const char *str, size_t str_len) /* {{{ */
736736
{
737+
/* do not write anything if the stream is full or str is empty */
738+
if (str_len == 0 || stream->full) {
739+
return 0;
740+
}
741+
737742
/* reset stream if it is finished */
738743
if (stream->finished) {
739744
stream->finished = 0;
740745
stream->len = 0;
741746
stream->full = 0;
742-
} else if (stream->full) {
743-
/* do not write anything if the stream is full */
744-
return 0;
745747
}
746748

747749
if (stream->use_buffer) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
FPM: Log message in shutdown function
3+
--SKIPIF--
4+
<?php include "skipif.inc"; ?>
5+
--FILE--
6+
<?php
7+
8+
require_once "tester.inc";
9+
10+
$cfg = <<<EOT
11+
[global]
12+
error_log = {{FILE:LOG}}
13+
log_limit = 1024
14+
log_buffering = yes
15+
[unconfined]
16+
listen = {{ADDR}}
17+
pm = dynamic
18+
pm.max_children = 5
19+
pm.start_servers = 1
20+
pm.min_spare_servers = 1
21+
pm.max_spare_servers = 3
22+
catch_workers_output = yes
23+
EOT;
24+
25+
$code = <<<EOT
26+
<?php
27+
register_shutdown_function(function() {
28+
error_log(str_repeat('e', 80));
29+
});
30+
EOT;
31+
32+
$tester = new FPM\Tester($cfg, $code);
33+
$tester->start();
34+
$tester->expectLogStartNotices();
35+
$tester->request()->expectEmptyBody();
36+
$tester->terminate();
37+
$tester->expectFastCGIErrorMessage('e', 1050, 80);
38+
$tester->expectLogMessage('NOTICE: PHP message: ' . str_repeat('e', 80), 1050);
39+
$tester->close();
40+
41+
?>
42+
Done
43+
--EXPECT--
44+
Done
45+
--CLEAN--
46+
<?php
47+
require_once "tester.inc";
48+
FPM\Tester::clean();
49+
?>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
FPM: Buffered worker output plain log with msg with flush split in buffer
3+
--SKIPIF--
4+
<?php include "skipif.inc"; ?>
5+
--FILE--
6+
<?php
7+
8+
require_once "tester.inc";
9+
10+
$cfg = <<<EOT
11+
[global]
12+
error_log = {{FILE:LOG}}
13+
[unconfined]
14+
listen = {{ADDR}}
15+
pm = dynamic
16+
pm.max_children = 5
17+
pm.start_servers = 1
18+
pm.min_spare_servers = 1
19+
pm.max_spare_servers = 3
20+
catch_workers_output = yes
21+
decorate_workers_output = no
22+
EOT;
23+
24+
$code = <<<EOT
25+
<?php
26+
file_put_contents('php://stderr', str_repeat('a', 1021) . "\0fabc");
27+
EOT;
28+
29+
$tester = new FPM\Tester($cfg, $code);
30+
$tester->start();
31+
$tester->expectLogStartNotices();
32+
$tester->request()->expectEmptyBody();
33+
$tester->terminate();
34+
$lines = $tester->getLogLines(2);
35+
var_dump($lines[0] === str_repeat('a', 1021) . "\0f\n");
36+
var_dump($lines[1] === "abc\n");
37+
$tester->close();
38+
39+
?>
40+
Done
41+
--EXPECT--
42+
bool(true)
43+
bool(true)
44+
Done
45+
--CLEAN--
46+
<?php
47+
require_once "tester.inc";
48+
FPM\Tester::clean();
49+
?>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
FPM: Buffered worker output plain log with msg with flush split in buffer
3+
--SKIPIF--
4+
<?php include "skipif.inc"; ?>
5+
--FILE--
6+
<?php
7+
8+
require_once "tester.inc";
9+
10+
$cfg = <<<EOT
11+
[global]
12+
error_log = {{FILE:LOG}}
13+
[unconfined]
14+
listen = {{ADDR}}
15+
pm = dynamic
16+
pm.max_children = 5
17+
pm.start_servers = 1
18+
pm.min_spare_servers = 1
19+
pm.max_spare_servers = 3
20+
catch_workers_output = yes
21+
decorate_workers_output = no
22+
EOT;
23+
24+
$code = <<<EOT
25+
<?php
26+
file_put_contents('php://stderr', str_repeat('a', 1022) . "\0fscf\0");
27+
EOT;
28+
29+
$tester = new FPM\Tester($cfg, $code);
30+
$tester->start();
31+
$tester->expectLogStartNotices();
32+
$tester->request()->expectEmptyBody();
33+
$tester->terminate();
34+
var_dump($tester->getLastLogLine() === str_repeat('a', 1022) . "\n");
35+
$tester->close();
36+
37+
?>
38+
Done
39+
--EXPECT--
40+
bool(true)
41+
Done
42+
--CLEAN--
43+
<?php
44+
require_once "tester.inc";
45+
FPM\Tester::clean();
46+
?>

0 commit comments

Comments
 (0)