comparison src/http/modules/ngx_http_fastcgi_module.c @ 7679:05e42236e95b

FastCGI: protection from responses with wrong length. Previous behaviour was to pass everything to the client, but this seems to be suboptimal and causes issues (ticket #1695). Fix is to drop extra data instead, as it naturally happens in most clients. Additionally, we now also issue a warning if the response is too short, and make sure the fact it is truncated is propagated to the client. The u->error flag is introduced to make it possible to propagate the error to the client in case of unbuffered proxying. For responses to HEAD requests there is an exception: we do allow both responses without body and responses with body matching the Content-Length header.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 06 Jul 2020 18:36:23 +0300
parents 8b68d50090e4
children da8d758aabeb
comparison
equal deleted inserted replaced
7678:bffcc5af1d72 7679:05e42236e95b
79 u_char *last; 79 u_char *last;
80 ngx_uint_t type; 80 ngx_uint_t type;
81 size_t length; 81 size_t length;
82 size_t padding; 82 size_t padding;
83 83
84 off_t rest;
85
84 ngx_chain_t *free; 86 ngx_chain_t *free;
85 ngx_chain_t *busy; 87 ngx_chain_t *busy;
86 88
87 unsigned fastcgi_stdout:1; 89 unsigned fastcgi_stdout:1;
88 unsigned large_stderr:1; 90 unsigned large_stderr:1;
89 unsigned header_sent:1; 91 unsigned header_sent:1;
92 unsigned closed:1;
90 93
91 ngx_array_t *split_parts; 94 ngx_array_t *split_parts;
92 95
93 ngx_str_t script_name; 96 ngx_str_t script_name;
94 ngx_str_t path_info; 97 ngx_str_t path_info;
2073 2076
2074 2077
2075 static ngx_int_t 2078 static ngx_int_t
2076 ngx_http_fastcgi_input_filter_init(void *data) 2079 ngx_http_fastcgi_input_filter_init(void *data)
2077 { 2080 {
2078 ngx_http_request_t *r = data; 2081 ngx_http_request_t *r = data;
2082
2083 ngx_http_upstream_t *u;
2084 ngx_http_fastcgi_ctx_t *f;
2079 ngx_http_fastcgi_loc_conf_t *flcf; 2085 ngx_http_fastcgi_loc_conf_t *flcf;
2080 2086
2087 u = r->upstream;
2088
2089 f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
2081 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 2090 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2082 2091
2083 r->upstream->pipe->length = flcf->keep_conn ? 2092 u->pipe->length = flcf->keep_conn ?
2084 (off_t) sizeof(ngx_http_fastcgi_header_t) : -1; 2093 (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;
2094
2095 if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
2096 || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED)
2097 {
2098 f->rest = 0;
2099
2100 } else if (r->method == NGX_HTTP_HEAD) {
2101 f->rest = -2;
2102
2103 } else {
2104 f->rest = u->headers_in.content_length_n;
2105 }
2085 2106
2086 return NGX_OK; 2107 return NGX_OK;
2087 } 2108 }
2088 2109
2089 2110
2104 2125
2105 r = p->input_ctx; 2126 r = p->input_ctx;
2106 f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module); 2127 f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
2107 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); 2128 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2108 2129
2130 if (p->upstream_done || f->closed) {
2131 r->upstream->keepalive = 0;
2132
2133 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
2134 "http fastcgi data after close");
2135
2136 return NGX_OK;
2137 }
2138
2109 b = NULL; 2139 b = NULL;
2110 prev = &buf->shadow; 2140 prev = &buf->shadow;
2111 2141
2112 f->pos = buf->pos; 2142 f->pos = buf->pos;
2113 f->last = buf->last; 2143 f->last = buf->last;
2126 } 2156 }
2127 2157
2128 if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) { 2158 if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
2129 f->state = ngx_http_fastcgi_st_padding; 2159 f->state = ngx_http_fastcgi_st_padding;
2130 2160
2161 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
2162 "http fastcgi closed stdout");
2163
2164 if (f->rest > 0) {
2165 ngx_log_error(NGX_LOG_ERR, p->log, 0,
2166 "upstream prematurely closed "
2167 "FastCGI stdout");
2168
2169 p->upstream_error = 1;
2170 p->upstream_eof = 0;
2171 f->closed = 1;
2172
2173 break;
2174 }
2175
2131 if (!flcf->keep_conn) { 2176 if (!flcf->keep_conn) {
2132 p->upstream_done = 1; 2177 p->upstream_done = 1;
2133 } 2178 }
2134 2179
2135 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
2136 "http fastcgi closed stdout");
2137
2138 continue; 2180 continue;
2139 } 2181 }
2140 2182
2141 if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) { 2183 if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
2142 2184
2143 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0, 2185 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
2144 "http fastcgi sent end request"); 2186 "http fastcgi sent end request");
2187
2188 if (f->rest > 0) {
2189 ngx_log_error(NGX_LOG_ERR, p->log, 0,
2190 "upstream prematurely closed "
2191 "FastCGI request");
2192
2193 p->upstream_error = 1;
2194 p->upstream_eof = 0;
2195 f->closed = 1;
2196
2197 break;
2198 }
2145 2199
2146 if (!flcf->keep_conn) { 2200 if (!flcf->keep_conn) {
2147 p->upstream_done = 1; 2201 p->upstream_done = 1;
2148 break; 2202 break;
2149 } 2203 }
2287 if (f->pos + f->length <= f->last) { 2341 if (f->pos + f->length <= f->last) {
2288 f->state = ngx_http_fastcgi_st_padding; 2342 f->state = ngx_http_fastcgi_st_padding;
2289 f->pos += f->length; 2343 f->pos += f->length;
2290 b->last = f->pos; 2344 b->last = f->pos;
2291 2345
2292 continue; 2346 } else {
2293 } 2347 f->length -= f->last - f->pos;
2294 2348 f->pos = f->last;
2295 f->length -= f->last - f->pos; 2349 b->last = f->last;
2296 2350 }
2297 b->last = f->last; 2351
2298 2352 if (f->rest == -2) {
2299 break; 2353 f->rest = r->upstream->headers_in.content_length_n;
2300 2354 }
2355
2356 if (f->rest >= 0) {
2357
2358 if (b->last - b->pos > f->rest) {
2359 ngx_log_error(NGX_LOG_WARN, p->log, 0,
2360 "upstream sent more data than specified in "
2361 "\"Content-Length\" header");
2362
2363 b->last = b->pos + f->rest;
2364 p->upstream_done = 1;
2365
2366 break;
2367 }
2368
2369 f->rest -= b->last - b->pos;
2370 }
2301 } 2371 }
2302 2372
2303 if (flcf->keep_conn) { 2373 if (flcf->keep_conn) {
2304 2374
2305 /* set p->length, minimal amount of data we want to see */ 2375 /* set p->length, minimal amount of data we want to see */
2389 2459
2390 if (f->state == ngx_http_fastcgi_st_padding) { 2460 if (f->state == ngx_http_fastcgi_st_padding) {
2391 2461
2392 if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) { 2462 if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
2393 2463
2464 if (f->rest > 0) {
2465 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2466 "upstream prematurely closed "
2467 "FastCGI request");
2468 u->error = 1;
2469 break;
2470 }
2471
2394 if (f->pos + f->padding < f->last) { 2472 if (f->pos + f->padding < f->last) {
2395 u->length = 0; 2473 u->length = 0;
2396 break; 2474 break;
2397 } 2475 }
2398 2476
2508 if (f->pos + f->length <= f->last) { 2586 if (f->pos + f->length <= f->last) {
2509 f->state = ngx_http_fastcgi_st_padding; 2587 f->state = ngx_http_fastcgi_st_padding;
2510 f->pos += f->length; 2588 f->pos += f->length;
2511 b->last = f->pos; 2589 b->last = f->pos;
2512 2590
2513 continue; 2591 } else {
2514 } 2592 f->length -= f->last - f->pos;
2515 2593 f->pos = f->last;
2516 f->length -= f->last - f->pos; 2594 b->last = f->last;
2517 b->last = f->last; 2595 }
2518 2596
2519 break; 2597 if (f->rest >= 0) {
2598
2599 if (b->last - b->pos > f->rest) {
2600 ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
2601 "upstream sent more data than specified in "
2602 "\"Content-Length\" header");
2603
2604 b->last = b->pos + f->rest;
2605 u->length = 0;
2606
2607 break;
2608 }
2609
2610 f->rest -= b->last - b->pos;
2611 }
2520 } 2612 }
2521 2613
2522 return NGX_OK; 2614 return NGX_OK;
2523 } 2615 }
2524 2616