]> git.kaiwu.me - nginx.git/commitdiff
Event pipe: do not free buffers still used by the downstream
authorIvan Ovchinnikov <i.ovchinnikov@f5.com>
Fri, 21 Aug 2026 20:48:15 +0000 (20:48 +0000)
committerIvan Ovchinnikov <33402471+route443@users.noreply.github.com>
Wed, 26 Aug 2026 02:16:57 +0000 (19:16 -0700)
When a body filter returned an error, ngx_event_pipe_drain_chains()
returned all buffers to p->free_raw_bufs, including those in p->busy,
which are still owned by the downstream. If upstream EOF was detected
in the same ngx_event_pipe() pass, ngx_event_pipe_read_upstream() then
freed them while still referenced. With HTTP/2 only the stream's fake
connection is marked on error, so ngx_http_v2_send_output_queue() kept
sending DATA frames pointing at the freed buffers, a use-after-free.

Fixed by keeping the p->busy chains intact, the buffers are freed with
the request pool.  This also stops the pipe from reusing them for new
upstream reads, which previously could overwrite response body data
still queued for the client.  Additionally, the pipe no longer treats
a downstream it has given up on as merely blocked, so the loop is
still guaranteed to terminate.

Co-authored-by: Roman Arutyunyan <arut@nginx.com>
src/event/ngx_event_pipe.c

index d774903ec6e78e68206c57c77c6fdf5ae3f980c9..7bbca4b24cafb8fcb7bf7260d8842ca6b7d7a4df 100644 (file)
@@ -253,6 +253,7 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
                 chain->next = NULL;
 
             } else if (!p->cacheable
+                       && !p->downstream_error
                        && p->downstream->data == p->output_ctx
                        && p->downstream->write->ready
                        && !p->downstream->write->delayed)
@@ -1111,11 +1112,7 @@ ngx_event_pipe_drain_chains(ngx_event_pipe_t *p)
     ngx_chain_t  *cl, *tl;
 
     for ( ;; ) {
-        if (p->busy) {
-            cl = p->busy;
-            p->busy = NULL;
-
-        } else if (p->out) {
+        if (p->out) {
             cl = p->out;
             p->out = NULL;