Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@ -0,0 +1,41 @@
set(SCUDO_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(SCUDO_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(SCUDO_TESTSUITES)
set(SCUDO_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
if(NOT COMPILER_RT_STANDALONE_BUILD)
list(APPEND SCUDO_TEST_DEPS scudo)
endif()
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
)
set(SCUDO_TEST_ARCH ${SCUDO_SUPPORTED_ARCH})
foreach(arch ${SCUDO_TEST_ARCH})
if(ANDROID)
if (${arch} STREQUAL "i386")
set(SCUDO_TEST_TARGET_ARCH i686-android)
else()
set(SCUDO_TEST_TARGET_ARCH ${arch}-android)
endif()
else()
set(SCUDO_TEST_TARGET_ARCH ${arch})
endif()
string(TOLOWER "-${arch}" SCUDO_TEST_CONFIG_SUFFIX)
get_test_cc_for_arch(${arch} SCUDO_TEST_TARGET_CC SCUDO_TEST_TARGET_CFLAGS)
string(TOUPPER ${arch} ARCH_UPPER_CASE)
set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}Config)
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
list(APPEND SCUDO_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
endforeach()
add_lit_testsuite(check-scudo "Running the Scudo Hardened Allocator tests"
${SCUDO_TESTSUITES}
DEPENDS ${SCUDO_TEST_DEPS})
set_target_properties(check-scudo PROPERTIES FOLDER "Compiler-RT Misc")

View File

@ -0,0 +1,23 @@
// RUN: %clang_scudo %s -o %t
// RUN: not %run %t pointers 2>&1 | FileCheck %s
// Tests that a non MinAlignment aligned pointer will trigger the associated
// error on deallocation.
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
assert(argc == 2);
if (!strcmp(argv[1], "pointers")) {
void *p = malloc(1U << 16);
assert(p);
free((void *)((uintptr_t)p | 1));
}
return 0;
}
// CHECK: ERROR: attempted to deallocate a chunk not properly aligned

View File

@ -0,0 +1,45 @@
// RUN: %clangxx_scudo %s -o %t
// RUN: not %run %t malloc 2>&1 | FileCheck %s
// RUN: not %run %t new 2>&1 | FileCheck %s
// RUN: not %run %t newarray 2>&1 | FileCheck %s
// RUN: not %run %t memalign 2>&1 | FileCheck %s
// Tests double-free error on pointers allocated with different allocation
// functions.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
assert(argc == 2);
if (!strcmp(argv[1], "malloc")) {
void *p = malloc(sizeof(int));
assert(p);
free(p);
free(p);
}
if (!strcmp(argv[1], "new")) {
int *p = new int;
assert(p);
delete p;
delete p;
}
if (!strcmp(argv[1], "newarray")) {
int *p = new int[8];
assert(p);
delete[] p;
delete[] p;
}
if (!strcmp(argv[1], "memalign")) {
void *p = nullptr;
posix_memalign(&p, 0x100, sizeof(int));
assert(p);
free(p);
free(p);
}
return 0;
}
// CHECK: ERROR: invalid chunk state

View File

