Check if return value from `LZ4_decompress_safe()` is negative.
`LZ4_decompress_safe()` returns an `int`, which was previously cast
to `size_t` without checking for possible negative values. This hides
possible errors (they hit the following assertion that decompressed size
equals image size). Instead, fail to decompress the image if return
value is negative.
Bug: 242914915
Test: art/test.py --host
Change-Id: I0e330bbbd55ccbef124230b6b50915ea8d009b03
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index ae6feec..07afa36 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -155,11 +155,19 @@
<< PrettyDuration(NanoTime() - compress_start_time);
if (kIsDebugBuild) {
dchecked_vector<uint8_t> decompressed(source.size());
- const size_t decompressed_size = LZ4_decompress_safe(
+ size_t decompressed_size;
+ std::string error_msg;
+ bool ok = LZ4_decompress_safe_checked(
reinterpret_cast<char*>(storage->data()),
reinterpret_cast<char*>(decompressed.data()),
storage->size(),
- decompressed.size());
+ decompressed.size(),
+ &decompressed_size,
+ &error_msg);
+ if (!ok) {
+ LOG(FATAL) << error_msg;
+ UNREACHABLE();
+ }
CHECK_EQ(decompressed_size, decompressed.size());
CHECK_EQ(memcmp(source.data(), decompressed.data(), source.size()), 0) << image_storage_mode;
}
diff --git a/runtime/image.cc b/runtime/image.cc
index b39ffe5..b8536db 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -19,6 +19,8 @@
#include <lz4.h>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/bit_utils.h"
#include "base/length_prefixed_array.h"
#include "base/utils.h"
@@ -170,6 +172,23 @@
return ConvertToPointerSize(pointer_size_);
}
+bool LZ4_decompress_safe_checked(const char* source,
+ char* dest,
+ int compressed_size,
+ int max_decompressed_size,
+ /*out*/ size_t* decompressed_size_checked,
+ /*out*/ std::string* error_msg) {
+ int decompressed_size = LZ4_decompress_safe(source, dest, compressed_size, max_decompressed_size);
+ if (UNLIKELY(decompressed_size < 0)) {
+ *error_msg = android::base::StringPrintf("LZ4_decompress_safe() returned negative size: %d",
+ decompressed_size);
+ return false;
+ } else {
+ *decompressed_size_checked = static_cast<size_t>(decompressed_size);
+ return true;
+ }
+}
+
bool ImageHeader::Block::Decompress(uint8_t* out_ptr,
const uint8_t* in_ptr,
std::string* error_msg) const {
@@ -182,11 +201,17 @@
case kStorageModeLZ4:
case kStorageModeLZ4HC: {
// LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
- const size_t decompressed_size = LZ4_decompress_safe(
+ size_t decompressed_size;
+ bool ok = LZ4_decompress_safe_checked(
reinterpret_cast<const char*>(in_ptr) + data_offset_,
reinterpret_cast<char*>(out_ptr) + image_offset_,
data_size_,
- image_size_);
+ image_size_,
+ &decompressed_size,
+ error_msg);
+ if (!ok) {
+ return false;
+ }
CHECK_EQ(decompressed_size, image_size_);
break;
}
diff --git a/runtime/image.h b/runtime/image.h
index 8f045e9..bc0c9dd 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -520,6 +520,14 @@
std::ostream& operator<<(std::ostream& os, const ImageSection& section);
+// Wrapper over LZ4_decompress_safe() that checks if return value is negative. See b/242914915.
+bool LZ4_decompress_safe_checked(const char* source,
+ char* dest,
+ int compressed_size,
+ int max_decompressed_size,
+ /*out*/ size_t* decompressed_size_checked,
+ /*out*/ std::string* error_msg);
+
} // namespace art
#endif // ART_RUNTIME_IMAGE_H_