Skip to content

Commit

Permalink
[scudo][standalone] Introduce the C & C++ wrappers
Browse files Browse the repository at this point in the history
Summary:
This CL adds C & C++ wrappers and associated tests. Those use default
configurations for a Scudo combined allocator that will likely be
tweaked in the future.

This is the final CL required to have a functional C & C++ allocator
based on Scudo.

The structure I have chosen is to define the core C allocation
primitives in an `.inc` file that can be customized through defines.
This allows to easily have 2 (or more) sets of wrappers backed by
different combined allocators, as demonstrated by the `Bionic`
wrappers: one set for the "default" allocator, one set for the "svelte"
allocator.

Currently all the tests added have been gtests, but I am planning to
add some more lit tests as well.

Reviewers: morehouse, eugenis, vitalybuka, hctim, rengolin

Reviewed By: morehouse

Subscribers: srhines, mgorny, delcypher, jfb, #sanitizers, llvm-commits

Tags: #llvm, #sanitizers

Differential Revision: https://reviews.llvm.org/D63612

llvm-svn: 364332
  • Loading branch information
Kostya Kortchinsky committed Jun 25, 2019
1 parent e8de8ba commit 37340e3
Show file tree
Hide file tree
Showing 11 changed files with 909 additions and 36 deletions.
75 changes: 50 additions & 25 deletions compiler-rt/lib/scudo/standalone/CMakeLists.txt
Expand Up @@ -33,29 +33,6 @@ if(ANDROID)
append_list_if(COMPILER_RT_HAS_Z_GLOBAL -Wl,-z,global SCUDO_LINK_FLAGS)
endif()

set(SCUDO_SOURCES
checksum.cc
crc32_hw.cc
common.cc
flags.cc
flags_parser.cc
fuchsia.cc
linux.cc
report.cc
secondary.cc
string_utils.cc)

# Enable the SSE 4.2 instruction set for crc32_hw.cc, if available.
if (COMPILER_RT_HAS_MSSE4_2_FLAG)
set_source_files_properties(crc32_hw.cc PROPERTIES COMPILE_FLAGS -msse4.2)
endif()

# Enable the AArch64 CRC32 feature for crc32_hw.cc, if available.
# Note that it is enabled by default starting with armv8.1-a.
if (COMPILER_RT_HAS_MCRC_FLAG)
set_source_files_properties(crc32_hw.cc PROPERTIES COMPILE_FLAGS -mcrc)
endif()

set(SCUDO_HEADERS
allocator_config.h
atomic_helpers.h
Expand Down Expand Up @@ -85,19 +62,67 @@ set(SCUDO_HEADERS
tsd.h
tsd_exclusive.h
tsd_shared.h
vector.h)
vector.h
wrappers_c_checks.h
wrappers_c.h)

set(SCUDO_SOURCES
checksum.cc
crc32_hw.cc
common.cc
flags.cc
flags_parser.cc
fuchsia.cc
linux.cc
report.cc
secondary.cc
string_utils.cc)

# Enable the SSE 4.2 instruction set for crc32_hw.cc, if available.
if (COMPILER_RT_HAS_MSSE4_2_FLAG)
set_source_files_properties(crc32_hw.cc PROPERTIES COMPILE_FLAGS -msse4.2)
endif()

# Enable the AArch64 CRC32 feature for crc32_hw.cc, if available.
# Note that it is enabled by default starting with armv8.1-a.
if (COMPILER_RT_HAS_MCRC_FLAG)
set_source_files_properties(crc32_hw.cc PROPERTIES COMPILE_FLAGS -mcrc)
endif()

set(SCUDO_SOURCES_C_WRAPPERS
wrappers_c.cc)

set(SCUDO_SOURCES_CXX_WRAPPERS
wrappers_cpp.cc)