@ -0,0 +1,92 @@
// RUN: %clangxx_scudo %s -lstdc++ -o %t
// RUN: %run %t ownership 2>&1
// RUN: %run %t ownership-and-size 2>&1
// RUN: %run %t heap-size 2>&1
// RUN: %env_scudo_opts="allocator_may_return_null=1" %run %t soft-limit 2>&1
// RUN: %env_scudo_opts="allocator_may_return_null=1" not %run %t hard-limit 2>&1
// Tests that the sanitizer interface functions behave appropriately.
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <vector>
#include <sanitizer/allocator_interface.h>
#include <sanitizer/scudo_interface.h>
int main(int argc, char **argv)
{
assert(argc == 2);
if (!strcmp(argv[1], "ownership")) {
// Ensures that __sanitizer_get_ownership can be called before any other
// allocator function, and that it behaves properly on a pointer not owned
// by us.
assert(!__sanitizer_get_ownership(argv));
}
if (!strcmp(argv[1], "ownership-and-size")) {
// Tests that __sanitizer_get_ownership and __sanitizer_get_allocated_size
// behave properly on chunks allocated by the Primary and Secondary.
void *p;
std::vector<ssize_t> sizes{1, 8, 16, 32, 1024, 32768,
1 << 16, 1 << 17, 1 << 20, 1 << 24};
for (size_t size : sizes) {
p = malloc(size);
assert(p);
assert(__sanitizer_get_ownership(p));
assert(__sanitizer_get_allocated_size(p) >= size);
free(p);
}
}
if (!strcmp(argv[1], "heap-size")) {
// Ensures that __sanitizer_get_heap_size can be called before any other
// allocator function.
assert(__sanitizer_get_heap_size() >= 0);
}
if (!strcmp(argv[1], "soft-limit")) {
// Verifies that setting the soft RSS limit at runtime works as expected.
std::vector<void *> pointers;
size_t size = 1 << 19; // 512Kb
for (int i = 0; i < 5; i++) {
void *p = malloc(size);
memset(p, 0, size);
pointers.push_back(p);
}
// Set the soft RSS limit to 1Mb.
__scudo_set_rss_limit(1, 0);
usleep(20000);
// The following allocation should return NULL.
void *p = malloc(size);
assert(!p);
// Remove the soft RSS limit.
__scudo_set_rss_limit(0, 0);
// The following allocation should succeed.
p = malloc(size);
assert(p);
free(p);
while (!pointers.empty()) {
free(pointers.back());
pointers.pop_back();
}
}
if (!strcmp(argv[1], "hard-limit")) {
// Verifies that setting the hard RSS limit at runtime works as expected.
std::vector<void *> pointers;
size_t size = 1 << 19; // 512Kb
for (int i = 0; i < 5; i++) {
void *p = malloc(size);
memset(p, 0, size);
pointers.push_back(p);
}
// Set the hard RSS limit to 1Mb
__scudo_set_rss_limit(1, 1);
usleep(20000);
// The following should trigger our death.
void *p = malloc(size);
}
return 0;
}

View File

@ -0,0 +1,61 @@
# -*- Python -*-
import os
# Setup config name.
config.name = 'Scudo' + config.name_suffix
# Setup source root.
config.test_source_root = os.path.dirname(__file__)
# Path to the shared & static libraries
shared_libscudo = os.path.join(config.compiler_rt_libdir, "libclang_rt.scudo-%s.so" % config.target_arch)
static_libscudo = os.path.join(config.compiler_rt_libdir, "libclang_rt.scudo-%s.a" % config.target_arch)
static_libscudo_cxx = os.path.join(config.compiler_rt_libdir, "libclang_rt.scudo_cxx-%s.a" % config.target_arch)
whole_archive = "-Wl,-whole-archive %s -Wl,-no-whole-archive " % static_libscudo
whole_archive_cxx = "-Wl,-whole-archive %s -Wl,-no-whole-archive " % static_libscudo_cxx
# Test suffixes.
config.suffixes = ['.c', '.cc', '.cpp']
# C & CXX flags.
c_flags = ([config.target_cflags] +
["-pthread",
"-fPIE",
"-pie",
"-O0",
"-UNDEBUG",
"-ldl",
"-Wl,--gc-sections"])
# Android doesn't want -lrt.
if not config.android:
c_flags += ["-lrt"]
cxx_flags = (c_flags + config.cxx_mode_flags + ["-std=c++11"])
def build_invocation(compile_flags):
return " " + " ".join([config.clang] + compile_flags) + " "
# Add clang substitutions.
config.substitutions.append(("%clang ", build_invocation(c_flags)))
config.substitutions.append(("%clang_scudo ", build_invocation(c_flags) + whole_archive))
config.substitutions.append(("%clangxx_scudo ", build_invocation(cxx_flags) + whole_archive + whole_archive_cxx))
config.substitutions.append(("%shared_libscudo", shared_libscudo))
# Platform-specific default SCUDO_OPTIONS for lit tests.
default_scudo_opts = ''
if config.android:
# Android defaults to abort_on_error=1, which doesn't work for us.
default_scudo_opts = 'abort_on_error=0'
if default_scudo_opts:
config.environment['SCUDO_OPTIONS'] = default_scudo_opts
default_scudo_opts += ':'
config.substitutions.append(('%env_scudo_opts=',
'env SCUDO_OPTIONS=' + default_scudo_opts))
# Hardened Allocator tests are currently supported on Linux only.
if config.host_os not in ['Linux']:
config.unsupported = True

