Skip to content

Commit 548efc6

Browse files
committed
hook up sscl categories with overflow/truncation sanitizers
Signed-off-by: Justin Stitt <[email protected]>
1 parent 0f52193 commit 548efc6

File tree

4 files changed

+144
-14
lines changed

4 files changed

+144
-14
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
830830

831831
const NoSanitizeList &getNoSanitizeList() const { return *NoSanitizeL; }
832832

833+
bool isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
834+
const QualType &Ty) const;
835+
833836
const XRayFunctionFilter &getXRayFilter() const {
834837
return *XRayFilter;
835838
}

clang/lib/AST/ASTContext.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,37 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
831831
return CanonTTP;
832832
}
833833

834+
/// Check if a type can have its sanitizer instrumentation elided.
835+
/// Determine this by its presence in a SCL alongside its specified categories.
836+
/// For example:
837+
/// ignorelist.txt>
838+
/// [{unsigned-integer-overflow,signed-integer-overflow}]
839+
/// type:*
840+
/// type:size_t=skip
841+
/// <ignorelist.txt
842+
/// Supplying the above ignorelist.txt will disable overflow sanitizer
843+
/// instrumentation for all types except "size_t".
844+
bool ASTContext::isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
845+
const QualType &Ty) const {
846+
// One may specifically allow a type "type:foo=allow"
847+
bool isAllowedBySCL =
848+
NoSanitizeL->containsType(Mask, Ty.getAsString(), "allow");
849+
850+
// There could also be no category present "type:foo", which is the same as
851+
// "allow"
852+
isAllowedBySCL |= NoSanitizeL->containsType(Mask, Ty.getAsString());
853+
854+
// Explicitly specifying "skip" is also possible "type:foo=skip"
855+
bool isSkippedBySCL =
856+
NoSanitizeL->containsType(Mask, Ty.getAsString(), "skip");
857+
858+
// Or "forbid", as there is currently no distinction between "skip" and
859+
// "forbid" for the purposes of the overflow/truncation sanitizer ignorelist.
860+
isSkippedBySCL |= NoSanitizeL->containsType(Mask, Ty.getAsString(), "forbid");
861+
862+
return isAllowedBySCL && !isSkippedBySCL;
863+
}
864+
834865
TargetCXXABI::Kind ASTContext::getCXXABIKind() const {
835866
auto Kind = getTargetInfo().getCXXABI().getKind();
836867
return getLangOpts().CXXABI.value_or(Kind);

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
197197
if (!Op.mayHaveIntegerOverflow())
198198
return true;
199199

200+
if (Op.Ty->isSignedIntegerType() &&
201+
Ctx.isTypeIgnoredBySanitizer(SanitizerKind::SignedIntegerOverflow,
202+
Op.Ty)) {
203+
return true;
204+
}
205+
206+
if (Op.Ty->isUnsignedIntegerType() &&
207+
Ctx.isTypeIgnoredBySanitizer(SanitizerKind::UnsignedIntegerOverflow,
208+
Op.Ty)) {
209+
return true;
210+
}
211+
200212
const UnaryOperator *UO = dyn_cast<UnaryOperator>(Op.E);
201213

202214
if (UO && UO->getOpcode() == UO_Minus &&
@@ -1121,6 +1133,10 @@ void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType,
11211133
if (!CGF.SanOpts.has(Check.second.second))
11221134
return;
11231135

1136+
// Does some SSCL ignore this type?
1137+
if (CGF.getContext().isTypeIgnoredBySanitizer(Check.second.second, DstType))
1138+
return;
1139+
11241140
llvm::Constant *StaticArgs[] = {
11251141
CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
11261142
CGF.EmitCheckTypeDescriptor(DstType),
@@ -1231,6 +1247,15 @@ void ScalarExprEmitter::EmitIntegerSignChangeCheck(Value *Src, QualType SrcType,
12311247
// Because here sign change check is interchangeable with truncation check.
12321248
return;
12331249
}
1250+
// Does an SSCL have an entry for the DstType under its respective sanitizer
1251+
// section?
1252+
if (DstSigned && CGF.getContext().isTypeIgnoredBySanitizer(
1253+
SanitizerKind::ImplicitSignedIntegerTruncation, DstType))
1254+
return;
1255+
if (!DstSigned &&
1256+
CGF.getContext().isTypeIgnoredBySanitizer(
1257+
SanitizerKind::ImplicitUnsignedIntegerTruncation, DstType))
1258+
return;
12341259
// That's it. We can't rule out any more cases with the data we have.
12351260

12361261
CodeGenFunction::SanitizerScope SanScope(&CGF);
@@ -2780,10 +2805,11 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
27802805
return Builder.CreateNSWAdd(InVal, Amount, Name);
27812806
[[fallthrough]];
27822807
case LangOptions::SOB_Trapping:
2783-
if (!E->canOverflow())
2808+
BinOpInfo Info = createBinOpInfoFromIncDec(
2809+
E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
2810+
if (!E->canOverflow() || CanElideOverflowCheck(CGF.getContext(), Info))
27842811
return Builder.CreateNSWAdd(InVal, Amount, Name);
2785-
return EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(
2786-
E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts())));
2812+
return EmitOverflowCheckedBinOp(Info);
27872813
}
27882814
llvm_unreachable("Unknown SignedOverflowBehaviorTy");
27892815
}
@@ -2986,7 +3012,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
29863012
value = EmitIncDecConsiderOverflowBehavior(E, value, isInc);
29873013
} else if (E->canOverflow() && type->isUnsignedIntegerType() &&
29883014
CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
2989-
!excludeOverflowPattern) {
3015+
!excludeOverflowPattern &&
3016+
!CGF.getContext().isTypeIgnoredBySanitizer(
3017+
SanitizerKind::UnsignedIntegerOverflow, E->getType())) {
29903018
value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(
29913019
E, value, isInc, E->getFPFeaturesInEffect(CGF.getLangOpts())));
29923020
} else {
Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,91 @@
1+
// Verify ubsan doesn't emit checks for ignorelisted types
2+
// RUN: echo "[{unsigned-integer-overflow,signed-integer-overflow}]" > %t-int.ignorelist
3+
// RUN: echo "type:int" >> %t-int.ignorelist
4+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t-int.ignorelist -emit-llvm %s -o - | FileCheck %s --check-prefix=INT
5+
6+
// RUN: echo "type:int" > %t-nosection.ignorelist
7+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t-nosection.ignorelist -emit-llvm %s -o - | FileCheck %s --check-prefix=INT
8+
9+
// RUN: echo "type:int=allow" > %t-allow-same-as-no-category.ignorelist
10+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t-allow-same-as-no-category.ignorelist -emit-llvm %s -o - | FileCheck %s --check-prefix=INT
11+
12+
// RUN: echo "[{unsigned-integer-overflow,signed-integer-overflow}]" > %t-myty.ignorelist
13+
// RUN: echo "type:*" >> %t-myty.ignorelist
14+
// RUN: echo "type:myty=skip" >> %t-myty.ignorelist
15+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t-myty.ignorelist -emit-llvm %s -o - | FileCheck %s --check-prefix=MYTY
16+
17+
// RUN: echo "[{implicit-signed-integer-truncation,implicit-unsigned-integer-truncation}]" > %t-trunc.ignorelist
18+
// RUN: echo "type:char" >> %t-trunc.ignorelist
19+
// RUN: echo "type:unsigned char" >> %t-trunc.ignorelist
20+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=implicit-signed-integer-truncation,implicit-unsigned-integer-truncation -fsanitize-ignorelist=%t-trunc.ignorelist -emit-llvm %s -o - | FileCheck %s --check-prefix=TRUNC
21+
122
// Verify ubsan vptr does not check down-casts on ignorelisted types.
223
// RUN: echo "type:_ZTI3Foo" > %t-type.ignorelist
3-
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -emit-llvm %s -o - | FileCheck %s --check-prefix=DEFAULT
4-
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -fsanitize-ignorelist=%t-type.ignorelist -emit-llvm %s -o - | FileCheck %s --check-prefix=TYPE
24+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -emit-llvm %s -o - | FileCheck %s --check-prefix=VPTR
25+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -fsanitize-ignorelist=%t-type.ignorelist -emit-llvm %s -o - | FileCheck %s --check-prefix=VPTR-TYPE
526

627
class Bar {
7-
public:
8-
virtual ~Bar() {}
28+
public:
29+
virtual ~Bar() {}
930
};
1031
class Foo : public Bar {};
1132

1233
Bar bar;
1334

14-
// DEFAULT: @_Z7checkmev
15-
// TYPE: @_Z7checkmev
35+
// VPTR: @_Z7checkmev
36+
// VPTR-TYPE: @_Z7checkmev
1637
void checkme() {
17-
// DEFAULT: call void @__ubsan_handle_dynamic_type_cache_miss({{.*}} (ptr @bar to
18-
// TYPE-NOT: @__ubsan_handle_dynamic_type_cache_miss
38+
// VPTR: call void @__ubsan_handle_dynamic_type_cache_miss({{.*}} (ptr @bar to
39+
// VPTR-TYPE-NOT: @__ubsan_handle_dynamic_type_cache_miss
1940
Foo* foo = static_cast<Foo*>(&bar); // down-casting
20-
// DEFAULT: ret void
21-
// TYPE: ret void
41+
// VPTR: ret void
42+
// VPTR-TYPE: ret void
2243
return;
2344
}
45+
46+
// INT-LABEL: ignore_int
47+
void ignore_int(int A, int B, unsigned C, unsigned D, long E) {
48+
// INT: llvm.uadd.with.overflow.i32
49+
(void)(C+D);
50+
// INT-NOT: llvm.sadd.with.overflow.i32
51+
(void)(A+B);
52+
// INT: llvm.sadd.with.overflow.i64
53+
(void)(++E);
54+
}
55+
56+
57+
typedef unsigned long myty;
58+
typedef myty derivative;
59+
// INT-LABEL: ignore_all_except_myty
60+
// MYTY-LABEL: ignore_all_except_myty
61+
void ignore_all_except_myty(myty A, myty B, int C, unsigned D, derivative E) {
62+
// MYTY-NOT: llvm.sadd.with.overflow.i32
63+
(void)(++C);
64+
65+
// MYTY-NOT: llvm.uadd.with.overflow.i32
66+
(void)(D+D);
67+
68+
// MYTY-NOT: llvm.umul.with.overflow.i64
69+
(void)(E*2);
70+
71+
// MYTY: llvm.uadd.with.overflow.i64
72+
(void)(A+B);
73+
}
74+
75+
// INT-LABEL: truncation
76+
// MYTY-LABEL: truncation
77+
// TRUNC-LABEL: truncation
78+
void truncation(char A, int B, unsigned char C, short D) {
79+
// TRUNC-NOT: %handler.implicit_conversion
80+
A = B;
81+
// TRUNC-NOT: %handler.implicit_conversion
82+
A = C;
83+
// TRUNC-NOT: %handler.implicit_conversion
84+
C = B;
85+
86+
// TRUNC: %handler.implicit_conversion
87+
D = B;
88+
89+
(void)A;
90+
(void)D;
91+
}

0 commit comments

Comments
 (0)