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 "art_dex_file_loader.h"
18
19 #include <sys/stat.h>
20
21 #include "android-base/stringprintf.h"
22
23 #include "base/file_magic.h"
24 #include "base/file_utils.h"
25 #include "base/mem_map.h"
26 #include "base/mman.h" // For the PROT_* and MAP_* constants.
27 #include "base/stl_util.h"
28 #include "base/systrace.h"
29 #include "base/unix_file/fd_file.h"
30 #include "base/zip_archive.h"
31 #include "dex/compact_dex_file.h"
32 #include "dex/dex_file.h"
33 #include "dex/dex_file_verifier.h"
34 #include "dex/standard_dex_file.h"
35
36 namespace art {
37
38 namespace {
39
40 class MemMapContainer : public DexFileContainer {
41 public:
MemMapContainer(MemMap && mem_map)42 explicit MemMapContainer(MemMap&& mem_map) : mem_map_(std::move(mem_map)) { }
~MemMapContainer()43 ~MemMapContainer() override { }
44
GetPermissions()45 int GetPermissions() override {
46 if (!mem_map_.IsValid()) {
47 return 0;
48 } else {
49 return mem_map_.GetProtect();
50 }
51 }
52
IsReadOnly()53 bool IsReadOnly() override {
54 return GetPermissions() == PROT_READ;
55 }
56
EnableWrite()57 bool EnableWrite() override {
58 CHECK(IsReadOnly());
59 if (!mem_map_.IsValid()) {
60 return false;
61 } else {
62 return mem_map_.Protect(PROT_READ | PROT_WRITE);
63 }
64 }
65
DisableWrite()66 bool DisableWrite() override {
67 CHECK(!IsReadOnly());
68 if (!mem_map_.IsValid()) {
69 return false;
70 } else {
71 return mem_map_.Protect(PROT_READ);
72 }
73 }
74
75 private:
76 MemMap mem_map_;
77 DISALLOW_COPY_AND_ASSIGN(MemMapContainer);
78 };
79
80 } // namespace
81
82 using android::base::StringPrintf;
83
84 static constexpr OatDexFile* kNoOatDexFile = nullptr;
85
86
GetMultiDexChecksums(const char * filename,std::vector<uint32_t> * checksums,std::string * error_msg,int zip_fd,bool * zip_file_only_contains_uncompressed_dex) const87 bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename,
88 std::vector<uint32_t>* checksums,
89 std::string* error_msg,
90 int zip_fd,
91 bool* zip_file_only_contains_uncompressed_dex) const {
92 CHECK(checksums != nullptr);
93 uint32_t magic;
94
95 File fd;
96 if (zip_fd != -1) {
97 if (ReadMagicAndReset(zip_fd, &magic, error_msg)) {
98 fd = File(DupCloexec(zip_fd), /* check_usage= */ false);
99 }
100 } else {
101 fd = OpenAndReadMagic(filename, &magic, error_msg);
102 }
103 if (fd.Fd() == -1) {
104 DCHECK(!error_msg->empty());
105 return false;
106 }
107 if (IsZipMagic(magic)) {
108 std::unique_ptr<ZipArchive> zip_archive(
109 ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
110 if (zip_archive.get() == nullptr) {
111 *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
112 error_msg->c_str());
113 return false;
114 }
115
116 uint32_t i = 0;
117 std::string zip_entry_name = GetMultiDexClassesDexName(i++);
118 std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
119 if (zip_entry.get() == nullptr) {
120 *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
121 zip_entry_name.c_str(), error_msg->c_str());
122 return false;
123 }
124
125 if (zip_file_only_contains_uncompressed_dex != nullptr) {
126 // Start by assuming everything is uncompressed.
127 *zip_file_only_contains_uncompressed_dex = true;
128 }
129
130 do {
131 if (zip_file_only_contains_uncompressed_dex != nullptr) {
132 if (!(zip_entry->IsUncompressed() && zip_entry->IsAlignedTo(alignof(DexFile::Header)))) {
133 *zip_file_only_contains_uncompressed_dex = false;
134 }
135 }
136 checksums->push_back(zip_entry->GetCrc32());
137 zip_entry_name = GetMultiDexClassesDexName(i++);
138 zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
139 } while (zip_entry.get() != nullptr);
140 return true;
141 }
142 if (IsMagicValid(magic)) {
143 std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
144 filename,
145 /* verify= */ false,
146 /* verify_checksum= */ false,
147 /* mmap_shared= */ false,
148 error_msg));
149 if (dex_file == nullptr) {
150 return false;
151 }
152 checksums->push_back(dex_file->GetHeader().checksum_);
153 return true;
154 }
155 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
156 return false;
157 }
158
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,std::unique_ptr<DexFileContainer> container) const159 std::unique_ptr<const DexFile> ArtDexFileLoader::Open(
160 const uint8_t* base,
161 size_t size,
162 const std::string& location,
163 uint32_t location_checksum,
164 const OatDexFile* oat_dex_file,
165 bool verify,
166 bool verify_checksum,
167 std::string* error_msg,
168 std::unique_ptr<DexFileContainer> container) const {
169 ScopedTrace trace(std::string("Open dex file from RAM ") + location);
170 return OpenCommon(base,
171 size,
172 /*data_base=*/ nullptr,
173 /*data_size=*/ 0u,
174 location,
175 location_checksum,
176 oat_dex_file,
177 verify,
178 verify_checksum,
179 error_msg,
180 std::move(container),
181 /*verify_result=*/ nullptr);
182 }
183
Open(const std::string & location,uint32_t location_checksum,MemMap && map,bool verify,bool verify_checksum,std::string * error_msg) const184 std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const std::string& location,
185 uint32_t location_checksum,
186 MemMap&& map,
187 bool verify,
188 bool verify_checksum,
189 std::string* error_msg) const {
190 ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
191 CHECK(map.IsValid());
192
193 size_t size = map.Size();
194 if (size < sizeof(DexFile::Header)) {
195 *error_msg = StringPrintf(
196 "DexFile: failed to open dex file '%s' that is too short to have a header",
197 location.c_str());
198 return nullptr;
199 }
200
201 uint8_t* begin = map.Begin();
202 std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
203 size,
204 /*data_base=*/ nullptr,
205 /*data_size=*/ 0u,
206 location,
207 location_checksum,
208 kNoOatDexFile,
209 verify,
210 verify_checksum,
211 error_msg,
212 std::make_unique<MemMapContainer>(std::move(map)),
213 /*verify_result=*/ nullptr);
214 // Opening CompactDex is only supported from vdex files.
215 if (dex_file != nullptr && dex_file->IsCompactDexFile()) {
216 *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files",
217 location.c_str());
218 return nullptr;
219 }
220 return dex_file;
221 }
222
Open(const char * filename,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const223 bool ArtDexFileLoader::Open(const char* filename,
224 const std::string& location,
225 bool verify,
226 bool verify_checksum,
227 std::string* error_msg,
228 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
229 uint32_t magic;
230 File fd = OpenAndReadMagic(filename, &magic, error_msg);
231 if (fd.Fd() == -1) {
232 DCHECK(!error_msg->empty());
233 return false;
234 }
235 return OpenWithMagic(
236 magic, fd.Release(), location, verify, verify_checksum, error_msg, dex_files);
237 }
238
Open(int fd,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const239 bool ArtDexFileLoader::Open(int fd,
240 const std::string& location,
241 bool verify,
242 bool verify_checksum,
243 std::string* error_msg,
244 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
245 uint32_t magic;
246 if (!ReadMagicAndReset(fd, &magic, error_msg)) {
247 DCHECK(!error_msg->empty());
248 return false;
249 }
250 return OpenWithMagic(magic, fd, location, verify, verify_checksum, error_msg, dex_files);
251 }
252
OpenWithMagic(uint32_t magic,int fd,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const253 bool ArtDexFileLoader::OpenWithMagic(uint32_t magic,
254 int fd,
255 const std::string& location,
256 bool verify,
257 bool verify_checksum,
258 std::string* error_msg,
259 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
260 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
261 DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
262 if (IsZipMagic(magic)) {
263 return OpenZip(fd, location, verify, verify_checksum, error_msg, dex_files);
264 }
265 if (IsMagicValid(magic)) {
266 std::unique_ptr<const DexFile> dex_file(OpenFile(fd,
267 location,
268 verify,
269 verify_checksum,
270 /* mmap_shared= */ false,
271 error_msg));
272 if (dex_file.get() != nullptr) {
273 dex_files->push_back(std::move(dex_file));
274 return true;
275 } else {
276 return false;
277 }
278 }
279 *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", location.c_str());
280 return false;
281 }
282
OpenDex(int fd,const std::string & location,bool verify,bool verify_checksum,bool mmap_shared,std::string * error_msg) const283 std::unique_ptr<const DexFile> ArtDexFileLoader::OpenDex(int fd,
284 const std::string& location,
285 bool verify,
286 bool verify_checksum,
287 bool mmap_shared,
288 std::string* error_msg) const {
289 ScopedTrace trace("Open dex file " + std::string(location));
290 return OpenFile(fd, location, verify, verify_checksum, mmap_shared, error_msg);
291 }
292
OpenZip(int fd,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const293 bool ArtDexFileLoader::OpenZip(int fd,
294 const std::string& location,
295 bool verify,
296 bool verify_checksum,
297 std::string* error_msg,
298 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
299 ScopedTrace trace("Dex file open Zip " + std::string(location));
300 DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
301 std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
302 if (zip_archive.get() == nullptr) {
303 DCHECK(!error_msg->empty());
304 return false;
305 }
306 return OpenAllDexFilesFromZip(
307 *zip_archive, location, verify, verify_checksum, error_msg, dex_files);
308 }
309
OpenFile(int fd,const std::string & location,bool verify,bool verify_checksum,bool mmap_shared,std::string * error_msg) const310 std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
311 const std::string& location,
312 bool verify,
313 bool verify_checksum,
314 bool mmap_shared,
315 std::string* error_msg) const {
316 ScopedTrace trace(std::string("Open dex file ") + std::string(location));
317 CHECK(!location.empty());
318 MemMap map;
319 {
320 File delayed_close(fd, /* check_usage= */ false);
321 struct stat sbuf;
322 memset(&sbuf, 0, sizeof(sbuf));
323 if (fstat(fd, &sbuf) == -1) {
324 *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
325 strerror(errno));
326 return nullptr;
327 }
328 if (S_ISDIR(sbuf.st_mode)) {
329 *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
330 return nullptr;
331 }
332 size_t length = sbuf.st_size;
333 map = MemMap::MapFile(length,
334 PROT_READ,
335 mmap_shared ? MAP_SHARED : MAP_PRIVATE,
336 fd,
337 0,
338 /*low_4gb=*/false,
339 location.c_str(),
340 error_msg);
341 if (!map.IsValid()) {
342 DCHECK(!error_msg->empty());
343 return nullptr;
344 }
345 }
346
347 const uint8_t* begin = map.Begin();
348 size_t size = map.Size();
349 if (size < sizeof(DexFile::Header)) {
350 *error_msg = StringPrintf(
351 "DexFile: failed to open dex file '%s' that is too short to have a header",
352 location.c_str());
353 return nullptr;
354 }
355
356 const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(begin);
357
358 std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
359 size,
360 /*data_base=*/ nullptr,
361 /*data_size=*/ 0u,
362 location,
363 dex_header->checksum_,
364 kNoOatDexFile,
365 verify,
366 verify_checksum,
367 error_msg,
368 std::make_unique<MemMapContainer>(std::move(map)),
369 /*verify_result=*/ nullptr);
370
371 // Opening CompactDex is only supported from vdex files.
372 if (dex_file != nullptr && dex_file->IsCompactDexFile()) {
373 *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files",
374 location.c_str());
375 return nullptr;
376 }
377 return dex_file;
378 }
379
OpenOneDexFileFromZip(const ZipArchive & zip_archive,const char * entry_name,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,DexFileLoaderErrorCode * error_code) const380 std::unique_ptr<const DexFile> ArtDexFileLoader::OpenOneDexFileFromZip(
381 const ZipArchive& zip_archive,
382 const char* entry_name,
383 const std::string& location,
384 bool verify,
385 bool verify_checksum,
386 std::string* error_msg,
387 DexFileLoaderErrorCode* error_code) const {
388 ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
389 CHECK(!location.empty());
390 std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
391 if (zip_entry == nullptr) {
392 *error_code = DexFileLoaderErrorCode::kEntryNotFound;
393 return nullptr;
394 }
395 if (zip_entry->GetUncompressedLength() == 0) {
396 *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
397 *error_code = DexFileLoaderErrorCode::kDexFileError;
398 return nullptr;
399 }
400
401 MemMap map;
402 if (zip_entry->IsUncompressed()) {
403 if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
404 // Do not mmap unaligned ZIP entries because
405 // doing so would fail dex verification which requires 4 byte alignment.
406 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
407 << "please zipalign to " << alignof(DexFile::Header) << " bytes. "
408 << "Falling back to extracting file.";
409 } else {
410 // Map uncompressed files within zip as file-backed to avoid a dirty copy.
411 map = zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg);
412 if (!map.IsValid()) {
413 LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
414 << "is your ZIP file corrupted? Falling back to extraction.";
415 // Try again with Extraction which still has a chance of recovery.
416 }
417 }
418 }
419
420 if (!map.IsValid()) {
421 // Default path for compressed ZIP entries,
422 // and fallback for stored ZIP entries.
423 map = zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg);
424 }
425
426 if (!map.IsValid()) {
427 *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
428 error_msg->c_str());
429 *error_code = DexFileLoaderErrorCode::kExtractToMemoryError;
430 return nullptr;
431 }
432 VerifyResult verify_result;
433 uint8_t* begin = map.Begin();
434 size_t size = map.Size();
435 std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
436 size,
437 /*data_base=*/ nullptr,
438 /*data_size=*/ 0u,
439 location,
440 zip_entry->GetCrc32(),
441 kNoOatDexFile,
442 verify,
443 verify_checksum,
444 error_msg,
445 std::make_unique<MemMapContainer>(std::move(map)),
446 &verify_result);
447 if (dex_file != nullptr && dex_file->IsCompactDexFile()) {
448 *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files",
449 location.c_str());
450 return nullptr;
451 }
452 if (dex_file == nullptr) {
453 if (verify_result == VerifyResult::kVerifyNotAttempted) {
454 *error_code = DexFileLoaderErrorCode::kDexFileError;
455 } else {
456 *error_code = DexFileLoaderErrorCode::kVerifyError;
457 }
458 return nullptr;
459 }
460 if (!dex_file->DisableWrite()) {
461 *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
462 *error_code = DexFileLoaderErrorCode::kMakeReadOnlyError;
463 return nullptr;
464 }
465 CHECK(dex_file->IsReadOnly()) << location;
466 if (verify_result != VerifyResult::kVerifySucceeded) {
467 *error_code = DexFileLoaderErrorCode::kVerifyError;
468 return nullptr;
469 }
470 *error_code = DexFileLoaderErrorCode::kNoError;
471 return dex_file;
472 }
473
474 // Technically we do not have a limitation with respect to the number of dex files that can be in a
475 // multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
476 // (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
477 // seems an excessive number.
478 static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
479
OpenAllDexFilesFromZip(const ZipArchive & zip_archive,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const480 bool ArtDexFileLoader::OpenAllDexFilesFromZip(
481 const ZipArchive& zip_archive,
482 const std::string& location,
483 bool verify,
484 bool verify_checksum,
485 std::string* error_msg,
486 std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
487 ScopedTrace trace("Dex file open from Zip " + std::string(location));
488 DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
489 DexFileLoaderErrorCode error_code;
490 std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
491 kClassesDex,
492 location,
493 verify,
494 verify_checksum,
495 error_msg,
496 &error_code));
497 if (dex_file.get() == nullptr) {
498 return false;
499 } else {
500 // Had at least classes.dex.
501 dex_files->push_back(std::move(dex_file));
502
503 // Now try some more.
504
505 // We could try to avoid std::string allocations by working on a char array directly. As we
506 // do not expect a lot of iterations, this seems too involved and brittle.
507
508 for (size_t i = 1; ; ++i) {
509 std::string name = GetMultiDexClassesDexName(i);
510 std::string fake_location = GetMultiDexLocation(i, location.c_str());
511 std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
512 name.c_str(),
513 fake_location,
514 verify,
515 verify_checksum,
516 error_msg,
517 &error_code));
518 if (next_dex_file.get() == nullptr) {
519 if (error_code != DexFileLoaderErrorCode::kEntryNotFound) {
520 LOG(WARNING) << "Zip open failed: " << *error_msg;
521 }
522 break;
523 } else {
524 dex_files->push_back(std::move(next_dex_file));
525 }
526
527 if (i == kWarnOnManyDexFilesThreshold) {
528 LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
529 << " dex files. Please consider coalescing and shrinking the number to "
530 " avoid runtime overhead.";
531 }
532
533 if (i == std::numeric_limits<size_t>::max()) {
534 LOG(ERROR) << "Overflow in number of dex files!";
535 break;
536 }
537 }
538
539 return true;
540 }
541 }
542
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)543 std::unique_ptr<DexFile> ArtDexFileLoader::OpenCommon(const uint8_t* base,
544 size_t size,
545 const uint8_t* data_base,
546 size_t data_size,
547 const std::string& location,
548 uint32_t location_checksum,
549 const OatDexFile* oat_dex_file,
550 bool verify,
551 bool verify_checksum,
552 std::string* error_msg,
553 std::unique_ptr<DexFileContainer> container,
554 VerifyResult* verify_result) {
555 return DexFileLoader::OpenCommon(base,
556 size,
557 data_base,
558 data_size,
559 location,
560 location_checksum,
561 oat_dex_file,
562 verify,
563 verify_checksum,
564 error_msg,
565 std::move(container),
566 verify_result);
567 }
568
569 } // namespace art
570