fcgi_strm_handle_stderr() emits one log line per STDERR record, appending
its own newline, but passes the record payload to app_log() with a bare
"%s", and neither app_log() nor __send_log() escape anything. FastCGI
applications routinely echo parts of the request in their warnings, so a
client whose input is reflected there can insert CR/LF and turn one record
into several log lines, or insert ESC sequences which the operator's
terminal interprets when reading the log. Verified with an application
writing "bad input 'x\r\nFAKE-INJECTED-LINE: ...\033[31m...'": the syslog
datagram carries all of it verbatim.
Escaping data emitted to logs is normally a configuration matter, but this
path bypasses the log-format machinery entirely, and the intent here is
clearly one record per line. Let's replace the control characters with a
dot before logging, which is a single pass over a record that is only ever
produced when the application writes to its stderr. Only controls are
replaced, so that the UTF-8 messages commonly found in such warnings are
left intact.
This has been there since the FCGI mux was introduced in 2.1 by commit
99eff65f4 ("MEDIUM: mux-fcgi: Add the FCGI multiplexer"). It may be
backported to all stable versions.
Reported-by: Claude (ANT-2026-9JD79F3M)
{
struct buffer *dbuf;
struct buffer tag;
- size_t ret;
+ size_t ret, i;
TRACE_ENTER(FCGI_EV_RX_RECORD|FCGI_EV_RX_STDERR, fconn->conn, fstrm);
dbuf = &fconn->dbuf;
fconn->drl -= ret;
TRACE_PROTO("FCGI STDERR record rcvd", FCGI_EV_RX_RECORD|FCGI_EV_RX_STDERR, fconn->conn, fstrm, 0, (size_t[]){ret});
+ for (i = 0; i < ret; i++) {
+ if (iscntrl((unsigned char)trash.area[i]))
+ trash.area[i] = '.';
+ }
+
trash.area[ret] = '\n';
trash.area[ret+1] = '\0';
tag.area = fconn->app->name; tag.data = strlen(fconn->app->name);