]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_http2: Fix possible beam bucket double free from session destroy.
authorYann Ylavic <ylavic@apache.org>
Tue, 8 Feb 2022 13:07:32 +0000 (13:07 +0000)
committerYann Ylavic <ylavic@apache.org>
Tue, 8 Feb 2022 13:07:32 +0000 (13:07 +0000)
When the session pool is destroyed, so is the beam's pool so we don't
want to run the beam cleanup twice.

ASan is reporting something like this (APR_POOL_DEBUG):

=================================================================
==81201==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000080ce8 at pc 0x7fdc78962cc9 bp 0x7fdc731ff4f0 sp 0x7fdc731ff4e8
READ of size 8 at 0x603000080ce8 thread T11
    #0 0x7fdc78962cc8 in recv_buffer_cleanup ~httpd/modules/http2/h2_bucket_beam.c:279
    #1 0x7fdc78962fdc in beam_cleanup ~httpd/modules/http2/h2_bucket_beam.c:306
    #2 0x7fdc7896300c in beam_pool_cleanup ~httpd/modules/http2/h2_bucket_beam.c:313
    #3 0x7fdc7c5a8239 in run_cleanups memory/unix/apr_pools.c:2689
    #4 0x7fdc7c5a50f9 in pool_clear_debug memory/unix/apr_pools.c:1867
    #5 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    #6 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    #7 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    #8 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    #9 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    #10 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    #11 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    #12 0x7fdc7c5a5827 in apr_pool_destroy_debug memory/unix/apr_pools.c:2014
    #13 0x7fdc789aeaa5 in h2_session_pre_close ~httpd/modules/http2/h2_session.c:1934
    #14 0x7fdc7896a20e in h2_c1_pre_close ~httpd/modules/http2/h2_c1.c:188
    #15 0x7fdc7896b538 in h2_c1_hook_pre_close ~httpd/modules/http2/h2_c1.c:308
    #16 0x5596139aeb28 in ap_run_pre_close_connection ~httpd/server/connection.c:45
    #17 0x5596139af353 in ap_prep_lingering_close ~httpd/server/connection.c:128
    #18 0x5596139af3f2 in ap_start_lingering_close ~httpd/server/connection.c:154
    #19 0x7fdc7835bdf0 in process_lingering_close ~httpd/server/mpm/event/event.c:1999
    #20 0x7fdc78359ccb in process_socket ~httpd/server/mpm/event/event.c:1540
    #21 0x7fdc783608d7 in worker_thread ~httpd/server/mpm/event/event.c:2756
    #22 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    #23 0x7fdc7c441d7f in start_thread nptl/pthread_create.c:481
    #24 0x7fdc7c337bde in clone (/lib/x86_64-linux-gnu/libc.so.6+0xfcbde)

0x603000080ce8 is located 8 bytes inside of 32-byte region [0x603000080ce0,0x603000080d00)
freed by thread T11 here:
    #0 0x7fdc7c887f07 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:122
    #1 0x7fdc7c5a5420 in pool_clear_debug memory/unix/apr_pools.c:1906
    #2 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    #3 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    #4 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    #5 0x7fdc7c5a5827 in apr_pool_destroy_debug memory/unix/apr_pools.c:2014
    #6 0x7fdc789aeaa5 in h2_session_pre_close ~httpd/modules/http2/h2_session.c:1934
    #7 0x7fdc7896a20e in h2_c1_pre_close ~httpd/modules/http2/h2_c1.c:188
    #8 0x7fdc7896b538 in h2_c1_hook_pre_close ~httpd/modules/http2/h2_c1.c:308
    #9 0x5596139aeb28 in ap_run_pre_close_connection ~httpd/server/connection.c:45
    #10 0x5596139af353 in ap_prep_lingering_close ~httpd/server/connection.c:128
    #11 0x5596139af3f2 in ap_start_lingering_close ~httpd/server/connection.c:154
    #12 0x7fdc7835bdf0 in process_lingering_close ~httpd/server/mpm/event/event.c:1999
    #13 0x7fdc78359ccb in process_socket ~httpd/server/mpm/event/event.c:1540
    #14 0x7fdc783608d7 in worker_thread ~httpd/server/mpm/event/event.c:2756
    #15 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    #16 0x7fdc7c441d7f in start_thread nptl/pthread_create.c:481

