summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/net_error_list.h3
-rw-r--r--net/flip/flip_framer.cc22
-rw-r--r--net/flip/flip_network_transaction_unittest.cc100
-rw-r--r--net/flip/flip_session.cc22
4 files changed, 131 insertions, 16 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 4003ee0..81ca271 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -291,6 +291,9 @@ NET_ERROR(INVALID_FLIP_STREAM, -335)
// There are no supported proxies in the provided list.
NET_ERROR(NO_SUPPORTED_PROXIES, -336)
+// There is a FLIP protocol framing error.
+NET_ERROR(FLIP_PROTOCOL_ERROR, -337)
+
// The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400)
diff --git a/net/flip/flip_framer.cc b/net/flip/flip_framer.cc
index 69d25c8..5332b25 100644
--- a/net/flip/flip_framer.cc
+++ b/net/flip/flip_framer.cc
@@ -123,9 +123,9 @@ size_t FlipFramer::BytesSafeToRead() const {
}
void FlipFramer::set_error(FlipError error) {
- DCHECK(false);
DCHECK(visitor_);
error_code_ = error;
+ CHANGE_STATE(FLIP_ERROR);
visitor_->OnError(this);
}
@@ -233,7 +233,13 @@ size_t FlipFramer::ProcessCommonHeader(const char* data, size_t len) {
break;
}
remaining_payload_ = current_frame.length();
- DCHECK_LE(remaining_payload_, 1000000u); // Sanity!
+
+ // This is just a sanity check for help debugging early frame errors.
+ if (remaining_payload_ > 1000000u) {
+ LOG(ERROR) <<
+ "Unexpectedly large frame. Flip session is likely corrupt.";
+ }
+
// if we're here, then we have the common header all received.
if (!current_frame.is_control_frame())
CHANGE_STATE(FLIP_FORWARD_STREAM_FRAME);
@@ -278,17 +284,13 @@ void FlipFramer::ProcessControlFrameHeader() {
if (current_control_frame.version() != kFlipProtocolVersion)
set_error(FLIP_UNSUPPORTED_VERSION);
- if (error_code_) {
- CHANGE_STATE(FLIP_ERROR);
- return;
- }
-
remaining_control_payload_ = current_control_frame.length();
- if (remaining_control_payload_ > kControlFrameBufferMaxSize) {
+ if (remaining_control_payload_ > kControlFrameBufferMaxSize)
set_error(FLIP_CONTROL_PAYLOAD_TOO_LARGE);
- CHANGE_STATE(FLIP_ERROR);
+
+ if (error_code_)
return;
- }
+
ExpandControlFrameBuffer(remaining_control_payload_);
CHANGE_STATE(FLIP_CONTROL_FRAME_PAYLOAD);
}
diff --git a/net/flip/flip_network_transaction_unittest.cc b/net/flip/flip_network_transaction_unittest.cc
index 0a6a8f2..1abcdc3 100644
--- a/net/flip/flip_network_transaction_unittest.cc
+++ b/net/flip/flip_network_transaction_unittest.cc
@@ -112,6 +112,19 @@ static const unsigned char kGetSyn[] = {
0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1',
};
+static const unsigned char kGetSynCompressed[] = {
+ 0x80, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x43,
+ 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x78, 0xbb,
+ 0xdf, 0xa2, 0x51, 0xb2, 0x62, 0x60, 0x66, 0x60,
+ 0xcb, 0x05, 0xe6, 0xc3, 0xfc, 0x14, 0x06, 0x66,
+ 0x77, 0xd7, 0x10, 0x06, 0x66, 0x90, 0xa0, 0x58,
+ 0x46, 0x49, 0x49, 0x81, 0x95, 0xbe, 0x3e, 0x30,
+ 0xe2, 0xf5, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52,
+ 0xf5, 0x92, 0xf3, 0x73, 0xf5, 0x19, 0xd8, 0xa1,
+ 0x1a, 0x19, 0x38, 0x60, 0xe6, 0x01, 0x00, 0x00,
+ 0x00, 0xff, 0xff
+};
+
static const unsigned char kGetSynReply[] = {
0x80, 0x01, 0x00, 0x02, // header
0x00, 0x00, 0x00, 0x45,
@@ -249,8 +262,8 @@ class DelayedSocketData : public StaticSocketDataProvider,
class FlipNetworkTransactionTest : public PlatformTest {
protected:
virtual void SetUp() {
- // Disable compression on this test.
- flip::FlipFramer::set_enable_compression_default(false);
+ // By default, all tests turn off compression.
+ EnableCompression(false);
}
virtual void TearDown() {
@@ -268,6 +281,10 @@ class FlipNetworkTransactionTest : public PlatformTest {
HttpResponseInfo response_info;
};
+ void EnableCompression(bool enabled) {
+ flip::FlipFramer::set_enable_compression_default(enabled);
+ }
+
TransactionHelperResult TransactionHelper(const HttpRequestInfo& request,
DelayedSocketData* data,
LoadLog* log) {
@@ -674,6 +691,52 @@ TEST_F(FlipNetworkTransactionTest, InvalidSynReply) {
}
}
+// Verify that we don't crash on some corrupt frames.
+TEST_F(FlipNetworkTransactionTest, CorruptFrameSessionError) {
+ static const unsigned char kSynReplyMassiveLength[] = {
+ 0x80, 0x01, 0x00, 0x02,
+ 0x0f, 0x11, 0x11, 0x26, // This is the length field with a big number
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x06, 's', 't', 'a', 't', 'u', 's',
+ 0x00, 0x03, '2', '0', '0',
+ 0x00, 0x03, 'u', 'r', 'l',
+ 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p',
+ };
+
+ struct SynReplyTests {
+ const unsigned char* syn_reply;
+ int syn_reply_length;
+ } test_cases[] = {
+ { kSynReplyMassiveLength, arraysize(kSynReplyMassiveLength) }
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ MockWrite writes[] = {
+ MockWrite(true, reinterpret_cast<const char*>(kGetSyn),
+ arraysize(kGetSyn)),
+ MockWrite(true, 0, 0) // EOF
+ };
+
+ MockRead reads[] = {
+ MockRead(true, reinterpret_cast<const char*>(test_cases[i].syn_reply),
+ test_cases[i].syn_reply_length),
+ MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame),
+ arraysize(kGetBodyFrame)),
+ MockRead(true, 0, 0) // EOF
+ };
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+ scoped_refptr<DelayedSocketData> data(
+ new DelayedSocketData(reads, 1, writes));
+ TransactionHelperResult out = TransactionHelper(request, data.get(), NULL);
+ EXPECT_EQ(ERR_FLIP_PROTOCOL_ERROR, out.rv);
+ }
+}
+
TEST_F(FlipNetworkTransactionTest, DISABLED_ServerPush) {
// Reply with the X-Associated-Content header.
static const unsigned char syn_reply[] = {
@@ -921,6 +984,39 @@ TEST_F(FlipNetworkTransactionTest, DISABLED_ConnectFailure) {
}
}
+// In this test, we enable compression, but get a uncompressed SynReply from
+// the server. Verify that teardown is all clean.
+TEST_F(FlipNetworkTransactionTest, DecompressFailureOnSynReply) {
+ MockWrite writes[] = {
+ MockWrite(true, reinterpret_cast<const char*>(kGetSynCompressed),
+ arraysize(kGetSynCompressed)),
+ MockWrite(true, 0, 0) // EOF
+ };
+
+ MockRead reads[] = {
+ MockRead(true, reinterpret_cast<const char*>(kGetSynReply),
+ arraysize(kGetSynReply)),
+ MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame),
+ arraysize(kGetBodyFrame)),
+ MockRead(true, 0, 0) // EOF
+ };
+
+ // For this test, we turn on the normal compression.
+ EnableCompression(true);
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+ scoped_refptr<DelayedSocketData> data(
+ new DelayedSocketData(reads, 1, writes));
+ TransactionHelperResult out = TransactionHelper(request, data.get(), NULL);
+ EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv);
+ data->Reset();
+
+ EnableCompression(false);
+}
+
// Test that the LoadLog contains good data for a simple GET request.
TEST_F(FlipNetworkTransactionTest, LoadLog) {
MockWrite writes[] = {
diff --git a/net/flip/flip_session.cc b/net/flip/flip_session.cc
index b5226b3..d91c37c 100644
--- a/net/flip/flip_session.cc
+++ b/net/flip/flip_session.cc
@@ -538,6 +538,12 @@ void FlipSession::OnReadComplete(int bytes_read) {
return;
}
+ // The FlipFramer will use callbacks onto |this| as it parses frames.
+ // When errors occur, those callbacks can lead to teardown of all references
+ // to |this|, so maintain a reference to self during this call for safe
+ // cleanup.
+ scoped_refptr<FlipSession> self(this);
+
char *data = read_buffer_->data();
while (bytes_read &&
flip_framer_.error_code() == flip::FlipFramer::FLIP_NO_ERROR) {
@@ -548,7 +554,8 @@ void FlipSession::OnReadComplete(int bytes_read) {
flip_framer_.Reset();
}
- ReadSocket();
+ if (state_ != CLOSED)
+ ReadSocket();
}
void FlipSession::OnWriteComplete(int result) {
@@ -607,9 +614,16 @@ void FlipSession::ReadSocket() {
if (read_pending_)
return;
+ if (state_ == CLOSED) {
+ NOTREACHED();
+ return;
+ }
+
+ CHECK(connection_.get());
+ CHECK(connection_->socket());
int bytes_read = connection_->socket()->Read(read_buffer_.get(),
- kReadBufferSize,
- &read_callback_);
+ kReadBufferSize,
+ &read_callback_);
switch (bytes_read) {
case 0:
// Socket is closed!
@@ -819,7 +833,7 @@ void FlipSession::GetSSLInfo(SSLInfo* ssl_info) {
void FlipSession::OnError(flip::FlipFramer* framer) {
LOG(ERROR) << "FlipSession error: " << framer->error_code();
- CloseSessionOnError(net::ERR_UNEXPECTED);
+ CloseSessionOnError(net::ERR_FLIP_PROTOCOL_ERROR);
}
void FlipSession::OnStreamFrameData(flip::FlipStreamId stream_id,