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 7 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
16 changes: 10 additions & 6 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2006,10 +2006,11 @@ Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
if (!SanOpts.has(SanitizerKind::Builtin))
return ArgValue;

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

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

CodeGenFunction::SanitizerScope SanScope(&CGF);
SmallVector<SanitizerKind::SanitizerOrdinal, 1> Ordinals;
if (SanitizeOverflow)
Ordinals.push_back(SanitizerKind::SO_SignedIntegerOverflow);
CodeGenFunction::SanitizerScope SanScope(&CGF, Ordinals);

Constant *Zero = Constant::getNullValue(ArgValue->getType());
Value *ResultAndOverflow = CGF.Builder.CreateBinaryIntrinsic(
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4156,7 +4156,7 @@ void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV) {
Handler = SanitizerHandler::NullabilityReturn;
}

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

// 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 @@ -4541,7 +4541,7 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
Handler = SanitizerHandler::NullabilityArg;
}

SanitizerScope SanScope(this);
SanitizerScope SanScope(this, {CheckKind});
llvm::Value *Cond = EmitNonNullRValueCheck(RV, ArgType);
llvm::Constant *StaticData[] = {
EmitCheckSourceLocation(ArgLoc),
Expand Down Expand Up @@ -5976,7 +5976,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
// attribute to insert handler calls.
if (SanOpts.hasOneOf(SanitizerKind::Address |
SanitizerKind::KernelAddress)) {
SanitizerScope SanScope(this);
SanitizerScope SanScope(this, {});
llvm::IRBuilder<>::InsertPointGuard IPGuard(Builder);
Builder.SetInsertPoint(CI);
auto *FnType = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
Expand Down
11 changes: 5 additions & 6 deletions clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1678,7 +1678,7 @@ namespace {
static void EmitSanitizerDtorCallback(
CodeGenFunction &CGF, StringRef Name, llvm::Value *Ptr,
std::optional<CharUnits::QuantityType> PoisonSize = {}) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
CodeGenFunction::SanitizerScope SanScope(&CGF, {});
// Pass in void pointer and size of region as arguments to runtime
// function
SmallVector<llvm::Value *, 2> Args = {Ptr};
Expand Down Expand Up @@ -2885,7 +2885,7 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
SanitizerMask::bitPosToMask(M), TypeName))
return;

SanitizerScope SanScope(this);
SanitizerScope SanScope(this, {M});
EmitSanitizerStatReport(SSK);

llvm::Metadata *MD =
Expand Down Expand Up @@ -2942,11 +2942,10 @@ 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;
SanitizerScope SanScope(this, {CheckOrdinal});

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

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

Expand Down
9 changes: 5 additions & 4 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,14 +765,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;
SanitizerScope SanScope(this, {CheckOrdinal});
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}}, SanitizerHandler::TypeMismatch,
StaticData, RHS);
}

void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
Expand Down Expand Up @@ -2852,7 +2853,7 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
if (requiresReturnValueNullabilityCheck()) {
auto Nullability = Ty->getNullability();
if (Nullability && *Nullability == NullabilityKind::NonNull) {
SanitizerScope SanScope(this);
SanitizerScope SanScope(this, {});
RetValNullabilityPrecondition =
Builder.CreateAnd(RetValNullabilityPrecondition,
Builder.CreateIsNotNull(Arg.getAnyValue()));
Expand Down
93 changes: 47 additions & 46 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,9 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,
if (Ty.isVolatileQualified())
return;

SanitizerScope SanScope(this);
SanitizerScope SanScope(
this, {SanitizerKind::SO_Null, SanitizerKind::SO_ObjectSize,
SanitizerKind::SO_Alignment, SanitizerKind::SO_Vptr});

SmallVector<std::pair<llvm::Value *, SanitizerKind::SanitizerOrdinal>, 3>
Checks;
Expand Down Expand Up @@ -989,7 +991,7 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
if (CE->getCastKind() == CK_ArrayToPointerDecay &&
!CE->getSubExpr()->isFlexibleArrayMemberLike(CGF.getContext(),
StrictFlexArraysLevel)) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
CodeGenFunction::SanitizerScope SanScope(&CGF, {});

IndexedType = CE->getSubExpr()->getType();
const ArrayType *AT = IndexedType->castAsArrayTypeUnsafe();
Expand All @@ -1002,7 +1004,7 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
}
}

CodeGenFunction::SanitizerScope SanScope(&CGF);
CodeGenFunction::SanitizerScope SanScope(&CGF, {});

