1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <malloc.h>
18 #include <stdint.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 
22 #include <unordered_map>
23 
24 #include <MemoryLocal.h>
25 #include <android-base/file.h>
26 #include <gtest/gtest.h>
27 #include <unwindstack/MapInfo.h>
28 #include <unwindstack/Memory.h>
29 
30 #include "DexFile.h"
31 #include "DexFileData.h"
32 #include "MemoryFake.h"
33 
34 namespace unwindstack {
35 
36 static constexpr size_t kNumLeakLoops = 5000;
37 static constexpr size_t kMaxAllowedLeakBytes = 1024;
38 
CheckForLeak(size_t loop,size_t * first_allocated_bytes,size_t * last_allocated_bytes)39 static void CheckForLeak(size_t loop, size_t* first_allocated_bytes, size_t* last_allocated_bytes) {
40   size_t allocated_bytes = mallinfo().uordblks;
41   if (*first_allocated_bytes == 0) {
42     *first_allocated_bytes = allocated_bytes;
43   } else if (*last_allocated_bytes > *first_allocated_bytes) {
44     // Check that the total memory did not increase too much over the first loop.
45     ASSERT_LE(*last_allocated_bytes - *first_allocated_bytes, kMaxAllowedLeakBytes)
46         << "Failed in loop " << loop << " first_allocated_bytes " << *first_allocated_bytes
47         << " last_allocated_bytes " << *last_allocated_bytes;
48   }
49   *last_allocated_bytes = allocated_bytes;
50 }
51 
TEST(DexFileTest,from_file_no_leak)52 TEST(DexFileTest, from_file_no_leak) {
53   TemporaryFile tf;
54   ASSERT_TRUE(tf.fd != -1);
55 
56   ASSERT_EQ(sizeof(kDexData),
57             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
58 
59   size_t first_allocated_bytes = 0;
60   size_t last_allocated_bytes = 0;
61   for (size_t i = 0; i < kNumLeakLoops; i++) {
62     MemoryFake memory;
63     MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
64     EXPECT_TRUE(DexFile::Create(0, sizeof(kDexData), &memory, &info) != nullptr);
65     ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
66   }
67 }
68 
TEST(DexFileTest,from_memory_no_leak)69 TEST(DexFileTest, from_memory_no_leak) {
70   MemoryFake memory;
71 
72   memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
73 
74   size_t first_allocated_bytes = 0;
75   size_t last_allocated_bytes = 0;
76   for (size_t i = 0; i < kNumLeakLoops; i++) {
77     EXPECT_TRUE(DexFile::Create(0x1000, sizeof(kDexData), &memory, nullptr) != nullptr);
78     ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
79   }
80 }
81 
TEST(DexFileTest,create_using_file)82 TEST(DexFileTest, create_using_file) {
83   TemporaryFile tf;
84   ASSERT_TRUE(tf.fd != -1);
85 
86   ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
87   ASSERT_EQ(sizeof(kDexData),
88             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
89 
90   MemoryFake memory;
91   MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
92   EXPECT_TRUE(DexFile::Create(0x500, sizeof(kDexData), &memory, &info) != nullptr);
93 }
94 
TEST(DexFileTest,create_using_file_non_zero_start)95 TEST(DexFileTest, create_using_file_non_zero_start) {
96   TemporaryFile tf;
97   ASSERT_TRUE(tf.fd != -1);
98 
99   ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
100   ASSERT_EQ(sizeof(kDexData),
101             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
102 
103   MemoryFake memory;
104   MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
105   EXPECT_TRUE(DexFile::Create(0x600, sizeof(kDexData), &memory, &info) != nullptr);
106 }
107 
TEST(DexFileTest,create_using_file_non_zero_offset)108 TEST(DexFileTest, create_using_file_non_zero_offset) {
109   TemporaryFile tf;
110   ASSERT_TRUE(tf.fd != -1);
111 
112   ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
113   ASSERT_EQ(sizeof(kDexData),
114             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
115 
116   MemoryFake memory;
117   MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
118   EXPECT_TRUE(DexFile::Create(0x400, sizeof(kDexData), &memory, &info) != nullptr);
119 }
120 
TEST(DexFileTest,create_using_memory_empty_file)121 TEST(DexFileTest, create_using_memory_empty_file) {
122   MemoryFake memory;
123   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
124   MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
125   EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info) != nullptr);
126 }
127 
TEST(DexFileTest,create_using_memory_file_does_not_exist)128 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
129   MemoryFake memory;
130   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
131   MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
132   EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info) != nullptr);
133 }
134 
TEST(DexFileTest,create_using_memory_file_is_malformed)135 TEST(DexFileTest, create_using_memory_file_is_malformed) {
136   TemporaryFile tf;
137   ASSERT_TRUE(tf.fd != -1);
138 
139   ASSERT_EQ(sizeof(kDexData) - 10,
140             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 10))));
141 
142   MemoryFake memory;
143   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
144   MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
145   std::shared_ptr<DexFile> dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
146   ASSERT_TRUE(dex_file != nullptr);
147 
148   // Check it came from memory by clearing memory and verifying it fails.
149   memory.Clear();
150   dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
151   EXPECT_TRUE(dex_file == nullptr);
152 }
153 
TEST(DexFileTest,create_using_memory_header_too_small)154 TEST(DexFileTest, create_using_memory_header_too_small) {
155   MemoryFake memory;
156   size_t size = 10;
157   memory.SetMemory(0x4000, kDexData, size);
158   EXPECT_TRUE(DexFile::Create(0x4000, size, &memory, nullptr) == nullptr);
159 }
160 
TEST(DexFileTest,create_using_memory_size_too_small)161 TEST(DexFileTest, create_using_memory_size_too_small) {
162   MemoryFake memory;
163   size_t size = sizeof(kDexData) - 1;
164   memory.SetMemory(0x4000, kDexData, size);
165   EXPECT_TRUE(DexFile::Create(0x4000, size, &memory, nullptr) == nullptr);
166 }
167 
TEST(DexFileTest,get_method)168 TEST(DexFileTest, get_method) {
169   MemoryFake memory;
170   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
171   MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
172   std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info));
173   ASSERT_TRUE(dex_file != nullptr);
174 
175   SharedString method;
176   uint64_t method_offset;
177   ASSERT_TRUE(dex_file->GetFunctionName(0x4102, &method, &method_offset));
178   EXPECT_EQ("Main.<init>", method);
179   EXPECT_EQ(2U, method_offset);
180 
181   ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
182   EXPECT_EQ("Main.main", method);
183   EXPECT_EQ(0U, method_offset);
184 }
185 
TEST(DexFileTest,get_method_empty)186 TEST(DexFileTest, get_method_empty) {
187   MemoryFake memory;
188   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
189   MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
190   std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info));
191   ASSERT_TRUE(dex_file != nullptr);
192 
193   SharedString method;
194   uint64_t method_offset;
195   EXPECT_FALSE(dex_file->GetFunctionName(0x100000, &method, &method_offset));
196 
197   EXPECT_FALSE(dex_file->GetFunctionName(0x98, &method, &method_offset));
198 }
199 
TEST(DexFileTest,get_method_from_cache)200 TEST(DexFileTest, get_method_from_cache) {
201   TemporaryFile tf;
202   ASSERT_TRUE(tf.fd != -1);
203   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
204   ASSERT_EQ(sizeof(kDexData),
205             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
206 
207   MemoryFake memory;
208   MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0, 0x5, tf.path);
209   std::shared_ptr<DexFile> dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
210   EXPECT_TRUE(dex_file != nullptr);
211 
212   SharedString method;
213   uint64_t method_offset;
214   ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
215   EXPECT_EQ("Main.main", method);
216   EXPECT_EQ(0U, method_offset);
217 
218   // Corrupt the dex file: change the name of the class.
219   int main = std::string(reinterpret_cast<const char*>(kDexData), sizeof(kDexData)).find("Main");
220   ASSERT_EQ(main, lseek(tf.fd, main, SEEK_SET));
221   ASSERT_EQ(4u, static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, "MAIN", 4))));
222 
223   // Check that we see the *old* cached value.
224   ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
225   EXPECT_EQ("Main.main", method);
226   EXPECT_EQ(0U, method_offset);
227 
228   // Check that for other methods we see the *new* updated value.
229   ASSERT_TRUE(dex_file->GetFunctionName(0x4102, &method, &method_offset));
230   EXPECT_EQ("MAIN.<init>", method);
231   EXPECT_EQ(2U, method_offset);
232 }
233 
234 }  // namespace unwindstack
235