1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "dex_file_loader.h"
18
19 #include "android-base/stringprintf.h"
20
21 #include "base/stl_util.h"
22 #include "compact_dex_file.h"
23 #include "dex_file.h"
24 #include "dex_file_verifier.h"
25 #include "standard_dex_file.h"
26 #include "ziparchive/zip_archive.h"
27
28 // system/core/zip_archive definitions.
29 struct ZipEntry;
30 typedef void* ZipArchiveHandle;
31
32 namespace art {
33
34 namespace {
35
36 class VectorContainer : public DexFileContainer {
37 public:
VectorContainer(std::vector<uint8_t> && vector)38 explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { }
~VectorContainer()39 virtual ~VectorContainer() OVERRIDE { }
40
GetPermissions()41 int GetPermissions() OVERRIDE {
42 return 0;
43 }
44
IsReadOnly()45 bool IsReadOnly() OVERRIDE {
46 return true;
47 }
48
EnableWrite()49 bool EnableWrite() OVERRIDE {
50 return false;
51 }
52
DisableWrite()53 bool DisableWrite() OVERRIDE {
54 return false;
55 }
56
57 private:
58 std::vector<uint8_t> vector_;
59 DISALLOW_COPY_AND_ASSIGN(VectorContainer);
60 };
61
62 } // namespace
63
64 using android::base::StringPrintf;
65
66 class DexZipArchive;
67
68 class DexZipEntry {
69 public:
70 // Extract this entry to memory.
71 // Returns null on failure and sets error_msg.
Extract(std::string * error_msg)72 const std::vector<uint8_t> Extract(std::string* error_msg) {
73 std::vector<uint8_t> map(GetUncompressedLength());
74 if (map.size() == 0) {
75 DCHECK(!error_msg->empty());
76 return map;
77 }
78 const int32_t error = ExtractToMemory(handle_, zip_entry_, map.data(), map.size());
79 if (error) {
80 *error_msg = std::string(ErrorCodeString(error));
81 }
82 return map;
83 }
84
~DexZipEntry()85 virtual ~DexZipEntry() {
86 delete zip_entry_;
87 }
88
GetUncompressedLength()89 uint32_t GetUncompressedLength() {
90 return zip_entry_->uncompressed_length;
91 }
92
GetCrc32()93 uint32_t GetCrc32() {
94 return zip_entry_->crc32;
95 }
96
97 private:
DexZipEntry(ZipArchiveHandle handle,::ZipEntry * zip_entry,const std::string & entry_name)98 DexZipEntry(ZipArchiveHandle handle,
99 ::ZipEntry* zip_entry,
100 const std::string& entry_name)
101 : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {}
102
103 ZipArchiveHandle handle_;
104 ::ZipEntry* const zip_entry_;
105 std::string const entry_name_;
106
107 friend class DexZipArchive;
108 DISALLOW_COPY_AND_ASSIGN(DexZipEntry);
109 };
110
111 class DexZipArchive {
112 public:
113 // return new DexZipArchive instance on success, null on error.
Open(const uint8_t * base,size_t size,std::string * error_msg)114 static DexZipArchive* Open(const uint8_t* base, size_t size, std::string* error_msg) {
115 ZipArchiveHandle handle;
116 uint8_t* nonconst_base = const_cast<uint8_t*>(base);
117 const int32_t error = OpenArchiveFromMemory(nonconst_base, size, "ZipArchiveMemory", &handle);
118 if (error) {
119 *error_msg = std::string(ErrorCodeString(error));
120 CloseArchive(handle);
121 return nullptr;
122 }
123 return new DexZipArchive(handle);
124 }
125
Find(const char * name,std::string * error_msg) const126 DexZipEntry* Find(const char* name, std::string* error_msg) const {
127 DCHECK(name != nullptr);
128 // Resist the urge to delete the space. <: is a bigraph sequence.
129 std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
130 const int32_t error = FindEntry(handle_, ZipString(name), zip_entry.get());
131 if (error) {
132 *error_msg = std::string(ErrorCodeString(error));
133 return nullptr;
134 }
135 return new DexZipEntry(handle_, zip_entry.release(), name);
136 }
137
~DexZipArchive()138 ~DexZipArchive() {
139 CloseArchive(handle_);
140 }
141
142
143 private:
DexZipArchive(ZipArchiveHandle handle)144 explicit DexZipArchive(ZipArchiveHandle handle) : handle_(handle) {}
145 ZipArchiveHandle handle_;
146
147 friend class DexZipEntry;
148 DISALLOW_COPY_AND_ASSIGN(DexZipArchive);
149 };
150
IsZipMagic(uint32_t magic)151 static bool IsZipMagic(uint32_t magic) {
152 return (('P' == ((magic >> 0) & 0xff)) &&
153 ('K' == ((magic >> 8) & 0xff)));
154 }
155
IsMagicValid(uint32_t magic)156 bool DexFileLoader::IsMagicValid(uint32_t magic) {
157 return IsMagicValid(reinterpret_cast<uint8_t*>(&magic));
158 }
159
IsMagicValid(const uint8_t * magic)160 bool DexFileLoader::IsMagicValid(const uint8_t* magic) {
161 return StandardDexFile::IsMagicValid(magic) ||
162 CompactDexFile::IsMagicValid(magic);
163 }
164
IsVersionAndMagicValid(const uint8_t * magic)165 bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) {
166 if (StandardDexFile::IsMagicValid(magic)) {
167 return StandardDexFile::IsVersionValid(magic);
168 }
169 if (CompactDexFile::IsMagicValid(magic)) {
170 return CompactDexFile::IsVersionValid(magic);
171 }
172 return false;
173 }
174
IsMultiDexLocation(const char * location)175 bool DexFileLoader::IsMultiDexLocation(const char* location) {
176 return strrchr(location, kMultiDexSeparator) != nullptr;
177 }
178
GetMultiDexClassesDexName(size_t index)179 std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) {
180 return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1);
181 }
182
GetMultiDexLocation(size_t index,const char * dex_location)183 std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) {
184 return (index == 0)
185 ? dex_location
186 : StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1);
187 }
188
GetDexCanonicalLocation(const char * dex_location)189 std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) {
190 CHECK_NE(dex_location, static_cast<const char*>(nullptr));
191 std::string base_location = GetBaseLocation(dex_location);
192 const char* suffix = dex_location + base_location.size();
193 DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
194 // Warning: Bionic implementation of realpath() allocates > 12KB on the stack.
195 // Do not run this code on a small stack, e.g. in signal handler.
196 UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
197 if (path != nullptr && path.get() != base_location) {
198 return std::string(path.get()) + suffix;
199 } else if (suffix[0] == 0) {
200 return base_location;
201 } else {
202 return dex_location;
203 }
204 }
205
206 // All of the implementations here should be independent of the runtime.
207 // TODO: implement all the virtual methods.
208
GetMultiDexChecksums(const char * filename ATTRIBUTE_UNUSED,std::vector<uint32_t> * checksums ATTRIBUTE_UNUSED,std::string * error_msg,int zip_fd ATTRIBUTE_UNUSED,bool * zip_file_only_contains_uncompress_dex ATTRIBUTE_UNUSED) const209 bool DexFileLoader::GetMultiDexChecksums(
210 const char* filename ATTRIBUTE_UNUSED,
211 std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED,
212 std::string* error_msg,
213 int zip_fd ATTRIBUTE_UNUSED,
214 bool* zip_file_only_contains_uncompress_dex ATTRIBUTE_UNUSED) const {
215 *error_msg = "UNIMPLEMENTED";
216 return false;
217 }
218
Open(const uint8_t * base,size_t size,const std::string & location,uint32_t location_checksum,const OatDexFile * oat_dex_file,bool verify,bool verify_checksum,std::string * error_msg) const219 std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
220 size_t size,
221 const std::string& location,
222 uint32_t location_checksum,
223 const OatDexFile* oat_dex_file,
224 bool verify,
225 bool verify_checksum,
226 std::string* error_msg) const {
227 return OpenCommon(base,
228 size,
229 /*data_base*/ nullptr,
230 /*data_size*/ 0,
231 location,
232 location_checksum,
233 oat_dex_file,
234 verify,
235 verify_checksum,
236 error_msg,
237 /*container*/ nullptr,
238 /*verify_result*/ nullptr);
239 }
240
OpenWithDataSection(const uint8_t * base,size_t size,const uint8_t * data_base,size_t data_size,const std::string & location,uint32_t location_checksum,const OatDexFile * oat_dex_file,bool verify,bool verify_checksum,std::string * error_msg) const241 std::unique_ptr<const DexFile> DexFileLoader::OpenWithDataSection(
242 const uint8_t* base,
243 size_t size,
244 const uint8_t* data_base,
245 size_t data_size,
246 const std::string& location,
247 uint32_t location_checksum,
248 const OatDexFile* oat_dex_file,
249 bool verify,
250 bool verify_checksum,
251 std::string* error_msg) const {
252 return OpenCommon(base,
253 size,
254 data_base,
255 data_size,
256 location,
257 location_checksum,
258 oat_dex_file,
259 verify,
260 verify_checksum,
261 error_msg,
262 /*container*/ nullptr,
263 /*verify_result*/ nullptr);
264 }
265
OpenAll(const uint8_t * base,size_t size,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const266 bool DexFileLoader::OpenAll(
267 const uint8_t* base,
268 size_t size,
269 const std::string& location,
270 bool verify,
271 bool verify_checksum,
272 std::string* error_msg,
273 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
274 DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
275 uint32_t magic = *reinterpret_cast<const uint32_t*>(base);
276 if (IsZipMagic(magic)) {
277 std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg));
278 if (zip_archive.get() == nullptr) {
279 DCHECK(!error_msg->empty());
280 return false;
281 }
282 return OpenAllDexFilesFromZip(*zip_archive.get(),
283 location,
284 verify,
285 verify_checksum,
286 error_msg,
287 dex_files);
288 }
289 if (IsMagicValid(magic)) {
290 const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base);
291 std::unique_ptr<const DexFile> dex_file(Open(base,
292 size,
293 location,
294 dex_header->checksum_,
295 /*oat_dex_file*/ nullptr,
296 verify,
297 verify_checksum,
298 error_msg));
299 if (dex_file.get() != nullptr) {
300 dex_files->push_back(std::move(dex_file));
301 return true;
302 } else {
303 return false;
304 }
305 }
306 *error_msg = StringPrintf("Expected valid zip or dex file");
307 return false;
308 }
309
OpenCommon(const uint8_t * base,size_t size,const uint8_t * data_base,size_t data_size,const std::string & location,uint32_t location_checksum,const OatDexFile * oat_dex_file,bool verify,bool verify_checksum,std::string * error_msg,std::unique_ptr<DexFileContainer> container,VerifyResult * verify_result)310 std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
311 size_t size,
312 const uint8_t* data_base,
313 size_t data_size,
314 const std::string& location,
315 uint32_t location_checksum,
316 const OatDexFile* oat_dex_file,
317 bool verify,
318 bool verify_checksum,
319 std::string* error_msg,
320 std::unique_ptr<DexFileContainer> container,
321 VerifyResult* verify_result) {
322 if (verify_result != nullptr) {
323 *verify_result = VerifyResult::kVerifyNotAttempted;
324 }
325 std::unique_ptr<DexFile> dex_file;
326 if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
327 if (data_size != 0) {
328 CHECK_EQ(base, data_base) << "Unsupported for standard dex";
329 }
330 dex_file.reset(new StandardDexFile(base,
331 size,
332 location,
333 location_checksum,
334 oat_dex_file,
335 std::move(container)));
336 } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
337 if (data_base == nullptr) {
338 // TODO: Is there a clean way to support both an explicit data section and reading the one
339 // from the header.
340 CHECK_EQ(data_size, 0u);
341 const CompactDexFile::Header* const header = CompactDexFile::Header::At(base);
342 data_base = base + header->data_off_;
343 data_size = header->data_size_;
344 }
345 dex_file.reset(new CompactDexFile(base,
346 size,
347 data_base,
348 data_size,
349 location,
350 location_checksum,
351 oat_dex_file,
352 std::move(container)));
353 // Disable verification for CompactDex input.
354 verify = false;
355 } else {
356 *error_msg = "Invalid or truncated dex file";
357 }
358 if (dex_file == nullptr) {
359 *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
360 error_msg->c_str());
361 return nullptr;
362 }
363 if (!dex_file->Init(error_msg)) {
364 dex_file.reset();
365 return nullptr;
366 }
367 if (verify && !DexFileVerifier::Verify(dex_file.get(),
368 dex_file->Begin(),
369 dex_file->Size(),
370 location.c_str(),
371 verify_checksum,
372 error_msg)) {
373 if (verify_result != nullptr) {
374 *verify_result = VerifyResult::kVerifyFailed;
375 }
376 return nullptr;
377 }
378 if (verify_result != nullptr) {
379 *verify_result = VerifyResult::kVerifySucceeded;
380 }
381 return dex_file;
382 }
383
OpenOneDexFileFromZip(const DexZipArchive & zip_archive,const char * entry_name,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,ZipOpenErrorCode * error_code) const384 std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
385 const DexZipArchive& zip_archive,
386 const char* entry_name,
387 const std::string& location,
388 bool verify,
389 bool verify_checksum,
390 std::string* error_msg,
391 ZipOpenErrorCode* error_code) const {
392 CHECK(!location.empty());
393 std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
394 if (zip_entry == nullptr) {
395 *error_code = ZipOpenErrorCode::kEntryNotFound;
396 return nullptr;
397 }
398 if (zip_entry->GetUncompressedLength() == 0) {
399 *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
400 *error_code = ZipOpenErrorCode::kDexFileError;
401 return nullptr;
402 }
403
404 std::vector<uint8_t> map(zip_entry->Extract(error_msg));
405 if (map.size() == 0) {
406 *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
407 error_msg->c_str());
408 *error_code = ZipOpenErrorCode::kExtractToMemoryError;
409 return nullptr;
410 }
411 VerifyResult verify_result;
412 std::unique_ptr<const DexFile> dex_file = OpenCommon(
413 map.data(),
414 map.size(),
415 /*data_base*/ nullptr,
416 /*data_size*/ 0u,
417 location,
418 zip_entry->GetCrc32(),
419 /*oat_dex_file*/ nullptr,
420 verify,
421 verify_checksum,
422 error_msg,
423 std::make_unique<VectorContainer>(std::move(map)),
424 &verify_result);
425 if (dex_file == nullptr) {
426 if (verify_result == VerifyResult::kVerifyNotAttempted) {
427 *error_code = ZipOpenErrorCode::kDexFileError;
428 } else {
429 *error_code = ZipOpenErrorCode::kVerifyError;
430 }
431 return nullptr;
432 }
433 if (verify_result != VerifyResult::kVerifySucceeded) {
434 *error_code = ZipOpenErrorCode::kVerifyError;
435 return nullptr;
436 }
437 *error_code = ZipOpenErrorCode::kNoError;
438 return dex_file;
439 }
440
441 // Technically we do not have a limitation with respect to the number of dex files that can be in a
442 // multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
443 // (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
444 // seems an excessive number.
445 static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
446
OpenAllDexFilesFromZip(const DexZipArchive & zip_archive,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const447 bool DexFileLoader::OpenAllDexFilesFromZip(
448 const DexZipArchive& zip_archive,
449 const std::string& location,
450 bool verify,
451 bool verify_checksum,
452 std::string* error_msg,
453 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
454 DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
455 ZipOpenErrorCode error_code;
456 std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
457 kClassesDex,
458 location,
459 verify,
460 verify_checksum,
461 error_msg,
462 &error_code));
463 if (dex_file.get() == nullptr) {
464 return false;
465 } else {
466 // Had at least classes.dex.
467 dex_files->push_back(std::move(dex_file));
468
469 // Now try some more.
470
471 // We could try to avoid std::string allocations by working on a char array directly. As we
472 // do not expect a lot of iterations, this seems too involved and brittle.
473
474 for (size_t i = 1; ; ++i) {
475 std::string name = GetMultiDexClassesDexName(i);
476 std::string fake_location = GetMultiDexLocation(i, location.c_str());
477 std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
478 name.c_str(),
479 fake_location,
480 verify,
481 verify_checksum,
482 error_msg,
483 &error_code));
484 if (next_dex_file.get() == nullptr) {
485 if (error_code != ZipOpenErrorCode::kEntryNotFound) {
486 LOG(WARNING) << "Zip open failed: " << *error_msg;
487 }
488 break;
489 } else {
490 dex_files->push_back(std::move(next_dex_file));
491 }
492
493 if (i == kWarnOnManyDexFilesThreshold) {
494 LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
495 << " dex files. Please consider coalescing and shrinking the number to "
496 " avoid runtime overhead.";
497 }
498
499 if (i == std::numeric_limits<size_t>::max()) {
500 LOG(ERROR) << "Overflow in number of dex files!";
501 break;
502 }
503 }
504
505 return true;
506 }
507 }
508 } // namespace art
509