| /* |
| * 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(); |
| } |