• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "data_protect.h"
17 #include "file-inl.h"
18 #include "file_items.h"
19 #include "panda_cache.h"
20 #include "trace/trace.h"
21 #include "zip_archive.h"
22 
23 namespace panda::panda_file {
24 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
25 const char *ARCHIVE_FILENAME = "classes.abc";
26 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
27 const char *ARCHIVE_SPLIT = "!/";
28 
29 const std::array<uint8_t, File::MAGIC_SIZE> File::MAGIC {'P', 'A', 'N', 'D', 'A', '\0', '\0', '\0'};
30 
31 // Name anonymous maps for perfing tools finding symbol file correctly.
32 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
33 const char *ANONMAPNAME_PERFIX = "panda-";
34 
GetMode(panda_file::File::OpenMode open_mode)35 os::file::Mode GetMode(panda_file::File::OpenMode open_mode)
36 {
37     switch (open_mode) {
38         case File::READ_ONLY: {
39             return os::file::Mode::READONLY;
40         }
41         case File::READ_WRITE: {
42 #ifdef PANDA_TARGET_WINDOWS
43             return os::file::Mode::READWRITE;
44 #else
45             return os::file::Mode::READONLY;
46 #endif
47         }
48         case File::WRITE_ONLY: {
49             return os::file::Mode::WRITEONLY;
50         }
51         default: {
52             break;
53         }
54     }
55 
56     UNREACHABLE();
57 }
58 
GetProt(panda_file::File::OpenMode mode)59 static uint32_t GetProt(panda_file::File::OpenMode mode)
60 {
61     uint32_t prot = os::mem::MMAP_PROT_READ;
62     if (mode == File::READ_WRITE) {
63         prot |= os::mem::MMAP_PROT_WRITE;
64     }
65     return prot;
66 }
67 
68 class AnonMemSet {
69 public:
70     using MemNameSet = std::map<std::string, std::string>;
71     using InsertResult = std::map<std::string, std::string>::iterator;
72 
GetInstance()73     static AnonMemSet &GetInstance()
74     {
75         static AnonMemSet anon_mem_set;
76         return anon_mem_set;
77     }
78 
Insert(const std::string & file_name,const std::string & anon_mem_name)79     InsertResult Insert(const std::string &file_name, const std::string &anon_mem_name)
80     {
81         return mem_name_set_.emplace(file_name, anon_mem_name).first;
82     }
83 
Remove(const std::string & file_name)84     void Remove(const std::string &file_name)
85     {
86         auto it = mem_name_set_.find(file_name);
87         if (it != mem_name_set_.end()) {
88             mem_name_set_.erase(it);
89         }
90     }
91 
92 private:
93     MemNameSet mem_name_set_;
94 };
95 
OpenPandaFileOrZip(std::string_view location,panda_file::File::OpenMode open_mode)96 std::unique_ptr<const File> OpenPandaFileOrZip(std::string_view location, panda_file::File::OpenMode open_mode)
97 {
98     std::string_view archive_filename = ARCHIVE_FILENAME;
99     std::size_t archive_split_index = location.find(ARCHIVE_SPLIT);
100     if (archive_split_index != std::string::npos) {
101         archive_filename = location.substr(archive_split_index + 2);  // 2 - archive split size
102         location = location.substr(0, archive_split_index);
103     }
104 
105     return OpenPandaFile(location, archive_filename, open_mode);
106 }
107 
108 // NOLINTNEXTLINE(google-runtime-references)
OpenPandaFileFromZipErrorHandler(ZipArchiveHandle & handle)109 void OpenPandaFileFromZipErrorHandler(ZipArchiveHandle &handle)
110 {
111     if (handle != nullptr) {
112         if (panda::CloseArchiveFile(handle) != ZIPARCHIVE_OK) {
113             LOG(ERROR, PANDAFILE) << "CloseArchiveFile failed!";
114         }
115     }
116 }
117 
OpenPandaFileFromZipFile(ZipArchiveHandle & handle,std::string_view location,EntryFileStat & entry,const std::string_view & archive_name)118 std::unique_ptr<const panda_file::File> OpenPandaFileFromZipFile(ZipArchiveHandle &handle, std::string_view location,
119                                                                  EntryFileStat &entry,
120                                                                  const std::string_view &archive_name)
121 {
122     uint32_t uncompressed_length = entry.GetUncompressedSize();
123     ASSERT(uncompressed_length != 0U);
124 
125     size_t size_to_mmap = AlignUp(uncompressed_length, panda::os::mem::GetPageSize());
126     void *mem = os::mem::MapRWAnonymousRaw(size_to_mmap, false);
127     if (mem == nullptr) {
128         LOG(ERROR, PANDAFILE) << "Can't mmap anonymous!";
129         return nullptr;
130     }
131     os::mem::BytePtr ptr(reinterpret_cast<std::byte *>(mem), size_to_mmap, os::mem::MmapDeleter);
132     std::stringstream ss;
133     ss << ANONMAPNAME_PERFIX << archive_name << " extracted in memory from " << location;
134     auto it = AnonMemSet::GetInstance().Insert(std::string(location), ss.str());
135     auto ret = os::mem::TagAnonymousMemory(reinterpret_cast<void *>(ptr.Get()), size_to_mmap, it->second.c_str());
136     if (ret.has_value()) {
137         LOG(ERROR, PANDAFILE) << "Can't tag mmap anonymous!";
138         return nullptr;
139     }
140 
141     auto extract_error = ExtractToMemory(handle, reinterpret_cast<uint8_t *>(ptr.Get()), size_to_mmap);
142     if (extract_error != 0) {
143         LOG(ERROR, PANDAFILE) << "Can't extract!";
144         return nullptr;
145     }
146 
147     os::mem::ConstBytePtr ConstPtr = ptr.ToConst();
148     return panda_file::File::OpenFromMemory(std::move(ConstPtr), location);
149 }
150 
151 // NOLINTNEXTLINE(google-runtime-references)
HandleArchive(ZipArchiveHandle & handle,FILE * fp,std::string_view location,EntryFileStat & entry,const std::string_view & archive_filename,panda_file::File::OpenMode open_mode)152 std::unique_ptr<const panda_file::File> HandleArchive(ZipArchiveHandle &handle, FILE *fp, std::string_view location,
153                                                       EntryFileStat &entry, const std::string_view &archive_filename,
154                                                       panda_file::File::OpenMode open_mode)
155 {
156     std::unique_ptr<const panda_file::File> file;
157     // compressed or not 4 aligned, use anonymous memory
158     if (entry.IsCompressed() || (entry.GetOffset() & 0x3U) != 0) {
159         file = OpenPandaFileFromZipFile(handle, location, entry, archive_filename);
160     } else {
161         LOG(INFO, PANDAFILE) << "Pandafile is uncompressed and 4 bytes aligned";
162         file = panda_file::File::OpenUncompressedArchive(fileno(fp), location, entry.GetUncompressedSize(),
163                                                          entry.GetOffset(), open_mode);
164     }
165     return file;
166 }
167 
OpenPandaFileFromZip(FILE * fp,std::string_view location,std::string_view archive_filename,panda_file::File::OpenMode open_mode)168 std::unique_ptr<const panda_file::File> OpenPandaFileFromZip(FILE *fp, std::string_view location,
169                                                              std::string_view archive_filename,
170                                                              panda_file::File::OpenMode open_mode)
171 {
172     // Open Zipfile and do the extraction.
173     ZipArchiveHandle zipfile = nullptr;
174     if (OpenArchiveFile(zipfile, fp) != ZIPARCHIVE_OK) {
175         LOG(ERROR, PANDAFILE) << "Can't open archive " << location;
176         return nullptr;
177     }
178     bool try_default = archive_filename.empty();
179     if (!try_default && LocateFile(zipfile, archive_filename.data()) != ZIPARCHIVE_OK) {
180         LOG(INFO, PANDAFILE) << "Can't find entry with name '" <<
181             archive_filename << "', will try " << ARCHIVE_FILENAME;
182         try_default = true;
183     }
184     if (try_default && LocateFile(zipfile, ARCHIVE_FILENAME) != ZIPARCHIVE_OK) {
185         OpenPandaFileFromZipErrorHandler(zipfile);
186         LOG(ERROR, PANDAFILE) << "Can't find entry with " << ARCHIVE_FILENAME;
187         return nullptr;
188     }
189 
190     EntryFileStat entry = EntryFileStat();
191     if (GetCurrentFileInfo(zipfile, &entry) != ZIPARCHIVE_OK) {
192         OpenPandaFileFromZipErrorHandler(zipfile);
193         LOG(ERROR, PANDAFILE) << "GetCurrentFileInfo error";
194         return nullptr;
195     }
196     // check that file is not empty, otherwise crash at CloseArchiveFile
197     if (entry.GetUncompressedSize() == 0) {
198         OpenPandaFileFromZipErrorHandler(zipfile);
199         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << (try_default ? ARCHIVE_FILENAME : archive_filename) << "'";
200         return nullptr;
201     }
202     if (OpenCurrentFile(zipfile) != ZIPARCHIVE_OK) {
203         CloseCurrentFile(zipfile);
204         OpenPandaFileFromZipErrorHandler(zipfile);
205         LOG(ERROR, PANDAFILE) << "Can't OpenCurrentFile!";
206         return nullptr;
207     }
208     GetCurrentFileOffset(zipfile, &entry);
209     std::unique_ptr<const panda_file::File> file = HandleArchive(zipfile, fp, location, entry,
210                                                                  archive_filename, open_mode);
211     CloseCurrentFile(zipfile);
212     OpenPandaFileFromZipErrorHandler(zipfile);
213     return file;
214 }
215 
OpenPandaFile(std::string_view location,std::string_view archive_filename,panda_file::File::OpenMode open_mode)216 std::unique_ptr<const panda_file::File> OpenPandaFile(std::string_view location, std::string_view archive_filename,
217                                                       panda_file::File::OpenMode open_mode)
218 {
219     trace::ScopedTrace scoped_trace("Open panda file " + std::string(location));
220     uint32_t magic;
221 
222 #ifdef PANDA_TARGET_WINDOWS
223     constexpr char const *mode = "rb";
224 #else
225     constexpr char const *mode = "rbe";
226 #endif
227 
228     FILE *fp = fopen(std::string(location).c_str(), mode);
229     if (fp == nullptr) {
230         LOG(ERROR, PANDAFILE) << "Can't fopen location: " << location;
231         return nullptr;
232     }
233     (void)fseek(fp, 0, SEEK_SET);
234     if (fread(&magic, sizeof(magic), 1, fp) != 1) {
235         fclose(fp);
236         LOG(ERROR, PANDAFILE) << "Can't read from file!(magic) " << location;
237         return nullptr;
238     }
239     (void)fseek(fp, 0, SEEK_SET);
240     std::unique_ptr<const panda_file::File> file;
241     if (IsZipMagic(magic)) {
242         file = OpenPandaFileFromZip(fp, location, archive_filename, open_mode);
243     } else {
244         file = panda_file::File::Open(location, open_mode);
245     }
246     fclose(fp);
247     return file;
248 }
249 
OpenPandaFileFromMemory(const void * buffer,size_t size,std::string tag)250 std::unique_ptr<const File> OpenPandaFileFromMemory(const void *buffer, size_t size, std::string tag)
251 {
252     size_t size_to_mmap = AlignUp(size, panda::os::mem::GetPageSize());
253     void *mem = os::mem::MapRWAnonymousRaw(size_to_mmap, false);
254     if (mem == nullptr) {
255         return nullptr;
256     }
257 
258     if (memcpy_s(mem, size_to_mmap, buffer, size) != 0) {
259         PLOG(ERROR, PANDAFILE) << "Failed to copy buffer into mem'";
260     }
261 
262     if (!tag.empty()) {
263         if (tag == "ArkTS Code") {
264             std::string memAddr = std::to_string(ToUintPtr(mem));
265             tag = tag + ":" + memAddr;
266         }
267         auto ret = os::mem::TagAnonymousMemory(mem, size_to_mmap, tag.c_str());
268         if (ret.has_value()) {
269             PLOG(DEBUG, PANDAFILE) << "Can't tag mmap anonymous, errno: " << errno;
270         }
271     }
272 
273     os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(mem), size_to_mmap, os::mem::MmapDeleter);
274     if (ptr.Get() == nullptr) {
275         PLOG(ERROR, PANDAFILE) << "Failed to open panda file from memory'";
276         return nullptr;
277     }
278     std::hash<void *> hash;
279     return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
280 }
281 
OpenPandaFileFromSecureMemory(uint8_t * buffer,size_t size)282 std::unique_ptr<const File> OpenPandaFileFromSecureMemory(uint8_t *buffer, size_t size)
283 {
284     if (buffer == nullptr) {
285         PLOG(ERROR, PANDAFILE) << "OpenPandaFileFromSecureMemory buffer is nullptr'";
286         return nullptr;
287     }
288 
289     if (!CheckSecureMem(reinterpret_cast<uintptr_t>(buffer), size)) {
290         PLOG(ERROR, PANDAFILE) << "Secure memory check failed, please execute in secure memory.";
291         return nullptr;
292     }
293 
294     std::byte *mem = reinterpret_cast<std::byte *>(buffer);
295     os::mem::ConstBytePtr ptr(mem, size, nullptr);
296     if (ptr.Get() == nullptr) {
297         PLOG(ERROR, PANDAFILE) << "Failed to open panda file from secure memory'";
298         return nullptr;
299     }
300 
301     std::hash<std::byte *> hash;
302     return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
303 }
304 
CheckSecureMem(uintptr_t mem,size_t size)305 inline bool CheckSecureMem(uintptr_t mem, size_t size)
306 {
307     static bool has_open = false;
308     static DataProtect start = DataProtect();
309     static DataProtect end = DataProtect();
310     uintptr_t secure_mem_start;
311     uintptr_t secure_mem_end;
312     if (!has_open) {
313         int fd = open(PROC_SELF_XPM_REGION_PATH, O_RDONLY);
314         if (fd < 0) {
315             LOG(ERROR, PANDAFILE) << "Can not open xpm proc file, do not check secure memory anymore.";
316             // No verification is performed when a file fails to be opened.
317             has_open = true;
318             return true;
319         }
320         char xpm_validate_region[XPM_PROC_LENGTH] = {0};
321         int ret = read(fd, xpm_validate_region, sizeof(xpm_validate_region));
322         if (ret <= 0) {
323             LOG(ERROR, PANDAFILE) << "Read xpm proc file failed";
324             close(fd);
325             return false;
326         }
327         close(fd);
328         if (sscanf_s(xpm_validate_region, "%lx-%lx", &secure_mem_start, &secure_mem_end) <= 0) {
329             LOG(ERROR, PANDAFILE) << "sscanf_s xpm validate region failed";
330             return false;
331         }
332         // The check is not performed when the file is already opened.
333         has_open = true;
334         LOG(DEBUG, PANDAFILE) << "Successfully open xpm region.";
335         start.Update(secure_mem_start);
336         end.Update(secure_mem_end);
337     }
338     secure_mem_start = start.GetOriginPointer();
339     secure_mem_end = end.GetOriginPointer();
340     // xpm proc does not exist, the read value is 0, and the check is not performed.
341     if (secure_mem_start == 0 && secure_mem_end == 0) {
342         LOG(ERROR, PANDAFILE) << "Secure memory check: xpm proc does not exist, do not check secure memory anymore.";
343         return true;
344     }
345     if (mem < secure_mem_start || (size > (std::numeric_limits<uintptr_t>::max() - mem)) ||
346         (mem + size) > secure_mem_end) {
347         LOG(ERROR, PANDAFILE) << "Secure memory check failed, mem out of secure memory region.";
348         return false;
349     }
350     return true;
351 }
352 
353 class ClassIdxIterator {
354 public:
355     using value_type = const uint8_t *;
356     using difference_type = std::ptrdiff_t;
357     using pointer = uint32_t *;
358     using reference = uint32_t &;
359     using iterator_category = std::random_access_iterator_tag;
360 
ClassIdxIterator(const File & file,const Span<const uint32_t> & span,size_t idx)361     ClassIdxIterator(const File &file, const Span<const uint32_t> &span, size_t idx)
362         : file_(file), span_(span), idx_(idx)
363     {
364     }
365 
366     ClassIdxIterator(const ClassIdxIterator &other) = default;
367     ClassIdxIterator(ClassIdxIterator &&other) = default;
368     ~ClassIdxIterator() = default;
369 
operator =(const ClassIdxIterator & other)370     ClassIdxIterator &operator=(const ClassIdxIterator &other)
371     {
372         if (&other != this) {
373             idx_ = other.idx_;
374         }
375 
376         return *this;
377     }
378 
operator =(ClassIdxIterator && other)379     ClassIdxIterator &operator=(ClassIdxIterator &&other) noexcept
380     {
381         idx_ = other.idx_;
382         return *this;
383     }
384 
operator +=(size_t n)385     ClassIdxIterator &operator+=(size_t n)
386     {
387         idx_ += n;
388         return *this;
389     }
390 
operator -=(size_t n)391     ClassIdxIterator &operator-=(size_t n)
392     {
393         idx_ -= n;
394         return *this;
395     }
396 
operator ++()397     ClassIdxIterator &operator++()
398     {
399         ++idx_;
400         return *this;
401     }
402 
operator --()403     ClassIdxIterator &operator--()
404     {
405         --idx_;
406         return *this;
407     }
408 
operator -(const ClassIdxIterator & other)409     difference_type operator-(const ClassIdxIterator &other)
410     {
411         return idx_ - other.idx_;
412     }
413 
operator *() const414     value_type operator*() const
415     {
416         uint32_t id = span_[idx_];
417         return file_.GetStringData(File::EntityId(id)).data;
418     }
419 
IsValid() const420     bool IsValid() const
421     {
422         return idx_ < span_.Size();
423     }
424 
GetId() const425     uint32_t GetId() const
426     {
427         return span_[idx_];
428     }
429 
Begin(const File & file,const Span<const uint32_t> & span)430     static ClassIdxIterator Begin(const File &file, const Span<const uint32_t> &span)
431     {
432         return ClassIdxIterator(file, span, 0);
433     }
434 
End(const File & file,const Span<const uint32_t> & span)435     static ClassIdxIterator End(const File &file, const Span<const uint32_t> &span)
436     {
437         return ClassIdxIterator(file, span, span.Size());
438     }
439 
440 private:
441     const File &file_;
442     const Span<const uint32_t> &span_;
443     size_t idx_;
444 };
445 
File(std::string filename,os::mem::ConstBytePtr && base)446 File::File(std::string filename, os::mem::ConstBytePtr &&base)
447     : base_(std::forward<os::mem::ConstBytePtr>(base)),
448       FILENAME(std::move(filename)),
449       FILENAME_HASH(CalcFilenameHash(FILENAME)),
450 #ifdef ENABLE_FULL_FILE_FIELDS
451       FULL_FILENAME(os::GetAbsolutePath(FILENAME)),
452       panda_cache_(std::make_unique<PandaCache>()),
453 #endif
454       UNIQ_ID(merge_hashes(FILENAME_HASH, GetHash32(reinterpret_cast<const uint8_t *>(GetHeader()), sizeof(Header))))
455 {
456 }
457 
~File()458 File::~File()
459 {
460     AnonMemSet::GetInstance().Remove(FILENAME);
461 }
462 
VersionToString(const std::array<uint8_t,File::VERSION_SIZE> & array)463 inline std::string VersionToString(const std::array<uint8_t, File::VERSION_SIZE> &array)
464 {
465     std::stringstream ss;
466 
467     for (size_t i = 0; i < File::VERSION_SIZE - 1; ++i) {
468         ss << static_cast<int>(array[i]);
469         ss << ".";
470     }
471     ss << static_cast<int>(array[File::VERSION_SIZE - 1]);
472 
473     return ss.str();
474 }
475 
476 // We can't use default std::array's comparision operators and need to implement
477 // own ones due to the bug in gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189
CompareVersions(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)478 inline int CompareVersions(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
479                            const std::array<uint8_t, File::VERSION_SIZE> &rhs)
480 {
481     for (size_t i = 0; i < File::VERSION_SIZE; i++) {
482         if (lhs[i] == rhs[i]) {
483             continue;
484         }
485         return lhs[i] - rhs[i];
486     }
487     return 0;
488 }
489 
operator <(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)490 inline bool operator<(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
491                       const std::array<uint8_t, File::VERSION_SIZE> &rhs)
492 {
493     return CompareVersions(lhs, rhs) < 0;
494 }
495 
operator >(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)496 inline bool operator>(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
497                       const std::array<uint8_t, File::VERSION_SIZE> &rhs)
498 {
499     return CompareVersions(lhs, rhs) > 0;
500 }
501 
502 /* static */
Open(std::string_view filename,OpenMode open_mode)503 std::unique_ptr<const File> File::Open(std::string_view filename, OpenMode open_mode)
504 {
505     trace::ScopedTrace scoped_trace("Open panda file " + std::string(filename));
506     os::file::Mode mode = GetMode(open_mode);
507     os::file::File file = os::file::Open(filename, mode);
508     if (!file.IsValid()) {
509         PLOG(ERROR, PANDAFILE) << "Failed to open panda file '" << filename << "'";
510         return nullptr;
511     }
512 
513     os::file::FileHolder fh_holder(file);
514 
515     auto res = file.GetFileSize();
516     if (!res) {
517         PLOG(ERROR, PANDAFILE) << "Failed to get size of panda file '" << filename << "'";
518         return nullptr;
519     }
520 
521     size_t size = res.Value();
522     if (size < sizeof(File::Header)) {
523         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "' - missing or incomplete header" <<
524                                  ". Abc file is corrupted";
525         return nullptr;
526     }
527 
528     os::mem::ConstBytePtr ptr = os::mem::MapFile(file, GetProt(open_mode), os::mem::MMAP_FLAG_PRIVATE, size).ToConst();
529     if (ptr.Get() == nullptr) {
530         PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
531         return nullptr;
532     }
533 
534     if (!CheckHeader(ptr, filename)) {
535         return nullptr;
536     }
537 
538     return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
539 }
540 
OpenUncompressedArchive(int fd,const std::string_view & filename,size_t size,uint32_t offset,OpenMode open_mode)541 std::unique_ptr<const File> File::OpenUncompressedArchive(int fd, const std::string_view &filename, size_t size,
542                                                           uint32_t offset, OpenMode open_mode)
543 {
544     trace::ScopedTrace scoped_trace("Open panda file " + std::string(filename));
545     auto file = os::file::File(fd);
546     if (!file.IsValid()) {
547         PLOG(ERROR, PANDAFILE) << "OpenUncompressedArchive: Failed to open panda file '" << filename << "'";
548         return nullptr;
549     }
550 
551     if (size < sizeof(File::Header)) {
552         LOG(ERROR, PANDAFILE) << "Invalid panda file size '" << filename << "'" << ". Abc file is corrupted";
553         return nullptr;
554     }
555     LOG(DEBUG, PANDAFILE) << " size=" << size << " offset=" << offset << " " << filename;
556 
557     os::mem::ConstBytePtr ptr =
558         os::mem::MapFile(file, GetProt(open_mode), os::mem::MMAP_FLAG_PRIVATE, size, offset).ToConst();
559     if (ptr.Get() == nullptr) {
560         PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
561         return nullptr;
562     }
563     if (!CheckHeader(ptr, filename)) {
564         return nullptr;
565     }
566 
567     return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
568 }
569 
570 template <typename T = uint32_t>
CheckHeaderElementOffset(size_t offset,size_t number,size_t file_size)571 bool CheckHeaderElementOffset(size_t offset, size_t number, size_t file_size)
572 {
573     auto number_size = number * sizeof(T);
574     if (offset > file_size || number_size > file_size || offset > file_size - number_size) {
575         return false;
576     }
577     return true;
578 }
579 
CheckHeader(const os::mem::ConstBytePtr & ptr,const std::string_view & filename)580 bool CheckHeader(const os::mem::ConstBytePtr &ptr, const std::string_view &filename)
581 {
582     if (ptr.Get() == nullptr || ptr.GetSize() < sizeof(File::Header)) {
583         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'" << ". Abc file is corrupted";
584         return false;
585     }
586     auto header = reinterpret_cast<const File::Header *>(reinterpret_cast<uintptr_t>(ptr.Get()));
587     if (header->magic != File::MAGIC) {
588         LOG(ERROR, PANDAFILE) << "Invalid magic number" << ". Abc file is corrupted";
589         return false;
590     }
591 
592     CheckFileVersion(header->version, filename);
593 
594     if (header->file_size < sizeof(File::Header) || header->file_size > ptr.GetSize()) {
595         LOG(ERROR, PANDAFILE) << "Invalid panda file size " << header->file_size << ". Abc file is corrupted";
596         return false;
597     }
598 
599     if (!CheckHeaderElementOffset<uint8_t>(header->foreign_off, header->foreign_size, header->file_size)) {
600         LOG(ERROR, PANDAFILE) << "Invalid panda file foreign_off " << header->foreign_off <<
601             " or foreign_size " << header->foreign_size << ". Abc file is corrupted";
602         return false;
603     }
604 
605     if (!CheckHeaderElementOffset(header->class_idx_off, header->num_classes, header->file_size)) {
606         LOG(ERROR, PANDAFILE) << "Invalid panda file class_idx_off " << header->class_idx_off <<
607             " or num_classes " << header->num_classes << ". Abc file is corrupted";
608         return false;
609     }
610 
611     if (!CheckHeaderElementOffset(header->lnp_idx_off, header->num_lnps, header->file_size)) {
612         LOG(ERROR, PANDAFILE) << "Invalid panda file lnp_idx_off " << header->lnp_idx_off <<
613             " or num_lnps " << header->num_lnps << ". Abc file is corrupted";
614         return false;
615     }
616 
617     if (ContainsLiteralArrayInHeader(header->version)) {
618         if (!CheckHeaderElementOffset(header->literalarray_idx_off, header->num_literalarrays, header->file_size)) {
619             LOG(ERROR, PANDAFILE) << "Invalid panda file literalarray_idx_off " << header->literalarray_idx_off <<
620                                      " or num_literalarrays " << header->num_literalarrays <<
621                                      ". Abc file is corrupted";
622             return false;
623         }
624     } else {
625         if (header->literalarray_idx_off != INVALID_INDEX || header->num_literalarrays != INVALID_OFFSET) {
626             LOG(ERROR, PANDAFILE) << "Invalid panda file literalarray_idx_off " << header->literalarray_idx_off <<
627                                      " or num_literalarrays " << header->num_literalarrays <<
628                                      ", The literalarray_idx_off and num_literalarrays should be reserved." <<
629                                      " Abc file is corrupted";
630             return false;
631         }
632     }
633 
634     if (!CheckHeaderElementOffset<File::IndexHeader>(header->index_section_off, header->num_indexes,
635         header->file_size)) {
636         LOG(ERROR, PANDAFILE) << "Invalid panda file index_section_off " << header->index_section_off <<
637             " or num_indexes " << header->num_indexes << ". Abc file is corrupted";
638         return false;
639     }
640 
641     return true;
642 }
643 
CheckFileVersion(const std::array<uint8_t,File::VERSION_SIZE> & file_version,const std::string_view & filename)644 void CheckFileVersion(const std::array<uint8_t, File::VERSION_SIZE> &file_version, const std::string_view &filename)
645 {
646 #ifdef ERROR_AS_FATAL
647 #define LOG_LEVEL FATAL
648 #else
649 #define LOG_LEVEL ERROR
650 #endif
651     if (file_version == version) {
652         return;
653     }
654     if (file_version < minVersion) {
655         LOG(LOG_LEVEL, PANDAFILE) << "Unable to open file '" << filename << "' with abc file version "
656             << VersionToString(file_version)
657             << ". Minimum supported abc file version on the current system image is " << VersionToString(minVersion)
658             << ". Please upgrade the sdk tools to generate supported version of abc files "
659             << "or execute the abc file on former version of system image";
660     } else if (file_version > version) {
661         LOG(LOG_LEVEL, PANDAFILE) << "Unable to open file '" << filename << "' with abc file version "
662             << VersionToString(file_version)
663             << ". Maximum supported abc file version on the current system image is " << VersionToString(version)
664             << ". Please upgrade the system image or use former version of SDK tools to generate abc files";
665     } else if (incompatibleVersion.count(file_version) != 0) {
666         LOG(LOG_LEVEL, PANDAFILE) << "Unable to open file '" << filename << "' with  abc file version "
667             << VersionToString(file_version) << ". Current system image version is "
668             << VersionToString(version) << ", while abc file version is " << VersionToString(file_version)
669             << ". The version "<< VersionToString(file_version)
670             << " is not a compatible version, can't run on system image of version " << VersionToString(version)
671             << ". Please use sdk tools and system image in pairs "
672             << "and make the version of sdk tools and system image consistent";
673     }
674 #undef LOG_LEVEL
675 }
676 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr)677 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr)
678 {
679     if (!CheckHeader(ptr, std::string_view())) {
680         return nullptr;
681     }
682 
683     return std::unique_ptr<File>(new File("", std::forward<os::mem::ConstBytePtr>(ptr)));
684 }
685 
686 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr,std::string_view filename)687 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr, std::string_view filename)
688 {
689     trace::ScopedTrace scoped_trace("Open panda file from RAM " + std::string(filename));
690 
691     if (!CheckHeader(ptr, filename)) {
692         return nullptr;
693     }
694 
695     return std::unique_ptr<File>(new File(filename.data(), std::forward<os::mem::ConstBytePtr>(ptr)));
696 }
697 
GetClassId(const uint8_t * mutf8_name) const698 File::EntityId File::GetClassId(const uint8_t *mutf8_name) const
699 {
700     auto class_hash_table = GetClassHashTable();
701     if (!class_hash_table.empty()) {
702         return GetClassIdFromClassHashTable(mutf8_name);
703     }
704 
705     auto class_idx = GetClasses();
706 
707     auto it = std::lower_bound(ClassIdxIterator::Begin(*this, class_idx), ClassIdxIterator::End(*this, class_idx),
708                                mutf8_name, utf::Mutf8Less());
709     if (!it.IsValid()) {
710         return EntityId();
711     }
712 
713     if (utf::CompareMUtf8ToMUtf8(mutf8_name, *it) == 0) {
714         return EntityId(it.GetId());
715     }
716 
717     return EntityId();
718 }
719 
CalcFilenameHash(const std::string & filename)720 uint32_t File::CalcFilenameHash(const std::string &filename)
721 {
722     return GetHash32String(reinterpret_cast<const uint8_t *>(filename.c_str()));
723 }
724 
GetLiteralArraysId() const725 File::EntityId File::GetLiteralArraysId() const
726 {
727     const Header *header = GetHeader();
728     return EntityId(header->literalarray_idx_off);
729 }
730 
GetClassIdFromClassHashTable(const uint8_t * mutf8_name) const731 File::EntityId File::GetClassIdFromClassHashTable(const uint8_t *mutf8_name) const
732 {
733     auto class_hash_table = GetClassHashTable();
734     auto hash = GetHash32String(mutf8_name);
735     auto pos = hash & (class_hash_table.size() - 1);
736     auto entity_pair = &class_hash_table[pos];
737 
738     if (entity_pair->descriptor_hash % class_hash_table.size() != pos) {
739         return File::EntityId();
740     }
741 
742     while (true) {
743         if (hash == entity_pair->descriptor_hash) {
744             auto entity_id = File::EntityId(entity_pair->entity_id_offset);
745             auto descriptor = GetStringData(entity_id).data;
746             if (entity_id.IsValid() && utf::CompareMUtf8ToMUtf8(descriptor, mutf8_name) == 0) {
747                 return entity_id;
748             }
749         }
750         if (entity_pair->next_pos == 0) {
751             break;
752         }
753         entity_pair = &class_hash_table[entity_pair->next_pos - 1];
754     }
755 
756     return File::EntityId();
757 }
758 
759 
ContainsLiteralArrayInHeader(const std::array<uint8_t,File::VERSION_SIZE> & version)760 bool ContainsLiteralArrayInHeader(const std::array<uint8_t, File::VERSION_SIZE> &version)
761 {
762     return panda::panda_file::IsVersionLessOrEqual(version, LAST_CONTAINS_LITERAL_IN_HEADER_VERSION);
763 }
764 
ValidateChecksum(uint32_t * cal_checksum_out) const765 bool File::ValidateChecksum(uint32_t *cal_checksum_out) const
766 {
767     if (UNLIKELY(GetHeader() == nullptr)) {
768         LOG(FATAL, PANDAFILE) << "Header pointer is nullptr. Abc file is corrupted";
769     }
770     constexpr uint32_t CHECKSUM_SIZE = 4U;
771     // The checksum calculation does not include magic or checksum, so the offset needs to be added
772     constexpr uint32_t FILE_CONTENT_OFFSET = File::MAGIC_SIZE + CHECKSUM_SIZE;
773     uint32_t file_size = GetHeader()->file_size;
774     uint32_t cal_checksum = adler32(1, GetBase() + FILE_CONTENT_OFFSET, file_size - FILE_CONTENT_OFFSET);
775 
776     if (cal_checksum_out != nullptr) {
777         *cal_checksum_out = cal_checksum;
778     }
779 
780     return GetHeader()->checksum == cal_checksum;
781 }
782 
ThrowIfWithCheck(bool cond,const std::string_view & msg,const std::string_view & tag) const783 void File::ThrowIfWithCheck(bool cond, const std::string_view& msg, const std::string_view& tag) const
784 {
785     if (UNLIKELY(cond)) {
786 #ifndef SUPPORT_KNOWN_EXCEPTION
787         uint32_t cal_checksum = 0;
788         bool is_checksum_match = ValidateChecksum(&cal_checksum);
789         if (!is_checksum_match) {
790             LOG(FATAL, PANDAFILE) << msg << ", checksum mismatch. The abc file has been corrupted. "
791                                          << "Expected checksum: 0x" << std::hex << GetHeader()->checksum
792                                          << ", Actual checksum: 0x" << std::hex << cal_checksum;
793         }
794 
795         if (!tag.empty()) {
796             LOG(FATAL, PANDAFILE) << msg << ", from method: " << tag;
797         } else {
798             LOG(FATAL, PANDAFILE) << msg;
799         }
800 #else
801         throw helpers::FileAccessException(msg);
802 #endif
803     }
804 }
805 }  // namespace panda::panda_file
806