Skip to content

Commit

Permalink
Add AddressSanitizer support to fibers.
Browse files Browse the repository at this point in the history
  • Loading branch information
JohanEngelen committed Jan 25, 2019
1 parent 54e51ef commit dcb9e4f
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ matrix:
# env: LLVM_VERSION=7.0.1 OPTS="-DBUILD_SHARED_LIBS=ON"
- os: linux
d: ldc-beta
env: LLVM_VERSION=6.0.1 OPTS="-DBUILD_SHARED_LIBS=OFF -DLIB_SUFFIX=64"
env: LLVM_VERSION=6.0.1 OPTS="-DBUILD_SHARED_LIBS=OFF -DLIB_SUFFIX=64 -DRT_SUPPORT_SANITIZERS=ON"
- os: linux
d: ldc
env: LLVM_VERSION=5.0.2 OPTS="-DBUILD_SHARED_LIBS=ON -DLIB_SUFFIX=64"
Expand Down
6 changes: 6 additions & 0 deletions runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,15 @@ set(LD_FLAGS "" CACHE STRING "Runt
set(C_SYSTEM_LIBS AUTO CACHE STRING "C system libraries for linking shared libraries and test runners, separated by ';'")
set(TARGET_SYSTEM AUTO CACHE STRING "Target OS/toolchain for cross-compilation (e.g., 'Linux;UNIX', 'Darwin;APPLE;UNIX', 'Windows;MSVC')")
set(LDC_TARGET_PRESET "" CACHE STRING "Use a preset target configuration for cross-compilation, by passing format OS-arch (e.g., 'Linux-arm', 'Mac-x64', 'Windows-aarch64', 'Android-x86')")
set(RT_SUPPORT_SANITIZERS OFF CACHE BOOL "Build runtime libraries with sanitizer support (e.g. for AddressSanitizer)")

set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})

if (RT_SUPPORT_SANITIZERS)
message(STATUS "Building runtime libraries with sanitizer support")
set(D_FLAGS "${D_FLAGS};-d-version=SupportSanitizers")
endif()

# Find gnu make, use specific version first (BSD) and fall back to default 'make' (LINUX)
find_program(GNU_MAKE_BIN NAMES gmake gnumake make)
if ("${GNU_MAKE_BIN}" STREQUAL "")
Expand Down
2 changes: 1 addition & 1 deletion runtime/druntime
4 changes: 4 additions & 0 deletions tests/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ config.gnu_make_bin = "@GNU_MAKE_BIN@"
config.ldc_host_arch = "@LDC_HOST_ARCH@"
config.ldc_with_lld = "@LDC_WITH_LLD@" == "ON"
config.spirv_enabled = @LLVM_SPIRV_FOUND@ +0 # LLVM_SPIRV_FOUND is blank if not set
config.rt_supports_sanitizers = "@RT_SUPPORT_SANITIZERS@" == "ON"

config.name = 'LDC'

Expand Down Expand Up @@ -92,6 +93,9 @@ for t in config.llvm_targetsstr.split(';'):
if config.spirv_enabled:
config.available_features.add('target_SPIRV')

if config.rt_supports_sanitizers:
config.available_features.add('RTSupportsSanitizers')

# Add specific features for Windows x86/x64 testing
if (platform.system() == 'Windows') and (config.default_target_bits == 32):
config.available_features.add('Windows_x86')
Expand Down
40 changes: 40 additions & 0 deletions tests/sanitizers/asan_fiber.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// AddressSanitizer: Test stack overflow detection of an array on a fiber's local stack.

// REQUIRES: ASan, RTSupportsSanitizers

// RUN: %ldc -g -fsanitize=address %s -of=%t%exe && not %t%exe 2>&1 | FileCheck %s
// RUN: %ldc -g -fsanitize=address %s -of=%t%exe -d-version=BAD_AFTER_YIELD && not %t%exe 2>&1 | FileCheck %s

import core.thread;

