Limited the number of pending operations per request

Each pending asynchronous operation holds a reference in
r->main->blocked, an 8-bit field, and the increments were not checked.
With 256 operations pending on a single request the counter wrapped to
zero, after which the request could be freed while those operations
still referenced its pool.  This is the same overflow that 4ec67cf86
addressed for r->main->count, and left unaddressed for r->main->blocked.

Operations beyond the limit are now performed synchronously where a
synchronous path exists, and refused otherwise, so that the counter
cannot overflow.

Reported by cyberlancer.
This commit is contained in:
Vadim Zhestikov authored and Sergey Kandaurov committed 2026-10-01 21:18:52 +04:00
1 parent adf76b2fea
commit 6adfc05ece
4 files changed
+53 -2

No files matched your search

+36
View File
@@ -87,6 +87,13 @@ ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_output_chain_ctx_t *ctx;
ngx_http_core_loc_conf_t *clcf;
ngx_http_copy_filter_conf_t *conf;
#if (NGX_HAVE_FILE_AIO)
ngx_output_chain_aio_pt aio_handler;
#endif
#if (NGX_THREADS)
ngx_int_t (*thread_handler)(ngx_thread_task_t *task,
ngx_file_t *file);
#endif
c = r->connection;
@@ -140,10 +147,39 @@ ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
#if (NGX_HAVE_FILE_AIO || NGX_THREADS)
ctx->aio = r->aio;
#if (NGX_HAVE_FILE_AIO)
aio_handler = ctx->aio_handler;
#endif
#if (NGX_THREADS)
thread_handler = ctx->thread_handler;
#endif
/*
* each pending operation holds a reference in r->main->blocked;
* with the counter close to its limit the file is read
* synchronously instead, so that it cannot overflow
*/
if (r->main->blocked >= NGX_HTTP_MAX_BLOCKED) {
#if (NGX_HAVE_FILE_AIO)
ctx->aio_handler = NULL;
#endif
#if (NGX_THREADS)
ctx->thread_handler = NULL;
#endif
}
#endif
rc = ngx_output_chain(ctx, in);
#if (NGX_HAVE_FILE_AIO)
ctx->aio_handler = aio_handler;
#endif
#if (NGX_THREADS)
ctx->thread_handler = thread_handler;
#endif
if (ctx->in == NULL) {
r->buffered &= ~NGX_HTTP_COPY_BUFFERED;
+10 -2
View File
@@ -440,6 +440,10 @@ ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
return NGX_HTTP_CACHE_SCARCE;
}
if (r->main->blocked >= NGX_HTTP_MAX_BLOCKED) {
return NGX_HTTP_CACHE_SCARCE;
}
c->waiting = 1;
if (c->wait_time == 0) {
@@ -692,7 +696,9 @@ ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
#if (NGX_HAVE_FILE_AIO)
if (clcf->aio == NGX_HTTP_AIO_ON && ngx_file_aio) {
if (clcf->aio == NGX_HTTP_AIO_ON && ngx_file_aio
&& r->main->blocked < NGX_HTTP_MAX_BLOCKED)
{
n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
if (n != NGX_AGAIN) {
@@ -717,7 +723,9 @@ ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
#if (NGX_THREADS)
if (clcf->aio == NGX_HTTP_AIO_THREADS) {
if (clcf->aio == NGX_HTTP_AIO_THREADS
&& r->main->blocked < NGX_HTTP_MAX_BLOCKED)
{
c->file.thread_task = c->thread_task;
c->file.thread_handler = ngx_http_cache_thread_handler;
c->file.thread_ctx = r;
+1
View File
@@ -11,6 +11,7 @@
#define NGX_HTTP_MAX_URI_CHANGES 10
#define NGX_HTTP_MAX_SUBREQUESTS 50
#define NGX_HTTP_MAX_BLOCKED 250
/* must be 2^n */
#define NGX_HTTP_LC_HEADER_LEN 32
+6
View File
@@ -4216,6 +4216,12 @@ ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
task->event.data = r;
task->event.handler = ngx_http_upstream_thread_event_handler;
if (r->main->blocked >= NGX_HTTP_MAX_BLOCKED) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"too many operations pending");
return NGX_ERROR;
}
if (ngx_thread_task_post(tp, task) != NGX_OK) {
return NGX_ERROR;
}