Skip to content
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: 2 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ CODEGENOPT(BoundsSafetyUniqueTraps, 1, 0, Benign) ///< When true, merging of
/// -fbounds-safety trap
/// instructions will be
/// prevented.

ENUM_CODEGENOPT(BoundsSafetyDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info
/* TO_UPSTREAM(BoundsSafety) OFF*/

/// Treat loops as finite: language, always, never.
Expand Down
32 changes: 32 additions & 0 deletions clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//==--- DiagnosticBoundsSafetyTrapKinds.td --------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// These are the trap diagnostics used by -fbounds-safety
//===----------------------------------------------------------------------===//

let Component = "Trap" in {
let CategoryName = "Bounds check failed" in {

// Used for falling back to the legacy infrastructure for constructing
// trap reason strings.
def trap_bs_fallback : Trap<"%0">;

// TODO: Split these up once we know what the diagnostics strings needs to be.
// Ideally we want to be able to generate trap codes and opt-remark strings
// from these definitions too so we'll need to split them up when we do that.
def trap_bs_upper_lower_overflow_bound : Trap<
"%enum_select<BoundsCheckContextKind>{"
"%Indexing{indexing}|"
"%TODO{TODO}"
"}0 %enum_select<BoundsSafetyPtrCheckKind>{"
"%Upper{above upper bound}|"
"%Overflow{overflows address space}|"
"%Lower{below lower bound"
"}}2 in %1">;

}
}
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticTrapKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ def trap_ubsan_arith_overflow : Trap<

}
}

// TO_UPSTREAM(BoundsSafety) ON
include "DiagnosticBoundsSafetyTrapKinds.td"
// TO_UPSTREAM(BoundsSafety) OFF
14 changes: 14 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2060,6 +2060,20 @@ defm bounds_safety_legacy_unique_traps : BoolOptionWithoutMarshalling<"f", "uniq
NegFlag<SetFalse, [], [ClangOption],
"legacy alias for -fno-bounds-safety-unique-traps">>;

def fbounds_safety_debug_trap_reasons_EQ
: Joined<["-"], "fbounds-safety-debug-trap-reasons=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Set how trap reasons are emitted. "
"`none` - Not emitted. This gives the smallest debug info; "
"`basic` - Emit a fixed trap message per check type. This increases the "
"debug info size but not as much as `detailed`; "
"`detailed` - Emit a more detailed trap message. This increases the "
"debug info size the most. Default is `detailed`.">,
Values<"none,basic,detailed">,
NormalizedValuesScope<"CodeGenOptions::SanitizeDebugTrapReasonKind">,
NormalizedValues<["None", "Basic", "Detailed"]>,
MarshallingInfoEnum<CodeGenOpts<"BoundsSafetyDebugTrapReasons">, "Detailed">;

// TO_UPSTREAM(BoundsSafety) OFF

defm lifetime_safety : BoolFOption<
Expand Down
99 changes: 72 additions & 27 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/DiagnosticTrap.h" // TO_UPSTREAM(BoundsSafety)
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/STLExtras.h"
Expand Down Expand Up @@ -122,8 +123,8 @@ void CodeGenFunction::EmitPtrCastLECheck(llvm::Value *LHS, llvm::Value *RHS,

void CodeGenFunction::EmitBoundsSafetyBoundsCheck(
llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper,
llvm::Value *Lower, bool AcceptNullPtr,
BoundsSafetyTrapCtx::Kind TrapCtx) {
llvm::Value *Lower, bool AcceptNullPtr, BoundsSafetyTrapCtx::Kind TrapCtx,
PartialDiagnostic *PD) {
if (!Upper && !Lower)
return;
assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN);
Expand Down Expand Up @@ -158,16 +159,30 @@ void CodeGenFunction::EmitBoundsSafetyBoundsCheck(
BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GT_UPPER_BOUND);
llvm::Value *OnePastTheEndPtr =
Builder.CreateGEP(ElemTy, Ptr, llvm::ConstantInt::get(SizeTy, 1));

TrapReason OverflowTR;
TrapReason UpperBoundTR;
if (PD) {
CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound,
UpperBoundTR)
<< *PD << diag::BoundsSafetyPtrCheckKind::Upper;
CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound,
OverflowTR)
<< *PD << diag::BoundsSafetyPtrCheckKind::Overflow;
}

