• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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