Mercurial > hg > nginx
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 |