ART: Harden dex-file verifier wrt/ LEB128
Use out-of-bounds checks when decoding (U)LEB128.
Bug: 31252966
Test: m test-art-host
Change-Id: Id6dbbceb37c4d8d698e7685c61bf31912e3bf69e
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 5132efc..5d70076 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -454,6 +454,27 @@
return result;
}
+
+#define DECODE_UNSIGNED_CHECKED_FROM_WITH_ERROR_VALUE(ptr, var, error_value) \
+ uint32_t var; \
+ if (!DecodeUnsignedLeb128Checked(&ptr, begin_ + size_, &var)) { \
+ return error_value; \
+ }
+
+#define DECODE_UNSIGNED_CHECKED_FROM(ptr, var) \
+ uint32_t var; \
+ if (!DecodeUnsignedLeb128Checked(&ptr, begin_ + size_, &var)) { \
+ ErrorStringPrintf("Read out of bounds"); \
+ return false; \
+ }
+
+#define DECODE_SIGNED_CHECKED_FROM(ptr, var) \
+ int32_t var; \
+ if (!DecodeSignedLeb128Checked(&ptr, begin_ + size_, &var)) { \
+ ErrorStringPrintf("Read out of bounds"); \
+ return false; \
+ }
+
bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
uint32_t* handler_offsets, uint32_t handlers_size) {
const uint8_t* handlers_base = DexFile::GetCatchHandlerData(*code_item, 0);
@@ -461,7 +482,7 @@
for (uint32_t i = 0; i < handlers_size; i++) {
bool catch_all;
size_t offset = ptr_ - handlers_base;
- int32_t size = DecodeSignedLeb128(&ptr_);
+ DECODE_SIGNED_CHECKED_FROM(ptr_, size);
if (UNLIKELY((size < -65536) || (size > 65536))) {
ErrorStringPrintf("Invalid exception handler size: %d", size);
@@ -478,12 +499,12 @@
handler_offsets[i] = static_cast<uint32_t>(offset);
while (size-- > 0) {
- uint32_t type_idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
if (!CheckIndex(type_idx, header_->type_ids_size_, "handler type_idx")) {
return false;
}
- uint32_t addr = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr);
if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) {
ErrorStringPrintf("Invalid handler addr: %x", addr);
return false;
@@ -491,7 +512,7 @@
}
if (catch_all) {
- uint32_t addr = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr);
if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) {
ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr);
return false;
@@ -726,7 +747,7 @@
}
bool DexFileVerifier::CheckEncodedArray() {
- uint32_t size = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
while (size--) {
if (!CheckEncodedValue()) {
@@ -738,16 +759,16 @@
}
bool DexFileVerifier::CheckEncodedAnnotation() {
- uint32_t idx = DecodeUnsignedLeb128(&ptr_);
- if (!CheckIndex(idx, header_->type_ids_size_, "encoded_annotation type_idx")) {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, anno_idx);
+ if (!CheckIndex(anno_idx, header_->type_ids_size_, "encoded_annotation type_idx")) {
return false;
}
- uint32_t size = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
uint32_t last_idx = 0;
for (uint32_t i = 0; i < size; i++) {
- idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, idx);
if (!CheckIndex(idx, header_->string_ids_size_, "annotation_element name_idx")) {
return false;
}
@@ -1002,7 +1023,7 @@
}
ptr_ = DexFile::GetCatchHandlerData(*code_item, 0);
- uint32_t handlers_size = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, handlers_size);
if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) {
ErrorStringPrintf("Invalid handlers_size: %ud", handlers_size);
@@ -1051,7 +1072,7 @@
}
bool DexFileVerifier::CheckIntraStringDataItem() {
- uint32_t size = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
const uint8_t* file_end = begin_ + size_;
for (uint32_t i = 0; i < size; i++) {
@@ -1137,15 +1158,15 @@
}
bool DexFileVerifier::CheckIntraDebugInfoItem() {
- DecodeUnsignedLeb128(&ptr_);
- uint32_t parameters_size = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, dummy);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameters_size);
if (UNLIKELY(parameters_size > 65536)) {
ErrorStringPrintf("Invalid parameters_size: %x", parameters_size);
return false;
}
for (uint32_t j = 0; j < parameters_size; j++) {
- uint32_t parameter_name = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameter_name);
if (parameter_name != 0) {
parameter_name--;
if (!CheckIndex(parameter_name, header_->string_ids_size_, "debug_info_item parameter_name")) {
@@ -1161,27 +1182,27 @@
return true;
}
case DexFile::DBG_ADVANCE_PC: {
- DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, advance_pc_dummy);
break;
}
case DexFile::DBG_ADVANCE_LINE: {
- DecodeSignedLeb128(&ptr_);
+ DECODE_SIGNED_CHECKED_FROM(ptr_, advance_line_dummy);
break;
}
case DexFile::DBG_START_LOCAL: {
- uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
if (UNLIKELY(reg_num >= 65536)) {
ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
- uint32_t name_idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
if (name_idx != 0) {
name_idx--;
if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL name_idx")) {
return false;
}
}
- uint32_t type_idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
if (type_idx != 0) {
type_idx--;
if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL type_idx")) {
@@ -1192,7 +1213,7 @@
}
case DexFile::DBG_END_LOCAL:
case DexFile::DBG_RESTART_LOCAL: {
- uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
if (UNLIKELY(reg_num >= 65536)) {
ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
@@ -1200,26 +1221,26 @@
break;
}
case DexFile::DBG_START_LOCAL_EXTENDED: {
- uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
if (UNLIKELY(reg_num >= 65536)) {
ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
- uint32_t name_idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
if (name_idx != 0) {
name_idx--;
if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED name_idx")) {
return false;
}
}
- uint32_t type_idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
if (type_idx != 0) {
type_idx--;
if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL_EXTENDED type_idx")) {
return false;
}
}
- uint32_t sig_idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, sig_idx);
if (sig_idx != 0) {
sig_idx--;
if (!CheckIndex(sig_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED sig_idx")) {
@@ -1229,7 +1250,7 @@
break;
}
case DexFile::DBG_SET_FILE: {
- uint32_t name_idx = DecodeUnsignedLeb128(&ptr_);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
if (name_idx != 0) {
name_idx--;
if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_SET_FILE name_idx")) {
@@ -2127,7 +2148,7 @@
const DexFile::AnnotationItem* annotation =
reinterpret_cast<const DexFile::AnnotationItem*>(begin_ + *offsets);
const uint8_t* data = annotation->annotation_;
- uint32_t idx = DecodeUnsignedLeb128(&data);
+ DECODE_UNSIGNED_CHECKED_FROM(data, idx);
if (UNLIKELY(last_idx >= idx && i != 0)) {
ErrorStringPrintf("Out-of-order entry types: %x then %x", last_idx, idx);
@@ -2442,7 +2463,10 @@
// Assume that the data is OK at this point. String data has been checked at this point.
const uint8_t* ptr = begin + string_id->string_data_off_;
- DecodeUnsignedLeb128(&ptr);
+ uint32_t dummy;
+ if (!DecodeUnsignedLeb128Checked(&ptr, begin + header->file_size_, &dummy)) {
+ return "(error)";
+ }
return reinterpret_cast<const char*>(ptr);
}
@@ -2604,7 +2628,11 @@
return false;
}
const uint8_t* str_data_ptr = begin + string_off;
- DecodeUnsignedLeb128(&str_data_ptr);
+ uint32_t dummy;
+ if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) {
+ *error_msg = "String size out of bounds for method flags verification";
+ return false;
+ }
*str = reinterpret_cast<const char*>(str_data_ptr);
return true;
}