View File

@ -0,0 +1,11 @@
@LIT_SITE_CFG_IN_HEADER@
config.name_suffix = "@SCUDO_TEST_CONFIG_SUFFIX@"
config.target_arch = "@SCUDO_TEST_TARGET_ARCH@"
config.target_cflags = "@SCUDO_TEST_TARGET_CFLAGS@"
# Load common config for all compiler-rt lit tests.
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
# Load tool-specific config that would do the real work.
lit_config.load_config(config, "@SCUDO_LIT_SOURCE_DIR@/lit.cfg")

View File

@ -0,0 +1,37 @@
// RUN: %clangxx_scudo %s -lstdc++ -o %t
// RUN: %run %t 2>&1
// Tests that a regular workflow of allocation, memory fill and free works as
// intended. Tests various sizes serviced by the primary and secondary
// allocators.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
int main(int argc, char **argv)
{
void *p;
std::vector<ssize_t> sizes{1, 8, 16, 32, 1024, 32768,
1 << 16, 1 << 17, 1 << 20, 1 << 24};
std::vector<int> offsets{1, 0, -1, -7, -8, -15, -16, -31, -32};
p = malloc(0);
assert(p);
free(p);
for (ssize_t size : sizes) {
for (int offset: offsets) {
ssize_t actual_size = size + offset;
if (actual_size <= 0)
continue;
p = malloc(actual_size);
assert(p);
memset(p, 0xff, actual_size);
free(p);
}
}
return 0;
}

View File

@ -0,0 +1,81 @@
// RUN: %clang_scudo %s -o %t
// RUN: %run %t valid 2>&1
// RUN: not %run %t invalid 2>&1
// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1
// Tests that the various aligned allocation functions work as intended. Also
// tests for the condition where the alignment is not a power of 2.
#include <assert.h>
#include <errno.h>
#include <malloc.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// Sometimes the headers may not have this...
void *aligned_alloc(size_t alignment, size_t size);
int main(int argc, char **argv)
{
void *p = NULL;
size_t alignment = 1U << 12;
size_t size = 1U << 12;
int err;
assert(argc == 2);
if (!strcmp(argv[1], "valid")) {
posix_memalign(&p, alignment, size);
assert(p);
assert(((uintptr_t)p & (alignment - 1)) == 0);
free(p);
p = aligned_alloc(alignment, size);
assert(p);
assert(((uintptr_t)p & (alignment - 1)) == 0);
free(p);
// Tests various combinations of alignment and sizes
for (int i = (sizeof(void *) == 4) ? 3 : 4; i < 19; i++) {
alignment = 1U << i;
for (int j = 1; j < 33; j++) {
size = 0x800 * j;
for (int k = 0; k < 3; k++) {
p = memalign(alignment, size - (2 * sizeof(void *) * k));
assert(p);
assert(((uintptr_t)p & (alignment - 1)) == 0);
free(p);
}
}
}
// For larger alignment, reduce the number of allocations to avoid running
// out of potential addresses (on 32-bit).
for (int i = 19; i <= 24; i++) {
for (int k = 0; k < 3; k++) {
p = memalign(alignment, 0x1000 - (2 * sizeof(void *) * k));
assert(p);
assert(((uintptr_t)p & (alignment - 1)) == 0);
free(p);
}
}
}
if (!strcmp(argv[1], "invalid")) {
// Alignment is not a power of 2.
p = memalign(alignment - 1, size);
assert(!p);
// Size is not a multiple of alignment.
p = aligned_alloc(alignment, size >> 1);
assert(!p);
void *p_unchanged = (void *)0x42UL;
p = p_unchanged;
// Alignment is not a power of 2.
err = posix_memalign(&p, 3, size);
assert(p == p_unchanged);
assert(err == EINVAL);
// Alignment is a power of 2, but not a multiple of size(void *).
err = posix_memalign(&p, 2, size);
assert(p == p_unchanged);
assert(err == EINVAL);
}
return 0;
}

