[mod_ajp13,mod_fastcgi] check resp w/ content len

limit response body from mod_ajp13 and mod_fastcgi to Content-Length,
if Content-Length is provided in response headers; discard excess
personal/stbuehler/tests-path
Glenn Strauss 2021-10-21 09:45:06 -04:00
parent e78cd76511
commit 1acf9db7d3
4 changed files with 48 additions and 36 deletions

View File

@ -729,7 +729,7 @@ static int http_response_append_buffer(request_st * const r, buffer * const mem,
}
else { /* (r->resp_body_scratchpad <= 0) */
r->resp_body_finished = 1;
if (r->resp_body_scratchpad < 0) {
if (__builtin_expect( (r->resp_body_scratchpad < 0), 0)) {
/*(silently truncate if data exceeds Content-Length)*/
len += r->resp_body_scratchpad;
r->resp_body_scratchpad = 0;
@ -794,7 +794,7 @@ static int http_response_append_mem(request_st * const r, const char * const mem
r->resp_body_scratchpad -= (off_t)len;
if (r->resp_body_scratchpad <= 0) {
r->resp_body_finished = 1;
if (r->resp_body_scratchpad < 0) {
if (__builtin_expect( (r->resp_body_scratchpad < 0), 0)) {
/*(silently truncate if data exceeds Content-Length)*/
len = (size_t)(r->resp_body_scratchpad + (off_t)len);
r->resp_body_scratchpad = 0;
@ -809,6 +809,46 @@ static int http_response_append_mem(request_st * const r, const char * const mem
}
int http_response_transfer_cqlen(request_st * const r, chunkqueue * const cq, size_t len) {
/*(intended for use as callback from modules setting opts->parse(),
* e.g. mod_fastcgi and mod_ajp13)
*(do not set r->resp_body_finished here since those protocols handle it)*/
if (0 == len) return 0;
if (__builtin_expect( (!r->resp_decode_chunked), 1)) {
const size_t olen = len;
if (r->resp_body_scratchpad >= 0) {
r->resp_body_scratchpad -= (off_t)len;
if (__builtin_expect( (r->resp_body_scratchpad < 0), 0)) {
/*(silently truncate if data exceeds Content-Length)*/
len = (size_t)(r->resp_body_scratchpad + (off_t)len);
r->resp_body_scratchpad = 0;
}
}
int rc = http_chunk_transfer_cqlen(r, cq, len);
if (__builtin_expect( (0 != rc), 0))
return -1;
if (__builtin_expect( (olen != len), 0)) /*discard excess data, if any*/
chunkqueue_mark_written(cq, (off_t)(olen - len));
}
else {
/* specialized use by opts->parse() to decode chunked encoding;
* FastCGI, AJP13 packet data is all type MEM_CHUNK
* (This extra copy can be avoided if FastCGI backend does not send
* Transfer-Encoding: chunked, which FastCGI is not supposed to do) */
uint32_t remain = (uint32_t)len, wr;
for (const chunk *c = cq->first; c && remain; c=c->next, remain-=wr) {
/*assert(c->type == MEM_CHUNK);*/
wr = buffer_clen(c->mem) - c->offset;
if (wr > remain) wr = remain;
if (0 != http_chunk_decode_append_mem(r, c->mem->ptr+c->offset, wr))
return -1;
}
chunkqueue_mark_written(cq, len);
}
return 0;
}
static int http_response_process_headers(request_st * const restrict r, http_response_opts * const restrict opts, char * const restrict s, const unsigned short hoff[8192], const int is_nph) {
int i = 1;

View File

@ -888,7 +888,7 @@ ajp13_recv_parse (request_st * const r, struct http_response_opts_t * const opts
return HANDLER_FINISHED;
}
chunkqueue_mark_written(hctx->rb, 7);
if (0 == http_chunk_transfer_cqlen(r, hctx->rb, len)) {
if (0 == http_response_transfer_cqlen(r, hctx->rb, len)) {
if (len != plen - 3)
chunkqueue_mark_written(hctx->rb, plen - 3 - len);
continue;
@ -896,6 +896,7 @@ ajp13_recv_parse (request_st * const r, struct http_response_opts_t * const opts
else {
/* error writing to tempfile;
* truncate response or send 500 if nothing sent yet */
hctx->send_content_body = 0;
return HANDLER_FINISHED;
}
}

View File

@ -353,38 +353,6 @@ static void fastcgi_get_packet_body(buffer * const b, handler_ctx * const hctx,
buffer_truncate(b, blen + packet->len - packet->padding);
}
__attribute_cold__
__attribute_noinline__
static int
mod_fastcgi_chunk_decode_transfer_cqlen (request_st * const r, chunkqueue * const src, const unsigned int len)
{
if (0 == len) return 0;
/* specialized for mod_fastcgi to decode chunked encoding;
* FastCGI packet data is all type MEM_CHUNK
* entire src cq is processed, minus packet.padding at end
* (This extra work can be avoided if FastCGI backend does not send
* Transfer-Encoding: chunked, which FastCGI is not supposed to do) */
uint32_t remain = len, wr;
for (const chunk *c = src->first; c && remain; c = c->next, remain -= wr) {
/*assert(c->type == MEM_CHUNK);*/
wr = buffer_clen(c->mem) - c->offset;
if (wr > remain) wr = remain;
if (0 != http_chunk_decode_append_mem(r, c->mem->ptr+c->offset, wr))
return -1;
}
chunkqueue_mark_written(src, len);
return 0;
}
static int
mod_fastcgi_transfer_cqlen (request_st * const r, chunkqueue * const src, const unsigned int len)
{
return (!r->resp_decode_chunked)
? http_chunk_transfer_cqlen(r, src, len)
: mod_fastcgi_chunk_decode_transfer_cqlen(r, src, len);
}
static handler_t fcgi_recv_parse(request_st * const r, struct http_response_opts_t *opts, buffer *b, size_t n) {
handler_ctx *hctx = (handler_ctx *)opts->pdata;
int fin = 0;
@ -468,9 +436,10 @@ static handler_t fcgi_recv_parse(request_st * const r, struct http_response_opts
}
#endif
} else if (hctx->send_content_body) {
if (0 != mod_fastcgi_transfer_cqlen(r, hctx->rb, packet.len - packet.padding)) {
if (0 != http_response_transfer_cqlen(r, hctx->rb, (size_t)(packet.len - packet.padding))) {
/* error writing to tempfile;
* truncate response or send 500 if nothing sent yet */
hctx->send_content_body = 0;
fin = 1;
}
if (packet.padding) chunkqueue_mark_written(hctx->rb, packet.padding);

View File

@ -9,6 +9,7 @@
#include "array.h"
struct stat_cache_entry;/* declaration */
struct chunkqueue; /* declaration */
int http_response_parse(server *srv, request_st *r);
@ -55,6 +56,7 @@ void http_response_send_file (request_st *r, buffer *path, struct stat_cache_ent
void http_response_backend_done (request_st *r);
void http_response_backend_error (request_st *r);
void http_response_upgrade_read_body_unknown(request_st *r);
int http_response_transfer_cqlen(request_st *r, struct chunkqueue *cq, size_t len);
__attribute_cold__
int http_response_omit_header(request_st *r, const data_string *ds);