| /* | 
 |  * Copyright (C) 2020 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include <android-base/logging.h> | 
 | #include <binder/Parcel.h> | 
 | #include <binder/IServiceManager.h> | 
 | #include <gtest/gtest.h> | 
 | #include <utils/CallStack.h> | 
 |  | 
 | #include <malloc.h> | 
 | #include <functional> | 
 | #include <vector> | 
 |  | 
 | struct DestructionAction { | 
 |     DestructionAction(std::function<void()> f) : mF(std::move(f)) {} | 
 |     ~DestructionAction() { mF(); }; | 
 | private: | 
 |     std::function<void()> mF; | 
 | }; | 
 |  | 
 | // Group of hooks | 
 | struct MallocHooks { | 
 |     decltype(__malloc_hook) malloc_hook; | 
 |     decltype(__realloc_hook) realloc_hook; | 
 |  | 
 |     static MallocHooks save() { | 
 |         return { | 
 |             .malloc_hook = __malloc_hook, | 
 |             .realloc_hook = __realloc_hook, | 
 |         }; | 
 |     } | 
 |  | 
 |     void overwrite() const { | 
 |         __malloc_hook = malloc_hook; | 
 |         __realloc_hook = realloc_hook; | 
 |     } | 
 | }; | 
 |  | 
 | static const MallocHooks orig_malloc_hooks = MallocHooks::save(); | 
 |  | 
 | // When malloc is hit, executes lambda. | 
 | namespace LambdaHooks { | 
 |     using AllocationHook = std::function<void(size_t)>; | 
 |     static std::vector<AllocationHook> lambdas = {}; | 
 |  | 
 |     static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg); | 
 |     static void* lambda_malloc_hook(size_t bytes, const void* arg); | 
 |  | 
 |     static const MallocHooks lambda_malloc_hooks = { | 
 |         .malloc_hook = lambda_malloc_hook, | 
 |         .realloc_hook = lambda_realloc_hook, | 
 |     }; | 
 |  | 
 |     static void* lambda_malloc_hook(size_t bytes, const void* arg) { | 
 |         { | 
 |             orig_malloc_hooks.overwrite(); | 
 |             lambdas.at(lambdas.size() - 1)(bytes); | 
 |             lambda_malloc_hooks.overwrite(); | 
 |         } | 
 |         return orig_malloc_hooks.malloc_hook(bytes, arg); | 
 |     } | 
 |  | 
 |     static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) { | 
 |         { | 
 |             orig_malloc_hooks.overwrite(); | 
 |             lambdas.at(lambdas.size() - 1)(bytes); | 
 |             lambda_malloc_hooks.overwrite(); | 
 |         } | 
 |         return orig_malloc_hooks.realloc_hook(ptr, bytes, arg); | 
 |     } | 
 |  | 
 | } | 
 |  | 
 | // Action to execute when malloc is hit. Supports nesting. Malloc is not | 
 | // restricted when the allocation hook is being processed. | 
 | __attribute__((warn_unused_result)) | 
 | DestructionAction OnMalloc(LambdaHooks::AllocationHook f) { | 
 |     MallocHooks before = MallocHooks::save(); | 
 |     LambdaHooks::lambdas.emplace_back(std::move(f)); | 
 |     LambdaHooks::lambda_malloc_hooks.overwrite(); | 
 |     return DestructionAction([before]() { | 
 |         before.overwrite(); | 
 |         LambdaHooks::lambdas.pop_back(); | 
 |     }); | 
 | } | 
 |  | 
 | // exported symbol, to force compiler not to optimize away pointers we set here | 
 | const void* imaginary_use; | 
 |  | 
 | TEST(TestTheTest, OnMalloc) { | 
 |     size_t mallocs = 0; | 
 |     { | 
 |         const auto on_malloc = OnMalloc([&](size_t bytes) { | 
 |             mallocs++; | 
 |             EXPECT_EQ(bytes, 40); | 
 |         }); | 
 |  | 
 |         imaginary_use = new int[10]; | 
 |     } | 
 |     EXPECT_EQ(mallocs, 1); | 
 | } | 
 |  | 
 |  | 
 | __attribute__((warn_unused_result)) | 
 | DestructionAction ScopeDisallowMalloc() { | 
 |     return OnMalloc([&](size_t bytes) { | 
 |         ADD_FAILURE() << "Unexpected allocation: " << bytes; | 
 |         using android::CallStack; | 
 |         std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get()) | 
 |                   << std::endl; | 
 |     }); | 
 | } | 
 |  | 
 | using android::IBinder; | 
 | using android::Parcel; | 
 | using android::String16; | 
 | using android::defaultServiceManager; | 
 | using android::sp; | 
 | using android::IServiceManager; | 
 |  | 
 | static sp<IBinder> GetRemoteBinder() { | 
 |     // This gets binder representing the service manager | 
 |     // the current IServiceManager API doesn't expose the binder, and | 
 |     // I want to avoid adding usages of the AIDL generated interface it | 
 |     // is using underneath, so to avoid people copying it. | 
 |     sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager")); | 
 |     EXPECT_NE(nullptr, binder); | 
 |     return binder; | 
 | } | 
 |  | 
 | TEST(BinderAllocation, ParcelOnStack) { | 
 |     const auto m = ScopeDisallowMalloc(); | 
 |     Parcel p; | 
 |     imaginary_use = p.data(); | 
 | } | 
 |  | 
 | TEST(BinderAllocation, GetServiceManager) { | 
 |     defaultServiceManager(); // first call may alloc | 
 |     const auto m = ScopeDisallowMalloc(); | 
 |     defaultServiceManager(); | 
 | } | 
 |  | 
 | // note, ping does not include interface descriptor | 
 | TEST(BinderAllocation, PingTransaction) { | 
 |     sp<IBinder> a_binder = GetRemoteBinder(); | 
 |     const auto m = ScopeDisallowMalloc(); | 
 |     a_binder->pingBinder(); | 
 | } | 
 |  | 
 | TEST(BinderAllocation, SmallTransaction) { | 
 |     String16 empty_descriptor = String16(""); | 
 |     sp<IServiceManager> manager = defaultServiceManager(); | 
 |  | 
 |     size_t mallocs = 0; | 
 |     const auto on_malloc = OnMalloc([&](size_t bytes) { | 
 |         mallocs++; | 
 |         // Parcel should allocate a small amount by default | 
 |         EXPECT_EQ(bytes, 128); | 
 |     }); | 
 |     manager->checkService(empty_descriptor); | 
 |  | 
 |     EXPECT_EQ(mallocs, 1); | 
 | } | 
 |  | 
 | int main(int argc, char** argv) { | 
 |     if (getenv("LIBC_HOOKS_ENABLE") == nullptr) { | 
 |         CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/)); | 
 |         execv(argv[0], argv); | 
 |         return 1; | 
 |     } | 
 |     ::testing::InitGoogleTest(&argc, argv); | 
 |     return RUN_ALL_TESTS(); | 
 | } |