| Steven Moreland | 042ae82 | 2020-05-27 17:45:17 +0000 | [diff] [blame] | 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 { | 
 | 28 |     DestructionAction(std::function<void()> f) : mF(std::move(f)) {} | 
 | 29 |     ~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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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)) | 
 | 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 |  | 
 | 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)) | 
 | 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 |  | 
 | 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 |  | 
 | 144 | TEST(BinderAllocation, ParcelOnStack) { | 
 | 145 |     const auto m = ScopeDisallowMalloc(); | 
 | 146 |     Parcel p; | 
 | 147 |     imaginary_use = p.data(); | 
 | 148 | } | 
 | 149 |  | 
 | 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 | 
 | 157 | TEST(BinderAllocation, PingTransaction) { | 
 | 158 |     sp<IBinder> a_binder = GetRemoteBinder(); | 
 | 159 |     const auto m = ScopeDisallowMalloc(); | 
 | 160 |     a_binder->pingBinder(); | 
 | 161 | } | 
 | 162 |  | 
 | 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 |  | 
 | 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 | } |