• 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::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::string* error_msg,
215     int zip_fd ATTRIBUTE_UNUSED,
216     bool* zip_file_only_contains_uncompress_dex ATTRIBUTE_UNUSED) const {
217   *error_msg = "UNIMPLEMENTED";
218   return false;
219 }
220 
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) const221 std::unique_ptr<const DexFile> DexFileLoader::Open(
222     const uint8_t* base,
223     size_t size,
224     const std::string& location,
225     uint32_t location_checksum,
226     const OatDexFile* oat_dex_file,
227     bool verify,
228     bool verify_checksum,
229     std::string* error_msg,
230     std::unique_ptr<DexFileContainer> container) const {
231   return OpenCommon(base,
232                     size,
233                     /*data_base=*/ nullptr,
234                     /*data_size=*/ 0,
235                     location,
236                     location_checksum,
237                     oat_dex_file,
238                     verify,
239                     verify_checksum,
240                     error_msg,
241                     std::move(container),
242                     /*verify_result=*/ nullptr);
243 }
244 
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) const245 std::unique_ptr<const DexFile> DexFileLoader::OpenWithDataSection(
246     const uint8_t* base,
247     size_t size,
248     const uint8_t* data_base,
249     size_t data_size,
250     const std::string& location,
251     uint32_t location_checksum,
252     const OatDexFile* oat_dex_file,
253     bool verify,
254     bool verify_checksum,
255     std::string* error_msg) const {
256   return OpenCommon(base,
257                     size,
258                     data_base,
259                     data_size,
260                     location,
261                     location_checksum,
262                     oat_dex_file,
263                     verify,
264                     verify_checksum,
265                     error_msg,
266                     /*container=*/ nullptr,
267                     /*verify_result=*/ nullptr);
268 }
269 
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) const270 bool DexFileLoader::OpenAll(
271     const uint8_t* base,
272     size_t size,
273     const std::string& location,
274     bool verify,
275     bool verify_checksum,
276     DexFileLoaderErrorCode* error_code,
277     std::string* error_msg,
278     std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
279   DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
280   uint32_t magic = *reinterpret_cast<const uint32_t*>(base);
281   if (IsZipMagic(magic)) {
282     std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg));
283     if (zip_archive.get() == nullptr) {
284       DCHECK(!error_msg->empty());
285       return false;
286     }
287     return OpenAllDexFilesFromZip(*zip_archive.get(),
288                                   location,
289                                   verify,
290                                   verify_checksum,
291                                   error_code,
292                                   error_msg,
293                                   dex_files);
294   }
295   if (IsMagicValid(magic)) {
296     const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base);
297     std::unique_ptr<const DexFile> dex_file(Open(base,
298                                                  size,
299                                                  location,
300                                                  dex_header->checksum_,
301                                                  /*oat_dex_file=*/ nullptr,
302                                                  verify,
303                                                  verify_checksum,
304                                                  error_msg));
305     if (dex_file.get() != nullptr) {
306       dex_files->push_back(std::move(dex_file));
307       return true;
308     } else {
309       return false;
310     }
311   }
312   *error_msg = StringPrintf("Expected valid zip or dex file");
313   return false;
314 }
315 
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)316 std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
317                                                    size_t size,
318                                                    const uint8_t* data_base,
319                                                    size_t data_size,
320                                                    const std::string& location,
321                                                    uint32_t location_checksum,
322                                                    const OatDexFile* oat_dex_file,
323                                                    bool verify,
324                                                    bool verify_checksum,
325                                                    std::string* error_msg,
326                                                    std::unique_ptr<DexFileContainer> container,
327                                                    VerifyResult* verify_result) {
328   if (verify_result != nullptr) {
329     *verify_result = VerifyResult::kVerifyNotAttempted;
330   }
331   std::unique_ptr<DexFile> dex_file;
332   if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
333     if (data_size != 0) {
334       CHECK_EQ(base, data_base) << "Unsupported for standard dex";
335     }
336     dex_file.reset(new StandardDexFile(base,
337                                        size,
338                                        location,
339                                        location_checksum,
340                                        oat_dex_file,
341                                        std::move(container)));
342   } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
343     if (data_base == nullptr) {
344       // TODO: Is there a clean way to support both an explicit data section and reading the one
345       // from the header.
346       CHECK_EQ(data_size, 0u);
347       const CompactDexFile::Header* const header = CompactDexFile::Header::At(base);
348       data_base = base + header->data_off_;
349       data_size = header->data_size_;
350     }
351     dex_file.reset(new CompactDexFile(base,
352                                       size,
353                                       data_base,
354                                       data_size,
355                                       location,
356                                       location_checksum,
357                                       oat_dex_file,
358                                       std::move(container)));
359     // Disable verification for CompactDex input.
360     verify = false;
361   } else {
362     *error_msg = "Invalid or truncated dex file";
363   }
364   if (dex_file == nullptr) {
365     *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
366                               error_msg->c_str());
367     return nullptr;
368   }
369   if (!dex_file->Init(error_msg)) {
370     dex_file.reset();
371     return nullptr;
372   }
373   if (verify && !dex::Verify(dex_file.get(),
374                              dex_file->Begin(),
375                              dex_file->Size(),
376                              location.c_str(),
377                              verify_checksum,
378                              error_msg)) {
379     if (verify_result != nullptr) {
380       *verify_result = VerifyResult::kVerifyFailed;
381     }
382     return nullptr;
383   }
384   if (verify_result != nullptr) {
385     *verify_result = VerifyResult::kVerifySucceeded;
386   }
387   return dex_file;
388 }
389 
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) const390 std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
391     const DexZipArchive& zip_archive,
392     const char* entry_name,
393     const std::string& location,
394     bool verify,
395     bool verify_checksum,
396     DexFileLoaderErrorCode* error_code,
397     std::string* error_msg) const {
398   CHECK(!location.empty());
399   std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
400   if (zip_entry == nullptr) {
401     *error_code = DexFileLoaderErrorCode::kEntryNotFound;
402     return nullptr;
403   }
404   if (zip_entry->GetUncompressedLength() == 0) {
405     *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
406     *error_code = DexFileLoaderErrorCode::kDexFileError;
407     return nullptr;
408   }
409 
410   std::vector<uint8_t> map(zip_entry->Extract(error_msg));
411   if (map.size() == 0) {
412     *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
413                               error_msg->c_str());
414     *error_code = DexFileLoaderErrorCode::kExtractToMemoryError;
415     return nullptr;
416   }
417   VerifyResult verify_result;
418   std::unique_ptr<const DexFile> dex_file = OpenCommon(
419       map.data(),
420       map.size(),
421       /*data_base=*/ nullptr,
422       /*data_size=*/ 0u,
423       location,
424       zip_entry->GetCrc32(),
425       /*oat_dex_file=*/ nullptr,
426       verify,
427       verify_checksum,
428       error_msg,
429       std::make_unique<VectorContainer>(std::move(map)),
430       &verify_result);
431   if (verify_result != VerifyResult::kVerifySucceeded) {
432     if (verify_result == VerifyResult::kVerifyNotAttempted) {
433       *error_code = DexFileLoaderErrorCode::kDexFileError;
434     } else {
435       *error_code = DexFileLoaderErrorCode::kVerifyError;
436     }
437     return nullptr;
438   }
439   *error_code = DexFileLoaderErrorCode::kNoError;
440   return dex_file;
441 }
442 
443 // Technically we do not have a limitation with respect to the number of dex files that can be in a
444 // multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
445 // (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
446 // seems an excessive number.
447 static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
448 
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) const449 bool DexFileLoader::OpenAllDexFilesFromZip(
450     const DexZipArchive& zip_archive,
451     const std::string& location,
452     bool verify,
453     bool verify_checksum,
454     DexFileLoaderErrorCode* error_code,
455     std::string* error_msg,
456     std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
457   DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
458   std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
459                                                                 kClassesDex,
460                                                                 location,
461                                                                 verify,
462                                                                 verify_checksum,
463                                                                 error_code,
464                                                                 error_msg));
465   if (*error_code != DexFileLoaderErrorCode::kNoError) {
466     return false;
467   } else {
468     // Had at least classes.dex.
469     dex_files->push_back(std::move(dex_file));
470 
471     // Now try some more.
472 
473     // We could try to avoid std::string allocations by working on a char array directly. As we
474     // do not expect a lot of iterations, this seems too involved and brittle.
475 
476     for (size_t i = 1; ; ++i) {
477       std::string name = GetMultiDexClassesDexName(i);
478       std::string fake_location = GetMultiDexLocation(i, location.c_str());
479       std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
480                                                                          name.c_str(),
481                                                                          fake_location,
482                                                                          verify,
483                                                                          verify_checksum,
484                                                                          error_code,
485                                                                          error_msg));
486       if (next_dex_file.get() == nullptr) {
487         if (*error_code != DexFileLoaderErrorCode::kEntryNotFound) {
488           LOG(WARNING) << "Zip open failed: " << *error_msg;
489         }
490         break;
491       } else {
492         dex_files->push_back(std::move(next_dex_file));
493       }
494 
495       if (i == kWarnOnManyDexFilesThreshold) {
496         LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
497                      << " dex files. Please consider coalescing and shrinking the number to "
498                         " avoid runtime overhead.";
499       }
500 
501       if (i == std::numeric_limits<size_t>::max()) {
502         LOG(ERROR) << "Overflow in number of dex files!";
503         break;
504       }
505     }
506 
507     return true;
508   }
509 }
510 }  // namespace art
511