Skip to content

[ubsan] Runtime and driver support for local-bounds #120515

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

Merged
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,8 @@ Sanitizers
``-fsanitize=type`` flag. This sanitizer detects violations of C/C++ type-based
aliasing rules.

- Implemented ``-f[no-]sanitize-trap=local-bounds``, and ``-f[no-]sanitize-recover=local-bounds``.

Python Binding Changes
----------------------
- Fixed an issue that led to crashes when calling ``Type.get_exception_specification_kind``.
Expand Down
17 changes: 14 additions & 3 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,9 +1028,20 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
// of the pipeline.
if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds))
PB.registerScalarOptimizerLateEPCallback(
[](FunctionPassManager &FPM, OptimizationLevel Level) {
FPM.addPass(
BoundsCheckingPass(BoundsCheckingPass::ReportingMode::Trap));
[this](FunctionPassManager &FPM, OptimizationLevel Level) {
BoundsCheckingPass::ReportingMode Mode;
if (CodeGenOpts.SanitizeTrap.has(SanitizerKind::LocalBounds)) {
Mode = BoundsCheckingPass::ReportingMode::Trap;
} else if (CodeGenOpts.SanitizeMinimalRuntime) {
Mode = CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds)
? BoundsCheckingPass::ReportingMode::MinRuntime
: BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
} else {
Mode = CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds)
? BoundsCheckingPass::ReportingMode::FullRuntime
: BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
}
FPM.addPass(BoundsCheckingPass(Mode));
});

// Don't add sanitizers if we are here from ThinLTO PostLink. That already
Expand Down
9 changes: 5 additions & 4 deletions clang/lib/Driver/SanitizerArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ using namespace llvm::opt;

static const SanitizerMask NeedsUbsanRt =
SanitizerKind::Undefined | SanitizerKind::Integer |
SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
SanitizerKind::CFI | SanitizerKind::FloatDivideByZero |
SanitizerKind::ObjCCast;
SanitizerKind::LocalBounds | SanitizerKind::ImplicitConversion |
SanitizerKind::Nullability | SanitizerKind::CFI |
SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast;
static const SanitizerMask NeedsUbsanCxxRt =
SanitizerKind::Vptr | SanitizerKind::CFI;
static const SanitizerMask NotAllowedWithTrap = SanitizerKind::Vptr;
Expand Down Expand Up @@ -69,7 +69,8 @@ static const SanitizerMask TrappingSupported =
SanitizerKind::LocalBounds | SanitizerKind::CFI |
SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast;
static const SanitizerMask MergeDefault = SanitizerKind::Undefined;
static const SanitizerMask TrappingDefault = SanitizerKind::CFI;
static const SanitizerMask TrappingDefault =
SanitizerKind::CFI | SanitizerKind::LocalBounds;
static const SanitizerMask CFIClasses =
SanitizerKind::CFIVCall | SanitizerKind::CFINVCall |
SanitizerKind::CFIMFCall | SanitizerKind::CFIDerivedCast |
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/bounds-checking.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fsanitize=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s
// RUN: %clang_cc1 -fsanitize=array-bounds -O -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s
// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s
//
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/ubsan/ubsan_checks.inc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ UBSAN_CHECK(ImplicitSignedIntegerTruncationOrSignChange,
UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "shift-base")
UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", "shift-exponent")
UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds")
UBSAN_CHECK(LocalOutOfBounds, "local-out-of-bounds", "local-bounds")
UBSAN_CHECK(UnreachableCall, "unreachable-call", "unreachable")
UBSAN_CHECK(MissingReturn, "missing-return", "return")
UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index", "vla-bound")
Expand Down
22 changes: 22 additions & 0 deletions compiler-rt/lib/ubsan/ubsan_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,28 @@ void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
Die();
}

static void handleLocalOutOfBoundsImpl(ReportOptions Opts) {
// FIXME: Pass more diagnostic info.
SymbolizedStackHolder CallerLoc;
CallerLoc.reset(getCallerLocation(Opts.pc));
Location Loc;
Loc = CallerLoc;
ErrorType ET = ErrorType::LocalOutOfBounds;
ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error, ET, "access out of bounds");
}

void __ubsan::__ubsan_handle_local_out_of_bounds() {
GET_REPORT_OPTIONS(false);
handleLocalOutOfBoundsImpl(Opts);
}

void __ubsan::__ubsan_handle_local_out_of_bounds_abort() {
GET_REPORT_OPTIONS(true);
handleLocalOutOfBoundsImpl(Opts);
Die();
}

