Skip to content

Commit 98c584c

Browse files
authored
Merge pull request #355 from firebase/feature/zlibwrapper-fixes
Feature/zlibwrapper fixes
2 parents fae8b6e + c1868a7 commit 98c584c

File tree

3 files changed

+138
-3
lines changed

3 files changed

+138
-3
lines changed

app/rest/tests/zlibwrapper_unittest.cc

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <iostream>
2222
#include <memory>
2323
#include <string>
24+
#include <string_view>
2425
#include <vector>
2526

2627
#include "gtest/gtest.h"
@@ -1047,5 +1048,135 @@ TEST(ZLibWrapperStandalone, ReadPastEndOfWindow) {
10471048
LOG(INFO) << "passed read-past-end-of-window test";
10481049
}
10491050

1051+
TEST(ZLibWrapperStandalone, BytewiseRead) {
1052+
std::string text =
1053+
"v nedrah tundry vydra v getrah tyrit v vedrah yadra kedra";
1054+
size_t text_len = text.size();
1055+
size_t archive_len = ZLib::MinCompressbufSize(text_len);
1056+
std::string archive(archive_len, '\0');
1057+
size_t decompressed_len = text_len + 1;
1058+
std::string decompressed(decompressed_len, '\0');
1059+
size_t decompressed_offset = 0;
1060+
1061+
ZLib compressor;
1062+
compressor.SetGzipHeaderMode();
1063+
int rc = compressor.Compress((Bytef*)archive.data(), &archive_len,
1064+
(Bytef*)text.data(), text_len);
1065+
ASSERT_EQ(rc, Z_OK);
1066+
1067+
ZLib zlib;
1068+
zlib.SetGzipHeaderMode();
1069+
for (size_t i = 0; i < archive_len; ++i) {
1070+
size_t source_len = 1;
1071+
size_t dest_len = decompressed_len - decompressed_offset;
1072+
rc = zlib.UncompressAtMost(
1073+
(Bytef*)decompressed.data() + decompressed_offset, &dest_len,
1074+
(Bytef*)archive.data() + i, &source_len);
1075+
ASSERT_EQ(rc, Z_OK);
1076+
ASSERT_EQ(source_len, 0);
1077+
decompressed_offset += dest_len;
1078+
}
1079+
1080+
ASSERT_TRUE(zlib.IsGzipFooterValid());
1081+
ASSERT_EQ(decompressed_offset, text_len);
1082+
1083+
std::string truncated_output(decompressed.data(), text_len);
1084+
ASSERT_EQ(truncated_output, text);
1085+
1086+
// if we haven't segfaulted by now, we pass
1087+
LOG(INFO) << "passed bytewise-read test";
1088+
}
1089+
1090+
TEST(ZLibWrapperStandaloneTest, TruncatedData) {
1091+
const int kBufferLen = 64;
1092+
std::string uncompressed = "Hello, World!";
1093+
std::string compressed(
1094+
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xf3\x48\xcd\xc9\xc9"
1095+
"\xd7\x51\x08\xcf\x2f\xca\x49\x51\x04\x00\xd0\xc3\x4a\xec\x0d"
1096+
"\x00\x00\x00",
1097+
33);
1098+
1099+
// Verify that "compressed" contains valid gzip data.
1100+
{
1101+
ZLib zlib;
1102+
zlib.SetGzipHeaderMode();
1103+
char uncompbuf[kBufferLen];
1104+
bzero(uncompbuf, kBufferLen);
1105+
uLongf uncomplen = kBufferLen;
1106+
int err = zlib.Uncompress(
1107+
reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
1108+
reinterpret_cast<const Bytef*>(compressed.c_str()), compressed.size());
1109+
ASSERT_EQ(err, Z_OK);
1110+
ASSERT_EQ(uncompressed, std::string_view(uncompbuf, uncomplen));
1111+
}
1112+
1113+
// Test truncated data with ZLib::Uncompress().
1114+
for (int len = compressed.size() - 1; len > 0; len--) {
1115+
SCOPED_TRACE(absl::StrCat("Decompressing first ", len, " out of ",
1116+
compressed.size(), " bytes"));
1117+
ZLib zlib;
1118+
zlib.SetGzipHeaderMode();
1119+
char uncompbuf[kBufferLen];
1120+
bzero(uncompbuf, kBufferLen);
1121+
uLongf uncomplen = kBufferLen;
1122+
int err = zlib.Uncompress(
1123+
reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
1124+
reinterpret_cast<const Bytef*>(compressed.c_str()), len);
1125+
ASSERT_NE(err, Z_OK);
1126+
}
1127+
1128+
// Test truncated data with ZLib::UncompressAtMost() and
1129+
// ZLib::UncompressDone().
1130+
for (int len = compressed.size() - 1; len > 0; len--) {
1131+
SCOPED_TRACE(absl::StrCat("Decompressing first ", len, " out of ",
1132+
compressed.size(), " bytes"));
1133+
ZLib zlib;
1134+
zlib.SetGzipHeaderMode();
1135+
char uncompbuf[kBufferLen];
1136+
bzero(uncompbuf, kBufferLen);
1137+
uLongf uncomplen = kBufferLen;
1138+
uLongf complen = len;
1139+
int err = zlib.UncompressAtMost(
1140+
reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
1141+
reinterpret_cast<const Bytef*>(compressed.c_str()), &complen);
1142+
ASSERT_EQ(err, Z_OK);
1143+
ASSERT_EQ(complen, 0);
1144+
if (uncomplen > 0) {
1145+
EXPECT_THAT(uncompressed,
1146+
testing::StartsWith(absl::string_view(uncompbuf, uncomplen)));
1147+
}
1148+
ASSERT_FALSE(zlib.UncompressChunkDone());
1149+
}
1150+
}
1151+
1152+
TEST(ZLibWrapperStandalone, GzipUncompressedLength) {
1153+
ZLib zlib;
1154+
1155+
// "Hello, World!", compressed.
1156+
std::string hello_world(
1157+
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xf3\x48\xcd\xc9\xc9"
1158+
"\xd7\x51\x08\xcf\x2f\xca\x49\x51\x04\x00\xd0\xc3\x4a\xec\x0d"
1159+
"\x00\x00\x00",
1160+
33);
1161+
EXPECT_EQ(13, zlib.GzipUncompressedLength(
1162+
reinterpret_cast<const Bytef*>(hello_world.c_str()),
1163+
hello_world.size()));
1164+
1165+
// Empty string, "", compressed.
1166+
std::string empty(
1167+
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\x03\x00\x00\x00\x00"
1168+
"\x00\x00\x00\x00\x00",
1169+
20);
1170+
EXPECT_EQ(0,
1171+
zlib.GzipUncompressedLength(
1172+
reinterpret_cast<const Bytef*>(empty.c_str()), empty.size()));
1173+
1174+
std::string bad_data("\x01\x01\x01\x01", 4);
1175+
for (int len = 0; len <= bad_data.size(); len++) {
1176+
EXPECT_EQ(0, zlib.GzipUncompressedLength(
1177+
reinterpret_cast<const Bytef*>(bad_data.c_str()), len));
1178+
}
1179+
}
1180+
10501181
} // namespace
10511182
} // namespace firebase

