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