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