app/rest/zlibwrapper.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,9 @@ int ZLib::UncompressChunk(Bytef* dest, uLongf* destLen, const Bytef* source,
703703
// mode, we also check the gzip footer to make sure we pass the gzip
704704
// consistency checks. We RETURN true iff both types of checks pass.
705705
bool ZLib::UncompressChunkDone() {
706-
assert(!first_chunk_ && uncomp_init_);
706+
if (first_chunk_ || !uncomp_init_) {
707+
return false;
708+
}
707709
// Make sure we're at the end-of-compressed-data point. This means
708710
// if we call inflate with Z_FINISH we won't consume any input or
709711
// write any output
@@ -784,7 +786,8 @@ int ZLib::Uncompress(Bytef* dest, uLongf* destLen, const Bytef* source,
784786

785787
// read uncompress length from gzip footer
786788
uLongf ZLib::GzipUncompressedLength(const Bytef* source, uLong len) {
787-
assert(len > 4);
789+
if (len <= 4) return 0; // malformed data.
790+
788791
return (static_cast<uLongf>(source[len - 1]) << 24) +
789792
(static_cast<uLongf>(source[len - 2]) << 16) +
790793
(static_cast<uLongf>(source[len - 3]) << 8) +

app/rest/zlibwrapper.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ class ZLib {
126126
int Uncompress(Bytef* dest, uLongf* destLen, const Bytef* source,
127127
uLong sourceLen);
128128

129-
// Get the uncompressed size from the gzip header.
129+
// Get the uncompressed size from the gzip header. Returns 0 if source is too
130+
// short (len < 5).
130131
uLongf GzipUncompressedLength(const Bytef* source, uLong len);
131132

132133
// Special helper function to help uncompress gzipped documents:

0 commit comments

Comments
 (0)