static void handleBuiltinUnreachableImpl(UnreachableData *Data,
ReportOptions Opts) {
ErrorType ET = ErrorType::UnreachableCall;
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/ubsan/ubsan_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ struct OutOfBoundsData {
/// \brief Handle an array index out of bounds error.
RECOVERABLE(out_of_bounds, OutOfBoundsData *Data, ValueHandle Index)

/// \brief Handle an local object access out of bounds error.
RECOVERABLE(local_out_of_bounds)

struct UnreachableData {
SourceLocation Loc;
};
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/ubsan/ubsan_interface.inc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ INTERFACE_FUNCTION(__ubsan_handle_nullability_return_v1)
INTERFACE_FUNCTION(__ubsan_handle_nullability_return_v1_abort)
INTERFACE_FUNCTION(__ubsan_handle_out_of_bounds)
INTERFACE_FUNCTION(__ubsan_handle_out_of_bounds_abort)
INTERFACE_FUNCTION(__ubsan_handle_local_out_of_bounds)
INTERFACE_FUNCTION(__ubsan_handle_local_out_of_bounds_abort)
INTERFACE_FUNCTION(__ubsan_handle_pointer_overflow)
INTERFACE_FUNCTION(__ubsan_handle_pointer_overflow_abort)
INTERFACE_FUNCTION(__ubsan_handle_shift_out_of_bounds)
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ HANDLER(negate_overflow, "negate-overflow")
HANDLER(divrem_overflow, "divrem-overflow")
HANDLER(shift_out_of_bounds, "shift-out-of-bounds")
HANDLER(out_of_bounds, "out-of-bounds")
HANDLER(local_out_of_bounds, "local-out-of-bounds")
HANDLER_RECOVER(builtin_unreachable, "builtin-unreachable")
HANDLER_RECOVER(missing_return, "missing-return")
HANDLER(vla_bound_not_positive, "vla-bound-not-positive")
Expand Down
12 changes: 9 additions & 3 deletions compiler-rt/test/ubsan/TestCases/Misc/local_bounds.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// RUN: %clangxx -fsanitize=local-bounds %s -O3 -o %t && %run %t 1
// RUN: %clangxx -fsanitize=local-bounds %s -O3 -o %t && not --crash %run %t 3

// FIXME: it's always trap for now.
// RUN: %clangxx -fsanitize=local-bounds -fno-sanitize-trap=local-bounds %s -O3 -o %t && not %run %t 3 2>&1 | FileCheck %s
// RUN: %clangxx -fsanitize=local-bounds -fno-sanitize-trap=local-bounds -fsanitize-recover=local-bounds %s -O3 -o %t && %run %t 3 2>&1 | FileCheck %s
// RUN: %clangxx -fsanitize=local-bounds -fno-sanitize-trap=local-bounds -fsanitize-recover=local-bounds -g %s -O3 -o %t && %run %t 3 2>&1 | FileCheck %s --check-prefixes=LINE

#include <cstdlib>

Expand All @@ -14,12 +15,17 @@ __attribute__((noinline)) void init(S *s) {
__asm__ __volatile__("" : : "r"(s) : "memory");
}

__attribute__((noinline, no_sanitize("memory"))) int test(char i) {
__attribute__((noinline, no_sanitize("memory", "address", "hwaddress"))) int
test(char i) {
S a;
init(&a);
S b;
init(&b);
return ((int *)(&a))[i];
// CHECK: error: access out of bounds
// CHECK: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
// LINE: local_bounds.cpp:[[#@LINE-3]]:{{.*}}runtime error: access out of bounds
// LINE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}local_bounds.cpp:[[#@LINE-4]]:
}

int main(int argc, char **argv) {
Expand Down
5 changes: 3 additions & 2 deletions compiler-rt/test/ubsan_minimal/TestCases/local_bounds.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %clangxx -fsanitize=local-bounds %s -O3 -o %t && %run %t 1
// RUN: %clangxx -fsanitize=local-bounds %s -O3 -o %t && not --crash %run %t 3

// FIXME: it's always trap for now.
// RUN: %clangxx -fsanitize=local-bounds -fno-sanitize-trap=local-bounds %s -O3 -o %t && not --crash %run %t 3 2>&1 | FileCheck %s
// RUN: %clangxx -fsanitize=local-bounds -fno-sanitize-trap=local-bounds -fsanitize-recover=local-bounds %s -O3 -o %t && %run %t 3 2>&1 | FileCheck %s

#include <cstdlib>

Expand All @@ -20,6 +20,7 @@ __attribute__((noinline, no_sanitize("memory"))) int test(char i) {
S b;
init(&b);
return ((int *)(&a))[i];
// CHECK: ubsan: local-out-of-bounds by 0x{{[[:xdigit:]]+$}}
}

int main(int argc, char **argv) {
Expand Down
Loading