QualType EltTy{Base->getType()->getPointeeOrArrayElementType(), 0};
if (llvm::Value *POS = CGF.LoadPassedObjectSize(Base, EltTy)) {
Expand Down Expand Up @@ -1224,10 +1226,8 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
if (!Bound)
return;

SanitizerScope SanScope(this);

auto CheckKind = SanitizerKind::SO_ArrayBounds;
ApplyDebugLocation ApplyTrapDI(*this, SanitizerAnnotateDebugInfo(CheckKind));
SanitizerScope SanScope(this, {CheckKind});

bool IndexSigned = IndexType->isSignedIntegerOrEnumerationType();
llvm::Value *IndexVal = Builder.CreateIntCast(Index, SizeTy, IndexSigned);
Expand All @@ -1245,30 +1245,23 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
}

llvm::DILocation *CodeGenFunction::SanitizerAnnotateDebugInfo(
SanitizerKind::SanitizerOrdinal CheckKindOrdinal) {
std::string Label;
switch (CheckKindOrdinal) {
#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
for (unsigned int i = 0; i < Label.length(); i++)
if (!std::isalpha(Label[i]))
Label[i] = '_';

ArrayRef<SanitizerKind::SanitizerOrdinal> Ordinals) {
llvm::DILocation *CheckDI = Builder.getCurrentDebugLocation();
// TODO: deprecate ClArrayBoundsPseudoFn
if (((ClArrayBoundsPseudoFn &&
CheckKindOrdinal == SanitizerKind::SO_ArrayBounds) ||
CGM.getCodeGenOpts().SanitizeAnnotateDebugInfo.has(CheckKindOrdinal)) &&
CheckDI)
CheckDI = getDebugInfo()->CreateSyntheticInlineAt(CheckDI, Label);

// TODO: the annotation could be more precise:
// 1) use the ordinal name if there is only one ordinal; and/or,
// 2) use the overarching SanitizerHandler if there are multiple ordinals
// (this may be confusing to users)
for (auto Ord : Ordinals) {
// TODO: deprecate ClArrayBoundsPseudoFn
if (((ClArrayBoundsPseudoFn && Ord == SanitizerKind::SO_ArrayBounds) ||
CGM.getCodeGenOpts().SanitizeAnnotateDebugInfo.has(Ord)) &&
CheckDI) {
CheckDI = getDebugInfo()->CreateSyntheticInlineAt(
CheckDI, "__ubsan_check_singularity");
break;
}
}

return CheckDI;
}
Expand Down Expand Up @@ -1994,8 +1987,11 @@ bool CodeGenFunction::EmitScalarRangeCheck(llvm::Value *Value, QualType Ty,
if (!getRangeForType(*this, Ty, Min, End, /*StrictEnums=*/true, IsBool))
return true;

SanitizerKind::SanitizerOrdinal Kind =
NeedsEnumCheck ? SanitizerKind::SO_Enum : SanitizerKind::SO_Bool;

auto &Ctx = getLLVMContext();
SanitizerScope SanScope(this);
SanitizerScope SanScope(this, {Kind});
llvm::Value *Check;
--End;
if (!Min) {
Expand All @@ -2009,8 +2005,6 @@ bool CodeGenFunction::EmitScalarRangeCheck(llvm::Value *Value, QualType Ty,
}
llvm::Constant *StaticArgs[] = {EmitCheckSourceLocation(Loc),
EmitCheckTypeDescriptor(Ty)};
SanitizerKind::SanitizerOrdinal Kind =
NeedsEnumCheck ? SanitizerKind::SO_Enum : SanitizerKind::SO_Bool;
EmitCheck(std::make_pair(Check, Kind), SanitizerHandler::LoadInvalidValue,
StaticArgs, Value);
return true;
Expand Down Expand Up @@ -3931,7 +3925,14 @@ void CodeGenFunction::EmitCfiCheckStub() {
// can be nullptr if the calling module has -fsanitize-trap behavior for this
// check kind; in this case __cfi_check_fail traps as well.
void CodeGenFunction::EmitCfiCheckFail() {
SanitizerScope SanScope(this);
// TODO: the SanitizerKind is not yet determined for this check (and might
// not even be available, if Data == nullptr). However, we still want to
// annotate the instrumentation. We approximate this by using all the CFI
// kinds.
SanitizerScope SanScope(
this, {SanitizerKind::SO_CFIVCall, SanitizerKind::SO_CFINVCall,
SanitizerKind::SO_CFIDerivedCast,
SanitizerKind::SO_CFIUnrelatedCast, SanitizerKind::SO_CFIICall});
FunctionArgList Args;
ImplicitParamDecl ArgData(getContext(), getContext().VoidPtrTy,
ImplicitParamKind::Other);
Expand Down Expand Up @@ -3994,8 +3995,6 @@ void CodeGenFunction::EmitCfiCheckFail() {
{Addr, AllVtables}),
IntPtrTy);

// TODO: the instructions above are not annotated with debug info. It is
// inconvenient to do so because we have not determined SanitizerKind yet.
const std::pair<int, SanitizerKind::SanitizerOrdinal> CheckKinds[] = {
{CFITCK_VCall, SanitizerKind::SO_CFIVCall},
{CFITCK_NVCall, SanitizerKind::SO_CFINVCall},
Expand All @@ -4007,7 +4006,8 @@ void CodeGenFunction::EmitCfiCheckFail() {
int Kind = CheckKindOrdinalPair.first;
SanitizerKind::SanitizerOrdinal Ordinal = CheckKindOrdinalPair.second;

ApplyDebugLocation ApplyTrapDI(*this, SanitizerAnnotateDebugInfo(Ordinal));
// TODO: we could apply SanitizerAnnotateDebugInfo(Ordinal) instead of
// relying on the SanitizerScope with all CFI ordinals

llvm::Value *Cond =
Builder.CreateICmpNE(CheckKind, llvm::ConstantInt::get(Int8Ty, Kind));
Expand All @@ -4030,9 +4030,10 @@ void CodeGenFunction::EmitCfiCheckFail() {

void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
if (SanOpts.has(SanitizerKind::Unreachable)) {
SanitizerScope SanScope(this);
auto CheckOrdinal = SanitizerKind::SO_Unreachable;
SanitizerScope SanScope(this, {CheckOrdinal});
EmitCheck(std::make_pair(static_cast<llvm::Value *>(Builder.getFalse()),
SanitizerKind::SO_Unreachable),
CheckOrdinal),
SanitizerHandler::BuiltinUnreachable,
EmitCheckSourceLocation(Loc), {});
}
Expand Down Expand Up @@ -6271,7 +6272,8 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
!isa<FunctionNoProtoType>(PointeeType)) {
if (llvm::Constant *PrefixSig =
CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) {
SanitizerScope SanScope(this);
auto CheckOrdinal = SanitizerKind::SO_Function;
SanitizerScope SanScope(this, {CheckOrdinal});
auto *TypeHash = getUBSanFunctionTypeHash(PointeeType);

llvm::Type *PrefixSigType = PrefixSig->getType();
Expand Down Expand Up @@ -6331,7 +6333,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
Builder.CreateICmpEQ(CalleeTypeHash, TypeHash);
llvm::Constant *StaticData[] = {EmitCheckSourceLocation(E->getBeginLoc()),
EmitCheckTypeDescriptor(CalleeType)};
EmitCheck(std::make_pair(CalleeTypeHashMatch, SanitizerKind::SO_Function),
EmitCheck(std::make_pair(CalleeTypeHashMatch, CheckOrdinal),
SanitizerHandler::FunctionTypeMismatch, StaticData,
{CalleePtr});

Expand All @@ -6350,10 +6352,9 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
// function pointer is a member of the bit set for the function type.
if (SanOpts.has(SanitizerKind::CFIICall) &&
(!TargetDecl || !isa<FunctionDecl>(TargetDecl))) {
SanitizerScope SanScope(this);
auto CheckOrdinal = SanitizerKind::SO_CFIICall;
SanitizerScope SanScope(this, {CheckOrdinal});
EmitSanitizerStatReport(llvm::SanStat_CFI_ICall);
ApplyDebugLocation ApplyTrapDI(
*this, SanitizerAnnotateDebugInfo(SanitizerKind::SO_CFIICall));

llvm::Metadata *MD;
if (CGM.getCodeGenOpts().SanitizeCfiICallGeneralizePointers)
Expand All @@ -6374,10 +6375,10 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
EmitCheckTypeDescriptor(QualType(FnType, 0)),
};
if (CGM.getCodeGenOpts().SanitizeCfiCrossDso && CrossDsoTypeId) {
EmitCfiSlowPathCheck(SanitizerKind::SO_CFIICall, TypeTest, CrossDsoTypeId,
CalleePtr, StaticData);
EmitCfiSlowPathCheck(CheckOrdinal, TypeTest, CrossDsoTypeId, CalleePtr,
StaticData);
} else {
EmitCheck(std::make_pair(TypeTest, SanitizerKind::SO_CFIICall),
EmitCheck(std::make_pair(TypeTest, CheckOrdinal),
SanitizerHandler::CFICheckFail, StaticData,
{CalleePtr, llvm::UndefValue::get(IntPtrTy)});
}
Expand Down
Loading
Loading