• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 "file-inl.h"
17 #include "file_items.h"
18 #include "file_item_container.h"
19 #include "file_reader.h"
20 #include "utils/string_helpers.h"
21 #include "zip_archive.h"
22 #include "file.h"
23 #include "external/panda_file_support.cpp"
24 
25 #include "assembly-emitter.h"
26 #include "assembly-parser.h"
27 
28 #include <array>
29 #include <cstdint>
30 #ifdef PANDA_TARGET_MOBILE
31 #include <unistd.h>
32 #endif
33 
34 #include <vector>
35 
36 #include <gtest/gtest.h>
37 
38 namespace ark::panda_file::test {
39 
40 static constexpr const char *ABC_FILE = "test_file.abc";
41 
GetPandaFile(std::vector<uint8_t> * data)42 static std::unique_ptr<const File> GetPandaFile(std::vector<uint8_t> *data)
43 {
44     os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(data->data()), data->size(),
45                               [](std::byte *, size_t) noexcept {});
46     return File::OpenFromMemory(std::move(ptr));
47 }
48 
GetEmptyPandaFileBytes()49 static std::vector<uint8_t> GetEmptyPandaFileBytes()
50 {
51     pandasm::Parser p;
52 
53     auto source = R"()";
54 
55     std::string srcFilename = "src.pa";
56     auto res = p.Parse(source, srcFilename);
57     ASSERT(p.ShowError().err == pandasm::Error::ErrorType::ERR_NONE);
58 
59     auto pf = pandasm::AsmEmitter::Emit(res.Value());
60     ASSERT(pf != nullptr);
61 
62     std::vector<uint8_t> data {};
63     const auto headerPtr = reinterpret_cast<const uint8_t *>(pf->GetHeader());
64     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
65     data.assign(headerPtr, headerPtr + sizeof(File::Header));
66 
67     ASSERT(data.size() == sizeof(File::Header));
68 
69     return data;
70 }
71 
CreateOrAddZipPandaFile(std::vector<uint8_t> * data,const char * zipArchiveName,const char * filename,int append,int level)72 int CreateOrAddZipPandaFile(std::vector<uint8_t> *data, const char *zipArchiveName, const char *filename, int append,
73                             int level)
74 {
75     return CreateOrAddFileIntoZip(zipArchiveName, filename, data, append, level);
76 }
77 
CheckAnonMemoryName(const char * zipArchiveName)78 bool CheckAnonMemoryName([[maybe_unused]] const char *zipArchiveName)
79 {
80     // check if [annon:panda-classes.abc extracted in memory from /xx/__OpenPandaFileFromZip__.zip]
81 #ifdef PANDA_TARGET_MOBILE
82     bool result = false;
83     const char *prefix = "[anon:panda-";
84     int pid = getpid();
85     std::stringstream ss;
86     ss << "/proc/" << pid << "/maps";
87     std::ifstream f;
88     f.open(ss.str(), std::ios::in);
89     EXPECT_TRUE(f.is_open());
90     for (std::string line; std::getline(f, line);) {
91         if (line.find(prefix) != std::string::npos && line.find(zipArchiveName) != std::string::npos) {
92             result = true;
93         }
94     }
95     f.close();
96     return result;
97 #else
98     return true;
99 #endif
100 }
101 
TEST(File,OpenMemory)102 TEST(File, OpenMemory)
103 {
104     {
105         auto data = GetEmptyPandaFileBytes();
106         auto ptr = GetPandaFile(&data);
107         EXPECT_NE(ptr, nullptr);
108     }
109 
110     {
111         auto data = GetEmptyPandaFileBytes();
112         data[0] = 0x0;  // Corrupt magic
113 
114         auto ptr = GetPandaFile(&data);
115         EXPECT_EQ(ptr, nullptr);
116     }
117 }
118 
TEST(File,GetClassByName)119 TEST(File, GetClassByName)
120 {
121     ItemContainer container;
122 
123     std::vector<std::string> names = {"C", "B", "A"};
124     std::vector<ClassItem *> classes;
125     classes.reserve(names.size());
126 
127     for (auto &name : names) {
128         classes.push_back(container.GetOrCreateClassItem(name));
129     }
130 
131     MemoryWriter memWriter;
132 
133     ASSERT_TRUE(container.Write(&memWriter));
134 
135     // Read panda file from memory
136 
137     auto data = memWriter.GetData();
138     auto pandaFile = GetPandaFile(&data);
139     ASSERT_NE(pandaFile, nullptr);
140 
141     for (size_t i = 0; i < names.size(); i++) {
142         EXPECT_EQ(pandaFile->GetClassId(reinterpret_cast<const uint8_t *>(names[i].c_str())).GetOffset(),
143                   classes[i]->GetOffset());
144     }
145 }
146 
TEST(File,OpenPandaFile)147 TEST(File, OpenPandaFile)
148 {
149     // Create ZIP
150     auto data = GetEmptyPandaFileBytes();
151     int ret;
152     const char *zipFilename = "__OpenPandaFile__.zip";
153     const char *filename1 = ARCHIVE_FILENAME;
154     const char *filename2 = "classses2.abc";  // just for testing.
155     ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_CREATE, Z_BEST_COMPRESSION);
156     ASSERT_EQ(ret, 0);
157     ret = CreateOrAddZipPandaFile(&data, zipFilename, filename2, APPEND_STATUS_ADDINZIP, Z_BEST_COMPRESSION);
158     ASSERT_EQ(ret, 0);
159 
160     // Open from ZIP
161     auto pf = OpenPandaFile(zipFilename);
162     EXPECT_NE(pf, nullptr);
163     EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
164     remove(zipFilename);
165 }
166 
TEST(File,OpenPandaFileFromMemory)167 TEST(File, OpenPandaFileFromMemory)
168 {
169     auto pf = OpenPandaFileFromMemory(nullptr, -1);
170     EXPECT_EQ(pf, nullptr);
171 
172     pf = OpenPandaFileFromMemory(nullptr, 1U);
173     EXPECT_EQ(pf, nullptr);
174 
175     std::string tag = "ArkTS Code";
176     pf = OpenPandaFileFromMemory(nullptr, -1, tag);
177     EXPECT_EQ(pf, nullptr);
178 
179     tag = "";
180     pf = OpenPandaFileFromMemory(nullptr, 1U, tag);
181     EXPECT_EQ(pf, nullptr);
182 }
183 
TEST(File,OpenPandaFileFromZipNameAnonMem)184 TEST(File, OpenPandaFileFromZipNameAnonMem)
185 {
186     // Create ZIP
187     auto data = GetEmptyPandaFileBytes();
188     int ret;
189     const char *zipFilename = "__OpenPandaFileFromZipNameAnonMem__.zip";
190     const char *filename1 = ARCHIVE_FILENAME;
191     ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_CREATE, Z_BEST_COMPRESSION);
192     ASSERT_EQ(ret, 0);
193 
194     // Open from ZIP
195     auto pf = OpenPandaFile(zipFilename);
196     EXPECT_NE(pf, nullptr);
197     EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
198     ASSERT_TRUE(CheckAnonMemoryName(zipFilename));
199     remove(zipFilename);
200 }
201 
TEST(File,OpenPandaFileOrZip)202 TEST(File, OpenPandaFileOrZip)
203 {
204     // Create ZIP
205     auto data = GetEmptyPandaFileBytes();
206     int ret;
207     const char *zipFilename = "__OpenPandaFileOrZip__.zip";
208     const char *filename1 = ARCHIVE_FILENAME;
209     const char *filename2 = "classes2.abc";  // just for testing.
210     ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_CREATE, Z_BEST_COMPRESSION);
211     ASSERT_EQ(ret, 0);
212     ret = CreateOrAddZipPandaFile(&data, zipFilename, filename2, APPEND_STATUS_ADDINZIP, Z_BEST_COMPRESSION);
213     ASSERT_EQ(ret, 0);
214 
215     // Open from ZIP
216     auto pf = OpenPandaFileOrZip(zipFilename);
217     EXPECT_NE(pf, nullptr);
218     EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
219     remove(zipFilename);
220 }
221 
TEST(File,OpenPandaFileFromZipErrorHandler)222 TEST(File, OpenPandaFileFromZipErrorHandler)
223 {
224     const char *fileName = "test_file_empty.panda";
225     {
226         auto writer = FileWriter(fileName);
227         ASSERT_TRUE(writer);
228     }
229     auto pf = OpenPandaFile(fileName);
230     EXPECT_EQ(pf, nullptr);
231 
232     const char *fileNameZip = "test_file_empty.zip";
233     {
234         auto writer = FileWriter(fileName);
235         ASSERT_TRUE(writer);
236         const std::vector<uint8_t> magic = {'P', 'K'};
237         ASSERT_TRUE(writer.WriteBytes(magic));
238     }
239     pf = OpenPandaFile(fileNameZip);
240     EXPECT_EQ(pf, nullptr);
241 
242     // Create ZIP
243     const char *fileNameZipWithEntry = "test_file_with_entry.zip";
244     std::vector<uint8_t> data = {};
245     int ret = CreateOrAddZipPandaFile(&data, fileNameZipWithEntry, ARCHIVE_FILENAME, APPEND_STATUS_CREATE,
246                                       Z_BEST_COMPRESSION);
247     ASSERT_EQ(ret, 0);
248     pf = OpenPandaFile(fileNameZipWithEntry);
249     EXPECT_EQ(pf, nullptr);
250 
251     auto fp = fopen(fileNameZipWithEntry, "ae");
252     EXPECT_NE(fp, nullptr);
253     const char *appendStr = "error";
254     EXPECT_EQ(fwrite(appendStr, 1U, 1U, fp), 1U);
255     fclose(fp);
256     pf = OpenPandaFile(fileNameZipWithEntry);
257     EXPECT_EQ(pf, nullptr);
258 
259     remove(fileName);
260     remove(fileNameZip);
261     remove(fileNameZipWithEntry);
262 }
263 
TEST(File,HandleArchive)264 TEST(File, HandleArchive)
265 {
266     {
267         ItemContainer container;
268         auto writer = FileWriter(ARCHIVE_FILENAME);
269         ASSERT_TRUE(container.Write(&writer));
270         ASSERT_TRUE(writer.Align(4U));  // to 4 bytes align
271     }
272 
273     std::vector<uint8_t> data;
274     {
275         std::ifstream in(ARCHIVE_FILENAME, std::ios::binary);
276         data.insert(data.end(), (std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
277         ASSERT_TRUE(!data.empty() && data.size() % 4U == 0U);
278     }
279 
280     // Create ZIP
281     const char *zipFilename = "__HandleArchive__.zip";
282     int ret = CreateOrAddZipPandaFile(&data, zipFilename, ARCHIVE_FILENAME, APPEND_STATUS_CREATE, Z_NO_COMPRESSION);
283     ASSERT_EQ(ret, 0);
284     auto pf = OpenPandaFile(zipFilename);
285     EXPECT_NE(pf, nullptr);
286 
287     remove(ARCHIVE_FILENAME);
288     remove(zipFilename);
289 }
290 
TEST(File,CheckHeader1)291 TEST(File, CheckHeader1)
292 {
293     // Write panda file to disk
294     ItemContainer container;
295 
296     auto writer = FileWriter(ABC_FILE);
297     ASSERT_TRUE(container.Write(&writer));
298 
299     // Read panda file from disk
300     auto fp = fopen(ABC_FILE, "rbe");
301     EXPECT_NE(fp, nullptr);
302 
303     os::mem::ConstBytePtr ptr = os::mem::MapFile(os::file::File(fileno(fp)), os::mem::MMAP_PROT_READ,
304                                                  os::mem::MMAP_FLAG_PRIVATE, writer.GetOffset())
305                                     .ToConst();
306     EXPECT_NE(ptr.Get(), nullptr);
307     EXPECT_TRUE(CheckHeader(ptr, ABC_FILE));
308     fclose(fp);
309 
310     remove(ABC_FILE);
311 }
312 
TEST(File,GetMode)313 TEST(File, GetMode)
314 {
315     // Write panda file to disk
316     ItemContainer container;
317 
318     auto writer = FileWriter(ABC_FILE);
319     ASSERT_TRUE(container.Write(&writer));
320 
321     // Read panda file from disk
322     EXPECT_NE(File::Open(ABC_FILE), nullptr);
323     EXPECT_EQ(File::Open(ABC_FILE, File::OpenMode::WRITE_ONLY), nullptr);
324 
325     remove(ABC_FILE);
326 }
327 
TEST(File,Open)328 TEST(File, Open)
329 {
330     EXPECT_EQ(File::Open(ABC_FILE), nullptr);
331 
332     auto fp = fopen(ABC_FILE, "we");
333     EXPECT_NE(fp, nullptr);
334     const char *writeStr = "error";
335     EXPECT_EQ(fwrite(writeStr, 1U, 1U, fp), 1U);
336     fclose(fp);
337     EXPECT_EQ(File::Open(ABC_FILE), nullptr);
338     EXPECT_EQ(File::Open(ABC_FILE, File::OpenMode::WRITE_ONLY), nullptr);
339 
340     remove(ABC_FILE);
341 }
342 
TEST(File,TestValidChecksum)343 TEST(File, TestValidChecksum)
344 {
345     ItemContainer container;
346     auto writer = FileWriter(ABC_FILE);
347     ASSERT_TRUE(container.Write(&writer));
348 
349     // Read panda file from disk
350     auto fp = fopen(ABC_FILE, "rbe");
351     EXPECT_NE(fp, nullptr);
352 
353     os::mem::ConstBytePtr ptr = os::mem::MapFile(os::file::File(fileno(fp)), os::mem::MMAP_PROT_READ,
354                                                  os::mem::MMAP_FLAG_PRIVATE, writer.GetOffset())
355                                     .ToConst();
356     EXPECT_TRUE(ValidateChecksum(ptr, ABC_FILE));
357     fclose(fp);
358 
359     remove(ABC_FILE);
360 }
361 
TEST(File,TestInvalidChecksum)362 TEST(File, TestInvalidChecksum)
363 {
364     ItemContainer container;
365     auto writer = FileWriter(ABC_FILE);
366     ASSERT_TRUE(container.Write(&writer));
367 
368     // Read panda file from disk, and make it invalid
369     auto fp = fopen(ABC_FILE, "rb+e");
370     EXPECT_NE(fp, nullptr);
371     File::Header hdr = {};
372     auto cnt = fread(&hdr, sizeof(File::Header), 1, fp);
373     EXPECT_GT(cnt, 0);
374     const uint32_t destructiveBytes = 100;
375     hdr.numIndexes = destructiveBytes;
376     fseek(fp, 0, SEEK_SET);
377     cnt = fwrite(&hdr, sizeof(uint8_t), sizeof(File::Header), fp);
378     EXPECT_GT(cnt, 0);
379     fclose(fp);
380 
381     // Read and Check
382     fp = fopen(ABC_FILE, "rbe");
383     EXPECT_NE(fp, nullptr);
384     cnt = fread(&hdr, sizeof(File::Header), 1, fp);
385     EXPECT_GT(cnt, 0);
386     os::mem::ConstBytePtr ptr = os::mem::MapFile(os::file::File(fileno(fp)), os::mem::MMAP_PROT_READ,
387                                                  os::mem::MMAP_FLAG_PRIVATE, writer.GetOffset())
388                                     .ToConst();
389     EXPECT_FALSE(ValidateChecksum(ptr, ABC_FILE));
390     fclose(fp);
391     remove(ABC_FILE);
392 }
393 
TEST(File,OpenUncompressedArchive)394 TEST(File, OpenUncompressedArchive)
395 {
396     // Invalid FD
397     EXPECT_EQ(File::OpenUncompressedArchive(-1, ABC_FILE, 0U, 0U), nullptr);
398 
399     // Invalid Size
400     EXPECT_EQ(File::OpenUncompressedArchive(1, ABC_FILE, 0U, 0U), nullptr);
401 
402     // Invalid Max Size
403     EXPECT_EQ(File::OpenUncompressedArchive(1, ABC_FILE, -1, 0U), nullptr);
404 
405     // Invalid ABC File
406     auto data = GetEmptyPandaFileBytes();
407     auto fp = fopen(ARCHIVE_FILENAME, "w+e");
408     EXPECT_NE(fp, nullptr);
409     data[0] = 0U;
410     EXPECT_EQ(fwrite(data.data(), sizeof(uint8_t), data.size(), fp), data.size());
411     (void)fseek(fp, 0, SEEK_SET);
412     EXPECT_EQ(File::OpenUncompressedArchive(fileno(fp), ARCHIVE_FILENAME, sizeof(File::Header), 0U), nullptr);
413     fclose(fp);
414 
415     remove(ABC_FILE);
416 }
417 
TEST(File,CheckLiteralArray)418 TEST(File, CheckLiteralArray)
419 {
420     // Write panda file to disk
421     ItemContainer container;
422 
423     auto writer = FileWriter(ABC_FILE);
424     ASSERT_TRUE(container.Write(&writer));
425 
426     // Read panda file from disk
427     auto fp = fopen(ABC_FILE, "rbe");
428     EXPECT_NE(fp, nullptr);
429 
430     os::mem::ConstBytePtr ptr =
431         os::mem::MapFile(os::file::File(fileno(fp)), os::mem::MMAP_PROT_READ | os::mem::MMAP_PROT_WRITE,
432                          os::mem::MMAP_FLAG_PRIVATE, writer.GetOffset())
433             .ToConst();
434     EXPECT_NE(ptr.Get(), nullptr);
435     EXPECT_TRUE(CheckHeader(ptr, ABC_FILE));
436 
437     fclose(fp);
438 
439     remove(ABC_FILE);
440 }
441 
TEST(File,OpenPandaFileUncompressed)442 TEST(File, OpenPandaFileUncompressed)
443 {
444     // Create ZIP
445     auto data = GetEmptyPandaFileBytes();
446     std::cout << "pandafile size = " << data.size() << std::endl;
447     int ret;
448     const char *zipFilename = "__OpenPandaFileUncompressed__.zip";
449     const char *filename1 = ARCHIVE_FILENAME;
450     const char *filename2 = "class.abc";  // just for testing.
451     ret = CreateOrAddZipPandaFile(&data, zipFilename, filename2, APPEND_STATUS_CREATE, Z_NO_COMPRESSION);
452     ASSERT_EQ(ret, 0);
453     ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_ADDINZIP, Z_NO_COMPRESSION);
454     ASSERT_EQ(ret, 0);
455 
456     // Open from ZIP
457     auto pf = OpenPandaFileOrZip(zipFilename);
458     EXPECT_NE(pf, nullptr);
459     EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
460     remove(zipFilename);
461 }
462 
TEST(File,LineNumberProgramDeduplication)463 TEST(File, LineNumberProgramDeduplication)
464 {
465     pandasm::Parser p;
466 
467     auto source = R"delim(
468         .function void foo() {
469             return.void
470         }
471 
472         .function void bar() {
473             return.void
474         }
475     )delim";
476 
477     std::string srcFilename = "src.pa";
478     auto res = p.Parse(source, srcFilename);
479     ASSERT(p.ShowError().err == pandasm::Error::ErrorType::ERR_NONE);
480 
481     ASSERT_EQ(res.Value().functionStaticTable.size(), 2);
482     for (auto &a : res.Value().functionStaticTable) {
483         ASSERT_TRUE(a.second.HasDebugInfo());
484     }
485 
486     auto pf = pandasm::AsmEmitter::Emit(res.Value());
487     ASSERT(pf != nullptr);
488 
489     auto reader = FileReader(std::move(pf));
490 
491     ASSERT_TRUE(reader.ReadContainer());
492 
493     int lnpCnt = 0;
494 
495     ASSERT_NE(reader.GetItems()->size(), 0);
496 
497     for (const auto &a : *reader.GetItems()) {
498         const auto typ = a.second->GetItemType();
499         if (typ == ItemTypes::LINE_NUMBER_PROGRAM_ITEM) {
500             lnpCnt++;
501         }
502     }
503 
504     reader.GetContainerPtr()->ComputeLayout();
505     reader.GetContainerPtr()->DeduplicateCodeAndDebugInfo();
506 
507     ASSERT_EQ(lnpCnt, 1);
508 }
509 
510 constexpr uint8_t FIZE_SIZE_ERROR_SIZE = 0x09;
TEST(File,CheckHeader)511 TEST(File, CheckHeader)
512 {
513     auto data = GetEmptyPandaFileBytes();
514     auto f = GetPandaFile(&data);
515     auto res = ark::panda_file::CheckHeader(f->GetPtr(), std::string_view(), data.size());
516     EXPECT_EQ(res, true);
517 
518     const int fileSizeIndex = offsetof(ark::panda_file::File::Header, fileSize);
519     data[fileSizeIndex] = FIZE_SIZE_ERROR_SIZE;  // Corrupt file size
520     data[fileSizeIndex + 1] = FIZE_SIZE_ERROR_SIZE;
521     data[fileSizeIndex + 2] = FIZE_SIZE_ERROR_SIZE;
522     data[fileSizeIndex + 3] = FIZE_SIZE_ERROR_SIZE;
523 
524     res = ark::panda_file::CheckHeader(f->GetPtr(), std::string_view(), data.size());
525     EXPECT_EQ(res, false);
526 }
527 
TEST(File,OpenPandaFileFromSecureMemory)528 TEST(File, OpenPandaFileFromSecureMemory)
529 {
530     // test buffer is nullptr
531     auto pf = OpenPandaFileFromSecureMemory(nullptr, -1);
532     EXPECT_EQ(pf, nullptr);
533 
534     // test buff is not nullptr
535     const size_t bufferSize = 1024;
536     std::array<uint8_t, bufferSize> buffer = {0};
537     pf = OpenPandaFileFromSecureMemory(buffer.data(), bufferSize);
538     EXPECT_EQ(pf, nullptr);
539 }
540 
TEST(File,LoadPandFileExt_Behavior)541 TEST(File, LoadPandFileExt_Behavior)
542 {
543     using Wrapper = panda_api::panda_file::PandaFileWrapper;
544 
545     // Before loading, all function pointers should be null
546     EXPECT_EQ(Wrapper::pOpenPandafileFromFdExt, nullptr);
547     EXPECT_EQ(Wrapper::pOpenPandafileFromMemoryExt, nullptr);
548     EXPECT_EQ(Wrapper::pQueryMethodSymByOffsetExt, nullptr);
549     EXPECT_EQ(Wrapper::pQueryMethodSymAndLineByOffsetExt, nullptr);
550     EXPECT_EQ(Wrapper::pQueryAllMethodSymsExt, nullptr);
551 
552     // Load symbols
553     panda_api::panda_file::LoadPandFileExt();
554 
555     bool allSet = Wrapper::pOpenPandafileFromFdExt != nullptr && Wrapper::pOpenPandafileFromMemoryExt != nullptr &&
556                   Wrapper::pQueryMethodSymByOffsetExt != nullptr &&
557                   Wrapper::pQueryMethodSymAndLineByOffsetExt != nullptr && Wrapper::pQueryAllMethodSymsExt != nullptr;
558 
559     EXPECT_TRUE(allSet) << "Function pointers are in all_set state";
560 }
561 
562 }  // namespace ark::panda_file::test
563