View File

@ -0,0 +1,47 @@
// RUN: %clangxx_scudo %s -o %t
// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t mallocdel 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s
// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t mallocdel 2>&1
// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t newfree 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s
// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t newfree 2>&1
// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t memaligndel 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s
// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t memaligndel 2>&1
// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t memalignrealloc 2>&1 | FileCheck --check-prefix=CHECK-realloc %s
// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t memalignrealloc 2>&1
// Tests that type mismatches between allocation and deallocation functions are
// caught when the related option is set.
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
assert(argc == 2);
if (!strcmp(argv[1], "mallocdel")) {
int *p = (int *)malloc(16);
assert(p);
delete p;
}
if (!strcmp(argv[1], "newfree")) {
int *p = new int;
assert(p);
free((void *)p);
}
if (!strcmp(argv[1], "memaligndel")) {
int *p = (int *)memalign(16, 16);
assert(p);
delete p;
}
if (!strcmp(argv[1], "memalignrealloc")) {
void *p = memalign(16, 16);
assert(p);
p = realloc(p, 32);
free(p);
}
return 0;
}
// CHECK-dealloc: ERROR: allocation type mismatch when deallocating address
// CHECK-realloc: ERROR: allocation type mismatch when reallocating address

View File

@ -0,0 +1,25 @@
// RUN: %clangxx_scudo %s -o %t
// RUN: %run %t 2>&1
// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t 2>&1
// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t 2>&1 | FileCheck %s
// Tests that the options can be passed using getScudoDefaultOptions, and that
// the environment ones take precedence over them.
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>
extern "C" const char* __scudo_default_options() {
return "DeallocationTypeMismatch=0"; // Defaults to true in scudo_flags.inc.
}
int main(int argc, char **argv)
{
int *p = (int *)malloc(16);
assert(p);
delete p;
return 0;
}
// CHECK: ERROR: allocation type mismatch when deallocating address

View File

@ -0,0 +1,39 @@
// RUN: %clang_scudo %s -o %t
// RUN: not %run %t malloc 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=QuarantineSizeKb=64 not %run %t quarantine 2>&1 | FileCheck %s
// Tests that header corruption of an allocated or quarantined chunk is caught.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
ssize_t offset = sizeof(void *) == 8 ? 8 : 0;
assert(argc == 2);
if (!strcmp(argv[1], "malloc")) {
// Simulate a header corruption of an allocated chunk (1-bit)
void *p = malloc(1U << 4);
assert(p);
((char *)p)[-(offset + 1)] ^= 1;
free(p);
}
if (!strcmp(argv[1], "quarantine")) {
void *p = malloc(1U << 4);
assert(p);
free(p);
// Simulate a header corruption of a quarantined chunk
((char *)p)[-(offset + 2)] ^= 1;
// Trigger the quarantine recycle
for (int i = 0; i < 0x100; i++) {
p = malloc(1U << 8);
free(p);
}
}
return 0;
}
// CHECK: ERROR: corrupted chunk header at address

View File

