mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 07:14:10 +08:00
981d4189aa
Bug: crashpad: 326459659,326458942,326459376,326459390,326459417,326458979,326459333,326459016,326458338,326458738,326459156,326459512,326458694 Change-Id: I04724530cbef50a8d3c18f306d16c0bbf3b0815b Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/5512394 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Arthur Wang <wuwang@chromium.org>
181 lines
5.7 KiB
C++
181 lines
5.7 KiB
C++
// Copyright 2017 The Crashpad Authors
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// http://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
#include "util/net/http_body_gzip.h"
|
||
|
||
#include <string.h>
|
||
|
||
#include <algorithm>
|
||
#include <memory>
|
||
#include <string>
|
||
#include <utility>
|
||
|
||
#include "base/containers/heap_array.h"
|
||
#include "base/numerics/safe_conversions.h"
|
||
#include "base/rand_util.h"
|
||
#include "gtest/gtest.h"
|
||
#include "third_party/zlib/zlib_crashpad.h"
|
||
#include "util/misc/zlib.h"
|
||
#include "util/net/http_body.h"
|
||
|
||
namespace crashpad {
|
||
namespace test {
|
||
namespace {
|
||
|
||
class ScopedZlibInflateStream {
|
||
public:
|
||
explicit ScopedZlibInflateStream(z_stream* zlib) : zlib_(zlib) {}
|
||
|
||
ScopedZlibInflateStream(const ScopedZlibInflateStream&) = delete;
|
||
ScopedZlibInflateStream& operator=(const ScopedZlibInflateStream&) = delete;
|
||
|
||
~ScopedZlibInflateStream() {
|
||
int zr = inflateEnd(zlib_);
|
||
EXPECT_EQ(zr, Z_OK) << "inflateEnd: " << ZlibErrorString(zr);
|
||
}
|
||
|
||
private:
|
||
z_stream* zlib_; // weak
|
||
};
|
||
|
||
void GzipInflate(const std::string& compressed,
|
||
std::string* decompressed,
|
||
size_t buf_size) {
|
||
decompressed->clear();
|
||
|
||
// There’s got to be at least a small buffer.
|
||
auto buf = base::HeapArray<uint8_t>::Uninit(
|
||
std::max(buf_size, static_cast<size_t>(1)));
|
||
z_stream zlib = {};
|
||
zlib.zalloc = Z_NULL;
|
||
zlib.zfree = Z_NULL;
|
||
zlib.opaque = Z_NULL;
|
||
zlib.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(&compressed[0]));
|
||
zlib.avail_in = base::checked_cast<uInt>(compressed.size());
|
||
zlib.next_out = buf.data();
|
||
zlib.avail_out = base::checked_cast<uInt>(buf.size());
|
||
|
||
int zr = inflateInit2(&zlib, ZlibWindowBitsWithGzipWrapper(0));
|
||
ASSERT_EQ(zr, Z_OK) << "inflateInit2: " << ZlibErrorString(zr);
|
||
ScopedZlibInflateStream zlib_inflate(&zlib);
|
||
|
||
zr = inflate(&zlib, Z_FINISH);
|
||
ASSERT_EQ(zr, Z_STREAM_END) << "inflate: " << ZlibErrorString(zr);
|
||
|
||
ASSERT_LE(zlib.avail_out, buf.size());
|
||
decompressed->assign(reinterpret_cast<char*>(buf.data()),
|
||
buf.size() - zlib.avail_out);
|
||
}
|
||
|
||
void TestGzipDeflateInflate(const std::string& string) {
|
||
std::unique_ptr<HTTPBodyStream> string_stream(
|
||
new StringHTTPBodyStream(string));
|
||
GzipHTTPBodyStream gzip_stream(std::move(string_stream));
|
||
|
||
// The minimum size of a gzip wrapper per RFC 1952: a 10-byte header and an
|
||
// 8-byte trailer.
|
||
constexpr size_t kGzipHeaderSize = 18;
|
||
|
||
// Per https://zlib.net/zlib_tech.html, in the worst case, zlib will store
|
||
// uncompressed data as-is, at an overhead of 5 bytes per 16384-byte block.
|
||
// Zero-length input will “compress” to a 2-byte zlib stream. Add the overhead
|
||
// of the gzip wrapper, assuming no optional fields are present.
|
||
size_t buf_size =
|
||
string.size() + kGzipHeaderSize +
|
||
(string.empty() ? 2 : (((string.size() + 16383) / 16384) * 5));
|
||
auto buf = base::HeapArray<uint8_t>::Uninit(buf_size);
|
||
FileOperationResult compressed_bytes =
|
||
gzip_stream.GetBytesBuffer(buf.data(), buf.size());
|
||
ASSERT_NE(compressed_bytes, -1);
|
||
ASSERT_LE(static_cast<size_t>(compressed_bytes), buf.size());
|
||
|
||
// Make sure that the stream is really at EOF.
|
||
uint8_t eof_buf[16];
|
||
ASSERT_EQ(gzip_stream.GetBytesBuffer(eof_buf, sizeof(eof_buf)), 0);
|
||
|
||
std::string compressed(reinterpret_cast<char*>(buf.data()), compressed_bytes);
|
||
|
||
ASSERT_GE(compressed.size(), kGzipHeaderSize);
|
||
EXPECT_EQ(compressed[0], '\37');
|
||
EXPECT_EQ(compressed[1], '\213');
|
||
EXPECT_EQ(compressed[2], Z_DEFLATED);
|
||
|
||
std::string decompressed;
|
||
ASSERT_NO_FATAL_FAILURE(
|
||
GzipInflate(compressed, &decompressed, string.size()));
|
||
|
||
EXPECT_EQ(decompressed, string);
|
||
|
||
// In block mode, compression should be identical.
|
||
string_stream.reset(new StringHTTPBodyStream(string));
|
||
GzipHTTPBodyStream block_gzip_stream(std::move(string_stream));
|
||
uint8_t block_buf[4096];
|
||
std::string block_compressed;
|
||
FileOperationResult block_compressed_bytes;
|
||
while ((block_compressed_bytes = block_gzip_stream.GetBytesBuffer(
|
||
block_buf, sizeof(block_buf))) > 0) {
|
||
block_compressed.append(reinterpret_cast<char*>(block_buf),
|
||
block_compressed_bytes);
|
||
}
|
||
ASSERT_EQ(block_compressed_bytes, 0);
|
||
EXPECT_EQ(block_compressed, compressed);
|
||
}
|
||
|
||
std::string MakeString(size_t size) {
|
||
std::string string;
|
||
for (size_t i = 0; i < size; ++i) {
|
||
string.append(1, static_cast<char>((i % 256) ^ ((i >> 8) % 256)));
|
||
}
|
||
return string;
|
||
}
|
||
|
||
constexpr size_t kFourKBytes = 4096;
|
||
constexpr size_t kManyBytes = 375017;
|
||
|
||
TEST(GzipHTTPBodyStream, Empty) {
|
||
TestGzipDeflateInflate(std::string());
|
||
}
|
||
|
||
TEST(GzipHTTPBodyStream, OneByte) {
|
||
TestGzipDeflateInflate(std::string("Z"));
|
||
}
|
||
|
||
TEST(GzipHTTPBodyStream, FourKBytes_NUL) {
|
||
TestGzipDeflateInflate(std::string(kFourKBytes, '\0'));
|
||
}
|
||
|
||
TEST(GzipHTTPBodyStream, ManyBytes_NUL) {
|
||
TestGzipDeflateInflate(std::string(kManyBytes, '\0'));
|
||
}
|
||
|
||
TEST(GzipHTTPBodyStream, FourKBytes_Deterministic) {
|
||
TestGzipDeflateInflate(MakeString(kFourKBytes));
|
||
}
|
||
|
||
TEST(GzipHTTPBodyStream, ManyBytes_Deterministic) {
|
||
TestGzipDeflateInflate(MakeString(kManyBytes));
|
||
}
|
||
|
||
TEST(GzipHTTPBodyStream, FourKBytes_Random) {
|
||
TestGzipDeflateInflate(base::RandBytesAsString(kFourKBytes));
|
||
}
|
||
|
||
TEST(GzipHTTPBodyStream, ManyBytes_Random) {
|
||
TestGzipDeflateInflate(base::RandBytesAsString(kManyBytes));
|
||
}
|
||
|
||
} // namespace
|
||
} // namespace test
|
||
} // namespace crashpad
|