Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(856)

Side by Side Diff: net/spdy/spdy_framer.cc

Issue 10874087: Merge 151720 - net: workaround compression leaks (Closed) Base URL: svn://svn.chromium.org/chrome/branches/1180/src/
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/spdy/spdy_framer.h ('k') | net/spdy/spdy_framer_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // TODO(rtenhove) clean up frame buffer size calculations so that we aren't 5 // TODO(rtenhove) clean up frame buffer size calculations so that we aren't
6 // constantly adding and subtracting header sizes; this is ugly and error- 6 // constantly adding and subtracting header sizes; this is ugly and error-
7 // prone. 7 // prone.
8 8
9 #include "net/spdy/spdy_framer.h" 9 #include "net/spdy/spdy_framer.h"
10 10
(...skipping 643 matching lines...) Expand 10 before | Expand all | Expand 10 after
654 wrote_header = frame->WriteString(it->first); 654 wrote_header = frame->WriteString(it->first);
655 wrote_header &= frame->WriteString(it->second); 655 wrote_header &= frame->WriteString(it->second);
656 } else { 656 } else {
657 wrote_header = frame->WriteStringPiece32(it->first); 657 wrote_header = frame->WriteStringPiece32(it->first);
658 wrote_header &= frame->WriteStringPiece32(it->second); 658 wrote_header &= frame->WriteStringPiece32(it->second);
659 } 659 }
660 DCHECK(wrote_header); 660 DCHECK(wrote_header);
661 } 661 }
662 } 662 }
663 663
664 // These constants are used by zlib to differentiate between normal data and
665 // cookie data. Cookie data is handled specially by zlib when compressing.
666 enum ZDataClass {
667 // kZStandardData is compressed normally, save that it will never match
668 // against any other class of data in the window.
669 kZStandardData = Z_CLASS_STANDARD,
670 // kZCookieData is compressed in its own Huffman blocks and only matches in
671 // its entirety and only against other kZCookieData blocks. Any matches must
672 // be preceeded by a kZStandardData byte, or a semicolon to prevent matching
673 // a suffix. It's assumed that kZCookieData ends in a semicolon to prevent
674 // prefix matches.
675 kZCookieData = Z_CLASS_COOKIE,
676 // kZHuffmanOnlyData is only Huffman compressed - no matches are performed
677 // against the window.
678 kZHuffmanOnlyData = Z_CLASS_HUFFMAN_ONLY,
679 };
680
681 // WriteZ writes |data| to the deflate context |out|. WriteZ will flush as
682 // needed when switching between classes of data.
683 static void WriteZ(const base::StringPiece& data,
684 ZDataClass clas,
685 z_stream* out) {
686 int rv;
687
688 // If we are switching from standard to non-standard data then we need to end
689 // the current Huffman context to avoid it leaking between them.
690 if (out->clas == kZStandardData &&
691 clas != kZStandardData) {
692 out->avail_in = 0;
693 rv = deflate(out, Z_PARTIAL_FLUSH);
694 DCHECK_EQ(Z_OK, rv);
695 DCHECK_EQ(0u, out->avail_in);
696 DCHECK_LT(0u, out->avail_out);
697 }
698
699 out->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data()));
700 out->avail_in = data.size();
701 out->clas = clas;
702 if (clas == kZStandardData) {
703 rv = deflate(out, Z_NO_FLUSH);
704 } else {
705 rv = deflate(out, Z_PARTIAL_FLUSH);
706 }
707 DCHECK_EQ(Z_OK, rv);
708 DCHECK_EQ(0u, out->avail_in);
709 DCHECK_LT(0u, out->avail_out);
710 }
711
712 // WriteLengthZ writes |n| as a |length|-byte, big-endian number to |out|.
713 static void WriteLengthZ(size_t n,
714 unsigned length,
715 ZDataClass clas,
716 z_stream* out) {
717 char buf[4];
718 DCHECK_LE(length, sizeof(buf));
719 for (unsigned i = 1; i <= length; i++) {
720 buf[length - i] = n;
721 n >>= 8;
722 }
723 WriteZ(base::StringPiece(buf, length), clas, out);
724 }
725
726 // WriteHeaderBlockToZ serialises |headers| to the deflate context |z| in a
727 // manner that resists the length of the compressed data from compromising
728 // cookie data.
729 void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers,
730 z_stream* z) const {
731 unsigned length_length = 4;
732 if (spdy_version_ < 3)
733 length_length = 2;
734
735 WriteLengthZ(headers->size(), length_length, kZStandardData, z);
736
737 std::map<std::string, std::string>::const_iterator it;
738 for (it = headers->begin(); it != headers->end(); ++it) {
739 WriteLengthZ(it->first.size(), length_length, kZStandardData, z);
740 WriteZ(it->first, kZStandardData, z);
741
742 if (it->first == "cookie") {
743 // We require the cookie values to end with a semi-colon and (save for
744 // the first) to start with one too. The values are already separated by
745 // semicolons in the header, but there's usually whitespace in there too.
746 // So we accumulate the values without whitespace in |cookie_values| and
747 // write them out, along with a final semicolon to terminate the last
748 // cookie.
749
750 std::string last_cookie;
751 std::vector<base::StringPiece> cookie_values;
752 size_t cookie_length = 0;
753 base::StringPiece cookie_data(it->second);
754
755 for (;;) {
756 while (!cookie_data.empty() &&
757 (cookie_data[0] == ' ' || cookie_data[0] == '\t')) {
758 cookie_data.remove_prefix(1);
759 }
760 if (cookie_data.empty())
761 break;
762
763 size_t i;
764 for (i = 0; i < cookie_data.size(); i++) {
765 if (cookie_data[i] == ';')
766 break;
767 }
768 if (i < cookie_data.size()) {
769 cookie_values.push_back(cookie_data.substr(0, i+1));
770 cookie_length += i+1;
771 cookie_data.remove_prefix(i + 1);
772 } else {
773 last_cookie = cookie_data.as_string() + ";";
774 cookie_values.push_back(last_cookie);
775 cookie_length += last_cookie.size();
776 cookie_data.remove_prefix(i);
777 }
778 }
779
780 WriteLengthZ(cookie_length, length_length, kZStandardData, z);
781 for (std::vector<base::StringPiece>::const_iterator
782 i = cookie_values.begin(); i != cookie_values.end(); i++) {
783 WriteZ(*i, kZCookieData, z);
784 }
785 } else if (it->first == "accept" ||
786 it->first == "accept-charset" ||
787 it->first == "accept-encoding" ||
788 it->first == "accept-language" ||
789 it->first == "host" ||
790 it->first == "version" ||
791 it->first == "method" ||
792 it->first == "scheme" ||
793 it->first == ":host" ||
794 it->first == ":version" ||
795 it->first == ":method" ||
796 it->first == ":scheme" ||
797 it->first == "user-agent") {
798 WriteLengthZ(it->second.size(), length_length, kZStandardData, z);
799 WriteZ(it->second, kZStandardData, z);
800 } else {
801 // Non-whitelisted headers are Huffman compressed in their own block, but
802 // don't match against the window.
803 WriteLengthZ(it->second.size(), length_length, kZStandardData, z);
804 WriteZ(it->second, kZHuffmanOnlyData, z);
805 }
806 }
807
808 z->avail_in = 0;
809 int rv = deflate(z, Z_SYNC_FLUSH);
810 DCHECK_EQ(Z_OK, rv);
811 z->clas = kZStandardData;
812 }
664 813
665 size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, 814 size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
666 size_t len) { 815 size_t len) {
667 DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_); 816 DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_);
668 size_t original_len = len; 817 size_t original_len = len;
669 818
670 if (remaining_control_header_ > 0) { 819 if (remaining_control_header_ > 0) {
671 size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, 820 size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len,
672 remaining_control_header_); 821 remaining_control_header_);
673 remaining_control_header_ -= bytes_read; 822 remaining_control_header_ -= bytes_read;
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after
1046 // Priority is 2 bits for <spdy3, 3 bits otherwise. 1195 // Priority is 2 bits for <spdy3, 3 bits otherwise.
1047 frame.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5)); 1196 frame.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5));
1048 frame.WriteUInt8((spdy_version_ < 3) ? 0 : credential_slot); 1197 frame.WriteUInt8((spdy_version_ < 3) ? 0 : credential_slot);
1049 WriteHeaderBlock(&frame, headers); 1198 WriteHeaderBlock(&frame, headers);
1050 DCHECK_EQ(frame.length(), frame_size); 1199 DCHECK_EQ(frame.length(), frame_size);
1051 1200
1052 scoped_ptr<SpdySynStreamControlFrame> syn_frame( 1201 scoped_ptr<SpdySynStreamControlFrame> syn_frame(
1053 reinterpret_cast<SpdySynStreamControlFrame*>(frame.take())); 1202 reinterpret_cast<SpdySynStreamControlFrame*>(frame.take()));
1054 if (compressed) { 1203 if (compressed) {
1055 return reinterpret_cast<SpdySynStreamControlFrame*>( 1204 return reinterpret_cast<SpdySynStreamControlFrame*>(
1056 CompressControlFrame(*syn_frame.get())); 1205 CompressControlFrame(*syn_frame.get(), headers));
1057 } 1206 }
1058 return syn_frame.release(); 1207 return syn_frame.release();
1059 } 1208 }
1060 1209
1061 SpdySynReplyControlFrame* SpdyFramer::CreateSynReply( 1210 SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(
1062 SpdyStreamId stream_id, 1211 SpdyStreamId stream_id,
1063 SpdyControlFlags flags, 1212 SpdyControlFlags flags,
1064 bool compressed, 1213 bool compressed,
1065 const SpdyHeaderBlock* headers) { 1214 const SpdyHeaderBlock* headers) {
1066 DCHECK_GT(stream_id, 0u); 1215 DCHECK_GT(stream_id, 0u);
(...skipping 12 matching lines...) Expand all
1079 if (spdy_version_ < 3) { 1228 if (spdy_version_ < 3) {
1080 frame.WriteUInt16(0); // Unused 1229 frame.WriteUInt16(0); // Unused
1081 } 1230 }
1082 WriteHeaderBlock(&frame, headers); 1231 WriteHeaderBlock(&frame, headers);
1083 DCHECK_EQ(frame.length(), frame_size); 1232 DCHECK_EQ(frame.length(), frame_size);
1084 1233
1085 scoped_ptr<SpdySynReplyControlFrame> reply_frame( 1234 scoped_ptr<SpdySynReplyControlFrame> reply_frame(
1086 reinterpret_cast<SpdySynReplyControlFrame*>(frame.take())); 1235 reinterpret_cast<SpdySynReplyControlFrame*>(frame.take()));
1087 if (compressed) { 1236 if (compressed) {
1088 return reinterpret_cast<SpdySynReplyControlFrame*>( 1237 return reinterpret_cast<SpdySynReplyControlFrame*>(
1089 CompressControlFrame(*reply_frame.get())); 1238 CompressControlFrame(*reply_frame.get(), headers));
1090 } 1239 }
1091 return reply_frame.release(); 1240 return reply_frame.release();
1092 } 1241 }
1093 1242
1094 SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream( 1243 SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(
1095 SpdyStreamId stream_id, 1244 SpdyStreamId stream_id,
1096 SpdyStatusCodes status) const { 1245 SpdyStatusCodes status) const {
1097 DCHECK_GT(stream_id, 0u); 1246 DCHECK_GT(stream_id, 0u);
1098 DCHECK_EQ(0u, stream_id & ~kStreamIdMask); 1247 DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
1099 DCHECK_NE(status, INVALID); 1248 DCHECK_NE(status, INVALID);
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
1175 if (spdy_version_ < 3) { 1324 if (spdy_version_ < 3) {
1176 frame.WriteUInt16(0); // Unused 1325 frame.WriteUInt16(0); // Unused
1177 } 1326 }
1178 WriteHeaderBlock(&frame, headers); 1327 WriteHeaderBlock(&frame, headers);
1179 DCHECK_EQ(frame.length(), frame_size); 1328 DCHECK_EQ(frame.length(), frame_size);
1180 1329
1181 scoped_ptr<SpdyHeadersControlFrame> headers_frame( 1330 scoped_ptr<SpdyHeadersControlFrame> headers_frame(
1182 reinterpret_cast<SpdyHeadersControlFrame*>(frame.take())); 1331 reinterpret_cast<SpdyHeadersControlFrame*>(frame.take()));
1183 if (compressed) { 1332 if (compressed) {
1184 return reinterpret_cast<SpdyHeadersControlFrame*>( 1333 return reinterpret_cast<SpdyHeadersControlFrame*>(
1185 CompressControlFrame(*headers_frame.get())); 1334 CompressControlFrame(*headers_frame.get(), headers));
1186 } 1335 }
1187 return headers_frame.release(); 1336 return headers_frame.release();
1188 } 1337 }
1189 1338
1190 SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate( 1339 SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate(
1191 SpdyStreamId stream_id, 1340 SpdyStreamId stream_id,
1192 uint32 delta_window_size) const { 1341 uint32 delta_window_size) const {
1193 DCHECK_GT(stream_id, 0u); 1342 DCHECK_GT(stream_id, 0u);
1194 DCHECK_EQ(0u, stream_id & ~kStreamIdMask); 1343 DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
1195 DCHECK_GT(delta_window_size, 0u); 1344 DCHECK_GT(delta_window_size, 0u);
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
1355 } else { 1504 } else {
1356 frame_size = SpdyFrame::kHeaderSize; 1505 frame_size = SpdyFrame::kHeaderSize;
1357 *header_length = frame_size; 1506 *header_length = frame_size;
1358 *payload_length = frame.length(); 1507 *payload_length = frame.length();
1359 *payload = frame.data() + SpdyFrame::kHeaderSize; 1508 *payload = frame.data() + SpdyFrame::kHeaderSize;
1360 } 1509 }
1361 return true; 1510 return true;
1362 } 1511 }
1363 1512
1364 SpdyControlFrame* SpdyFramer::CompressControlFrame( 1513 SpdyControlFrame* SpdyFramer::CompressControlFrame(
1365 const SpdyControlFrame& frame) { 1514 const SpdyControlFrame& frame,
1515 const SpdyHeaderBlock* headers) {
1366 z_stream* compressor = GetHeaderCompressor(); 1516 z_stream* compressor = GetHeaderCompressor();
1367 if (!compressor) 1517 if (!compressor)
1368 return NULL; 1518 return NULL;
1369 1519
1370 int payload_length; 1520 int payload_length;
1371 int header_length; 1521 int header_length;
1372 const char* payload; 1522 const char* payload;
1373 1523
1374 base::StatsCounter compressed_frames("spdy.CompressedFrames"); 1524 base::StatsCounter compressed_frames("spdy.CompressedFrames");
1375 base::StatsCounter pre_compress_bytes("spdy.PreCompressSize"); 1525 base::StatsCounter pre_compress_bytes("spdy.PreCompressSize");
1376 base::StatsCounter post_compress_bytes("spdy.PostCompressSize"); 1526 base::StatsCounter post_compress_bytes("spdy.PostCompressSize");
1377 1527
1378 if (!enable_compression_) 1528 if (!enable_compression_)
1379 return reinterpret_cast<SpdyControlFrame*>(DuplicateFrame(frame)); 1529 return reinterpret_cast<SpdyControlFrame*>(DuplicateFrame(frame));
1380 1530
1381 if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload)) 1531 if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload))
1382 return NULL; 1532 return NULL;
1383 1533
1384 // Create an output frame. 1534 // Create an output frame.
1385 int compressed_max_size = deflateBound(compressor, payload_length); 1535 int compressed_max_size = deflateBound(compressor, payload_length);
1536 // Since we'll be performing lots of flushes when compressing the data,
1537 // zlib's lower bounds may be insufficient.
1538 compressed_max_size *= 2;
1539
1386 size_t new_frame_size = header_length + compressed_max_size; 1540 size_t new_frame_size = header_length + compressed_max_size;
1387 if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) && 1541 if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) &&
1388 spdy_version_ < 3) { 1542 spdy_version_ < 3) {
1389 new_frame_size += 2; 1543 new_frame_size += 2;
1390 } 1544 }
1391 DCHECK_GE(new_frame_size, frame.length() + SpdyFrame::kHeaderSize); 1545 DCHECK_GE(new_frame_size, frame.length() + SpdyFrame::kHeaderSize);
1392 scoped_ptr<SpdyControlFrame> new_frame(new SpdyControlFrame(new_frame_size)); 1546 scoped_ptr<SpdyControlFrame> new_frame(new SpdyControlFrame(new_frame_size));
1393 memcpy(new_frame->data(), frame.data(), 1547 memcpy(new_frame->data(), frame.data(),
1394 frame.length() + SpdyFrame::kHeaderSize); 1548 frame.length() + SpdyFrame::kHeaderSize);
1395 1549
1396 compressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload));
1397 compressor->avail_in = payload_length;
1398 compressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) + 1550 compressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) +
1399 header_length; 1551 header_length;
1400 compressor->avail_out = compressed_max_size; 1552 compressor->avail_out = compressed_max_size;
1401 1553 WriteHeaderBlockToZ(headers, compressor);
1402 // Make sure that all the data we pass to zlib is defined.
1403 // This way, all Valgrind reports on the compressed data are zlib's fault.
1404 (void)VALGRIND_CHECK_MEM_IS_DEFINED(compressor->next_in,
1405 compressor->avail_in);
1406
1407 int rv = deflate(compressor, Z_SYNC_FLUSH);
1408 if (rv != Z_OK) { // How can we know that it compressed everything?
1409 // This shouldn't happen, right?
1410 LOG(WARNING) << "deflate failure: " << rv;
1411 return NULL;
1412 }
1413
1414 int compressed_size = compressed_max_size - compressor->avail_out; 1554 int compressed_size = compressed_max_size - compressor->avail_out;
1415 1555
1416 // We trust zlib. Also, we can't do anything about it. 1556 // We trust zlib. Also, we can't do anything about it.
1417 // See http://www.zlib.net/zlib_faq.html#faq36 1557 // See http://www.zlib.net/zlib_faq.html#faq36
1418 (void)VALGRIND_MAKE_MEM_DEFINED(new_frame->data() + header_length, 1558 (void)VALGRIND_MAKE_MEM_DEFINED(new_frame->data() + header_length,
1419 compressed_size); 1559 compressed_size);
1420 1560
1421 new_frame->set_length( 1561 new_frame->set_length(
1422 header_length + compressed_size - SpdyFrame::kHeaderSize); 1562 header_length + compressed_size - SpdyFrame::kHeaderSize);
1423 1563
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after
1622 } 1762 }
1623 } 1763 }
1624 return stream_id; 1764 return stream_id;
1625 } 1765 }
1626 1766
1627 void SpdyFramer::set_enable_compression(bool value) { 1767 void SpdyFramer::set_enable_compression(bool value) {
1628 enable_compression_ = value; 1768 enable_compression_ = value;
1629 } 1769 }
1630 1770
1631 } // namespace net 1771 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/spdy_framer.h ('k') | net/spdy/spdy_framer_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698