@ -0,0 +1,40 @@
// RUN: %clang_scudo %s -o %t
// RUN: %run %t 2>&1
// Verifies that calling malloc in a preinit_array function succeeds, and that
// the resulting pointer can be freed at program termination.
// On some Android versions, calling mmap() from a preinit function segfaults.
// It looks like __mmap2.S ends up calling a NULL function pointer.
// UNSUPPORTED: android
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static void *global_p = NULL;
void __init(void) {
global_p = malloc(1);
if (!global_p)
exit(1);
}
void __fini(void) {
if (global_p)
free(global_p);
}
int main(int argc, char **argv)
{
void *p = malloc(1);
assert(p);
free(p);
return 0;
}
__attribute__((section(".preinit_array"), used))
void (*__local_preinit)(void) = __init;
__attribute__((section(".fini_array"), used))
void (*__local_fini)(void) = __fini;

View File

@ -0,0 +1,20 @@
// Test that the preloaded runtime works without linking the static library.
// RUN: %clang %s -lstdc++ -o %t
// RUN: env LD_PRELOAD=%shared_libscudo not %run %t 2>&1 | FileCheck %s
// This way of setting LD_PRELOAD does not work with Android test runner.
// REQUIRES: !android
#include <assert.h>
int main(int argc, char *argv[]) {
int *p = new int;
assert(p);
*p = 0;
delete p;
delete p;
return 0;
}
// CHECK: ERROR: invalid chunk state

View File

