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 <memory>
23 #include <unordered_map>
24
25 #include <MemoryLocal.h>
26 #include <android-base/file.h>
27 #include <gtest/gtest.h>
28 #include <unwindstack/MapInfo.h>
29 #include <unwindstack/Memory.h>
30
31 #include "DexFile.h"
32 #include "DexFileData.h"
33 #include "utils/MemoryFake.h"
34
35 namespace unwindstack {
36
37 static constexpr size_t kNumLeakLoops = 5000;
38 static constexpr size_t kMaxAllowedLeakBytes = 4 * 1024;
39
CheckForLeak(size_t loop,size_t * first_allocated_bytes,size_t * last_allocated_bytes)40 static void CheckForLeak(size_t loop, size_t* first_allocated_bytes, size_t* last_allocated_bytes) {
41 size_t allocated_bytes = mallinfo().uordblks;
42 if (*first_allocated_bytes == 0) {
43 *first_allocated_bytes = allocated_bytes;
44 } else if (*last_allocated_bytes > *first_allocated_bytes) {
45 // Check that the total memory did not increase too much over the first loop.
46 ASSERT_LE(*last_allocated_bytes - *first_allocated_bytes, kMaxAllowedLeakBytes)
47 << "Failed in loop " << loop << " first_allocated_bytes " << *first_allocated_bytes
48 << " last_allocated_bytes " << *last_allocated_bytes;
49 }
50 *last_allocated_bytes = allocated_bytes;
51 }
52
TEST(DexFileTest,from_file_no_leak)53 TEST(DexFileTest, from_file_no_leak) {
54 #if !defined(__BIONIC__)
55 GTEST_SKIP() << "Leak checking depends on bionic.";
56 #endif
57
58 TemporaryFile tf;
59 ASSERT_TRUE(tf.fd != -1);
60
61 ASSERT_EQ(sizeof(kDexData),
62 static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
63
64 size_t first_allocated_bytes = 0;
65 size_t last_allocated_bytes = 0;
66 for (size_t i = 0; i < kNumLeakLoops; i++) {
67 MemoryFake memory;
68 auto info = MapInfo::Create(0, 0x10000, 0, 0x5, tf.path);
69 EXPECT_TRUE(DexFile::Create(0, sizeof(kDexData), &memory, info.get()) != nullptr);
70 ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
71 }
72 }
73
TEST(DexFileTest,from_memory_no_leak)74 TEST(DexFileTest, from_memory_no_leak) {
75 #if !defined(__BIONIC__)
76 GTEST_SKIP() << "Leak checking depends on bionic.";
77 #endif
78
79 MemoryFake memory;
80
81 memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
82
83 size_t first_allocated_bytes = 0;
84 size_t last_allocated_bytes = 0;
85 for (size_t i = 0; i < kNumLeakLoops; i++) {
86 EXPECT_TRUE(DexFile::Create(0x1000, sizeof(kDexData), &memory, nullptr) != nullptr);
87 ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
88 }
89 }
90
TEST(DexFileTest,create_using_file)91 TEST(DexFileTest, create_using_file) {
92 TemporaryFile tf;
93 ASSERT_TRUE(tf.fd != -1);
94
95 ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
96 ASSERT_EQ(sizeof(kDexData),
97 static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
98
99 MemoryFake memory;
100 auto info = MapInfo::Create(0, 0x10000, 0, 0x5, tf.path);
101 EXPECT_TRUE(DexFile::Create(0x500, sizeof(kDexData), &memory, info.get()) != nullptr);
102 }
103
TEST(DexFileTest,create_using_file_non_zero_start)104 TEST(DexFileTest, create_using_file_non_zero_start) {
105 TemporaryFile tf;
106 ASSERT_TRUE(tf.fd != -1);
107
108 ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
109 ASSERT_EQ(sizeof(kDexData),
110 static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
111
112 MemoryFake memory;
113 auto info = MapInfo::Create(0x100, 0x10000, 0, 0x5, tf.path);
114 EXPECT_TRUE(DexFile::Create(0x600, sizeof(kDexData), &memory, info.get()) != nullptr);
115 }
116
TEST(DexFileTest,create_using_file_non_zero_offset)117 TEST(DexFileTest, create_using_file_non_zero_offset) {
118 TemporaryFile tf;
119 ASSERT_TRUE(tf.fd != -1);
120
121 ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
122 ASSERT_EQ(sizeof(kDexData),
123 static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
124
125 MemoryFake memory;
126 auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, tf.path);
127 EXPECT_TRUE(DexFile::Create(0x400, sizeof(kDexData), &memory, info.get()) != nullptr);
128 }
129
TEST(DexFileTest,create_using_memory_empty_file)130 TEST(DexFileTest, create_using_memory_empty_file) {
131 MemoryFake memory;
132 memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
133 auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
134 EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()) != nullptr);
135 }
136
TEST(DexFileTest,create_using_memory_file_does_not_exist)137 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
138 MemoryFake memory;
139 memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
140 auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
141 EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()) != nullptr);
142 }
143
TEST(DexFileTest,create_using_memory_file_is_malformed)144 TEST(DexFileTest, create_using_memory_file_is_malformed) {
145 TemporaryFile tf;
146 ASSERT_TRUE(tf.fd != -1);
147
148 ASSERT_EQ(sizeof(kDexData) - 10,
149 static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 10))));
150
151 MemoryFake memory;
152 memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
153 auto info = MapInfo::Create(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
154 std::shared_ptr<DexFile> dex_file =
155 DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
156 ASSERT_TRUE(dex_file != nullptr);
157
158 // Check it came from memory by clearing memory and verifying it fails.
159 memory.Clear();
160 dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
161 EXPECT_TRUE(dex_file == nullptr);
162 }
163
TEST(DexFileTest,create_using_memory_header_too_small)164 TEST(DexFileTest, create_using_memory_header_too_small) {
165 MemoryFake memory;
166 size_t size = 10;
167 memory.SetMemory(0x4000, kDexData, size);
168 EXPECT_TRUE(DexFile::Create(0x4000, size, &memory, nullptr) == nullptr);
169 }
170
TEST(DexFileTest,create_using_memory_size_too_small)171 TEST(DexFileTest, create_using_memory_size_too_small) {
172 MemoryFake memory;
173 size_t size = sizeof(kDexData) - 1;
174 memory.SetMemory(0x4000, kDexData, size);
175 EXPECT_TRUE(DexFile::Create(0x4000, size, &memory, nullptr) == nullptr);
176 }
177
TEST(DexFileTest,get_method)178 TEST(DexFileTest, get_method) {
179 MemoryFake memory;
180 memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
181 auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
182 std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()));
183 ASSERT_TRUE(dex_file != nullptr);
184
185 SharedString method;
186 uint64_t method_offset;
187 ASSERT_TRUE(dex_file->GetFunctionName(0x4102, &method, &method_offset));
188 EXPECT_EQ("Main.<init>", method);
189 EXPECT_EQ(2U, method_offset);
190
191 ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
192 EXPECT_EQ("Main.main", method);
193 EXPECT_EQ(0U, method_offset);
194 }
195
TEST(DexFileTest,get_method_empty)196 TEST(DexFileTest, get_method_empty) {
197 MemoryFake memory;
198 memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
199 auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
200 std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()));
201 ASSERT_TRUE(dex_file != nullptr);
202
203 SharedString method;
204 uint64_t method_offset;
205 EXPECT_FALSE(dex_file->GetFunctionName(0x100000, &method, &method_offset));
206
207 EXPECT_FALSE(dex_file->GetFunctionName(0x98, &method, &method_offset));
208 }
209
TEST(DexFileTest,get_method_from_cache)210 TEST(DexFileTest, get_method_from_cache) {
211 TemporaryFile tf;
212 ASSERT_TRUE(tf.fd != -1);
213 ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
214 ASSERT_EQ(sizeof(kDexData),
215 static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
216
217 MemoryFake memory;
218 auto info = MapInfo::Create(0x4000, 0x10000, 0, 0x5, tf.path);
219 std::shared_ptr<DexFile> dex_file =
220 DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
221 EXPECT_TRUE(dex_file != nullptr);
222
223 SharedString method;
224 uint64_t method_offset;
225 ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
226 EXPECT_EQ("Main.main", method);
227 EXPECT_EQ(0U, method_offset);
228
229 // Corrupt the dex file: change the name of the class.
230 int main = std::string(reinterpret_cast<const char*>(kDexData), sizeof(kDexData)).find("Main");
231 ASSERT_EQ(main, lseek(tf.fd, main, SEEK_SET));
232 ASSERT_EQ(4u, static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, "MAIN", 4))));
233
234 // Check that we see the *old* cached value.
235 ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
236 EXPECT_EQ("Main.main", method);
237 EXPECT_EQ(0U, method_offset);
238
239 // Check that for other methods we see the *new* updated value.
240 ASSERT_TRUE(dex_file->GetFunctionName(0x4102, &method, &method_offset));
241 EXPECT_EQ("MAIN.<init>", method);
242 EXPECT_EQ(2U, method_offset);
243 }
244
245 } // namespace unwindstack
246