blob: e1f5ed5381a5677388fcbbb2fbcc07f02b5181d3 [file] [log] [blame]
Steven Moreland042ae822020-05-27 17:45:17 +00001/*
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
27struct DestructionAction {
28 DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
29 ~DestructionAction() { mF(); };
30private:
31 std::function<void()> mF;
32};
33
34// Group of hooks
35struct MallocHooks {
36 decltype(__malloc_hook) malloc_hook;
37 decltype(__realloc_hook) realloc_hook;
38
39 static MallocHooks save() {
40 return {
41 .malloc_hook = __malloc_hook,
42 .realloc_hook = __realloc_hook,
43 };
44 }
45
46 void overwrite() const {
47 __malloc_hook = malloc_hook;
48 __realloc_hook = realloc_hook;
49 }
50};
51
52static const MallocHooks orig_malloc_hooks = MallocHooks::save();
53
54// When malloc is hit, executes lambda.
55namespace 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
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
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))
90DestructionAction 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
101const void* imaginary_use;
102
103TEST(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))
118DestructionAction 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
127using android::IBinder;
128using android::Parcel;
129using android::String16;
130using android::defaultServiceManager;
131using android::sp;
132using android::IServiceManager;
133
134static 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
144TEST(BinderAllocation, ParcelOnStack) {
145 const auto m = ScopeDisallowMalloc();
146 Parcel p;
147 imaginary_use = p.data();
148}
149
150TEST(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
157TEST(BinderAllocation, PingTransaction) {
158 sp<IBinder> a_binder = GetRemoteBinder();
159 const auto m = ScopeDisallowMalloc();
160 a_binder->pingBinder();
161}
162
163TEST(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
178int 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}