Skip to content

Commit

Permalink
mod_http2: Fix possible beam bucket UAF in session destroy.
Browse files Browse the repository at this point in the history
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:

=================================================================
==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 /home/yle/src/ylavic/httpd/modules/http2/h2_bucket_beam.c:279
    apache#1 0x7fdc78962fdc in beam_cleanup /home/yle/src/ylavic/httpd/modules/http2/h2_bucket_beam.c:306
    apache#2 0x7fdc7896300c in beam_pool_cleanup /home/yle/src/ylavic/httpd/modules/http2/h2_bucket_beam.c:313
    apache#3 0x7fdc7c5a8239 in run_cleanups memory/unix/apr_pools.c:2689
    apache#4 0x7fdc7c5a50f9 in pool_clear_debug memory/unix/apr_pools.c:1867
    apache#5 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    apache#6 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    apache#7 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    apache#8 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    apache#9 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    apache#10 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    apache#11 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    apache#12 0x7fdc7c5a5827 in apr_pool_destroy_debug memory/unix/apr_pools.c:2014
    apache#13 0x7fdc789aeaa5 in h2_session_pre_close /home/yle/src/ylavic/httpd/modules/http2/h2_session.c:1934
    apache#14 0x7fdc7896a20e in h2_c1_pre_close /home/yle/src/ylavic/httpd/modules/http2/h2_c1.c:188
    apache#15 0x7fdc7896b538 in h2_c1_hook_pre_close /home/yle/src/ylavic/httpd/modules/http2/h2_c1.c:308
    apache#16 0x5596139aeb28 in ap_run_pre_close_connection /home/yle/src/ylavic/httpd/server/connection.c:45
    apache#17 0x5596139af353 in ap_prep_lingering_close /home/yle/src/ylavic/httpd/server/connection.c:128
    apache#18 0x5596139af3f2 in ap_start_lingering_close /home/yle/src/ylavic/httpd/server/connection.c:154
    apache#19 0x7fdc7835bdf0 in process_lingering_close /home/yle/src/ylavic/httpd/server/mpm/event/event.c:1999
    apache#20 0x7fdc78359ccb in process_socket /home/yle/src/ylavic/httpd/server/mpm/event/event.c:1540
    apache#21 0x7fdc783608d7 in worker_thread /home/yle/src/ylavic/httpd/server/mpm/event/event.c:2756
    apache#22 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    apache#23 0x7fdc7c441d7f in start_thread nptl/pthread_create.c:481
    apache#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
    apache#1 0x7fdc7c5a5420 in pool_clear_debug memory/unix/apr_pools.c:1906
    apache#2 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    apache#3 0x7fdc7c5a5179 in pool_clear_debug memory/unix/apr_pools.c:1880
    apache#4 0x7fdc7c5a562e in pool_destroy_debug memory/unix/apr_pools.c:1965
    apache#5 0x7fdc7c5a5827 in apr_pool_destroy_debug memory/unix/apr_pools.c:2014
    apache#6 0x7fdc789aeaa5 in h2_session_pre_close /home/yle/src/ylavic/httpd/modules/http2/h2_session.c:1934
    apache#7 0x7fdc7896a20e in h2_c1_pre_close /home/yle/src/ylavic/httpd/modules/http2/h2_c1.c:188
    apache#8 0x7fdc7896b538 in h2_c1_hook_pre_close /home/yle/src/ylavic/httpd/modules/http2/h2_c1.c:308
    apache#9 0x5596139aeb28 in ap_run_pre_close_connection /home/yle/src/ylavic/httpd/server/connection.c:45
    apache#10 0x5596139af353 in ap_prep_lingering_close /home/yle/src/ylavic/httpd/server/connection.c:128
    apache#11 0x5596139af3f2 in ap_start_lingering_close /home/yle/src/ylavic/httpd/server/connection.c:154
    apache#12 0x7fdc7835bdf0 in process_lingering_close /home/yle/src/ylavic/httpd/server/mpm/event/event.c:1999
    apache#13 0x7fdc78359ccb in process_socket /home/yle/src/ylavic/httpd/server/mpm/event/event.c:1540
    apache#14 0x7fdc783608d7 in worker_thread /home/yle/src/ylavic/httpd/server/mpm/event/event.c:2756
    apache#15 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    apache#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
    apache#1 0x7fdc7c5a4d00 in pool_alloc memory/unix/apr_pools.c:1787
    apache#2 0x7fdc7c5a507a in apr_palloc_debug memory/unix/apr_pools.c:1828
    apache#3 0x7fdc7c4d8160 in apr_brigade_create buckets/apr_brigade.c:90
    apache#4 0x7fdc7c4d82d8 in apr_brigade_split_ex buckets/apr_brigade.c:107
    apache#5 0x7fdc78967f7c in h2_beam_receive /home/yle/src/ylavic/httpd/modules/http2/h2_bucket_beam.c:729
    apache#6 0x7fdc789b65f0 in buffer_output_receive /home/yle/src/ylavic/httpd/modules/http2/h2_stream.c:847
    apache#7 0x7fdc789bb655 in h2_stream_read_output /home/yle/src/ylavic/httpd/modules/http2/h2_stream.c:1372
    apache#8 0x7fdc789aa155 in on_stream_output /home/yle/src/ylavic/httpd/modules/http2/h2_session.c:1313
    apache#9 0x7fdc789956ba in mplx_pollset_poll /home/yle/src/ylavic/httpd/modules/http2/h2_mplx.c:1299
    apache#10 0x7fdc7898deb8 in h2_mplx_c1_poll /home/yle/src/ylavic/httpd/modules/http2/h2_mplx.c:532
    apache#11 0x7fdc789ae04b in h2_session_process /home/yle/src/ylavic/httpd/modules/http2/h2_session.c:1863
    apache#12 0x7fdc78969b0f in h2_c1_run /home/yle/src/ylavic/httpd/modules/http2/h2_c1.c:138
    apache#13 0x7fdc7896b302 in h2_c1_hook_process_connection /home/yle/src/ylavic/httpd/modules/http2/h2_c1.c:286
    apache#14 0x5596139ae4b6 in ap_run_process_connection /home/yle/src/ylavic/httpd/server/connection.c:43
    apache#15 0x7fdc78358d67 in process_socket /home/yle/src/ylavic/httpd/server/mpm/event/event.c:1353
    apache#16 0x7fdc783608d7 in worker_thread /home/yle/src/ylavic/httpd/server/mpm/event/event.c:2756
    apache#17 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    apache#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
    apache#1 0x7fdc7c5d4534 in apr_thread_create threadproc/unix/thread.c:228
    apache#2 0x7fdc7836273d in start_threads /home/yle/src/ylavic/httpd/server/mpm/event/event.c:3035
    apache#3 0x7fdc7c5d3e57 in dummy_worker threadproc/unix/thread.c:153
    apache#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
    apache#1 0x7fdc7c5d4534 in apr_thread_create threadproc/unix/thread.c:228
    apache#2 0x7fdc78363d9f in child_main /home/yle/src/ylavic/httpd/server/mpm/event/event.c:3262
    apache#3 0x7fdc7836483b in make_child /home/yle/src/ylavic/httpd/server/mpm/event/event.c:3421
    apache#4 0x7fdc78364b89 in startup_children /home/yle/src/ylavic/httpd/server/mpm/event/event.c:3444
    apache#5 0x7fdc78368abc in event_run /home/yle/src/ylavic/httpd/server/mpm/event/event.c:3932
    apache#6 0x5596139b6d18 in ap_run_mpm /home/yle/src/ylavic/httpd/server/mpm_common.c:101
    apache#7 0x55961399098b in main /home/yle/src/ylavic/httpd/server/main.c:880
    apache#8 0x7fdc7c2627ec in __libc_start_main ../csu/libc-start.c:332

SUMMARY: AddressSanitizer: heap-use-after-free /home/yle/src/ylavic/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
  • Loading branch information
ylavic committed Feb 7, 2022
1 parent c3ba313 commit 6ba9a7e
Showing 1 changed file with 50 additions and 22 deletions.
72 changes: 50 additions & 22 deletions modules/http2/h2_bucket_beam.c
Expand Up @@ -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;

Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 6ba9a7e

Please sign in to comment.