@ -0,0 +1,124 @@
// RUN: %clang_scudo %s -o %t
// RUN: %env_scudo_opts="QuarantineSizeMb=1:QuarantineSizeKb=64" not %run %t unused 2>&1
// RUN: %env_scudo_opts="QuarantineSizeMb=1:QuarantineChunksUpToSize=256" not %run %t unused 2>&1
// RUN: %env_scudo_opts="QuarantineSizeKb=0:ThreadLocalQuarantineSizeKb=0" %run %t zeroquarantine 2>&1
// RUN: %env_scudo_opts=QuarantineSizeKb=64 %run %t smallquarantine 2>&1
// RUN: %env_scudo_opts=QuarantineChunksUpToSize=256 %run %t threshold 2>&1
// RUN: %env_scudo_opts="QuarantineSizeMb=1" %run %t oldquarantine 2>&1
// Tests that the quarantine prevents a chunk from being reused right away.
// Also tests that a chunk will eventually become available again for
// allocation when the recycling criteria has been met. Finally, tests the
// threshold up to which a chunk is quarantine, and the old quarantine behavior.
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <sanitizer/allocator_interface.h>
int main(int argc, char **argv)
{
void *p, *old_p;
size_t allocated_bytes, size = 1U << 8, alignment = 1U << 8;
assert(argc == 2);
// First, warm up the allocator for the classes used.
p = malloc(size);
assert(p);
free(p);
p = malloc(size + 1);
assert(p);
free(p);
assert(posix_memalign(&p, alignment, size) == 0);
assert(p);
free(p);
assert(posix_memalign(&p, alignment, size + 1) == 0);
assert(p);
free(p);
if (!strcmp(argv[1], "zeroquarantine")) {
// Verifies that a chunk is deallocated right away when the local and
// global quarantine sizes are 0.
allocated_bytes = __sanitizer_get_current_allocated_bytes();
p = malloc(size);
assert(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() == allocated_bytes);
}
if (!strcmp(argv[1], "smallquarantine")) {
// The delayed freelist will prevent a chunk from being available right
// away.
p = malloc(size);
assert(p);
old_p = p;
free(p);
p = malloc(size);
assert(p);
assert(old_p != p);
free(p);
// Eventually the chunk should become available again.
char found = 0;
for (int i = 0; i < 0x200 && !found; i++) {
p = malloc(size);
assert(p);
found = (p == old_p);
free(p);
}
assert(found);
}
if (!strcmp(argv[1], "threshold")) {
// Verifies that a chunk of size greater than the threshold will be freed
// right away. Alignment has no impact on the threshold.
allocated_bytes = __sanitizer_get_current_allocated_bytes();
p = malloc(size + 1);
assert(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() == allocated_bytes);
assert(posix_memalign(&p, alignment, size + 1) == 0);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() == allocated_bytes);
// Verifies that a chunk of size lower or equal to the threshold will be
// quarantined.
p = malloc(size);
assert(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
allocated_bytes = __sanitizer_get_current_allocated_bytes();
assert(posix_memalign(&p, alignment, size) == 0);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
}
if (!strcmp(argv[1], "oldquarantine")) {
// Verifies that we quarantine everything if the deprecated quarantine
// option is specified. Alignment has no impact on the threshold.
allocated_bytes = __sanitizer_get_current_allocated_bytes();
p = malloc(size);
assert(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
allocated_bytes = __sanitizer_get_current_allocated_bytes();
assert(posix_memalign(&p, alignment, size) == 0);
assert(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
// Secondary backed allocation.
allocated_bytes = __sanitizer_get_current_allocated_bytes();
p = malloc(1U << 19);
assert(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
free(p);
assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes);
}
return 0;
}

View File

@ -0,0 +1,23 @@
// RUN: %clangxx_scudo %s -o %t
// RUN: rm -rf %T/random_shuffle_tmp_dir
// RUN: mkdir %T/random_shuffle_tmp_dir
// RUN: %run %t 100 > %T/random_shuffle_tmp_dir/out1
// RUN: %run %t 100 > %T/random_shuffle_tmp_dir/out2
// RUN: %run %t 10000 > %T/random_shuffle_tmp_dir/out1
// RUN: %run %t 10000 > %T/random_shuffle_tmp_dir/out2
// RUN: not diff %T/random_shuffle_tmp_dir/out?
// RUN: rm -rf %T/random_shuffle_tmp_dir
// Tests that the allocator shuffles the chunks before returning to the user.
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv) {
int alloc_size = argc == 2 ? atoi(argv[1]) : 100;
char *base = new char[alloc_size];
for (int i = 0; i < 20; i++) {
char *p = new char[alloc_size];
printf("%zd\n", base - p);
}
}

View File

@ -0,0 +1,90 @@
// RUN: %clangxx_scudo %s -lstdc++ -o %t
// RUN: %run %t pointers 2>&1
// RUN: %run %t contents 2>&1
// RUN: %run %t usablesize 2>&1
// Tests that our reallocation function returns the same pointer when the
// requested size can fit into the previously allocated chunk. Also tests that
// a new chunk is returned if the size is greater, and that the contents of the
// chunk are left unchanged. Finally, checks that realloc copies the usable
// size of the old chunk to the new one (as opposed to the requested size).
#include <assert.h>
#include <malloc.h>
#include <string.h>
#include <vector>
int main(int argc, char **argv)
{
void *p, *old_p;
// Those sizes will exercise both allocators (Primary & Secondary).
std::vector<size_t> sizes{1, 16, 1024, 32768, 1 << 16, 1 << 17, 1 << 20};
assert(argc == 2);
if (!strcmp(argv[1], "usablesize")) {
// This tests a sketchy behavior inherited from poorly written libraries
// that have become somewhat standard. When realloc'ing a chunk, the
// copied contents should span the usable size of the chunk, not the
// requested size.
size_t size = 496, usable_size;
p = nullptr;
// Make sure we get a chunk with a usable size actually larger than size.
do {
if (p) free(p);
size += 16;
p = malloc(size);
usable_size = malloc_usable_size(p);
assert(usable_size >= size);
} while (usable_size == size);
for (int i = 0; i < usable_size; i++)
reinterpret_cast<char *>(p)[i] = 'A';
old_p = p;
// Make sure we get a different chunk so that the data is actually copied.
do {
size *= 2;
p = realloc(p, size);
assert(p);
} while (p == old_p);
// The contents of the new chunk must match the old one up to usable_size.
for (int i = 0; i < usable_size; i++)
assert(reinterpret_cast<char *>(p)[i] == 'A');
free(p);
} else {
for (size_t size : sizes) {
if (!strcmp(argv[1], "pointers")) {
old_p = p = realloc(nullptr, size);
assert(p);
size = malloc_usable_size(p);
// Our realloc implementation will return the same pointer if the size
// requested is lower than or equal to the usable size of the associated
// chunk.
p = realloc(p, size - 1);
assert(p == old_p);
p = realloc(p, size);
assert(p == old_p);
// And a new one if the size is greater.
p = realloc(p, size + 1);
assert(p != old_p);
// A size of 0 will free the chunk and return nullptr.
p = realloc(p, 0);
assert(!p);
old_p = nullptr;
}
if (!strcmp(argv[1], "contents")) {
p = realloc(nullptr, size);
assert(p);
for (int i = 0; i < size; i++)
reinterpret_cast<char *>(p)[i] = 'A';
p = realloc(p, size + 1);
// The contents of the reallocated chunk must match the original one.
for (int i = 0; i < size; i++)
assert(reinterpret_cast<char *>(p)[i] == 'A');
}
}
}
return 0;
}
// CHECK: ERROR: invalid chunk type when reallocating address

View File

@ -0,0 +1,56 @@
// RUN: %clang_scudo %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit
// RUN: %env_scudo_opts="soft_rss_limit_mb=256" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit
// RUN: %env_scudo_opts="hard_rss_limit_mb=256" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit
// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit
// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=1" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit-returnnull
// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=0:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit
// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=1:can_use_proc_maps_statm=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit-returnnull
// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit
// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=1" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit
// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=0:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit
// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=1:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit
// Tests that the soft and hard RSS limits work as intended. Without limit or
// with a high limit, the test should pass without any malloc returning NULL or
// the program dying.
// If a limit is specified, it should return some NULL or die depending on
// allocator_may_return_null. This should also work without statm.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static const size_t kNumAllocs = 128;
static const size_t kAllocSize = 1 << 20; // 1MB.
static void *allocs[kNumAllocs];
int main(int argc, char *argv[]) {
int returned_null = 0;
for (int i = 0; i < kNumAllocs; i++) {
if ((i & 0xf) == 0)
usleep(50000);
allocs[i] = malloc(kAllocSize);
if (allocs[i])
memset(allocs[i], 0xff, kAllocSize); // Dirty the pages.
else
returned_null++;
}
for (int i = 0; i < kNumAllocs; i++)
free(allocs[i]);
if (returned_null == 0)
printf("All malloc calls succeeded\n");
else
printf("%d malloc calls returned NULL\n", returned_null);
return 0;
}
// CHECK-nolimit: All malloc calls succeeded
// CHECK-softlimit: soft RSS limit exhausted
// CHECK-softlimit-NOT: malloc calls
// CHECK-softlimit-returnnull: soft RSS limit exhausted
// CHECK-softlimit-returnnull: malloc calls returned NULL
// CHECK-hardlimit: hard RSS limit exhausted
// CHECK-hardlimit-NOT: malloc calls

View File

@ -0,0 +1,53 @@
// RUN: %clang_scudo %s -o %t
// RUN: %run %t after 2>&1 | FileCheck %s
// RUN: %run %t before 2>&1 | FileCheck %s
// Test that we hit a guard page when writing past the end of a chunk
// allocated by the Secondary allocator, or writing too far in front of it.
#include <assert.h>
#include <malloc.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void handler(int signo, siginfo_t *info, void *uctx) {
if (info->si_code == SEGV_ACCERR) {
fprintf(stderr, "SCUDO SIGSEGV\n");
exit(0);
}
exit(1);
}
int main(int argc, char **argv)
{
// The size must be large enough to be serviced by the secondary allocator.
long page_size = sysconf(_SC_PAGESIZE);
size_t size = (1U << 17) + page_size;
struct sigaction a;
assert(argc == 2);
memset(&a, 0, sizeof(a));
a.sa_sigaction = handler;
a.sa_flags = SA_SIGINFO;
char *p = (char *)malloc(size);
assert(p);
memset(p, 'A', size); // This should not trigger anything.
// Set up the SIGSEGV handler now, as the rest should trigger an AV.
sigaction(SIGSEGV, &a, NULL);
if (!strcmp(argv[1], "after")) {
for (int i = 0; i < page_size; i++)
p[size + i] = 'A';
}
if (!strcmp(argv[1], "before")) {
for (int i = 1; i < page_size; i++)
p[-i] = 'A';
}
free(p);
return 1; // A successful test means we shouldn't reach this.
}
// CHECK: SCUDO SIGSEGV

