chromium/components/gwp_asan/client/sampling_malloc_shims_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "components/gwp_asan/client/sampling_malloc_shims.h"

#include <stdlib.h>

#include <cstdlib>
#include <memory>
#include <string>

#include "base/functional/callback_helpers.h"
#include "base/memory/page_size.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/gtest_util.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_key.h"
#include "components/gwp_asan/client/guarded_page_allocator.h"
#include "components/gwp_asan/client/gwp_asan.h"
#include "components/gwp_asan/common/crash_key_name.h"
#include "partition_alloc/buildflags.h"
#include "partition_alloc/shim/allocator_shim.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"

// These tests install global allocator shims so they are not safe to run in
// multi-threaded contexts. Instead they're implemented as multi-process tests.

#if BUILDFLAG(IS_WIN)
#include <malloc.h>
static size_t GetUsableSize(void* mem) {
  return _msize(mem);
}
#elif BUILDFLAG(IS_APPLE)
#include <malloc/malloc.h>
static size_t GetUsableSize(void* mem) {
  return malloc_size(mem);
}
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include <malloc.h>
static size_t GetUsableSize(void* mem) {}
#endif

namespace gwp_asan {
namespace internal {

extern GuardedPageAllocator& GetMallocGpaForTesting();

namespace {

constexpr size_t kSamplingFrequency =;
// Number of loop iterations required to hit a sampled allocation.
// The probability of not hitting a sample allocation in kLoopIterations
// is (1 - 1/kSamplingFrequency)^kLoopIterations. In this case that is
// (4/5)^100 < 3*10^-10.
constexpr size_t kLoopIterations =;

constexpr int kSuccess =;
constexpr int kFailure =;

class SamplingMallocShimsTest : public base::MultiProcessTest {};

// Return whether some of the allocations returned by the calling the allocate
// parameter are sampled to the guarded allocator. Keep count of failures
// encountered.
bool allocationCheck(std::function<void*(void)> allocate,
                     std::function<void(void*)> free,
                     int* failures) {}

MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    BasicFunctionality,
    SamplingMallocShimsTest::multiprocessTestSetup) {}

// Flaky on Mac: https://crbug.com/1087372
#if BUILDFLAG(IS_APPLE)
#define MAYBE_BasicFunctionality
#else
#define MAYBE_BasicFunctionality
#endif
TEST_F(SamplingMallocShimsTest, MAYBE_BasicFunctionality) {}

MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    Realloc,
    SamplingMallocShimsTest::multiprocessTestSetup) {}

TEST_F(SamplingMallocShimsTest, Realloc) {}

MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    Calloc,
    SamplingMallocShimsTest::multiprocessTestSetup) {}

TEST_F(SamplingMallocShimsTest, Calloc) {}

// GetCrashKeyValue() operates on a per-component basis, can't read the crash
// key from the gwp_asan_client component in a component build.
#if !defined(COMPONENT_BUILD)
MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    CrashKey,
    SamplingMallocShimsTest::multiprocessTestSetup) {
  if (crash_reporter::GetCrashKeyValue(kMallocCrashKey) !=
      GetMallocGpaForTesting().GetCrashKey()) {
    return kFailure;
  }

  return kSuccess;
}

TEST_F(SamplingMallocShimsTest, CrashKey) {
  runTest("CrashKey");
}
#endif  // !defined(COMPONENT_BUILD)

// malloc_usable_size() is not currently used/shimmed on Android.
#if !BUILDFLAG(IS_ANDROID)
MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    GetSizeEstimate,
    SamplingMallocShimsTest::multiprocessTestSetup) {}

TEST_F(SamplingMallocShimsTest, GetSizeEstimate) {}
#endif

#if BUILDFLAG(IS_WIN)
MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    AlignedRealloc,
    SamplingMallocShimsTest::multiprocessTestSetup) {
  // Exercise the _aligned_* shims and ensure that we handle them stably.
  constexpr size_t kAllocationSize = 123;
  constexpr size_t kAllocationAlignment = 64;
  for (size_t i = 0; i < kLoopIterations; i++) {
    void* ptr = _aligned_malloc(kAllocationSize, kAllocationAlignment);
    CHECK(ptr);
    ptr = _aligned_realloc(ptr, kAllocationSize * 2, kAllocationAlignment);
    CHECK(ptr);
    _aligned_free(ptr);
  }

  return kSuccess;
}

TEST_F(SamplingMallocShimsTest, AlignedRealloc) {
  runTest("AlignedRealloc");
}
#endif  // BUILDFLAG(IS_WIN)

// PartitionAlloc-Everywhere does not support batch_malloc / batch_free.
#if BUILDFLAG(IS_APPLE) && !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
MULTIPROCESS_TEST_MAIN_WITH_SETUP(
    BatchFree,
    SamplingMallocShimsTest::multiprocessTestSetup) {
  void* ptrs[AllocatorState::kMaxMetadata + 1];
  for (size_t i = 0; i < AllocatorState::kMaxMetadata; i++) {
    ptrs[i] = GetMallocGpaForTesting().Allocate(16);
    CHECK(ptrs[i]);
  }
  // Check that all GPA allocations were consumed.
  CHECK_EQ(GetMallocGpaForTesting().Allocate(16), nullptr);

  ptrs[AllocatorState::kMaxMetadata] =
      malloc_zone_malloc(malloc_default_zone(), 16);
  CHECK(ptrs[AllocatorState::kMaxMetadata]);

  malloc_zone_batch_free(malloc_default_zone(), ptrs,
                         AllocatorState::kMaxMetadata + 1);

  // Check that GPA allocations were freed.
  CHECK(GetMallocGpaForTesting().Allocate(16));

  return kSuccess;
}

TEST_F(SamplingMallocShimsTest, BatchFree) {
  runTest("BatchFree");
}
#endif  // BUILDFLAG(IS_APPLE) && !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)

}  // namespace

}  // namespace internal
}  // namespace gwp_asan