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.
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;
#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;
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) {
#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) {
#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;
#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
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;
}