From: Oto Šťáva Date: Wed, 5 Oct 2022 11:30:26 +0000 (+0200) Subject: daemon/http: fix iteration context leaks X-Git-Tag: v6.0.2~42^2~40 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=24f8a70488d22e4ee4448d50f1b7002880c6b079;p=thirdparty%2Fknot-resolver.git daemon/http: fix iteration context leaks --- diff --git a/daemon/http.c b/daemon/http.c index 08e337e99..ee067aa45 100644 --- a/daemon/http.c +++ b/daemon/http.c @@ -72,7 +72,7 @@ struct pl_http_sess_data { struct nghttp2_session *h2; queue_http_stream streams; /* Streams present in the wire buffer. */ - trie_t *stream_write_data; /* Dictionary of stream data that needs to be freed after write. */ + trie_t *stream_write_queues; /* Dictionary of stream data that needs to be freed after write. */ int32_t incomplete_stream; int32_t last_stream; /* The last used stream - mostly the same as incomplete_stream, but can be used after completion for sending HTTP status codes. */ @@ -295,8 +295,8 @@ static int http_send_response(struct pl_http_sess_data *http, int32_t stream_id, max_age_len = asprintf(&max_age, "%s%" PRIu32, directive_max_age, ctx->payload.ttl); kr_require(max_age_len >= 0); - /* TODO: add a per-group option for content-type if we need to - * support protocols other than DNS here */ + /* TODO: add a per-protolayer_grp option for content-type if we + * need to support protocols other than DNS here */ push_nv(&hdrs, MAKE_STATIC_NV("content-type", "application/dns-message")); push_nv(&hdrs, MAKE_STATIC_KEY_NV("content-length", size, size_len)); push_nv(&hdrs, MAKE_STATIC_KEY_NV("cache-control", max_age, max_age_len)); @@ -314,14 +314,28 @@ static int http_send_response(struct pl_http_sess_data *http, int32_t stream_id, /* Keep reference to data, since we need to free it later on. * Due to HTTP/2 flow control, this stream data may be sent at a later point, or not at all. */ - trie_val_t *stream_data_p = trie_get_ins(http->stream_write_data, (char *)&stream_id, sizeof(stream_id)); - if (kr_fails_assert(stream_data_p)) { - kr_log_debug(DOH, "[%p] failed to insert to stream_write_data\n", (void *)h2); - if (ctx) - protolayer_break(ctx, kr_error(EIO)); - return kr_error(EIO); + if (ctx) { + protolayer_iter_ctx_queue_t **ctx_queue = + (protolayer_iter_ctx_queue_t **)trie_get_ins( + http->stream_write_queues, + (char *)&stream_id, sizeof(stream_id)); + + if (kr_fails_assert(ctx_queue)) { + kr_log_debug(DOH, "[%p] failed to insert to stream_write_data\n", (void *)h2); + if (ctx) + protolayer_break(ctx, kr_error(EIO)); + return kr_error(EIO); + } + + if (!*ctx_queue) { + *ctx_queue = malloc(sizeof(**ctx_queue)); + kr_require(*ctx_queue); + queue_init(**ctx_queue); + } + + queue_push(**ctx_queue, ctx); } - *stream_data_p = ctx; + ret = nghttp2_session_send(h2); if(ret) { kr_log_debug(DOH, "[%p] nghttp2_session_send failed: %s\n", (void *)h2, nghttp2_strerror(ret)); @@ -762,7 +776,6 @@ static int on_frame_recv_callback(nghttp2_session *h2, const nghttp2_frame *fram static int on_stream_close_callback(nghttp2_session *h2, int32_t stream_id, uint32_t error_code, void *user_data) { - struct protolayer_iter_ctx *ctx; struct pl_http_sess_data *http = user_data; int ret; @@ -771,9 +784,18 @@ static int on_stream_close_callback(nghttp2_session *h2, int32_t stream_id, if (http->incomplete_stream == stream_id) http_cleanup_stream(http); - ret = trie_del(http->stream_write_data, (char *)&stream_id, sizeof(stream_id), (trie_val_t*)&ctx); - if (ret == KNOT_EOK && ctx) - protolayer_break(ctx, error_code == 0 ? 0 : kr_error(EIO)); + protolayer_iter_ctx_queue_t *queue; + ret = trie_del(http->stream_write_queues, (char *)&stream_id, sizeof(stream_id), (trie_val_t*)&queue); + if (ret == KNOT_EOK && queue) { + uint32_t e = error_code == 0 ? 0 : kr_error(EIO); + while (queue_len(*queue) > 0) { + struct protolayer_iter_ctx *ctx = queue_head(*queue); + protolayer_break(ctx, e); + queue_pop(*queue); + } + queue_deinit(*queue); + free(queue); + } return 0; } @@ -836,7 +858,7 @@ static int pl_http_sess_init(struct protolayer_manager *manager, callbacks, on_stream_close_callback); queue_init(http->streams); - http->stream_write_data = trie_create(NULL); + http->stream_write_queues = trie_create(NULL); http->incomplete_stream = -1; http->last_stream = -1; http->current_method = HTTP_METHOD_NONE; @@ -863,7 +885,17 @@ exit_callbacks: static int stream_write_data_break_err(trie_val_t *val, void *baton) { - protolayer_break(*val, kr_error(EIO)); + protolayer_iter_ctx_queue_t *queue = *val; + if (!queue) + return 0; + + while (queue_len(*queue) > 0) { + struct protolayer_iter_ctx *ctx = queue_head(*queue); + protolayer_break(ctx, kr_error(EIO)); + queue_pop(*queue); + } + queue_deinit(*queue); + free(queue); return 0; } @@ -880,8 +912,8 @@ static int pl_http_sess_deinit(struct protolayer_manager *manager, queue_pop(http->streams); } - trie_apply(http->stream_write_data, stream_write_data_break_err, NULL); - trie_free(http->stream_write_data); + trie_apply(http->stream_write_queues, stream_write_data_break_err, NULL); + trie_free(http->stream_write_queues); http_cleanup_stream(http); queue_deinit(http->streams);