View File

@ -0,0 +1,41 @@
// RUN: %clangxx_scudo -fsized-deallocation %s -o %t
// RUN: %env_scudo_opts=DeleteSizeMismatch=1 %run %t gooddel 2>&1
// RUN: %env_scudo_opts=DeleteSizeMismatch=1 not %run %t baddel 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=DeleteSizeMismatch=0 %run %t baddel 2>&1
// RUN: %env_scudo_opts=DeleteSizeMismatch=1 %run %t gooddelarr 2>&1
// RUN: %env_scudo_opts=DeleteSizeMismatch=1 not %run %t baddelarr 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=DeleteSizeMismatch=0 %run %t baddelarr 2>&1
// Ensures that the sized delete operator errors out when the appropriate
// option is passed and the sizes do not match between allocation and
// deallocation functions.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <new>
int main(int argc, char **argv)
{
assert(argc == 2);
if (!strcmp(argv[1], "gooddel")) {
long long *p = new long long;
operator delete(p, sizeof(long long));
}
if (!strcmp(argv[1], "baddel")) {
long long *p = new long long;
operator delete(p, 2);
}
if (!strcmp(argv[1], "gooddelarr")) {
char *p = new char[64];
operator delete[](p, 64);
}
if (!strcmp(argv[1], "baddelarr")) {
char *p = new char[63];
operator delete[](p, 64);
}
return 0;
}
// CHECK: ERROR: invalid sized delete on chunk at address

