C++程序  |  228行  |  6.57 KB

/*
 * Copyright (C) 2018 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 <stdint.h>
#include <stdlib.h>
#include <time.h>

#include <gtest/gtest.h>

#if defined(__BIONIC__)

#include <vector>

#include <procinfo/process_map.h>

#include "utils.h"

extern "C" void malloc_disable();
extern "C" void malloc_enable();
extern "C" int malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t base,
                              size_t size, void* arg), void* arg);

struct AllocDataType {
  void* ptr;
  size_t size;
  size_t size_reported;
  size_t count;
};

struct TestDataType {
  size_t total_allocated_bytes;
  std::vector<AllocDataType> allocs;
};

static void AllocPtr(TestDataType* test_data, size_t size) {
  test_data->allocs.resize(test_data->allocs.size() + 1);
  AllocDataType* alloc = &test_data->allocs.back();
  void* ptr = malloc(size);
  ASSERT_TRUE(ptr != nullptr);
  alloc->ptr = ptr;
  alloc->size = malloc_usable_size(ptr);
  alloc->size_reported = 0;
  alloc->count = 0;
}

static void FreePtrs(TestDataType* test_data) {
  for (size_t i = 0; i < test_data->allocs.size(); i++) {
    free(test_data->allocs[i].ptr);
  }
}

static void SavePointers(uintptr_t base, size_t size, void* data) {
  TestDataType* test_data = reinterpret_cast<TestDataType*>(data);

  test_data->total_allocated_bytes += size;

  uintptr_t end;
  if (__builtin_add_overflow(base, size, &end)) {
    // Skip this entry.
    return;
  }

  for (size_t i = 0; i < test_data->allocs.size(); i++) {
    uintptr_t ptr = reinterpret_cast<uintptr_t>(test_data->allocs[i].ptr);
    if (ptr >= base && ptr < end) {
      test_data->allocs[i].count++;

      uintptr_t max_size = end - ptr;
      if (max_size > test_data->allocs[i].size) {
        test_data->allocs[i].size_reported = test_data->allocs[i].size;
      } else {
        test_data->allocs[i].size_reported = max_size;
      }
    }
  }
}

static void VerifyPtrs(TestDataType* test_data) {
  test_data->total_allocated_bytes = 0;

  // Find all of the maps that are [anon:libc_malloc].
  ASSERT_TRUE(android::procinfo::ReadMapFile(
      "/proc/self/maps",
      [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
        if (std::string(name) == "[anon:libc_malloc]") {
          malloc_disable();
          malloc_iterate(start, end - start, SavePointers, test_data);
          malloc_enable();
        }
      }));

  for (size_t i = 0; i < test_data->allocs.size(); i++) {
    EXPECT_EQ(1UL, test_data->allocs[i].count) << "Failed on size " << test_data->allocs[i].size;
    if (test_data->allocs[i].count == 1) {
      EXPECT_EQ(test_data->allocs[i].size, test_data->allocs[i].size_reported);
    }
  }
}

static void AllocateSizes(TestDataType* test_data, const std::vector<size_t>& sizes) {
  static constexpr size_t kInitialAllocations = 40;
  static constexpr size_t kNumAllocs = 50;
  for (size_t size : sizes) {
    // Verify that if the tcache is enabled, that tcache pointers
    // are found by allocating and freeing 20 pointers (should be larger
    // than the total number of cache entries).
    for (size_t i = 0; i < kInitialAllocations; i++) {
      void* ptr = malloc(size);
      ASSERT_TRUE(ptr != nullptr);
      memset(ptr, 0, size);
      free(ptr);
    }
    for (size_t i = 0; i < kNumAllocs; i++) {
      AllocPtr(test_data, size);
    }
  }
}
#endif

// Verify that small allocs can be found properly.
TEST(malloc_iterate, small_allocs) {
#if defined(__BIONIC__)
  SKIP_WITH_HWASAN;
  TestDataType test_data;

  // Try to cycle through all of the different small bins.
  // This is specific to the implementation of jemalloc and should be
  // adjusted if a different native memory allocator is used.
  std::vector<size_t> sizes{8,    16,   32,   48,    64,    80,    96,    112,   128,  160,
                            192,  224,  256,  320,   384,   448,   512,   640,   768,  896,
                            1024, 1280, 1536, 1792,  2048,  2560,  3072,  3584,  4096, 5120,
                            6144, 7168, 8192, 10240, 12288, 14336, 16384, 32768, 65536};
  AllocateSizes(&test_data, sizes);

  SCOPED_TRACE("");
  VerifyPtrs(&test_data);

  FreePtrs(&test_data);
#else
  GTEST_SKIP() << "bionic-only test";
#endif
}

// Verify that large allocs can be found properly.
TEST(malloc_iterate, large_allocs) {
#if defined(__BIONIC__)
  SKIP_WITH_HWASAN;
  TestDataType test_data;

  // Try some larger sizes.
  std::vector<size_t> sizes{131072, 262144, 524288, 1048576, 2097152};
  AllocateSizes(&test_data, sizes);

  SCOPED_TRACE("");
  VerifyPtrs(&test_data);

  FreePtrs(&test_data);
#else
  GTEST_SKIP() << "bionic-only test";
#endif
}

// Verify that there are no crashes attempting to get pointers from
// non-allocated pointers.
TEST(malloc_iterate, invalid_pointers) {
#if defined(__BIONIC__)
  SKIP_WITH_HWASAN;
  TestDataType test_data = {};

  // Find all of the maps that are not [anon:libc_malloc].
  ASSERT_TRUE(android::procinfo::ReadMapFile(
      "/proc/self/maps",
      [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
        if (std::string(name) != "[anon:libc_malloc]") {
          malloc_disable();
          malloc_iterate(start, end - start, SavePointers, &test_data);
          malloc_enable();
        }
      }));

  ASSERT_EQ(0UL, test_data.total_allocated_bytes);
#else
  GTEST_SKIP() << "bionic-only test";
#endif
}

TEST(malloc_iterate, malloc_disable_prevents_allocs) {
#if defined(__BIONIC__)
  SKIP_WITH_HWASAN;
  pid_t pid;
  if ((pid = fork()) == 0) {
    malloc_disable();
    void* ptr = malloc(1024);
    if (ptr == nullptr) {
      exit(1);
    }
    memset(ptr, 0, 1024);
    exit(0);
  }
  ASSERT_NE(-1, pid);

  // Expect that the malloc will hang forever, and that if the process
  // does not return for two seconds, it is hung.
  sleep(2);
  pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG));
  if (wait_pid <= 0) {
    kill(pid, SIGKILL);
  }
  ASSERT_NE(-1, wait_pid) << "Unknown failure in waitpid.";
  ASSERT_EQ(0, wait_pid) << "malloc_disable did not prevent allocation calls.";
#else
  GTEST_SKIP() << "bionic-only test";
#endif
}