• 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 // system/core/zip_archive definitions.
29 struct ZipEntry;
30 typedef void* ZipArchiveHandle;
31 
32 namespace art {
33 
34 namespace {
35 
36 class VectorContainer : public DexFileContainer {
37  public:
VectorContainer(std::vector<uint8_t> && vector)38   explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { }
~VectorContainer()39   virtual ~VectorContainer() OVERRIDE { }
40 
GetPermissions()41   int GetPermissions() OVERRIDE {
42     return 0;
43   }
44 
IsReadOnly()45   bool IsReadOnly() OVERRIDE {
46     return true;
47   }
48 
EnableWrite()49   bool EnableWrite() OVERRIDE {
50     return false;
51   }
52 
DisableWrite()53   bool DisableWrite() OVERRIDE {
54     return false;
55   }
56 
57  private:
58   std::vector<uint8_t> vector_;
59   DISALLOW_COPY_AND_ASSIGN(VectorContainer);
60 };
61 
62 }  // namespace
63 
64 using android::base::StringPrintf;
65 
66 class DexZipArchive;
67 
68 class DexZipEntry {
69  public:
70   // Extract this entry to memory.
71   // Returns null on failure and sets error_msg.
Extract(std::string * error_msg)72   const std::vector<uint8_t> Extract(std::string* error_msg) {
73     std::vector<uint8_t> map(GetUncompressedLength());
74     if (map.size() == 0) {
75       DCHECK(!error_msg->empty());
76       return map;
77     }
78     const int32_t error = ExtractToMemory(handle_, zip_entry_, map.data(), map.size());
79     if (error) {
80       *error_msg = std::string(ErrorCodeString(error));
81     }
82     return map;
83   }
84 
~DexZipEntry()85   virtual ~DexZipEntry() {
86     delete zip_entry_;
87   }
88 
GetUncompressedLength()89   uint32_t GetUncompressedLength() {
90     return zip_entry_->uncompressed_length;
91   }
92 
GetCrc32()93   uint32_t GetCrc32() {
94     return zip_entry_->crc32;
95   }
96 
97  private:
DexZipEntry(ZipArchiveHandle handle,::ZipEntry * zip_entry,const std::string & entry_name)98   DexZipEntry(ZipArchiveHandle handle,
99               ::ZipEntry* zip_entry,
100            const std::string& entry_name)
101     : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {}
102 
103   ZipArchiveHandle handle_;
104   ::ZipEntry* const zip_entry_;
105   std::string const entry_name_;
106 
107   friend class DexZipArchive;
108   DISALLOW_COPY_AND_ASSIGN(DexZipEntry);
109 };
110 
111 class DexZipArchive {
112  public:
113   // return new DexZipArchive instance on success, null on error.
Open(const uint8_t * base,size_t size,std::string * error_msg)114   static DexZipArchive* Open(const uint8_t* base, size_t size, std::string* error_msg) {
115     ZipArchiveHandle handle;
116     uint8_t* nonconst_base = const_cast<uint8_t*>(base);
117     const int32_t error = OpenArchiveFromMemory(nonconst_base, size, "ZipArchiveMemory", &handle);
118     if (error) {
119       *error_msg = std::string(ErrorCodeString(error));
120       CloseArchive(handle);
121       return nullptr;
122     }
123     return new DexZipArchive(handle);
124   }
125 
Find(const char * name,std::string * error_msg) const126   DexZipEntry* Find(const char* name, std::string* error_msg) const {
127     DCHECK(name != nullptr);
128     // Resist the urge to delete the space. <: is a bigraph sequence.
129     std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
130     const int32_t error = FindEntry(handle_, ZipString(name), zip_entry.get());
131     if (error) {
132       *error_msg = std::string(ErrorCodeString(error));
133       return nullptr;
134     }
135     return new DexZipEntry(handle_, zip_entry.release(), name);
136   }
137 
~DexZipArchive()138   ~DexZipArchive() {
139     CloseArchive(handle_);
140   }
141 
142 
143  private:
DexZipArchive(ZipArchiveHandle handle)144   explicit DexZipArchive(ZipArchiveHandle handle) : handle_(handle) {}
145   ZipArchiveHandle handle_;
146 
147   friend class DexZipEntry;
148   DISALLOW_COPY_AND_ASSIGN(DexZipArchive);
149 };
150 
IsZipMagic(uint32_t magic)151 static bool IsZipMagic(uint32_t magic) {
152   return (('P' == ((magic >> 0) & 0xff)) &&
153           ('K' == ((magic >> 8) & 0xff)));
154 }
155 
IsMagicValid(uint32_t magic)156 bool DexFileLoader::IsMagicValid(uint32_t magic) {
157   return IsMagicValid(reinterpret_cast<uint8_t*>(&magic));
158 }
159 
IsMagicValid(const uint8_t * magic)160 bool DexFileLoader::IsMagicValid(const uint8_t* magic) {
161   return StandardDexFile::IsMagicValid(magic) ||
162       CompactDexFile::IsMagicValid(magic);
163 }
164 
IsVersionAndMagicValid(const uint8_t * magic)165 bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) {
166   if (StandardDexFile::IsMagicValid(magic)) {
167     return StandardDexFile::IsVersionValid(magic);
168   }
169   if (CompactDexFile::IsMagicValid(magic)) {
170     return CompactDexFile::IsVersionValid(magic);
171   }
172   return false;
173 }
174 
IsMultiDexLocation(const char * location)175 bool DexFileLoader::IsMultiDexLocation(const char* location) {
176   return strrchr(location, kMultiDexSeparator) != nullptr;
177 }
178 
GetMultiDexClassesDexName(size_t index)179 std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) {
180   return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1);
181 }
182 
GetMultiDexLocation(size_t index,const char * dex_location)183 std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) {
184   return (index == 0)
185       ? dex_location
186       : StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1);
187 }
188 
GetDexCanonicalLocation(const char * dex_location)189 std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) {
190   CHECK_NE(dex_location, static_cast<const char*>(nullptr));
191   std::string base_location = GetBaseLocation(dex_location);
192   const char* suffix = dex_location + base_location.size();
193   DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
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   } else if (suffix[0] == 0) {
200     return base_location;
201   } else {
202     return dex_location;
203   }
204 }
205 
206 // All of the implementations here should be independent of the runtime.
207 // TODO: implement all the virtual methods.
208 
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) const209 bool DexFileLoader::GetMultiDexChecksums(
210     const char* filename ATTRIBUTE_UNUSED,
211     std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED,
212     std::string* error_msg,
213     int zip_fd ATTRIBUTE_UNUSED,
214     bool* zip_file_only_contains_uncompress_dex ATTRIBUTE_UNUSED) const {
215   *error_msg = "UNIMPLEMENTED";
216   return false;
217 }
218 
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) const219 std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
220                                                    size_t size,
221                                                    const std::string& location,
222                                                    uint32_t location_checksum,
223                                                    const OatDexFile* oat_dex_file,
224                                                    bool verify,
225                                                    bool verify_checksum,
226                                                    std::string* error_msg) const {
227   return OpenCommon(base,
228                     size,
229                     /*data_base*/ nullptr,
230                     /*data_size*/ 0,
231                     location,
232                     location_checksum,
233                     oat_dex_file,
234                     verify,
235                     verify_checksum,
236                     error_msg,
237                     /*container*/ nullptr,
238                     /*verify_result*/ nullptr);
239 }
240 
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) const241 std::unique_ptr<const DexFile> DexFileLoader::OpenWithDataSection(
242     const uint8_t* base,
243     size_t size,
244     const uint8_t* data_base,
245     size_t data_size,
246     const std::string& location,
247     uint32_t location_checksum,
248     const OatDexFile* oat_dex_file,
249     bool verify,
250     bool verify_checksum,
251     std::string* error_msg) const {
252   return OpenCommon(base,
253                     size,
254                     data_base,
255                     data_size,
256                     location,
257                     location_checksum,
258                     oat_dex_file,
259                     verify,
260                     verify_checksum,
261                     error_msg,
262                     /*container*/ nullptr,
263                     /*verify_result*/ nullptr);
264 }
265 
OpenAll(const uint8_t * base,size_t size,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const266 bool DexFileLoader::OpenAll(
267     const uint8_t* base,
268     size_t size,
269     const std::string& location,
270     bool verify,
271     bool verify_checksum,
272     std::string* error_msg,
273     std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
274   DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
275   uint32_t magic = *reinterpret_cast<const uint32_t*>(base);
276   if (IsZipMagic(magic)) {
277     std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg));
278     if (zip_archive.get() == nullptr) {
279       DCHECK(!error_msg->empty());
280       return false;
281     }
282     return OpenAllDexFilesFromZip(*zip_archive.get(),
283                                   location,
284                                   verify,
285                                   verify_checksum,
286                                   error_msg,
287                                   dex_files);
288   }
289   if (IsMagicValid(magic)) {
290     const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base);
291     std::unique_ptr<const DexFile> dex_file(Open(base,
292                                                  size,
293                                                  location,
294                                                  dex_header->checksum_,
295                                                  /*oat_dex_file*/ nullptr,
296                                                  verify,
297                                                  verify_checksum,
298                                                  error_msg));
299     if (dex_file.get() != nullptr) {
300       dex_files->push_back(std::move(dex_file));
301       return true;
302     } else {
303       return false;
304     }
305   }
306   *error_msg = StringPrintf("Expected valid zip or dex file");
307   return false;
308 }
309 
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)310 std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
311                                                    size_t size,
312                                                    const uint8_t* data_base,
313                                                    size_t data_size,
314                                                    const std::string& location,
315                                                    uint32_t location_checksum,
316                                                    const OatDexFile* oat_dex_file,
317                                                    bool verify,
318                                                    bool verify_checksum,
319                                                    std::string* error_msg,
320                                                    std::unique_ptr<DexFileContainer> container,
321                                                    VerifyResult* verify_result) {
322   if (verify_result != nullptr) {
323     *verify_result = VerifyResult::kVerifyNotAttempted;
324   }
325   std::unique_ptr<DexFile> dex_file;
326   if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
327     if (data_size != 0) {
328       CHECK_EQ(base, data_base) << "Unsupported for standard dex";
329     }
330     dex_file.reset(new StandardDexFile(base,
331                                        size,
332                                        location,
333                                        location_checksum,
334                                        oat_dex_file,
335                                        std::move(container)));
336   } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
337     if (data_base == nullptr) {
338       // TODO: Is there a clean way to support both an explicit data section and reading the one
339       // from the header.
340       CHECK_EQ(data_size, 0u);
341       const CompactDexFile::Header* const header = CompactDexFile::Header::At(base);
342       data_base = base + header->data_off_;
343       data_size = header->data_size_;
344     }
345     dex_file.reset(new CompactDexFile(base,
346                                       size,
347                                       data_base,
348                                       data_size,
349                                       location,
350                                       location_checksum,
351                                       oat_dex_file,
352                                       std::move(container)));
353     // Disable verification for CompactDex input.
354     verify = false;
355   } else {
356     *error_msg = "Invalid or truncated dex file";
357   }
358   if (dex_file == nullptr) {
359     *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
360                               error_msg->c_str());
361     return nullptr;
362   }
363   if (!dex_file->Init(error_msg)) {
364     dex_file.reset();
365     return nullptr;
366   }
367   if (verify && !DexFileVerifier::Verify(dex_file.get(),
368                                          dex_file->Begin(),
369                                          dex_file->Size(),
370                                          location.c_str(),
371                                          verify_checksum,
372                                          error_msg)) {
373     if (verify_result != nullptr) {
374       *verify_result = VerifyResult::kVerifyFailed;
375     }
376     return nullptr;
377   }
378   if (verify_result != nullptr) {
379     *verify_result = VerifyResult::kVerifySucceeded;
380   }
381   return dex_file;
382 }
383 
OpenOneDexFileFromZip(const DexZipArchive & zip_archive,const char * entry_name,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,ZipOpenErrorCode * error_code) const384 std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
385     const DexZipArchive& zip_archive,
386     const char* entry_name,
387     const std::string& location,
388     bool verify,
389     bool verify_checksum,
390     std::string* error_msg,
391     ZipOpenErrorCode* error_code) const {
392   CHECK(!location.empty());
393   std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
394   if (zip_entry == nullptr) {
395     *error_code = ZipOpenErrorCode::kEntryNotFound;
396     return nullptr;
397   }
398   if (zip_entry->GetUncompressedLength() == 0) {
399     *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
400     *error_code = ZipOpenErrorCode::kDexFileError;
401     return nullptr;
402   }
403 
404   std::vector<uint8_t> map(zip_entry->Extract(error_msg));
405   if (map.size() == 0) {
406     *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
407                               error_msg->c_str());
408     *error_code = ZipOpenErrorCode::kExtractToMemoryError;
409     return nullptr;
410   }
411   VerifyResult verify_result;
412   std::unique_ptr<const DexFile> dex_file = OpenCommon(
413       map.data(),
414       map.size(),
415       /*data_base*/ nullptr,
416       /*data_size*/ 0u,
417       location,
418       zip_entry->GetCrc32(),
419       /*oat_dex_file*/ nullptr,
420       verify,
421       verify_checksum,
422       error_msg,
423       std::make_unique<VectorContainer>(std::move(map)),
424       &verify_result);
425   if (dex_file == nullptr) {
426     if (verify_result == VerifyResult::kVerifyNotAttempted) {
427       *error_code = ZipOpenErrorCode::kDexFileError;
428     } else {
429       *error_code = ZipOpenErrorCode::kVerifyError;
430     }
431     return nullptr;
432   }
433   if (verify_result != VerifyResult::kVerifySucceeded) {
434     *error_code = ZipOpenErrorCode::kVerifyError;
435     return nullptr;
436   }
437   *error_code = ZipOpenErrorCode::kNoError;
438   return dex_file;
439 }
440 
441 // Technically we do not have a limitation with respect to the number of dex files that can be in a
442 // multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
443 // (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
444 // seems an excessive number.
445 static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
446 
OpenAllDexFilesFromZip(const DexZipArchive & zip_archive,const std::string & location,bool verify,bool verify_checksum,std::string * error_msg,std::vector<std::unique_ptr<const DexFile>> * dex_files) const447 bool DexFileLoader::OpenAllDexFilesFromZip(
448     const DexZipArchive& zip_archive,
449     const std::string& location,
450     bool verify,
451     bool verify_checksum,
452     std::string* error_msg,
453     std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
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 }  // namespace art
509