previously allocated by thread T11 here:
    #0 0x7fdc7c8882b8 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7fdc7c5a4d00 in pool_alloc memory/unix/apr_pools.c:1787
    #2 0x7fdc7c5a507a in apr_palloc_debug memory/unix/apr_pools.c:1828
    #3 0x7fdc7c4d8160 in apr_brigade_create buckets/apr_brigade.c:90
    #4 0x7fdc7c4d82d8 in apr_brigade_split_ex buckets/apr_brigade.c:107
    #5 0x7fdc78967f7c in h2_beam_receive ~httpd/modules/http2/h2_bucket_beam.c:729
    #6 0x7fdc789b65f0 in buffer_output_receive ~httpd/modules/http2/h2_stream.c:847
    #7 0x7fdc789bb655 in h2_stream_read_output ~httpd/modules/http2/h2_stream.c:1372
    #8 0x7fdc789aa155 in on_stream_output ~httpd/modules/http2/h2_session.c:1313
    #9 0x7fdc789956ba in mplx_pollset_poll ~httpd/modules/http2/h2_mplx.c:1299
    #10 0x7fdc7898deb8 in h2_mplx_c1_poll ~httpd/modules/http2/h2_mplx.c:532
    #11 0x7fdc789ae04b in h2_session_process ~httpd/modules/http2/h2_session.c:1863
    #12 0x7fdc78969b0f in h2_c1_run ~httpd/modules/http2/h2_c1.c:138
    #13 0x7fdc7896b302 in h2_c1_hook_process_connection ~httpd/modules/http2/h2_c1.c:286
    #14 0x5596139ae4b6 in ap_run_process_connection ~httpd/server/connection.c:43
    #15 0x7fdc78358d67 in process_socket ~httpd/server/mpm/event/event.c:1353
    #16 0x7fdc783608d7 in worker_thread ~httpd/server/mpm/event/event.c:2756
    #17 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    #18 0x7fdc7c441d7f in start_thread nptl/pthread_create.c:481

Thread T11 created by T2 here:
    #0 0x7fdc7c7baa22 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cc:208
    #1 0x7fdc7c5d4534 in apr_thread_create threadproc/unix/thread.c:228
    #2 0x7fdc7836273d in start_threads ~httpd/server/mpm/event/event.c:3035
    #3 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    #4 0x7fdc7c441d7f in start_thread nptl/pthread_create.c:481

Thread T2 created by T0 here:
    #0 0x7fdc7c7baa22 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cc:208
    #1 0x7fdc7c5d4534 in apr_thread_create threadproc/unix/thread.c:228
    #2 0x7fdc78363d9f in child_main ~httpd/server/mpm/event/event.c:3262
    #3 0x7fdc7836483b in make_child ~httpd/server/mpm/event/event.c:3421
    #4 0x7fdc78364b89 in startup_children ~httpd/server/mpm/event/event.c:3444
    #5 0x7fdc78368abc in event_run ~httpd/server/mpm/event/event.c:3932
    #6 0x5596139b6d18 in ap_run_mpm ~httpd/server/mpm_common.c:101
    #7 0x55961399098b in main ~httpd/server/main.c:880
    #8 0x7fdc7c2627ec in __libc_start_main ../csu/libc-start.c:332

SUMMARY: AddressSanitizer: heap-use-after-free ~httpd/modules/http2/h2_bucket_beam.c:279 in recv_buffer_cleanup
Shadow bytes around the buggy address:
  0x0c0680008140: fa fa 00 00 00 00 fa fa fd fd fd fa fa fa fd fd
  0x0c0680008150: fd fd fa fa fd fd fd fd fa fa fd fd fd fd fa fa
  0x0c0680008160: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fd
  0x0c0680008170: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd
  0x0c0680008180: fd fd fa fa fd fd fd fd fa fa fd fd fd fa fa fa
=>0x0c0680008190: fd fd fd fa fa fa fd fd fd fa fa fa fd[fd]fd fd
  0x0c06800081a0: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd
  0x0c06800081b0: fd fd fa fa fd fd fd fd fa fa fd fd fd fd fa fa
  0x0c06800081c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c06800081d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c06800081e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==81201==ABORTING

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1897868 13f79535-47bb-0310-9956-ffa450edef68

modules/http2/h2_bucket_beam.c

