Commit 421f7401 authored by Anders Bakken's avatar Anders Bakken Committed by Daniel Stenberg
Browse files

http2: Fix crashes when parent stream gets aborted

Closes #1125
parent a387d881
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
@@ -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. */
+8 −0
Original line number Diff line number Diff line
@@ -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 */
+6 −3
Original line number Diff line number Diff line
@@ -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
+7 −0
Original line number Diff line number Diff line
@@ -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 {