View File

@ -0,0 +1,73 @@
// RUN: %clangxx_scudo %s -lstdc++ -o %t
// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t malloc 2>&1
// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t calloc 2>&1
// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1
// RUN: %run %t usable 2>&1
// Tests for various edge cases related to sizes, notably the maximum size the
// allocator can allocate. Tests that an integer overflow in the parameters of
// calloc is caught.
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <limits>
#include <new>
int main(int argc, char **argv) {
assert(argc == 2);
const char *action = argv[1];
fprintf(stderr, "%s:\n", action);
#if __LP64__ || defined(_WIN64)
static const size_t kMaxAllowedMallocSize = 1ULL << 40;
static const size_t kChunkHeaderSize = 16;
#else
static const size_t kMaxAllowedMallocSize = 2UL << 30;
static const size_t kChunkHeaderSize = 8;
#endif
if (!strcmp(action, "malloc")) {
void *p = malloc(kMaxAllowedMallocSize);
assert(!p);
p = malloc(kMaxAllowedMallocSize - kChunkHeaderSize);
assert(!p);
} else if (!strcmp(action, "calloc")) {
// Trigger an overflow in calloc.
size_t size = std::numeric_limits<size_t>::max();
void *p = calloc((size / 0x1000) + 1, 0x1000);
assert(!p);
} else if (!strcmp(action, "new")) {
void *p = operator new(kMaxAllowedMallocSize);
assert(!p);
} else if (!strcmp(action, "new-nothrow")) {
void *p = operator new(kMaxAllowedMallocSize, std::nothrow);
assert(!p);
} else if (!strcmp(action, "usable")) {
// Playing with the actual usable size of a chunk.
void *p = malloc(1007);
assert(p);
size_t size = malloc_usable_size(p);
assert(size >= 1007);
memset(p, 'A', size);
p = realloc(p, 2014);
assert(p);
size = malloc_usable_size(p);
assert(size >= 2014);
memset(p, 'B', size);
free(p);
} else {
assert(0);
}
return 0;
}
// CHECK: allocator is terminating the process

Some files were not shown because too many files have changed in this diff Show More