Commit b8931162 authored by rtenneti@chromium.org's avatar rtenneti@chromium.org

SPDY - Added unit tests for use after free of SpdySession

and unit test to test yielding + async during Read.

A test that crashes when scoped_refptr to SpdySession deleted
from DoLoop.

Added the following test per rch to test interactions of
yielding + async during Read.

Do the following MockReads and verify all the data is consumed
and SpdySession has yielded during Read.
  SYNCHRONOUS 8K
  SYNCHRONOUS 8K
  SYNCHRONOUS 8K
  SYNCHRONOUS 2K
  ASYNC 8K
  SYNCHRONOUS 8K
  SYNCHRONOUS 8K
  SYNCHRONOUS 8K
  SYNCHRONOUS 2K

R=rch@chromium.org
BUG=175574,166958
TEST=net unit tests

Review URL: https://chromiumcodereview.appspot.com/12207122

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182012 0039d316-1c4b-4281-b951-d872f2087c98
parent 29478705
......@@ -1807,16 +1807,18 @@ TEST_F(SpdySessionSpdy2Test, TestYieldingDuringReadData) {
EXPECT_TRUE(data.at_read_eof());
}
// Test that SpdySession::DoRead yields while reading the data. This test makes
// 4 reads of kMaxReadBytes / 4 (-spdy_data_frame_size), one read of
// kMaxReadBytes (-spdy_data_frame_size), and some reads of kMaxReadBytes + 8k
// (-spdy_data_frame_size), bytes of data available on the socket for reading.
// It then verifies that DoRead has yielded even though there is data available
// for it to read (i.e, socket()->Read didn't return ERR_IO_PENDING during
// socket reads). Also verifies that SpdySession reads only
// SpdySession::kReadBufferSize data from the underlying transport.
// TODO(rtennti): Make this test work after fixes to DeterministicSocketData.
TEST_F(SpdySessionSpdy2Test, DISABLED_TestYieldingDuringLargeReadData) {
// Test that SpdySession::DoRead() tests interactions of yielding + async,
// by doing the following MockReads.
//
// MockRead of SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K
// ASYNC 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K.
//
// The above reads 26K synchronously. Since that is less that 32K, we will
// attempt to read again. However, that DoRead() will return ERR_IO_PENDING
// (because of async read), so DoRead() will yield. When we come back, DoRead()
// will read the results from the async read, and rest of the data
// synchronously.
TEST_F(SpdySessionSpdy2Test, TestYieldingDuringAsyncReadData) {
MockConnect connect_data(SYNCHRONOUS, OK);
BufferedSpdyFramer framer(2, false);
......@@ -1828,36 +1830,24 @@ TEST_F(SpdySessionSpdy2Test, DISABLED_TestYieldingDuringLargeReadData) {
// Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size).
ASSERT_EQ(32 * 1024, kMaxReadBytes);
TestDataStream test_stream;
const int kSmallPayloadSize = kMaxReadBytes / 4 - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> small_payload(
new net::IOBuffer(kSmallPayloadSize));
char* small_payload_data = small_payload->data();
test_stream.GetBytes(small_payload_data, kSmallPayloadSize);
// Build buffer of size kMaxReadBytes - (-spdy_data_frame_size).
TestDataStream test_stream1;
const int kMaxReadBytesPayloadSize = kMaxReadBytes - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> max_bytes_payload(
new net::IOBuffer(kMaxReadBytesPayloadSize));
char* max_bytes_payload_data = max_bytes_payload->data();
test_stream1.GetBytes(max_bytes_payload_data, kMaxReadBytesPayloadSize);
// Build buffer of size kMaxReadBytes + kSmallPayloadSize
// (-spdy_data_frame_size).
const int kEightKPayloadSize = kMaxReadBytes / 4 - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> eightk_payload(
new net::IOBuffer(kEightKPayloadSize));
char* eightk_payload_data = eightk_payload->data();
test_stream.GetBytes(eightk_payload_data, kEightKPayloadSize);
// Build buffer of 2k size.
TestDataStream test_stream2;
const int kLargePayloadSize =
kMaxReadBytes + kSmallPayloadSize - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> large_payload(
new net::IOBuffer(kLargePayloadSize));
char* large_payload_data = large_payload->data();
test_stream2.GetBytes(large_payload_data, kLargePayloadSize);
scoped_ptr<SpdyFrame> small_data_frame(framer.CreateDataFrame(
1, small_payload_data, kSmallPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> max_bytes_data_frame(framer.CreateDataFrame(
1, max_bytes_payload_data, kMaxReadBytesPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> large_data_frame(framer.CreateDataFrame(
1, large_payload_data, kLargePayloadSize, DATA_FLAG_NONE));
const int kTwoKPayloadSize = kEightKPayloadSize - 6 * 1024;
scoped_refptr<net::IOBuffer> twok_payload(
new net::IOBuffer(kTwoKPayloadSize));
char* twok_payload_data = twok_payload->data();
test_stream2.GetBytes(twok_payload_data, kTwoKPayloadSize);
scoped_ptr<SpdyFrame> eightk_data_frame(framer.CreateDataFrame(
1, eightk_payload_data, kEightKPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> twok_data_frame(framer.CreateDataFrame(
1, twok_payload_data, kTwoKPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> finish_data_frame(framer.CreateDataFrame(
1, "h", 1, DATA_FLAG_FIN));
......@@ -1865,14 +1855,17 @@ TEST_F(SpdySessionSpdy2Test, DISABLED_TestYieldingDuringLargeReadData) {
MockRead reads[] = {
CreateMockRead(*resp1, 1),
CreateMockRead(*small_data_frame, 2),
CreateMockRead(*small_data_frame, 3, SYNCHRONOUS),
CreateMockRead(*small_data_frame, 4, SYNCHRONOUS),
CreateMockRead(*small_data_frame, 5, SYNCHRONOUS),
CreateMockRead(*max_bytes_data_frame, 6),
CreateMockRead(*large_data_frame, 7, SYNCHRONOUS),
CreateMockRead(*finish_data_frame, 8, SYNCHRONOUS),
MockRead(ASYNC, 0, 9) // EOF
CreateMockRead(*eightk_data_frame, 2),
CreateMockRead(*eightk_data_frame, 3, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 4, SYNCHRONOUS),
CreateMockRead(*twok_data_frame, 5, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 6, ASYNC),
CreateMockRead(*eightk_data_frame, 7, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 8, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 9, SYNCHRONOUS),
CreateMockRead(*twok_data_frame, 10, SYNCHRONOUS),
CreateMockRead(*finish_data_frame, 11, SYNCHRONOUS),
MockRead(ASYNC, 0, 12) // EOF
};
// Create SpdySession and SpdyStream and send the request.
......@@ -1907,7 +1900,7 @@ TEST_F(SpdySessionSpdy2Test, DISABLED_TestYieldingDuringLargeReadData) {
EXPECT_TRUE(spdy_stream1->HasUrl());
spdy_stream1->SendRequest(false);
// Set up the TaskObserver to verify SpdySession::DoRead posts a task.
// Set up the TaskObserver to monitor SpdySession::DoRead posting of tasks.
SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
// Run until 1st read.
......@@ -1917,7 +1910,7 @@ TEST_F(SpdySessionSpdy2Test, DISABLED_TestYieldingDuringLargeReadData) {
EXPECT_EQ(0u, observer.executed_count());
// Read all the data and verify SpdySession::DoRead has posted a task.
data.RunFor(10);
data.RunFor(12);
// Verify task observer's executed_count is 1, which indicates DoRead has
// posted only one task and thus yielded though there is data available for
......@@ -1927,4 +1920,82 @@ TEST_F(SpdySessionSpdy2Test, DISABLED_TestYieldingDuringLargeReadData) {
EXPECT_TRUE(data.at_read_eof());
}
// Send a GoAway frame when SpdySession is in DoLoop. If scoped_refptr to
// <SpdySession> is deleted from SpdySession::DoLoop(), we get a crash because
// GoAway could delete the SpdySession from the SpdySessionPool and the last
// reference to SpdySession.
TEST_F(SpdySessionSpdy2Test, GoAwayWhileInDoLoop) {
MockConnect connect_data(SYNCHRONOUS, OK);
BufferedSpdyFramer framer(2, false);
scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM));
MockWrite writes[] = {
CreateMockWrite(*req1, 0),
};
scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1));
scoped_ptr<SpdyFrame> body1(ConstructSpdyBodyFrame(1, true));
scoped_ptr<SpdyFrame> goaway(ConstructSpdyGoAway());
MockRead reads[] = {
CreateMockRead(*resp1, 1),
CreateMockRead(*body1, 2),
CreateMockRead(*goaway, 3),
MockRead(ASYNC, 0, 4) // EOF
};
// Create SpdySession and SpdyStream and send the request.
DeterministicSocketData data(reads, arraysize(reads),
writes, arraysize(writes));
data.set_connect_data(connect_data);
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
CreateDeterministicNetworkSession();
scoped_refptr<SpdySession> session = CreateInitializedSession();
scoped_refptr<SpdyStream> spdy_stream1;
TestCompletionCallback callback1;
GURL url1("http://www.google.com");
EXPECT_EQ(OK, session->CreateStream(url1, MEDIUM, &spdy_stream1,
BoundNetLog(), callback1.callback()));
EXPECT_EQ(0u, spdy_stream1->stream_id());
scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
(*headers)["method"] = "GET";
(*headers)["scheme"] = url1.scheme();
(*headers)["host"] = url1.host();
(*headers)["url"] = url1.path();
(*headers)["version"] = "HTTP/1.1";
spdy_stream1->set_spdy_headers(headers.Pass());
EXPECT_TRUE(spdy_stream1->HasUrl());
spdy_stream1->SendRequest(false);
// Run until 1st read.
EXPECT_EQ(0u, spdy_stream1->stream_id());
data.RunFor(1);
EXPECT_EQ(1u, spdy_stream1->stream_id());
// Drop the reference to the session.
session = NULL;
// Run until GoAway.
data.RunFor(2);
// Drop the reference to the stream which deletes its reference to the
// SpdySession. Only references to SpdySession are held by DoLoop and
// SpdySessionPool. If DoLoop doesn't hold the reference, we get a crash if
// SpdySession is deleted from the SpdySessionPool.
spdy_stream1 = NULL;
data.RunFor(2);
EXPECT_TRUE(data.at_write_eof());
EXPECT_TRUE(data.at_read_eof());
}
} // namespace net
......@@ -1936,16 +1936,18 @@ TEST_F(SpdySessionSpdy3Test, TestYieldingDuringReadData) {
EXPECT_TRUE(data.at_read_eof());
}
// Test that SpdySession::DoRead yields while reading the data. This test makes
// 4 reads of kMaxReadBytes / 4 (-spdy_data_frame_size), one read of
// kMaxReadBytes (-spdy_data_frame_size), and some reads of kMaxReadBytes + 8k
// (-spdy_data_frame_size), bytes of data available on the socket for reading.
// It then verifies that DoRead has yielded even though there is data available
// for it to read (i.e, socket()->Read didn't return ERR_IO_PENDING during
// socket reads). Also verifies that SpdySession reads only
// SpdySession::kReadBufferSize data from the underlying transport.
// TODO(rtennti): Make this test work after fixes to DeterministicSocketData.
TEST_F(SpdySessionSpdy3Test, DISABLED_TestYieldingDuringLargeReadData) {
// Test that SpdySession::DoRead() tests interactions of yielding + async,
// by doing the following MockReads.
//
// MockRead of SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K
// ASYNC 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K.
//
// The above reads 26K synchronously. Since that is less that 32K, we will
// attempt to read again. However, that DoRead() will return ERR_IO_PENDING
// (because of async read), so DoRead() will yield. When we come back, DoRead()
// will read the results from the async read, and rest of the data
// synchronously.
TEST_F(SpdySessionSpdy3Test, TestYieldingDuringAsyncReadData) {
MockConnect connect_data(SYNCHRONOUS, OK);
BufferedSpdyFramer framer(3, false);
......@@ -1957,36 +1959,24 @@ TEST_F(SpdySessionSpdy3Test, DISABLED_TestYieldingDuringLargeReadData) {
// Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size).
ASSERT_EQ(32 * 1024, kMaxReadBytes);
TestDataStream test_stream;
const int kSmallPayloadSize = kMaxReadBytes / 4 - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> small_payload(
new net::IOBuffer(kSmallPayloadSize));
char* small_payload_data = small_payload->data();
test_stream.GetBytes(small_payload_data, kSmallPayloadSize);
// Build buffer of size kMaxReadBytes - (-spdy_data_frame_size).
TestDataStream test_stream1;
const int kMaxReadBytesPayloadSize = kMaxReadBytes - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> max_bytes_payload(
new net::IOBuffer(kMaxReadBytesPayloadSize));
char* max_bytes_payload_data = max_bytes_payload->data();
test_stream1.GetBytes(max_bytes_payload_data, kMaxReadBytesPayloadSize);
// Build buffer of size kMaxReadBytes + kSmallPayloadSize
// (-spdy_data_frame_size).
const int kEightKPayloadSize = kMaxReadBytes / 4 - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> eightk_payload(
new net::IOBuffer(kEightKPayloadSize));
char* eightk_payload_data = eightk_payload->data();
test_stream.GetBytes(eightk_payload_data, kEightKPayloadSize);
// Build buffer of 2k size.
TestDataStream test_stream2;
const int kLargePayloadSize =
kMaxReadBytes + kSmallPayloadSize - SpdyDataFrame::size();
scoped_refptr<net::IOBuffer> large_payload(
new net::IOBuffer(kLargePayloadSize));
char* large_payload_data = large_payload->data();
test_stream2.GetBytes(large_payload_data, kLargePayloadSize);
scoped_ptr<SpdyFrame> small_data_frame(framer.CreateDataFrame(
1, small_payload_data, kSmallPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> max_bytes_data_frame(framer.CreateDataFrame(
1, max_bytes_payload_data, kMaxReadBytesPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> large_data_frame(framer.CreateDataFrame(
1, large_payload_data, kLargePayloadSize, DATA_FLAG_NONE));
const int kTwoKPayloadSize = kEightKPayloadSize - 6 * 1024;
scoped_refptr<net::IOBuffer> twok_payload(
new net::IOBuffer(kTwoKPayloadSize));
char* twok_payload_data = twok_payload->data();
test_stream2.GetBytes(twok_payload_data, kTwoKPayloadSize);
scoped_ptr<SpdyFrame> eightk_data_frame(framer.CreateDataFrame(
1, eightk_payload_data, kEightKPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> twok_data_frame(framer.CreateDataFrame(
1, twok_payload_data, kTwoKPayloadSize, DATA_FLAG_NONE));
scoped_ptr<SpdyFrame> finish_data_frame(framer.CreateDataFrame(
1, "h", 1, DATA_FLAG_FIN));
......@@ -1994,14 +1984,17 @@ TEST_F(SpdySessionSpdy3Test, DISABLED_TestYieldingDuringLargeReadData) {
MockRead reads[] = {
CreateMockRead(*resp1, 1),
CreateMockRead(*small_data_frame, 2),
CreateMockRead(*small_data_frame, 3, SYNCHRONOUS),
CreateMockRead(*small_data_frame, 4, SYNCHRONOUS),
CreateMockRead(*small_data_frame, 5, SYNCHRONOUS),
CreateMockRead(*max_bytes_data_frame, 6),
CreateMockRead(*large_data_frame, 7, SYNCHRONOUS),
CreateMockRead(*finish_data_frame, 8, SYNCHRONOUS),
MockRead(ASYNC, 0, 9) // EOF
CreateMockRead(*eightk_data_frame, 2),
CreateMockRead(*eightk_data_frame, 3, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 4, SYNCHRONOUS),
CreateMockRead(*twok_data_frame, 5, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 6, ASYNC),
CreateMockRead(*eightk_data_frame, 7, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 8, SYNCHRONOUS),
CreateMockRead(*eightk_data_frame, 9, SYNCHRONOUS),
CreateMockRead(*twok_data_frame, 10, SYNCHRONOUS),
CreateMockRead(*finish_data_frame, 11, SYNCHRONOUS),
MockRead(ASYNC, 0, 12) // EOF
};
// Create SpdySession and SpdyStream and send the request.
......@@ -2036,7 +2029,7 @@ TEST_F(SpdySessionSpdy3Test, DISABLED_TestYieldingDuringLargeReadData) {
EXPECT_TRUE(spdy_stream1->HasUrl());
spdy_stream1->SendRequest(false);
// Set up the TaskObserver to verify SpdySession::DoRead posts a task.
// Set up the TaskObserver to monitor SpdySession::DoRead posting of tasks.
SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
// Run until 1st read.
......@@ -2046,7 +2039,7 @@ TEST_F(SpdySessionSpdy3Test, DISABLED_TestYieldingDuringLargeReadData) {
EXPECT_EQ(0u, observer.executed_count());
// Read all the data and verify SpdySession::DoRead has posted a task.
data.RunFor(10);
data.RunFor(12);
// Verify task observer's executed_count is 1, which indicates DoRead has
// posted only one task and thus yielded though there is data available for
......@@ -2056,4 +2049,82 @@ TEST_F(SpdySessionSpdy3Test, DISABLED_TestYieldingDuringLargeReadData) {
EXPECT_TRUE(data.at_read_eof());
}
// Send a GoAway frame when SpdySession is in DoLoop. If scoped_refptr to
// <SpdySession> is deleted from SpdySession::DoLoop(), we get a crash because
// GoAway could delete the SpdySession from the SpdySessionPool and the last
// reference to SpdySession.
TEST_F(SpdySessionSpdy3Test, GoAwayWhileInDoLoop) {
MockConnect connect_data(SYNCHRONOUS, OK);
BufferedSpdyFramer framer(3, false);
scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM));
MockWrite writes[] = {
CreateMockWrite(*req1, 0),
};
scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1));
scoped_ptr<SpdyFrame> body1(ConstructSpdyBodyFrame(1, true));
scoped_ptr<SpdyFrame> goaway(ConstructSpdyGoAway());
MockRead reads[] = {
CreateMockRead(*resp1, 1),
CreateMockRead(*body1, 2),
CreateMockRead(*goaway, 3),
MockRead(ASYNC, 0, 4) // EOF
};
// Create SpdySession and SpdyStream and send the request.
DeterministicSocketData data(reads, arraysize(reads),
writes, arraysize(writes));
data.set_connect_data(connect_data);
session_deps_.host_resolver->set_synchronous_mode(true);
session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
CreateDeterministicNetworkSession();
scoped_refptr<SpdySession> session = CreateInitializedSession();
scoped_refptr<SpdyStream> spdy_stream1;
TestCompletionCallback callback1;
GURL url1("http://www.google.com");
EXPECT_EQ(OK, session->CreateStream(url1, MEDIUM, &spdy_stream1,
BoundNetLog(), callback1.callback()));
EXPECT_EQ(0u, spdy_stream1->stream_id());
scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
(*headers)[":method"] = "GET";
(*headers)[":scheme"] = url1.scheme();
(*headers)[":host"] = url1.host();
(*headers)[":path"] = url1.path();
(*headers)[":version"] = "HTTP/1.1";
spdy_stream1->set_spdy_headers(headers.Pass());
EXPECT_TRUE(spdy_stream1->HasUrl());
spdy_stream1->SendRequest(false);
// Run until 1st read.
EXPECT_EQ(0u, spdy_stream1->stream_id());
data.RunFor(1);
EXPECT_EQ(1u, spdy_stream1->stream_id());
// Drop the reference to the session.
session = NULL;
// Run until GoAway.
data.RunFor(2);
// Drop the reference to the stream which deletes its reference to the
// SpdySession. Only references to SpdySession are held by DoLoop and
// SpdySessionPool. If DoLoop doesn't hold the reference, we get a crash if
// SpdySession is deleted from the SpdySessionPool.
spdy_stream1 = NULL;
data.RunFor(2);
EXPECT_TRUE(data.at_write_eof());
EXPECT_TRUE(data.at_read_eof());
}
} // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment