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