Commit 7e0e73a9 authored by Stefan Eissing's avatar Stefan Eissing
Browse files

Merge of r1766308 from trunk:

mod_http2: fixed potential crash in beam memory handling introduced in 1.7.x changes


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1766311 13f79535-47bb-0310-9956-ffa450edef68
parent 5e7b6680
Loading
Loading
Loading
Loading
+52 −35
Original line number Diff line number Diff line
@@ -139,25 +139,6 @@ static apr_bucket *h2_beam_bucket_create(h2_bucket_beam *beam,
    return h2_beam_bucket_make(b, beam, bred, n);
}

/*static apr_status_t beam_bucket_setaside(apr_bucket *b, apr_pool_t *pool)
{
    apr_status_t status = APR_SUCCESS;
    h2_beam_proxy *d = b->data;
    if (d->bred) {
        const char *data;
        apr_size_t len;
        
        status = apr_bucket_read(d->bred, &data, &len, APR_BLOCK_READ);
        if (status == APR_SUCCESS) {
            b = apr_bucket_heap_make(b, (char *)data + b->start, b->length, NULL);
            if (b == NULL) {
                return APR_ENOMEM;
            }
        }
    }
    return status;
}*/

const apr_bucket_type_t h2_bucket_type_beam = {
    "BEAM", 5, APR_BUCKET_DATA,
    beam_bucket_destroy,
@@ -431,11 +412,12 @@ static apr_status_t beam_close(h2_bucket_beam *beam)
    return APR_SUCCESS;
}

static apr_status_t beam_cleanup(void *data)
static void beam_set_red_pool(h2_bucket_beam *beam, apr_pool_t *pool);

static apr_status_t beam_red_cleanup(void *data)
{
    h2_bucket_beam *beam = data;
    
    beam_close(beam);
    r_purge_reds(beam);
    h2_blist_cleanup(&beam->red);
    report_consumption(beam, 0);
@@ -447,38 +429,64 @@ static apr_status_t beam_cleanup(void *data)
    }
    h2_blist_cleanup(&beam->purge);
    h2_blist_cleanup(&beam->hold);
    beam_set_red_pool(beam, NULL); 
    
    return APR_SUCCESS;
}

static void beam_set_red_pool(h2_bucket_beam *beam, apr_pool_t *pool) 
{
    if (beam->red_pool != pool) {
        if (beam->red_pool) {
            apr_pool_cleanup_kill(beam->red_pool, beam, beam_red_cleanup);
        }
        beam->red_pool = pool;
        if (beam->red_pool) {
            apr_pool_pre_cleanup_register(beam->red_pool, beam, beam_red_cleanup);
        }
    }
}

static apr_status_t beam_cleanup(void *data)
{
    h2_bucket_beam *beam = data;
    apr_status_t status;
    
    beam_close(beam);
    if (beam->red_pool) {
        status = beam_red_cleanup(beam);
    }
    return APR_SUCCESS;
}

apr_status_t h2_beam_destroy(h2_bucket_beam *beam)
{
    apr_pool_cleanup_kill(beam->red_pool, beam, beam_cleanup);
    apr_pool_cleanup_kill(beam->pool, beam, beam_cleanup);
    return beam_cleanup(beam);
}