index 3e4b1e855efc0beca15eacfd6dd447498006567b..9deb971e76a14298c171eebb52a36733a22d5d42 100644 (file)
@@ -276,11 +276,13 @@ static void h2_blist_cleanup(h2_blist *bl)
 
 static void recv_buffer_cleanup(h2_bucket_beam *beam)
 {
-    if (beam->recv_buffer && !APR_BRIGADE_EMPTY(beam->recv_buffer)) {
-        apr_bucket_brigade *bb = beam->recv_buffer;
+    apr_bucket_brigade *bb = beam->recv_buffer;
+
+    beam->recv_buffer = NULL;
+
+    if (bb && !APR_BRIGADE_EMPTY(bb)) {
         apr_off_t bblen = 0;
         
-        beam->recv_buffer = NULL;
         apr_brigade_length(bb, 0, &bblen);
         beam->recv_bytes += bblen;
         
@@ -297,27 +299,48 @@ static void recv_buffer_cleanup(h2_bucket_beam *beam)
     }
 }
 
-static apr_status_t beam_cleanup(h2_bucket_beam *beam, int from_pool)
+static void beam_shutdown(h2_bucket_beam *beam, apr_shutdown_how_e how)
 {
-    beam->cons_io_cb = NULL;
-    beam->recv_cb = NULL;
+    if (!beam->pool) {
+        /* pool being cleared already */
+        return;
+    }
 
-    h2_blist_cleanup(&beam->buckets_to_send);
-    recv_buffer_cleanup(beam);
-    purge_consumed_buckets(beam);
-    return APR_SUCCESS;
+    /* shutdown both receiver and sender? */
+    if (how == APR_SHUTDOWN_READWRITE) {
+        beam->cons_io_cb = NULL;
+        beam->recv_cb = NULL;
+    }
+
+    /* shutdown receiver (or both)? */
+    if (how != APR_SHUTDOWN_WRITE) {
+        recv_buffer_cleanup(beam);
+        beam->recv_cb = NULL;
+    }
+
+    /* shutdown sender (or both)? */
+    if (how != APR_SHUTDOWN_READ) {
+        h2_blist_cleanup(&beam->buckets_to_send);
+        purge_consumed_buckets(beam);
+    }
 }
 
-static apr_status_t beam_pool_cleanup(void *data)
+static apr_status_t beam_cleanup(void *data)
 {
-    return beam_cleanup(data, 1);
+    h2_bucket_beam *beam = data;
+    beam_shutdown(beam, APR_SHUTDOWN_READWRITE);
+    beam->pool = NULL; /* the pool is clearing now */
+    return APR_SUCCESS;
 }
 
 apr_status_t h2_beam_destroy(h2_bucket_beam *beam, conn_rec *c)
 {
-    apr_pool_cleanup_kill(beam->pool, beam, beam_pool_cleanup);
-    H2_BEAM_LOG(beam, c, APLOG_TRACE2, 0, "destroy", NULL);
-    return beam_cleanup(beam, 0);
+    if (beam->pool) {
+        H2_BEAM_LOG(beam, c, APLOG_TRACE2, 0, "destroy", NULL);
+        apr_pool_cleanup_run(beam->pool, beam, beam_cleanup);
+    }
+    H2_BEAM_LOG(beam, c, APLOG_TRACE2, 0, "destroyed", NULL);
+    return APR_SUCCESS;
 }
 
 apr_status_t h2_beam_create(h2_bucket_beam **pbeam, conn_rec *from,
@@ -346,7 +369,7 @@ apr_status_t h2_beam_create(h2_bucket_beam **pbeam, conn_rec *from,
     if (APR_SUCCESS != rv) goto cleanup;
     rv = apr_thread_cond_create(&beam->change, pool);
     if (APR_SUCCESS != rv) goto cleanup;
-    apr_pool_pre_cleanup_register(pool, beam, beam_pool_cleanup);
+    apr_pool_pre_cleanup_register(pool, beam, beam_cleanup);
 
 cleanup:
     H2_BEAM_LOG(beam, from, APLOG_TRACE2, rv, "created", NULL);
@@ -405,15 +428,14 @@ void h2_beam_abort(h2_bucket_beam *beam, conn_rec *c)
             beam->was_empty_cb(beam->was_empty_ctx, beam);
         }
         /* no more consumption reporting to sender */
-        beam->cons_io_cb = NULL;
-        beam->cons_ctx = NULL;
-        purge_consumed_buckets(beam);
-        h2_blist_cleanup(&beam->buckets_to_send);
         report_consumption(beam, 1);
+        beam->cons_ctx = NULL;
+
+        beam_shutdown(beam, APR_SHUTDOWN_WRITE);
     }
     else {
         /* receiver aborts */
-        recv_buffer_cleanup(beam);
+        beam_shutdown(beam, APR_SHUTDOWN_READ);
     }
     apr_thread_cond_broadcast(beam->change);
     apr_thread_mutex_unlock(beam->lock);
@@ -437,6 +459,8 @@ static apr_status_t append_bucket(h2_bucket_beam *beam,
         goto cleanup;
     }
 
+    ap_assert(beam->pool);
+
     b = APR_BRIGADE_FIRST(bb);
     if (APR_BUCKET_IS_METADATA(b)) {
         APR_BUCKET_REMOVE(b);
@@ -536,6 +560,8 @@ apr_status_t h2_beam_send(h2_bucket_beam *beam, conn_rec *from,
     apr_size_t space_left = 0;
     int was_empty;
 
+    ap_assert(beam->pool);
+
     /* Called from the sender thread to add buckets to the beam */
     apr_thread_mutex_lock(beam->lock);
     ap_assert(beam->from == from);
@@ -599,11 +625,13 @@ apr_status_t h2_beam_receive(h2_bucket_beam *beam,
 
 transfer:
     if (beam->aborted) {
-        recv_buffer_cleanup(beam);
+        beam_shutdown(beam, APR_SHUTDOWN_READ);
         rv = APR_ECONNABORTED;
         goto leave;
     }
 
+    ap_assert(beam->pool);
+
     /* transfer enough buckets from our receiver brigade, if we have one */
     while (remain >= 0
            && beam->recv_buffer