mirror of
https://github.com/haproxy/haproxy.git
synced 2026-04-24 15:49:14 -04:00
BUG/MEDIUM: mux-fcgi: prevent record-length truncation with large bufsize
FCGI content_length is a 16-bit field but fcgi_set_record_size() is called with size_t/uint32_t arguments. With tune.bufsize >= 65544 (legal; cfgparse-global.c only enforces <= INT_MAX-16), a single HTX DATA block or accumulated outbuf can exceed 65535 bytes. The implicit conversion to uint16_t silently truncates the length field while b_add(mbuf, outbuf.data) writes the full body. A client posting ~99000 bytes can craft the body so that bytes after the truncated length are parsed by PHP-FPM as fresh FCGI records on the connection: a smuggled BEGIN_REQUEST + PARAMS with arbitrary SCRIPT_FILENAME / PHP_VALUE bypasses all haproxy ACLs. Fix the zero-copy path by refusing it when the block exceeds 65535 bytes (falls through to copy). Fix the copy path by capping outbuf.size to 65535 + header so the data-fill loop naturally stops at the FCGI maximum and emits the rest in a subsequent record. The PARAMS path at line 2084 is similarly affected but harder to trigger (requires combined header+param size > 65535) and is covered by the same outbuf.size cap pattern if applied there. This patch must be backported to all stable versions.
This commit is contained in:
parent
e6c3660327
commit
0aeae23056
1 changed files with 18 additions and 1 deletions
|
|
@ -1914,6 +1914,11 @@ static size_t fcgi_strm_send_params(struct fcgi_conn *fconn, struct fcgi_strm *f
|
|||
if (outbuf.size < FCGI_RECORD_HEADER_SZ)
|
||||
goto full;
|
||||
|
||||
/* FCGI record size is uint16_t; cap output to avoid truncation in
|
||||
* fcgi_set_record_size() when tune.bufsize is large. */
|
||||
if (outbuf.size > 0xFFFF + FCGI_RECORD_HEADER_SZ)
|
||||
outbuf.size = 0xFFFF + FCGI_RECORD_HEADER_SZ;
|
||||
|
||||
/* vsn: 1(FCGI_VERSION), type: (4)FCGI_PARAMS, id: fstrm->id,
|
||||
* len: 0x0000 (fill later), padding: 0x00, rsv: 0x00 */
|
||||
memcpy(outbuf.area, "\x01\x04\x00\x00\x00\x00\x00\x00", FCGI_RECORD_HEADER_SZ);
|
||||
|
|
@ -2153,7 +2158,14 @@ static size_t fcgi_strm_send_stdin(struct fcgi_conn *fconn, struct fcgi_strm *fs
|
|||
goto end;
|
||||
type = htx_get_blk_type(blk);
|
||||
size = htx_get_blksz(blk);
|
||||
if (unlikely(size == count && b_size(mbuf) == b_size(buf) &&
|
||||
/* FCGI content_len is uint16_t. With tune.bufsize >= 65544 a single
|
||||
* HTX block can exceed 65535 bytes; the implicit truncation in
|
||||
* fcgi_set_record_size() would then desynchronize the record
|
||||
* stream and let the client smuggle a forged FCGI request to the
|
||||
* backend. Refuse zero-copy in that case and let the copy path
|
||||
* split the data across multiple records.
|
||||
*/
|
||||
if (unlikely(size <= 0xFFFF && size == count && b_size(mbuf) == b_size(buf) &&
|
||||
htx_nbblks(htx) == 1 && type == HTX_BLK_DATA)) {
|
||||
void *old_area = mbuf->area;
|
||||
int eom = (htx->flags & HTX_FL_EOM);
|
||||
|
|
@ -2212,6 +2224,11 @@ static size_t fcgi_strm_send_stdin(struct fcgi_conn *fconn, struct fcgi_strm *fs
|
|||
if (outbuf.size < FCGI_RECORD_HEADER_SZ + extra_bytes)
|
||||
goto full;
|
||||
|
||||
/* FCGI content_len is uint16_t; cap output to avoid truncation in
|
||||
* fcgi_set_record_size() when tune.bufsize is large. */
|
||||
if (outbuf.size > 0xFFFF + FCGI_RECORD_HEADER_SZ)
|
||||
outbuf.size = 0xFFFF + FCGI_RECORD_HEADER_SZ;
|
||||
|
||||
/* vsn: 1(FCGI_VERSION), type: (5)FCGI_STDIN, id: fstrm->id,
|
||||
* len: 0x0000 (fill later), padding: 0x00, rsv: 0x00 */
|
||||
memcpy(outbuf.area, "\x01\x05\x00\x00\x00\x00\x00\x00", FCGI_RECORD_HEADER_SZ);
|
||||
|
|
|
|||
Loading…
Reference in a new issue