From 421f740164cf53ef9dfb6bc96d0b6a8321b32fd5 Mon Sep 17 00:00:00 2001 From: Anders Bakken Date: Mon, 14 Nov 2016 15:32:00 -0800 Subject: http2: Fix crashes when parent stream gets aborted Closes #1125 --- lib/http2.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/http2.h | 8 +++++++ lib/url.c | 9 ++++--- lib/urldata.h | 7 ++++++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/lib/http2.c b/lib/http2.c index 2ef173140..16684da9d 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -2108,6 +2108,82 @@ CURLcode Curl_http2_switched(struct connectdata *conn, return CURLE_OK; } +void Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child, + bool exclusive) +{ + struct Curl_http2_dep **tail; + struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep)); + dep->data = child; + + if(parent->set.stream_dependents && exclusive) { + struct Curl_http2_dep *node = parent->set.stream_dependents; + while(node) { + node->data->set.stream_depends_on = child; + node = node->next; + } + + tail = &child->set.stream_dependents; + while(*tail) + tail = &(*tail)->next; + + DEBUGASSERT(!*tail); + *tail = parent->set.stream_dependents; + parent->set.stream_dependents = 0; + } + + tail = &parent->set.stream_dependents; + while(*tail) { + (*tail)->data->set.stream_depends_e = FALSE; + tail = &(*tail)->next; + } + + DEBUGASSERT(!*tail); + *tail = dep; + + child->set.stream_depends_on = parent; + child->set.stream_depends_e = exclusive; +} + +void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child) +{ + struct Curl_http2_dep *last = 0; + struct Curl_http2_dep *data = parent->set.stream_dependents; + DEBUGASSERT(child->set.stream_depends_on == parent); + + while(data && data->data != child) { + last = data; + data = data->next; + } + + DEBUGASSERT(data); + + if(data) { + if(last) { + last->next = data->next; + } + else { + parent->set.stream_dependents = data->next; + } + free(data); + } + + child->set.stream_depends_on = 0; + child->set.stream_depends_e = FALSE; +} + +void Curl_http2_cleanup_dependencies(struct Curl_easy *data) +{ + while(data->set.stream_dependents) { + struct Curl_easy *tmp = data->set.stream_dependents->data; + Curl_http2_remove_child(data, tmp); + if(data->set.stream_depends_on) + Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE); + } + + if(data->set.stream_depends_on) + Curl_http2_remove_child(data->set.stream_depends_on, data); +} + #else /* !USE_NGHTTP2 */ /* Satisfy external references even if http2 is not compiled in. */ diff --git a/lib/http2.h b/lib/http2.h index 891753590..f405b3aeb 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -53,6 +53,11 @@ void Curl_http2_setup_conn(struct connectdata *conn); void Curl_http2_setup_req(struct Curl_easy *data); void Curl_http2_done(struct connectdata *conn, bool premature); CURLcode Curl_http2_done_sending(struct connectdata *conn); +void Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child, + bool exclusive); +void Curl_http2_remove_child(struct Curl_easy *parent, + struct Curl_easy *child); +void Curl_http2_cleanup_dependencies(struct Curl_easy *data); #else /* USE_NGHTTP2 */ #define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL @@ -65,6 +70,9 @@ CURLcode Curl_http2_done_sending(struct connectdata *conn); #define Curl_http2_init_userset(x) #define Curl_http2_done(x,y) #define Curl_http2_done_sending(x) +#define Curl_http2_add_child(x, y, z) +#define Curl_http2_remove_child(x, y) +#define Curl_http2_cleanup_dependencies(x) #endif #endif /* HEADER_CURL_HTTP2_H */ diff --git a/lib/url.c b/lib/url.c index c1c3a931b..9ee1e6cec 100644 --- a/lib/url.c +++ b/lib/url.c @@ -464,6 +464,7 @@ CURLcode Curl_close(struct Curl_easy *data) /* this destroys the channel and we cannot use it anymore after this */ Curl_resolver_cleanup(data->state.resolver); + Curl_http2_cleanup_dependencies(data); Curl_convert_close(data); /* No longer a dirty share, if it exists */ @@ -2847,9 +2848,11 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option, return CURLE_NOT_BUILT_IN; #else struct Curl_easy *dep = va_arg(param, struct Curl_easy *); - if(dep && GOOD_EASY_HANDLE(dep)) { - data->set.stream_depends_on = dep; - data->set.stream_depends_e = (option == CURLOPT_STREAM_DEPENDS_E); + if(!dep || GOOD_EASY_HANDLE(dep)) { + if(data->set.stream_depends_on) { + Curl_http2_remove_child(data->set.stream_depends_on, data); + } + Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E)); } break; #endif diff --git a/lib/urldata.h b/lib/urldata.h index ccad53e04..595e797b7 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1280,6 +1280,11 @@ struct auth { be RFC compliant */ }; +struct Curl_http2_dep { + struct Curl_http2_dep *next; + struct Curl_easy *data; +}; + struct UrlState { /* Points to the connection cache */ @@ -1747,6 +1752,8 @@ struct UserDefined { struct Curl_easy *stream_depends_on; bool stream_depends_e; /* set or don't set the Exclusive bit */ int stream_weight; + + struct Curl_http2_dep *stream_dependents; }; struct Names { -- cgit v1.2.3