You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bc...@apache.org on 2016/10/20 17:02:56 UTC
[trafficserver] 01/02: TS-4916 Add safety net to avoid
H2-infinite-loop deadlock.
This is an automated email from the ASF dual-hosted git repository.
bcall pushed a commit to branch 7.0.x
in repository https://git-dual.apache.org/repos/asf/trafficserver.git
commit 79cbf777e21ff38186b13c99d0084233ba39607d
Author: Gancho Tenev <gt...@gmail.com>
AuthorDate: Mon Oct 17 15:10:12 2016 -0700
TS-4916 Add safety net to avoid H2-infinite-loop deadlock.
Current Http2ConnectionState implementation uses a memory pool for
instantiating streams and DLL<> stream_list for storing active streams.
Destroying a stream before deleting it from stream_list and then creating
a new one + reusing the same chunk from the memory pool right away always
leads to destroying the DLL structure (deadlocks, inconsistencies).
Added a safety net since the consequences are disastrous.
Until the design/implementation changes it seems less error prone
to (double) delete before destroying (noop if already deleted).
(cherry picked from commit a6f9337f61c980739e08bbefeb5f6409b7dcdac1)
---
proxy/http2/Http2ConnectionState.cc | 17 ++++++++++++++---
proxy/http2/Http2ConnectionState.h | 2 +-
proxy/http2/Http2Stream.cc | 12 ++++++++++++
3 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/proxy/http2/Http2ConnectionState.cc b/proxy/http2/Http2ConnectionState.cc
index 1fd0f88..c14ebf3 100644
--- a/proxy/http2/Http2ConnectionState.cc
+++ b/proxy/http2/Http2ConnectionState.cc
@@ -908,6 +908,10 @@ Http2ConnectionState::create_stream(Http2StreamId new_id)
Http2Stream *new_stream = THREAD_ALLOC_INIT(http2StreamAllocator, this_ethread());
new_stream->init(new_id, client_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE));
+
+ ink_assert(NULL != new_stream);
+ ink_assert(!stream_list.in(new_stream));
+
stream_list.push(new_stream);
if (client_streamid) {
latest_streamid_in = new_id;
@@ -938,6 +942,7 @@ Http2ConnectionState::find_stream(Http2StreamId id) const
if (s->get_id() == id) {
return s;
}
+ ink_assert(s != s->link.next);
}
return NULL;
}
@@ -952,6 +957,7 @@ Http2ConnectionState::restart_streams()
if (!s->is_closed() && s->get_state() == HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE && min(this->client_rwnd, s->client_rwnd) > 0) {
s->send_response_body();
}
+ ink_assert(s != next);
s = next;
}
}
@@ -963,9 +969,10 @@ Http2ConnectionState::cleanup_streams()
while (s) {
Http2Stream *next = s->link.next;
this->delete_stream(s);
+ ink_assert(s != next);
s = next;
}
- ink_assert(stream_list.head == NULL);
+ ink_assert(stream_list.empty());
if (!is_state_closed()) {
ua_session->get_netvc()->add_to_keep_alive_queue();
@@ -973,12 +980,14 @@ Http2ConnectionState::cleanup_streams()
}
}
-void
+bool
Http2ConnectionState::delete_stream(Http2Stream *stream)
{
+ ink_assert(NULL != stream);
+
// If stream has already been removed from the list, just go on
if (!stream_list.in(stream)) {
- return;
+ return false;
}
DebugHttp2Stream(ua_session, stream->get_id(), "Delete stream");
@@ -999,6 +1008,8 @@ Http2ConnectionState::delete_stream(Http2Stream *stream)
stream_list.remove(stream);
stream->initiating_close();
+
+ return true;
}
void
diff --git a/proxy/http2/Http2ConnectionState.h b/proxy/http2/Http2ConnectionState.h
index eb6f930..7f52927 100644
--- a/proxy/http2/Http2ConnectionState.h
+++ b/proxy/http2/Http2ConnectionState.h
@@ -174,7 +174,7 @@ public:
Http2Stream *create_stream(Http2StreamId new_id);
Http2Stream *find_stream(Http2StreamId id) const;
void restart_streams();
- void delete_stream(Http2Stream *stream);
+ bool delete_stream(Http2Stream *stream);
void release_stream(Http2Stream *stream);
void cleanup_streams();
diff --git a/proxy/http2/Http2Stream.cc b/proxy/http2/Http2Stream.cc
index 8b80d1b..4d3a07b 100644
--- a/proxy/http2/Http2Stream.cc
+++ b/proxy/http2/Http2Stream.cc
@@ -675,6 +675,18 @@ Http2Stream::destroy()
}
chunked_handler.clear();
super::destroy();
+
+ // Current Http2ConnectionState implementation uses a memory pool for instantiating streams and DLL<> stream_list for storing
+ // active streams. Destroying a stream before deleting it from stream_list and then creating a new one + reusing the same chunk
+ // from the memory pool right away always leads to destroying the DLL structure (deadlocks, inconsistencies).
+ // The following is meant as a safety net since the consequences are disastrous. Until the design/implementation changes it seems
+ // less error prone to (double) delete before destroying (noop if already deleted).
+ if (parent) {
+ if (static_cast<Http2ClientSession *>(parent)->connection_state.delete_stream(this)) {
+ Warning("Http2Stream was about to be deallocated without removing it from the active stream list");
+ }
+ }
+
THREAD_FREE(this, http2StreamAllocator, this_ethread());
}
--
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.