if(COMPILER_RT_HAS_SCUDO_STANDALONE)
add_compiler_rt_object_libraries(RTScudoStandalone
ARCHS ${SCUDO_STANDALONE_SUPPORTED_ARCH}
SOURCES ${SCUDO_SOURCES}
ADDITIONAL_HEADERS ${SCUDO_HEADERS}
CFLAGS ${SCUDO_CFLAGS})
add_compiler_rt_object_libraries(RTScudoStandaloneCWrappers
ARCHS ${SCUDO_STANDALONE_SUPPORTED_ARCH}
SOURCES ${SCUDO_SOURCES_C_WRAPPERS}
ADDITIONAL_HEADERS ${SCUDO_HEADERS}
CFLAGS ${SCUDO_CFLAGS})
add_compiler_rt_object_libraries(RTScudoStandaloneCxxWrappers
ARCHS ${SCUDO_STANDALONE_SUPPORTED_ARCH}
SOURCES ${SCUDO_SOURCES_CXX_WRAPPERS}
ADDITIONAL_HEADERS ${SCUDO_HEADERS}
CFLAGS ${SCUDO_CFLAGS})

add_compiler_rt_runtime(clang_rt.scudo_standalone
STATIC
ARCHS ${SCUDO_STANDALONE_SUPPORTED_ARCH}
SOURCES ${SCUDO_SOURCES}
SOURCES ${SCUDO_SOURCES} ${SCUDO_SOURCES_C_WRAPPERS}
ADDITIONAL_HEADERS ${SCUDO_HEADERS}
CFLAGS ${SCUDO_CFLAGS}
PARENT_TARGET scudo_standalone)
add_compiler_rt_runtime(clang_rt.scudo_standalone_cxx
STATIC
ARCHS ${SCUDO_STANDALONE_SUPPORTED_ARCH}
SOURCES ${SCUDO_SOURCES_CXX_WRAPPERS}
ADDITIONAL_HEADERS ${SCUDO_HEADERS}
CFLAGS ${SCUDO_CFLAGS}
PARENT_TARGET scudo_standalone)
Expand Down
44 changes: 33 additions & 11 deletions compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt
Expand Up @@ -10,7 +10,10 @@ set(SCUDO_UNITTEST_CFLAGS
-I${COMPILER_RT_SOURCE_DIR}/include
-I${COMPILER_RT_SOURCE_DIR}/lib
-I${COMPILER_RT_SOURCE_DIR}/lib/scudo/standalone
-DGTEST_HAS_RTTI=0)
-DGTEST_HAS_RTTI=0
# Extra flags for the C++ tests
-fsized-deallocation
-Wno-mismatched-new-delete)

set(SCUDO_TEST_ARCH ${SCUDO_STANDALONE_SUPPORTED_ARCH})

Expand All @@ -21,27 +24,30 @@ foreach(lib ${SANITIZER_TEST_CXX_LIBRARIES})
endforeach()
list(APPEND LINK_FLAGS -pthread)

set(TEST_HEADERS)
set(SCUDO_TEST_HEADERS)
foreach (header ${SCUDO_HEADERS})
list(APPEND TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header})
list(APPEND SCUDO_TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header})
endforeach()

