Skip to content

[ubsan] Add more -fsanitize-annotate-debug-info checks #141997

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
merged 29 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6bc5c7c
[ubsan] Add more -fsanitize-annotate-debug-info checks
thurstond May 28, 2025
41735cb
Refactor to use explicit CheckOrdinal variable
thurstond May 29, 2025
3e53462
Mention why void* is used instead of ApplyDebugLocation*
thurstond May 29, 2025
fbadcee
!ApplyTrapDI assertion
thurstond May 29, 2025
6f82a48
Cleanup
thurstond May 29, 2025
58d76a6
ApplyTrapDI and stdin cleanup
thurstond May 29, 2025
4f9a4de
Add missing SanitizerOrdinal to SanitizerScope
thurstond May 29, 2025
4041270
Rename to __ubsan_check_debug_info_anchor
thurstond May 30, 2025
93ef358
Unnecessary braces
thurstond May 30, 2025
5da37cd
Update tests
thurstond May 30, 2025
9dee8ea
Use SanitizerHandler in name
thurstond May 31, 2025
05d047b
Simplify no-handler case
thurstond Jun 2, 2025
827dcbd
Add note about SO_Vptr ambiguity
thurstond Jun 2, 2025
2a816fe
Florian1 feedback
thurstond Jun 2, 2025
ed2c9b5
Use std::make_unique
thurstond Jun 2, 2025
16d6bee
Remove useless assert
thurstond Jun 2, 2025
8176e88
Merge remote-tracking branch 'origin/main' into ubsan_annotate
vitalybuka Jun 3, 2025
2eee786
SanitizerDebugLocation
vitalybuka Jun 3, 2025
637c4a2
Merge remote-tracking branch 'origin/main' into ubsan_annotate
vitalybuka Jun 3, 2025
ff5ffbf
Replace manual ApplyDebugLocation annotation with SanitizerDebugLocation
thurstond Jun 3, 2025
1df8fe4
Revert "Replace manual ApplyDebugLocation annotation with SanitizerDe…
thurstond Jun 3, 2025
aead894
Replace manual ApplyDebugLocation with SanitizerDebugLocation, and also
thurstond Jun 3, 2025
11a74d1
Refactor EmitTypeCheck into three more precise SanitizerDebugLocations
thurstond Jun 3, 2025
be32bd7
assert(IsSanitizerScope); in EmitVTablePtrCheck
thurstond Jun 3, 2025
81ecf62
Re-run CI
thurstond Jun 3, 2025
5a510a3
Use ordinal for label if unique
thurstond Jun 4, 2025
31291d0
Undo test change
thurstond Jun 4, 2025
3dedb83
Remove newline
thurstond Jun 4, 2025
0ded77d
Merge remote-tracking branch 'upstream' into ubsan_annotate (and
thurstond Jun 5, 2025
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
28 changes: 19 additions & 9 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2009,11 +2009,12 @@ Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
if (!SanOpts.has(SanitizerKind::Builtin))
return ArgValue;

SanitizerScope SanScope(this);
auto CheckOrdinal = SanitizerKind::SO_Builtin;
auto CheckHandler = SanitizerHandler::InvalidBuiltin;
SanitizerDebugLocation SanScope(this, {CheckOrdinal}, CheckHandler);
Value *Cond = Builder.CreateICmpNE(
ArgValue, llvm::Constant::getNullValue(ArgValue->getType()));
EmitCheck(std::make_pair(Cond, SanitizerKind::SO_Builtin),
SanitizerHandler::InvalidBuiltin,
EmitCheck(std::make_pair(Cond, CheckOrdinal), CheckHandler,
{EmitCheckSourceLocation(E->getExprLoc()),
llvm::ConstantInt::get(Builder.getInt8Ty(), Kind)},
{});
Expand All @@ -2025,10 +2026,11 @@ Value *CodeGenFunction::EmitCheckedArgForAssume(const Expr *E) {
if (!SanOpts.has(SanitizerKind::Builtin))
return ArgValue;

SanitizerScope SanScope(this);
auto CheckOrdinal = SanitizerKind::SO_Builtin;
auto CheckHandler = SanitizerHandler::InvalidBuiltin;
SanitizerDebugLocation SanScope(this, {CheckOrdinal}, CheckHandler);
EmitCheck(
std::make_pair(ArgValue, SanitizerKind::SO_Builtin),
SanitizerHandler::InvalidBuiltin,
std::make_pair(ArgValue, CheckOrdinal), CheckHandler,
{EmitCheckSourceLocation(E->getExprLoc()),
llvm::ConstantInt::get(Builder.getInt8Ty(), BCK_AssumePassedFalse)},
std::nullopt);
Expand All @@ -2051,7 +2053,15 @@ static Value *EmitOverflowCheckedAbs(CodeGenFunction &CGF, const CallExpr *E,
return EmitAbs(CGF, ArgValue, true);
}

CodeGenFunction::SanitizerScope SanScope(&CGF);
SmallVector<SanitizerKind::SanitizerOrdinal, 1> Ordinals;
SanitizerHandler CheckHandler;
if (SanitizeOverflow) {
Ordinals.push_back(SanitizerKind::SO_SignedIntegerOverflow);
CheckHandler = SanitizerHandler::NegateOverflow;
} else
CheckHandler = SanitizerHandler::SubOverflow;

SanitizerDebugLocation SanScope(&CGF, Ordinals, CheckHandler);

Constant *Zero = Constant::getNullValue(ArgValue->getType());
Value *ResultAndOverflow = CGF.Builder.CreateBinaryIntrinsic(
Expand All @@ -2063,12 +2073,12 @@ static Value *EmitOverflowCheckedAbs(CodeGenFunction &CGF, const CallExpr *E,
// TODO: support -ftrapv-handler.
if (SanitizeOverflow) {
CGF.EmitCheck({{NotOverflow, SanitizerKind::SO_SignedIntegerOverflow}},
SanitizerHandler::NegateOverflow,
CheckHandler,
{CGF.EmitCheckSourceLocation(E->getArg(0)->getExprLoc()),
CGF.EmitCheckTypeDescriptor(E->getType())},
{ArgValue});
} else
CGF.EmitTrapCheck(NotOverflow, SanitizerHandler::SubOverflow);
CGF.EmitTrapCheck(NotOverflow, CheckHandler);

Value *CmpResult = CGF.Builder.CreateICmpSLT(ArgValue, Zero, "abscond");
return CGF.Builder.CreateSelect(CmpResult, Result, ArgValue, "abs");
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4184,7 +4184,7 @@ void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV) {
Handler = SanitizerHandler::NullabilityReturn;
}

SanitizerScope SanScope(this);
SanitizerDebugLocation SanScope(this, {CheckKind}, Handler);

// Make sure the "return" source location is valid. If we're checking a
// nullability annotation, make sure the preconditions for the check are met.
Expand Down Expand Up @@ -4569,7 +4569,7 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
Handler = SanitizerHandler::NullabilityArg;
}

SanitizerScope SanScope(this);
SanitizerDebugLocation SanScope(this, {CheckKind}, Handler);
llvm::Value *Cond = EmitNonNullRValueCheck(RV, ArgType);
llvm::Constant *StaticData[] = {
EmitCheckSourceLocation(ArgLoc),
Expand Down
18 changes: 10 additions & 8 deletions clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2817,7 +2817,8 @@ void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXRecordDecl *RD,
RD = LeastDerivedClassWithSameLayout(RD);

auto [Ordinal, _] = SanitizerInfoFromCFICheckKind(TCK);
ApplyDebugLocation ApplyTrapDI(*this, SanitizerAnnotateDebugInfo(Ordinal));
SanitizerDebugLocation SanScope(this, {Ordinal},
SanitizerHandler::CFICheckFail);

EmitVTablePtrCheck(RD, VTable, TCK, Loc);
}
Expand All @@ -2842,7 +2843,8 @@ void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T, Address Derived,
ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl);

auto [Ordinal, _] = SanitizerInfoFromCFICheckKind(TCK);
ApplyDebugLocation ApplyTrapDI(*this, SanitizerAnnotateDebugInfo(Ordinal));
SanitizerDebugLocation SanScope(this, {Ordinal},
SanitizerHandler::CFICheckFail);

llvm::BasicBlock *ContBlock = nullptr;

Expand Down Expand Up @@ -2874,6 +2876,8 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
llvm::Value *VTable,
CFITypeCheckKind TCK,
SourceLocation Loc) {
assert(IsSanitizerScope);

if (!CGM.getCodeGenOpts().SanitizeCfiCrossDso &&
!CGM.HasHiddenLTOVisibility(RD))
return;
Expand All @@ -2885,7 +2889,6 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
SanitizerMask::bitPosToMask(M), TypeName))
return;

