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_, ZipString(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 && !DexFileVerifier::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