# add_scudo_unittest(<name>
# SOURCES <sources list>
# HEADERS <extra headers list>)
macro(add_scudo_unittest testname)
cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN})
cmake_parse_arguments(TEST "" "" "SOURCES;ADDITIONAL_RTOBJECTS" ${ARGN})
if(COMPILER_RT_HAS_SCUDO_STANDALONE)
foreach(arch ${SCUDO_TEST_ARCH})
# Additional runtime objects get added along RTScudoStandalone
set(SCUDO_TEST_RTOBJECTS $<TARGET_OBJECTS:RTScudoStandalone.${arch}>)
foreach(rtobject ${TEST_ADDITIONAL_RTOBJECTS})
list(APPEND SCUDO_TEST_RTOBJECTS $<TARGET_OBJECTS:${rtobject}.${arch}>)
endforeach()
# Add the static runtime library made of all the runtime objects
set(RUNTIME RT${testname}.${arch})
add_library(${RUNTIME} STATIC ${SCUDO_TEST_RTOBJECTS})
set(ScudoUnitTestsObjects)
add_library("RTScudoStandalone.test.${arch}" STATIC
$<TARGET_OBJECTS:RTScudoStandalone.${arch}>)
generate_compiler_rt_tests(ScudoUnitTestsObjects ScudoUnitTests
"${testname}-${arch}-Test" ${arch}
SOURCES ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}
COMPILE_DEPS ${TEST_HEADERS}
COMPILE_DEPS ${SCUDO_TEST_HEADERS}
DEPS gtest scudo_standalone
RUNTIME RTScudoStandalone.test.${arch}
RUNTIME ${RUNTIME}
CFLAGS ${SCUDO_UNITTEST_CFLAGS}
LINK_FLAGS ${LINK_FLAGS})
endforeach()
Expand Down Expand Up @@ -72,3 +78,19 @@ set(SCUDO_UNIT_TEST_SOURCES

add_scudo_unittest(ScudoUnitTest
SOURCES ${SCUDO_UNIT_TEST_SOURCES})

set(SCUDO_C_UNIT_TEST_SOURCES
wrappers_c_test.cc
scudo_unit_test_main.cc)

add_scudo_unittest(ScudoCUnitTest
SOURCES ${SCUDO_C_UNIT_TEST_SOURCES}
ADDITIONAL_RTOBJECTS RTScudoStandaloneCWrappers)

set(SCUDO_CXX_UNIT_TEST_SOURCES
wrappers_cpp_test.cc
scudo_unit_test_main.cc)

add_scudo_unittest(ScudoCxxUnitTest
SOURCES ${SCUDO_CXX_UNIT_TEST_SOURCES}
ADDITIONAL_RTOBJECTS RTScudoStandaloneCWrappers RTScudoStandaloneCxxWrappers)
225 changes: 225 additions & 0 deletions compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cc
@@ -0,0 +1,225 @@
//===-- wrappers_c_test.cc --------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "platform.h"

#include "gtest/gtest.h"

#include <limits.h>
#include <malloc.h>
#include <unistd.h>

// Note that every C allocation function in the test binary will be fulfilled
// by Scudo (this includes the gtest APIs, etc.), which is a test by itself.
// But this might also lead to unexpected side-effects, since the allocation and
// deallocation operations in the TEST functions will coexist with others (see
// the EXPECT_DEATH comment below).

// We have to use a small quarantine to make sure that our double-free tests
// trigger. Otherwise EXPECT_DEATH ends up reallocating the chunk that was just
// freed (this depends on the size obviously) and the following free succeeds.
extern "C" __attribute__((visibility("default"))) const char *
__scudo_default_options() {
return "quarantine_size_kb=256:thread_local_quarantine_size_kb=128:"
"quarantine_max_chunk_size=512";
}

static const size_t Size = 100U;

TEST(ScudoWrappersCTest, Malloc) {
void *P = malloc(Size);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % FIRST_32_SECOND_64(8U, 16U), 0U);
EXPECT_DEATH(
free(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(P) | 1U)), "");
free(P);
EXPECT_DEATH(free(P), "");

P = malloc(0U);
EXPECT_NE(P, nullptr);
free(P);

errno = 0;
EXPECT_EQ(malloc(SIZE_MAX), nullptr);
EXPECT_EQ(errno, ENOMEM);
}

TEST(ScudoWrappersCTest, Calloc) {
void *P = calloc(1U, Size);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
for (size_t I = 0; I < Size; I++)
EXPECT_EQ((reinterpret_cast<uint8_t *>(P))[I], 0U);
free(P);

P = calloc(1U, 0U);
EXPECT_NE(P, nullptr);
free(P);
P = calloc(0U, 1U);
EXPECT_NE(P, nullptr);
free(P);

errno = 0;
EXPECT_EQ(calloc(SIZE_MAX, 1U), nullptr);
EXPECT_EQ(errno, ENOMEM);
errno = 0;
EXPECT_EQ(calloc(static_cast<size_t>(LONG_MAX) + 1U, 2U), nullptr);
if (SCUDO_ANDROID)
EXPECT_EQ(errno, ENOMEM);
errno = 0;
EXPECT_EQ(calloc(SIZE_MAX, SIZE_MAX), nullptr);
EXPECT_EQ(errno, ENOMEM);
}