// Note: the ordering of `foo` and `prefoo` is intentional to ease FileCheck checking line numbers,
// because of the order in which ASan reports the stack buffer overflow.

void foo(int* ptr)
{
version (BAD_AFTER_YIELD)
Fiber.yield();

// CHECK: stack-buffer-overflow
// CHECK: WRITE of size 4
// CHECK-NEXT: #0 {{.*}} in {{.*foo.*}} {{.*}}asan_fiber.d:[[@LINE+1]]
ptr[10] = 1;

}

// CHECK-NOT: wild pointer
// CHECK: Address {{.*}} is located in stack of
// CHECK-NEXT: #0 {{.*}} in {{.*prefoo.*}} {{.*}}asan_fiber.d:[[@LINE+1]]
void prefoo()
{
int[10] a;
foo(&a[0]);
}

void main()
{
auto fib = new Fiber(&prefoo);
fib.call();
version (BAD_AFTER_YIELD)
fib.call();
}
43 changes: 43 additions & 0 deletions tests/sanitizers/asan_fiber_main.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// AddressSanitizer: Test stack overflow detection inside a fiber of an array on main's stack.

// REQUIRES: ASan, RTSupportsSanitizers

// RUN: %ldc -g -fsanitize=address %s -of=%t1%exe && not %t1%exe 2>&1 | FileCheck %s
// RUN: %ldc -g -fsanitize=address %s -of=%t22%exe -d-version=BAD_AFTER_YIELD && not %t22%exe 2>&1 | FileCheck %s

// Test with fake stack enabled
// RUN: env %env_asan_opts=detect_stack_use_after_return=true not %t1%exe 2>&1 | FileCheck %s --check-prefix=FAKESTACK
// RUN: env %env_asan_opts=detect_stack_use_after_return=true not %t22%exe 2>&1 | FileCheck %s --check-prefix=FAKESTACK

import core.thread;

void foo(int* arr)
{
version (BAD_AFTER_YIELD)
Fiber.yield();

// CHECK: stack-buffer-overflow
// CHECK: WRITE of size 4
// CHECK-NEXT: #0 {{.*}} in {{.*foo.*}} {{.*}}asan_fiber_main.d:[[@LINE+1]]
arr[10] = 1; // out-of-bounds write
}

// Without fake stack, ASan only keeps track of the current stack and thus reports
// the bad memory location as a "wild pointer".
// But with fake stack enabled we get something much better:
// FAKESTACK: Address {{.*}} is located in stack of
// FAKESTACK: #0 {{.*}} in {{.*main.*}} {{.*}}asan_fiber_main.d:[[@LINE+1]]
void main()
{
int[10] a;
int b;

// Use an extra variable instead of passing `&a[0]` directly to `foo`.
// This is to keep `a` on the stack: `ptr` may be heap allocated because
// it is used in the lambda (delegate).
int* ptr = &a[0];
auto fib = new Fiber(() => foo(ptr));
fib.call();
version (BAD_AFTER_YIELD)
fib.call();
}
6 changes: 5 additions & 1 deletion tests/sanitizers/lit.local.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ if 'ASan' in config.available_features:
# On Darwin, ASan defaults to `abort_on_error=1`, which would make tests run
# much slower. Let's override this and run lit tests with 'abort_on_error=0'.
# Also, make sure we do not overwhelm the syslog while testing.
config.environment['ASAN_OPTIONS'] = 'abort_on_error=0:log_to_syslog=0'
default_asan_options = 'abort_on_error=0:log_to_syslog=0'
config.environment['ASAN_OPTIONS'] = default_asan_options
# And add a convenience substitution so we can append to the default ASAN_OPTIONS
config.substitutions.append(('%env_asan_opts=',
'env ASAN_OPTIONS=' + default_asan_options + ':'))

# Note: To get line numbers in stack traces on Darwin, we need to run dsymutil on the binary,
# because llvm-symbolizer does not look at the object file for debug info.
Expand Down

0 comments on commit dcb9e4f

Please sign in to comment.