apr_status_t h2_beam_create(h2_bucket_beam **pbeam, apr_pool_t *red_pool, 
apr_status_t h2_beam_create(h2_bucket_beam **pbeam, apr_pool_t *pool, 
                            int id, const char *tag, 
                            apr_size_t max_buf_size)
{
    h2_bucket_beam *beam;
    apr_status_t status = APR_SUCCESS;
    
    beam = apr_pcalloc(red_pool, sizeof(*beam));
    beam = apr_pcalloc(pool, sizeof(*beam));
    if (!beam) {
        return APR_ENOMEM;
    }

    beam->id = id;
    beam->tag = tag;
    beam->pool = pool;
    H2_BLIST_INIT(&beam->red);
    H2_BLIST_INIT(&beam->hold);
    H2_BLIST_INIT(&beam->purge);
    H2_BPROXY_LIST_INIT(&beam->proxies);
    beam->red_pool = red_pool;
    beam->max_buf_size = max_buf_size;
    apr_pool_pre_cleanup_register(pool, beam, beam_cleanup);

    apr_pool_pre_cleanup_register(red_pool, beam, beam_cleanup);
    *pbeam = beam;
    
    return status;
@@ -609,10 +617,20 @@ apr_status_t h2_beam_shutdown(h2_bucket_beam *beam, apr_read_type_e block,
    return status;
}

static void move_to_hold(h2_bucket_beam *beam, 
                         apr_bucket_brigade *red_brigade)
{
    apr_bucket *b;
    while (red_brigade && !APR_BRIGADE_EMPTY(red_brigade)) {
        b = APR_BRIGADE_FIRST(red_brigade);
        APR_BUCKET_REMOVE(b);
        H2_BLIST_INSERT_TAIL(&beam->red, b);
    }
}

static apr_status_t append_bucket(h2_bucket_beam *beam, 
                                  apr_bucket *bred,
                                  apr_read_type_e block,
                                  apr_pool_t *pool,
                                  h2_beam_lock *pbl)
{
    const char *data;
@@ -659,14 +677,11 @@ static apr_status_t append_bucket(h2_bucket_beam *beam,
     * its pool/bucket_alloc from a foreign thread and that will
     * corrupt. */
    status = APR_ENOTIMPL;
    if (beam->closed && bred->length > 0) {
        status = APR_EOF;
    }
    else if (APR_BUCKET_IS_TRANSIENT(bred)) {
    if (APR_BUCKET_IS_TRANSIENT(bred)) {
        /* this takes care of transient buckets and converts them
         * into heap ones. Other bucket types might or might not be
         * affected by this. */
        status = apr_bucket_setaside(bred, pool);
        status = apr_bucket_setaside(bred, beam->red_pool);
    }
    else if (APR_BUCKET_IS_HEAP(bred)) {
        /* For heap buckets read from a green thread is fine. The
@@ -702,7 +717,7 @@ static apr_status_t append_bucket(h2_bucket_beam *beam,
        }
        if (can_beam) {
            beam->last_beamed = fd;
            status = apr_bucket_setaside(bred, pool);
            status = apr_bucket_setaside(bred, beam->red_pool);
        }
        /* else: enter ENOTIMPL case below */
    }
@@ -722,7 +737,7 @@ static apr_status_t append_bucket(h2_bucket_beam *beam,
        }
        status = apr_bucket_read(bred, &data, &len, APR_BLOCK_READ);
        if (status == APR_SUCCESS) {
            status = apr_bucket_setaside(bred, pool);
            status = apr_bucket_setaside(bred, beam->red_pool);
        }
    }
    
@@ -750,6 +765,7 @@ apr_status_t h2_beam_send(h2_bucket_beam *beam,
        r_purge_reds(beam);
        
        if (beam->aborted) {
            move_to_hold(beam, red_brigade);
            status = APR_ECONNABORTED;
        }
        else if (red_brigade) {
@@ -757,7 +773,8 @@ apr_status_t h2_beam_send(h2_bucket_beam *beam,
            while (!APR_BRIGADE_EMPTY(red_brigade)
                   && status == APR_SUCCESS) {
                bred = APR_BRIGADE_FIRST(red_brigade);
                status = append_bucket(beam, bred, block, beam->red_pool, &bl);
                beam_set_red_pool(beam, red_brigade->p);
                status = append_bucket(beam, bred, block, &bl);
            }
            report_production(beam, force_report);
            if (beam->m_cond) {
+1 −0
Original line number Diff line number Diff line
@@ -172,6 +172,7 @@ int h2_beam_no_files(void *ctx, h2_bucket_beam *beam, apr_file_t *file);
struct h2_bucket_beam {
    int id;
    const char *tag;
    apr_pool_t *pool;
    h2_blist red;
    h2_blist hold;
    h2_blist purge;
+0 −30
Original line number Diff line number Diff line
@@ -247,8 +247,6 @@ conn_rec *h2_slave_create(conn_rec *master, int slave_id,
    apr_pool_t *pool;
    conn_rec *c;
    void *cfg;
    unsigned int free_bits;
    unsigned long l, lor;
    
    AP_DEBUG_ASSERT(master);
    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
@@ -275,34 +273,6 @@ conn_rec *h2_slave_create(conn_rec *master, int slave_id,
    
    memcpy(c, master, sizeof(conn_rec));
        
    /* Each conn_rec->id is supposed to be unique at a point in time. Since
     * some modules (and maybe external code) uses this id as an identifier
     * for the request_rec they handle, it needs to be unique for slave 
     * connections also.
     * The connection id is generated by the MPM and most MPMs use the formula
     *    id := (child_num * max_threads) + thread_num
     * which means that there is a maximum id of about
     *    idmax := max_child_count * max_threads
     * If we assume 2024 child processes with 2048 threads max, we get
     *    idmax ~= 2024 * 2048 = 2 ** 22
     * On 32 bit systems, we have not much space left, but on 64 bit systems
     * (and higher?) we can use the upper 32 bits without fear of collision.
     * 32 bits is just what we need, since a connection can only handle so
     * many streams. 
     */
    l = master->id;
    lor = 0;
    if (sizeof(unsigned long) >= 8 && l < APR_UINT32_MAX) {
        free_bits = 32;
    }
    else {
        /* Assume that we never encounter ranges stream ids where this 
         * leads to many collisions. With 32 bit longs, we have a hard time
         * to make server wide unique ids. */
        free_bits = 16;
        lor= (1 << 31);
    }
    c->id = (l^((unsigned long)slave_id << free_bits))|lor;
    c->master                 = master;
    c->pool                   = pool;   
    c->conn_config            = ap_create_conn_config(pool);
+5 −0
Original line number Diff line number Diff line
@@ -724,6 +724,11 @@ static int h2_h2_late_fixups(request_rec *r)
            /* check if we copy vs. setaside files in this location */
            task->output.copy_files = h2_config_geti(h2_config_rget(r), 
                                                     H2_CONF_COPY_FILES);
            if (task->output.copy_files) {
                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
                              "h2_slave_out(%s): copy_files on", task->id);
                h2_beam_on_file_beam(task->output.beam, h2_beam_no_files, NULL);
            }
        }
    }
    return DECLINED;
+23 −24
Original line number Diff line number Diff line
@@ -752,12 +752,9 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam)
    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, m->c,
                  "h2_mplx(%s): out open", task->id);
                      
    if (!stream->output) {
        h2_beam_buffer_size_set(beam, m->stream_max_mem);
        h2_beam_timeout_set(beam, m->stream_timeout);
        h2_beam_on_consumed(beam, stream_output_consumed, task);
        h2_beam_on_produced(beam, output_produced, m);
        beamed_count = h2_beam_get_files_beamed(beam);
    h2_beam_on_consumed(stream->output, stream_output_consumed, task);
    h2_beam_on_produced(stream->output, output_produced, m);
    beamed_count = h2_beam_get_files_beamed(stream->output);
    if (m->tx_handles_reserved >= beamed_count) {
        m->tx_handles_reserved -= beamed_count;
    }
@@ -765,12 +762,12 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam)
        m->tx_handles_reserved = 0;
    }
    if (!task->output.copy_files) {
            h2_beam_on_file_beam(beam, can_beam_file, m);
        }
        h2_beam_mutex_set(beam, beam_enter, task->cond, m);
        stream->output = beam;
        h2_beam_on_file_beam(stream->output, can_beam_file, m);
    }
    
    /* time to protect the beam against multi-threaded use */
    h2_beam_mutex_set(stream->output, beam_enter, task->cond, m);
    
    /* we might see some file buckets in the output, see
     * if we have enough handles reserved. */
    check_tx_reservation(m);
@@ -946,7 +943,8 @@ static h2_task *next_stream_task(h2_mplx *m)
            
            slave->sbh = m->c->sbh;
            slave->aborted = 0;
            task = h2_task_create(slave, stream->id, stream->request, stream->input, m);
            task = h2_task_create(slave, stream->id, stream->request, 
                                  stream->input, stream->output, m);
            h2_ihash_add(m->tasks, task);
            
            m->c->keepalives++;
@@ -967,7 +965,10 @@ static h2_task *next_stream_task(h2_mplx *m)
                h2_beam_on_file_beam(stream->input, can_beam_file, m);
                h2_beam_mutex_set(stream->input, beam_enter, task->cond, m);
            }

            if (stream->output) {
                h2_beam_buffer_size_set(stream->output, m->stream_max_mem);
                h2_beam_timeout_set(stream->output, m->stream_timeout);
            }
            ++m->workers_busy;
        }
    }
@@ -1014,7 +1015,6 @@ static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
                      "h2_mplx(%ld): task(%s) done", m->id, task->id);
        out_close(m, task);
        stream = h2_ihash_get(m->streams, task->stream_id);
        
        if (ngn) {
            apr_off_t bytes = 0;
@@ -1041,6 +1041,7 @@ static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
            h2_ngn_shed_done_ngn(m->ngn_shed, task->engine);
        }
        
        stream = h2_ihash_get(m->streams, task->stream_id);
        if (!m->aborted && stream && m->redo_tasks
            && h2_ihash_get(m->redo_tasks, task->stream_id)) {
            /* reset and schedule again */
@@ -1052,10 +1053,6 @@ static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
        
        task->worker_done = 1;
        task->done_at = apr_time_now();
        if (task->output.beam) {
            h2_beam_on_consumed(task->output.beam, NULL, NULL);
            h2_beam_mutex_set(task->output.beam, NULL, NULL, NULL);
        }
        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
                      "h2_mplx(%s): request done, %f ms elapsed", task->id, 
                      (task->done_at - task->started_at) / 1000.0);
@@ -1083,6 +1080,8 @@ static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
                          task->id);
            /* more data will not arrive, resume the stream */
            have_out_data_for(m, stream, 0);
            h2_beam_on_consumed(stream->output, NULL, NULL);
            h2_beam_mutex_set(stream->output, NULL, NULL, NULL);
        }
        else {
            /* stream no longer active, was it placed in hold? */
Loading