Loading libs/binder/Parcel.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -2836,6 +2836,8 @@ status_t Parcel::continueWrite(size_t desired) } } else { desired = std::max(desired, (size_t)128); // This is the first data. Easy! uint8_t* data = (uint8_t*)malloc(desired); if (!data) { Loading libs/binder/TEST_MAPPING +3 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,9 @@ { "name": "binderVendorDoubleLoadTest" }, { "name": "binderAllocationLimits" }, { "name": "binderDriverInterfaceTest" }, Loading libs/binder/tests/Android.bp +15 −0 Original line number Diff line number Diff line Loading @@ -169,3 +169,18 @@ cc_test { test_suites: ["device-tests"], require_root: true, } cc_test { name: "binderAllocationLimits", defaults: ["binder_test_defaults"], srcs: ["binderAllocationLimits.cpp"], shared_libs: [ "libbinder", "liblog", "libutils", "libutilscallstack", "libbase", ], test_suites: ["device-tests"], require_root: true, } libs/binder/tests/binderAllocationLimits.cpp 0 → 100644 +186 −0 Original line number Diff line number Diff line /* * 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(); } Loading
libs/binder/Parcel.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -2836,6 +2836,8 @@ status_t Parcel::continueWrite(size_t desired) } } else { desired = std::max(desired, (size_t)128); // This is the first data. Easy! uint8_t* data = (uint8_t*)malloc(desired); if (!data) { Loading
libs/binder/TEST_MAPPING +3 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,9 @@ { "name": "binderVendorDoubleLoadTest" }, { "name": "binderAllocationLimits" }, { "name": "binderDriverInterfaceTest" }, Loading
libs/binder/tests/Android.bp +15 −0 Original line number Diff line number Diff line Loading @@ -169,3 +169,18 @@ cc_test { test_suites: ["device-tests"], require_root: true, } cc_test { name: "binderAllocationLimits", defaults: ["binder_test_defaults"], srcs: ["binderAllocationLimits.cpp"], shared_libs: [ "libbinder", "liblog", "libutils", "libutilscallstack", "libbase", ], test_suites: ["device-tests"], require_root: true, }
libs/binder/tests/binderAllocationLimits.cpp 0 → 100644 +186 −0 Original line number Diff line number Diff line /* * 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(); }