1 /*
2 * Copyright (C) 2020 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 <android-base/logging.h>
18 #include <binder/Parcel.h>
19 #include <binder/IServiceManager.h>
20 #include <gtest/gtest.h>
21 #include <utils/CallStack.h>
22
23 #include <malloc.h>
24 #include <functional>
25 #include <vector>
26
27 struct DestructionAction {
DestructionActionDestructionAction28 DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
~DestructionActionDestructionAction29 ~DestructionAction() { mF(); };
30 private:
31 std::function<void()> mF;
32 };
33
34 // Group of hooks
35 struct MallocHooks {
36 decltype(__malloc_hook) malloc_hook;
37 decltype(__realloc_hook) realloc_hook;
38
saveMallocHooks39 static MallocHooks save() {
40 return {
41 .malloc_hook = __malloc_hook,
42 .realloc_hook = __realloc_hook,
43 };
44 }
45
overwriteMallocHooks46 void overwrite() const {
47 __malloc_hook = malloc_hook;
48 __realloc_hook = realloc_hook;
49 }
50 };
51
52 static const MallocHooks orig_malloc_hooks = MallocHooks::save();
53
54 // When malloc is hit, executes lambda.
55 namespace LambdaHooks {
56 using AllocationHook = std::function<void(size_t)>;
57 static std::vector<AllocationHook> lambdas = {};
58
59 static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg);
60 static void* lambda_malloc_hook(size_t bytes, const void* arg);
61
62 static const MallocHooks lambda_malloc_hooks = {
63 .malloc_hook = lambda_malloc_hook,
64 .realloc_hook = lambda_realloc_hook,
65 };
66
lambda_malloc_hook(size_t bytes,const void * arg)67 static void* lambda_malloc_hook(size_t bytes, const void* arg) {
68 {
69 orig_malloc_hooks.overwrite();
70 lambdas.at(lambdas.size() - 1)(bytes);
71 lambda_malloc_hooks.overwrite();
72 }
73 return orig_malloc_hooks.malloc_hook(bytes, arg);
74 }
75
lambda_realloc_hook(void * ptr,size_t bytes,const void * arg)76 static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) {
77 {
78 orig_malloc_hooks.overwrite();
79 lambdas.at(lambdas.size() - 1)(bytes);
80 lambda_malloc_hooks.overwrite();
81 }
82 return orig_malloc_hooks.realloc_hook(ptr, bytes, arg);
83 }
84
85 }
86
87 // Action to execute when malloc is hit. Supports nesting. Malloc is not
88 // restricted when the allocation hook is being processed.
89 __attribute__((warn_unused_result))
OnMalloc(LambdaHooks::AllocationHook f)90 DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
91 MallocHooks before = MallocHooks::save();
92 LambdaHooks::lambdas.emplace_back(std::move(f));
93 LambdaHooks::lambda_malloc_hooks.overwrite();
94 return DestructionAction([before]() {
95 before.overwrite();
96 LambdaHooks::lambdas.pop_back();
97 });
98 }
99
100 // exported symbol, to force compiler not to optimize away pointers we set here
101 const void* imaginary_use;
102
TEST(TestTheTest,OnMalloc)103 TEST(TestTheTest, OnMalloc) {
104 size_t mallocs = 0;
105 {
106 const auto on_malloc = OnMalloc([&](size_t bytes) {
107 mallocs++;
108 EXPECT_EQ(bytes, 40);
109 });
110
111 imaginary_use = new int[10];
112 }
113 EXPECT_EQ(mallocs, 1);
114 }
115
116
117 __attribute__((warn_unused_result))
ScopeDisallowMalloc()118 DestructionAction ScopeDisallowMalloc() {
119 return OnMalloc([&](size_t bytes) {
120 ADD_FAILURE() << "Unexpected allocation: " << bytes;
121 using android::CallStack;
122 std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
123 << std::endl;
124 });
125 }
126
127 using android::IBinder;
128 using android::Parcel;
129 using android::String16;
130 using android::defaultServiceManager;
131 using android::sp;
132 using android::IServiceManager;
133
GetRemoteBinder()134 static sp<IBinder> GetRemoteBinder() {
135 // This gets binder representing the service manager
136 // the current IServiceManager API doesn't expose the binder, and
137 // I want to avoid adding usages of the AIDL generated interface it
138 // is using underneath, so to avoid people copying it.
139 sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager"));
140 EXPECT_NE(nullptr, binder);
141 return binder;
142 }
143
TEST(BinderAllocation,ParcelOnStack)144 TEST(BinderAllocation, ParcelOnStack) {
145 const auto m = ScopeDisallowMalloc();
146 Parcel p;
147 imaginary_use = p.data();
148 }
149
TEST(BinderAllocation,GetServiceManager)150 TEST(BinderAllocation, GetServiceManager) {
151 defaultServiceManager(); // first call may alloc
152 const auto m = ScopeDisallowMalloc();
153 defaultServiceManager();
154 }
155
156 // note, ping does not include interface descriptor
TEST(BinderAllocation,PingTransaction)157 TEST(BinderAllocation, PingTransaction) {
158 sp<IBinder> a_binder = GetRemoteBinder();
159 const auto m = ScopeDisallowMalloc();
160 a_binder->pingBinder();
161 }
162
TEST(BinderAllocation,SmallTransaction)163 TEST(BinderAllocation, SmallTransaction) {
164 String16 empty_descriptor = String16("");
165 sp<IServiceManager> manager = defaultServiceManager();
166
167 size_t mallocs = 0;
168 const auto on_malloc = OnMalloc([&](size_t bytes) {
169 mallocs++;
170 // Parcel should allocate a small amount by default
171 EXPECT_EQ(bytes, 128);
172 });
173 manager->checkService(empty_descriptor);
174
175 EXPECT_EQ(mallocs, 1);
176 }
177
main(int argc,char ** argv)178 int main(int argc, char** argv) {
179 if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
180 CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
181 execv(argv[0], argv);
182 return 1;
183 }
184 ::testing::InitGoogleTest(&argc, argv);
185 return RUN_ALL_TESTS();
186 }
187