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