// Emitting the upper bound check first since it's more
// optimization-friendly. This is because the upper bound calculation (ptr
// + size) is often marked 'inbounds' if 'ptr' is '__counted_by' or an
// array decay of a sized array. This allows ConstraintElimination to use
// this information to infer subsequent pointer arithmetic (ptr + i; where
// 'i <= size') doesn't wrap.
EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(OnePastTheEndPtr, Upper),
BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx);
BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx,
PD ? &UpperBoundTR : nullptr);
EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(Ptr, OnePastTheEndPtr),
BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx);
BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx,
PD ? &OverflowTR : nullptr);
} else {
// Path where the size of the access is assumed to be 1 byte. This is used
// for
Expand All @@ -189,14 +204,31 @@ void CodeGenFunction::EmitBoundsSafetyBoundsCheck(
//
BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GE_UPPER_BOUND);
llvm::Value *Check = Builder.CreateICmpULT(Ptr, Upper);
EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx);

TrapReason UpperTR;
if (PD) {
CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound, UpperTR)
<< *PD << diag::BoundsSafetyPtrCheckKind::Upper;
}

EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx,
PD ? &UpperTR : nullptr);
}
}

if (Lower) {
BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_LT_LOWER_BOUND);
llvm::Value *Check = Builder.CreateICmpUGE(Ptr, Lower);
EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx);

TrapReason LowerBoundTR;
if (PD) {
CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound,
LowerBoundTR)
<< *PD << diag::BoundsSafetyPtrCheckKind::Lower;
}

EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx,
PD ? &LowerBoundTR : nullptr);
}
if (NullCheckBranch)
NullCheckBranch->setSuccessor(1, Builder.GetInsertBlock());
Expand Down Expand Up @@ -4855,8 +4887,7 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
SanitizerHandler CheckHandlerID,
bool NoMerge, const TrapReason *TR,
StringRef Annotation,
StringRef BoundsSafetyTrapMessage) {
StringRef Annotation) {
llvm::BasicBlock *Cont = createBasicBlock("cont");

// If we're optimizing, collapse all calls to trap down to just one per
Expand All @@ -4869,22 +4900,25 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
llvm::StringRef TrapMessage;
llvm::StringRef TrapCategory;
auto DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons();
/* TO_UPSTREAM(BoundsSafety) ON*/
CodeGenOptions::SanitizeDebugTrapReasonKind DebugTrapReasonKind;
if (CheckHandlerID == SanitizerHandler::BoundsSafety) {
DebugTrapReasonKind =
CGM.getCodeGenOpts().getBoundsSafetyDebugTrapReasons();
} else {
DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons();
}

if (TR && !TR->isEmpty() &&
DebugTrapReasonKind ==
CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed) {
(DebugTrapReasonKind ==
CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed ||
CheckHandlerID == SanitizerHandler::BoundsSafety)) {
TrapMessage = TR->getMessage();
TrapCategory = TR->getCategory();
} else {
/* TO_UPSTREAM(BoundsSafety) ON*/
// FIXME: Move to using `TrapReason` (rdar://158623471).
if (CheckHandlerID == SanitizerHandler::BoundsSafety) {
TrapMessage = BoundsSafetyTrapMessage;
TrapCategory = GetBoundsSafetyTrapMessagePrefix();
} else {
TrapMessage = GetUBSanTrapForHandler(CheckHandlerID);
TrapCategory = "Undefined Behavior Sanitizer";
}
assert(CheckHandlerID != SanitizerHandler::BoundsSafety);
TrapMessage = GetUBSanTrapForHandler(CheckHandlerID);
TrapCategory = "Undefined Behavior Sanitizer";
}

if (getDebugInfo() && !(TrapMessage.empty() && TrapCategory.empty()) &&
Expand Down Expand Up @@ -4979,19 +5013,28 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
EmitBlock(Cont);
}

void CodeGenFunction::EmitBoundsSafetyTrapCheck(llvm::Value *Checked,
BoundsSafetyTrapKind kind,
BoundsSafetyTrapCtx::Kind TrapCtx) {
void CodeGenFunction::EmitBoundsSafetyTrapCheck(
llvm::Value *Checked, BoundsSafetyTrapKind kind,
BoundsSafetyTrapCtx::Kind TrapCtx, TrapReason *TR) {
auto OptRemark = GetBoundsSafetyOptRemarkForTrap(kind);
assert(BoundsSafetyOptRemarkScope::InScope(this, OptRemark));

// Fallback: If a TrapReason object isn't provided or we are asked to provide
// a "basic" description.
TrapReason TempTR;
if (!TR || CGM.getCodeGenOpts().getBoundsSafetyDebugTrapReasons() ==
CodeGenOptions::SanitizeDebugTrapReasonKind::Basic) {
CGM.BuildTrapReason(diag::trap_bs_fallback, TempTR)
<< GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx);
}

// We still need to pass `OptRemark` because not all emitted instructions
// can be covered by BoundsSafetyOptRemarkScope. This is because EmitTrapCheck
// caches basic blocks that contain instructions that need annotating.
EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety,
/*NoMerge=*/CGM.getCodeGenOpts().BoundsSafetyUniqueTraps,
/*TR=*/nullptr, GetBoundsSafetyOptRemarkString(OptRemark),
GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx));
TempTR.isEmpty() ? TR : &TempTR,
GetBoundsSafetyOptRemarkString(OptRemark));
}

llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) {
Expand Down Expand Up @@ -5326,9 +5369,11 @@ CodeGenFunction::EmitWidePtrArraySubscriptExpr(const ArraySubscriptExpr *E,
}
assert(!!Upper && !!Lower);
llvm::Type *ElemTy = ConvertTypeForMem(E->getType());
auto PD = CGM.BuildPartialTrapReason();
PD << diag::BoundsCheckContextKind::Indexing << E;
EmitBoundsSafetyBoundsCheck(ElemTy, Addr.getBasePointer(), Upper, Lower,
/*AcceptNullPtr=*/false,
/*TrapCtx=*/BoundsSafetyTrapCtx::DEREF);
/*AcceptNullPtr=*/false,
/*TrapCtx=*/BoundsSafetyTrapCtx::DEREF, &PD);
}
return MakeAddrLValue(Addr, E->getType(), BaseInfo, TBAAInfo);
}
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2977,7 +2977,8 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitBoundsSafetyBoundsCheck(
llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper,
llvm::Value *Lower, bool AcceptNullPt = false,
BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN);
BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN,
PartialDiagnostic *PD = nullptr);
void EmitBoundsSafetyRangeCheck(
llvm::Value *LowerBound, llvm::Value *LowerAccess,
llvm::Value *UpperAccess, llvm::Value *UpperBound,
Expand Down Expand Up @@ -5425,7 +5426,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// conditional branch to it, for the -ftrapv checks.
void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID,
bool NoMerge = false, const TrapReason *TR = nullptr,
StringRef Annotation = "", StringRef TrapMessage = "");
StringRef Annotation = "");

/* TO_UPSTREAM(BoundsSafety) ON*/
/// Create a basic block that will call the trap intrinsic for -fbounds-safety, and
Expand All @@ -5435,7 +5436,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// must be in scope when this method is called.
void EmitBoundsSafetyTrapCheck(
llvm::Value *Checked, BoundsSafetyTrapKind kind,
BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN);
BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN,
TrapReason *TR = nullptr);
/* TO_UPSTREAM(BoundsSafety) OFF*/

/// Emit a call to trap or debugtrap and attach function attribute
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1841,6 +1841,15 @@ class CodeGenModule : public CodeGenTypeCache {
return TrapReasonBuilder(&getDiags(), DiagID, TR);
}

/* TO_UPSTREAM(BoundsSafety) ON*/
PartialDiagnostic BuildPartialTrapReason() {
// When building trap reasons we sometimes don't know exactly
// which diagnostic is going to be emitted so we just specify
// `0` as place holder.
return PartialDiagnostic(0, getContext().getDiagAllocator());
}
/* TO_UPSTREAM(BoundsSafety) OFF*/

private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7076,6 +7076,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job,
CmdArgs.push_back("-fbounds-safety-unique-traps");
}
}

Args.AddLastArg(CmdArgs, options::OPT_fbounds_safety_debug_trap_reasons_EQ);
/* TO_UPSTREAM(BoundsSafety) OFF*/

// Handle -f[no-]wrapv and -f[no-]strict-overflow, which are used by both
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ int main(int argc, char **argv) {

// IR-DAG: ![[LOC_10_16]] = !DILocation(line: 10, column: 16{{.*}})
// IR-DAG: ![[LT_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]])
// IR-DAG: ![[LT_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds"
// IR-DAG: ![[LT_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[idx]'"
// IR-DAG: ![[GE_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[GE_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]])
// IR-DAG: ![[GE_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds"
// IR-DAG: ![[GE_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing below lower bound in 'array[idx]'"
//
// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5
// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ int main() {

// IR-DAG: ![[LOC_10_14]] = !DILocation(line: 10, column: 14{{.*}})
// IR-DAG: ![[LT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]])
// IR-DAG: ![[LT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds"
// IR-DAG: ![[LT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[6]'"
// IR-DAG: ![[GT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[GT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]])
// IR-DAG: ![[GT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds"
// IR-DAG: ![[GT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing below lower bound in 'array[6]'"
// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5
// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]])
// IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@

// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \
// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=basic \
// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s

// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \
// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=detailed \
// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s

int operation(int index) {
int array[] = {0, 1, 2};
Expand All @@ -15,5 +21,8 @@ int operation(int index) {
// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]]

// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]])
// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}}
// CHECK-DAG: [[LOC]] = !DILocation(line: 6, column: 10, scope: {{![0-9]+}})

// BASIC-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}}
// DETAILED-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[index]'", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}}

// CHECK-DAG: [[LOC]] = !DILocation(line: 12, column: 10, scope: {{![0-9]+}})
Loading