TEST(ScudoWrappersCTest, Memalign) {
void *P;
for (size_t I = FIRST_32_SECOND_64(2U, 3U); I <= 18U; I++) {
const size_t Alignment = 1U << I;

P = memalign(Alignment, Size);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
free(P);

P = nullptr;
EXPECT_EQ(posix_memalign(&P, Alignment, Size), 0);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
free(P);
}

EXPECT_EQ(memalign(4096U, SIZE_MAX), nullptr);
EXPECT_EQ(posix_memalign(&P, 15U, Size), EINVAL);
EXPECT_EQ(posix_memalign(&P, 4096U, SIZE_MAX), ENOMEM);

// Android's memalign accepts non power-of-2 alignments, and 0.
if (SCUDO_ANDROID) {
for (size_t Alignment = 0U; Alignment <= 128U; Alignment++) {
P = memalign(Alignment, 1024U);
EXPECT_NE(P, nullptr);
free(P);
}
}
}

TEST(ScudoWrappersCTest, AlignedAlloc) {
const size_t Alignment = 4096U;
void *P = aligned_alloc(Alignment, Alignment * 4U);
EXPECT_NE(P, nullptr);
EXPECT_LE(Alignment * 4U, malloc_usable_size(P));
EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
free(P);

errno = 0;
P = aligned_alloc(Alignment, Size);
EXPECT_EQ(P, nullptr);
EXPECT_EQ(errno, EINVAL);
}

TEST(ScudoWrappersCTest, Realloc) {
// realloc(nullptr, N) is malloc(N)
void *P = realloc(nullptr, 0U);
EXPECT_NE(P, nullptr);
free(P);

P = malloc(Size);
EXPECT_NE(P, nullptr);
// realloc(P, 0U) is free(P) and returns nullptr
EXPECT_EQ(realloc(P, 0U), nullptr);

P = malloc(Size);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
memset(P, 0x42, Size);

P = realloc(P, Size * 2U);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size * 2U, malloc_usable_size(P));
for (size_t I = 0; I < Size; I++)
EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);

P = realloc(P, Size / 2U);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size / 2U, malloc_usable_size(P));
for (size_t I = 0; I < Size / 2U; I++)
EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
free(P);

EXPECT_DEATH(P = realloc(P, Size), "");

errno = 0;
EXPECT_EQ(realloc(nullptr, SIZE_MAX), nullptr);
EXPECT_EQ(errno, ENOMEM);
P = malloc(Size);
EXPECT_NE(P, nullptr);
errno = 0;
EXPECT_EQ(realloc(P, SIZE_MAX), nullptr);
EXPECT_EQ(errno, ENOMEM);
free(P);

// Android allows realloc of memalign pointers.
if (SCUDO_ANDROID) {
const size_t Alignment = 1024U;
P = memalign(Alignment, Size);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
memset(P, 0x42, Size);

P = realloc(P, Size * 2U);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size * 2U, malloc_usable_size(P));
for (size_t I = 0; I < Size; I++)
EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
free(P);
}
}

#ifndef M_DECAY_TIME
#define M_DECAY_TIME -100
#endif

#ifndef M_PURGE
#define M_PURGE -101
#endif

TEST(ScudoWrappersCTest, Mallopt) {
errno = 0;
EXPECT_EQ(mallopt(-1000, 1), 0);
// mallopt doesn't set errno.
EXPECT_EQ(errno, 0);

EXPECT_EQ(mallopt(M_PURGE, 0), 1);

EXPECT_EQ(mallopt(M_DECAY_TIME, 1), 1);
EXPECT_EQ(mallopt(M_DECAY_TIME, 0), 1);
EXPECT_EQ(mallopt(M_DECAY_TIME, 1), 1);
EXPECT_EQ(mallopt(M_DECAY_TIME, 0), 1);
}

TEST(ScudoWrappersCTest, OtherAlloc) {
const size_t PageSize = sysconf(_SC_PAGESIZE);

void *P = pvalloc(Size);
EXPECT_NE(P, nullptr);
EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
EXPECT_LE(PageSize, malloc_usable_size(P));
free(P);

EXPECT_EQ(pvalloc(SIZE_MAX), nullptr);

P = pvalloc(Size);
EXPECT_NE(P, nullptr);
EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
free(P);

EXPECT_EQ(valloc(SIZE_MAX), nullptr);
}

0 comments on commit 37340e3

Please sign in to comment.