SanitizerScope SanScope(this);
EmitSanitizerStatReport(SSK);

llvm::Metadata *MD =
Expand Down Expand Up @@ -2942,11 +2945,11 @@ bool CodeGenFunction::ShouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *RD) {
llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
const CXXRecordDecl *RD, llvm::Value *VTable, llvm::Type *VTableTy,
uint64_t VTableByteOffset) {
SanitizerScope SanScope(this);
auto CheckOrdinal = SanitizerKind::SO_CFIVCall;
auto CheckHandler = SanitizerHandler::CFICheckFail;
SanitizerDebugLocation SanScope(this, {CheckOrdinal}, CheckHandler);

EmitSanitizerStatReport(llvm::SanStat_CFI_VCall);
ApplyDebugLocation ApplyTrapDI(
*this, SanitizerAnnotateDebugInfo(SanitizerKind::SO_CFIVCall));

llvm::Metadata *MD =
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
Expand All @@ -2965,8 +2968,7 @@ llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
if (SanOpts.has(SanitizerKind::CFIVCall) &&
!getContext().getNoSanitizeList().containsType(SanitizerKind::CFIVCall,
TypeName)) {
EmitCheck(std::make_pair(CheckResult, SanitizerKind::SO_CFIVCall),
SanitizerHandler::CFICheckFail, {}, {});
EmitCheck(std::make_pair(CheckResult, CheckOrdinal), CheckHandler, {}, {});
}

return Builder.CreateBitCast(Builder.CreateExtractValue(CheckedLoad, 0),
Expand Down
82 changes: 82 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@
using namespace clang;
using namespace clang::CodeGen;

// TODO: consider deprecating ClArrayBoundsPseudoFn; functionality is subsumed
// by -fsanitize-annotate-debug-info
static llvm::cl::opt<bool> ClArrayBoundsPseudoFn(
"array-bounds-pseudofn", llvm::cl::Hidden, llvm::cl::Optional,
llvm::cl::desc("Emit debug info that places array-bounds instrumentation "
"in an inline function called __ubsan_check_array_bounds."));

static uint32_t getTypeAlignIfRequired(const Type *Ty, const ASTContext &Ctx) {
auto TI = Ctx.getTypeInfo(Ty);
if (TI.isAlignRequired())
Expand Down Expand Up @@ -6412,3 +6419,78 @@ CodeGenFunction::LexicalScope::~LexicalScope() {
ForceCleanup();
}
}

static std::string SanitizerHandlerToCheckLabel(SanitizerHandler Handler) {
std::string Label;
switch (Handler) {
#define SANITIZER_CHECK(Enum, Name, Version) \
case Enum: \
Label = "__ubsan_check_" #Name; \
break;

LIST_SANITIZER_CHECKS
#undef SANITIZER_CHECK
};

// Label doesn't require sanitization
return Label;
}

static std::string
SanitizerOrdinalToCheckLabel(SanitizerKind::SanitizerOrdinal Ordinal) {
std::string Label;
switch (Ordinal) {
#define SANITIZER(NAME, ID) \
case SanitizerKind::SO_##ID: \
Label = "__ubsan_check_" NAME; \
break;
#include "clang/Basic/Sanitizers.def"
default:
llvm_unreachable("unexpected sanitizer kind");
}

// Sanitize label (convert hyphens to underscores; also futureproof against
// non-alpha)
for (unsigned int i = 0; i < Label.length(); i++)
if (!std::isalpha(Label[i]))
Label[i] = '_';

return Label;
}

llvm::DILocation *CodeGenFunction::SanitizerAnnotateDebugInfo(
ArrayRef<SanitizerKind::SanitizerOrdinal> Ordinals,
SanitizerHandler Handler) {
std::string Label;
if (Ordinals.size() == 1)
Label = SanitizerOrdinalToCheckLabel(Ordinals[0]);
else
Label = SanitizerHandlerToCheckLabel(Handler);

llvm::DILocation *CheckDI = Builder.getCurrentDebugLocation();

for (auto Ord : Ordinals) {
// TODO: deprecate ClArrayBoundsPseudoFn
if (((ClArrayBoundsPseudoFn && Ord == SanitizerKind::SO_ArrayBounds) ||
CGM.getCodeGenOpts().SanitizeAnnotateDebugInfo.has(Ord)) &&
CheckDI) {
return getDebugInfo()->CreateSyntheticInlineAt(CheckDI, Label);
}
}

return CheckDI;
}

SanitizerDebugLocation::SanitizerDebugLocation(
CodeGenFunction *CGF, ArrayRef<SanitizerKind::SanitizerOrdinal> Ordinals,
SanitizerHandler Handler)
: CGF(CGF),
Apply(*CGF, CGF->SanitizerAnnotateDebugInfo(Ordinals, Handler)) {
assert(!CGF->IsSanitizerScope);
CGF->IsSanitizerScope = true;
}

SanitizerDebugLocation::~SanitizerDebugLocation() {
assert(CGF->IsSanitizerScope);
CGF->IsSanitizerScope = false;
}
12 changes: 12 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define LLVM_CLANG_LIB_CODEGEN_CGDEBUGINFO_H

#include "CGBuilder.h"
#include "SanitizerHandler.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExternalASTSource.h"
Expand Down Expand Up @@ -974,6 +975,17 @@ class ApplyInlineDebugLocation {
~ApplyInlineDebugLocation();
};

class SanitizerDebugLocation {
CodeGenFunction *CGF;
ApplyDebugLocation Apply;

public:
SanitizerDebugLocation(CodeGenFunction *CGF,
ArrayRef<SanitizerKind::SanitizerOrdinal> Ordinals,
SanitizerHandler Handler);
~SanitizerDebugLocation();
};

} // namespace CodeGen
} // namespace clang

Expand Down
7 changes: 4 additions & 3 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,14 +766,15 @@ void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS,

// Check if the right hand side of the assignment is nonnull, if the left
// hand side must be nonnull.
SanitizerScope SanScope(this);
auto CheckOrdinal = SanitizerKind::SO_NullabilityAssign;
auto CheckHandler = SanitizerHandler::TypeMismatch;
SanitizerDebugLocation SanScope(this, {CheckOrdinal}, CheckHandler);
llvm::Value *IsNotNull = Builder.CreateIsNotNull(RHS);
llvm::Constant *StaticData[] = {
EmitCheckSourceLocation(Loc), EmitCheckTypeDescriptor(LHS.getType()),
llvm::ConstantInt::get(Int8Ty, 0), // The LogAlignment info is unused.
llvm::ConstantInt::get(Int8Ty, TCK_NonnullAssign)};
EmitCheck({{IsNotNull, SanitizerKind::SO_NullabilityAssign}},
SanitizerHandler::TypeMismatch, StaticData, RHS);
EmitCheck({{IsNotNull, CheckOrdinal}}, CheckHandler, StaticData, RHS);
}

void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
Expand Down
Loading
Loading