Commit b6b4f60b authored by Tudor Bosman's avatar Tudor Bosman

Enforce max uncompressed size; use safe LZ4 decompressor, which requires a newer version of LZ4

Test Plan: folly/io/test, whatever contbuild dreams up for hphp

Reviewed By: meyering@fb.com

Subscribers: meyering, hphp-diffs@, ps, jhj, kma, sainbar, lesha

FB internal diff: D1429372

@override-unit-failures
parent 0e2b9093
......@@ -279,7 +279,7 @@ if test "$ac_cv_func_pthread_yield" = "no"; then
AC_CHECK_FUNCS([sched_yield])
fi
AC_CHECK_HEADER([lz4.h], AC_CHECK_LIB([lz4], [main]))
AC_CHECK_HEADER([lz4.h], AC_CHECK_LIB([lz4], [LZ4_decompress_safe]))
AC_CHECK_HEADER([snappy.h], AC_CHECK_LIB([snappy], [main]))
AC_CHECK_HEADER([zlib.h], AC_CHECK_LIB([z], [main]))
AC_CHECK_HEADER([lzma.h], AC_CHECK_LIB([lzma], [main]))
......
......@@ -49,7 +49,14 @@ Codec::Codec(CodecType type) : type_(type) { }
// Ensure consistent behavior in the nullptr case
std::unique_ptr<IOBuf> Codec::compress(const IOBuf* data) {
return !data->empty() ? doCompress(data) : IOBuf::create(0);
uint64_t len = data->computeChainDataLength();
if (len == 0) {
return IOBuf::create(0);
} else if (len > maxUncompressedLength()) {
throw std::runtime_error("Codec: uncompressed length too large");
}
return doCompress(data);
}
std::unique_ptr<IOBuf> Codec::uncompress(const IOBuf* data,
......@@ -86,7 +93,7 @@ bool Codec::doNeedsUncompressedLength() const {
}
uint64_t Codec::doMaxUncompressedLength() const {
return std::numeric_limits<uint64_t>::max() - 1;
return UNLIMITED_UNCOMPRESSED_LENGTH;
}
namespace {
......@@ -211,9 +218,7 @@ bool LZ4Codec::doNeedsUncompressedLength() const {
}
uint64_t LZ4Codec::doMaxUncompressedLength() const {
// From lz4.h: "Max supported value is ~1.9GB"; I wish we had something
// more accurate.
return 1.8 * (uint64_t(1) << 30);
return LZ4_MAX_INPUT_SIZE;
}
std::unique_ptr<IOBuf> LZ4Codec::doCompress(const IOBuf* data) {
......@@ -270,15 +275,20 @@ std::unique_ptr<IOBuf> LZ4Codec::doUncompress(
}
} else {
actualUncompressedLength = uncompressedLength;
DCHECK_NE(actualUncompressedLength, UNKNOWN_UNCOMPRESSED_LENGTH);
if (actualUncompressedLength == UNKNOWN_UNCOMPRESSED_LENGTH ||
actualUncompressedLength > maxUncompressedLength()) {
throw std::runtime_error("LZ4Codec: invalid uncompressed length");
}
}
auto out = IOBuf::create(actualUncompressedLength);
auto p = cursor.peek();
int n = LZ4_uncompress(reinterpret_cast<const char*>(p.first),
reinterpret_cast<char*>(out->writableTail()),
actualUncompressedLength);
if (n != p.second) {
auto out = IOBuf::create(actualUncompressedLength);
int n = LZ4_decompress_safe(reinterpret_cast<const char*>(p.first),
reinterpret_cast<char*>(out->writableTail()),
p.second,
actualUncompressedLength);
if (n != actualUncompressedLength) {
throw std::runtime_error(to<std::string>(
"LZ4 decompression returned invalid value ", n));
}
......
......@@ -84,6 +84,7 @@ class Codec {
* Return the maximum length of data that may be compressed with this codec.
* NO_COMPRESSION and ZLIB support arbitrary lengths;
* LZ4 supports up to 1.9GiB; SNAPPY supports up to 4GiB.
* May return UNLIMITED_UNCOMPRESSED_LENGTH if unlimited.
*/
uint64_t maxUncompressedLength() const;
......@@ -120,6 +121,7 @@ class Codec {
* an empty IOBuf chain will return an empty IOBuf chain.
*/
static constexpr uint64_t UNKNOWN_UNCOMPRESSED_LENGTH = uint64_t(-1);
static constexpr uint64_t UNLIMITED_UNCOMPRESSED_LENGTH = uint64_t(-2);
std::unique_ptr<IOBuf> uncompress(
const IOBuf* data,
......
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