Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AddressSanitizer support to fibers. #2975

Merged
merged 1 commit into from
Feb 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
Copy link
Member

@kinke kinke Feb 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but a cached RT_SUPPORT_SANITIZERS CMake variable might be 1 instead of ON. I think that's why I added

ldc/CMakeLists.txt

Lines 483 to 488 in 4b101ba

# translate 1/0 to ON/OFF
if(LDC_WITH_LLD)
set(LDC_WITH_LLD ON)
else()
set(LDC_WITH_LLD OFF)
endif()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to merge anyway in order to move forward.


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