Skip to content

Commit 729329f

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

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
@@ -807,6 +807,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
807807

808808
const NoSanitizeList &getNoSanitizeList() const { return *NoSanitizeL; }
809809

810+
bool isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
811+
const QualType &Ty) const;
812+
810813
const XRayFunctionFilter &getXRayFilter() const {
811814
return *XRayFilter;
812815
}

clang/lib/AST/ASTContext.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,37 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
817817
return CanonTTP;
818818
}
819819

820+
/// Check if a type can have its sanitizer instrumentation elided.
821+
/// Determine this by its presence in a SCL alongside its specified categories.
822+
/// For example:
823+
/// ignorelist.txt>
824+
/// [{unsigned-integer-overflow,signed-integer-overflow}]
825+
/// type:*
826+
/// type:size_t=skip
827+
/// <ignorelist.txt
828+
/// Supplying the above ignorelist.txt will disable overflow sanitizer
829+
/// instrumentation for all types except "size_t".
830+
bool ASTContext::isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
831+
const QualType &Ty) const {
832+
// One may specifically allow a type "type:foo=allow"
833+
bool isAllowedBySCL =
834+
NoSanitizeL->containsType(Mask, Ty.getAsString(), "allow");
835+
836+
// There could also be no category present "type:foo", which is the same as
837+
// "allow"
838+
isAllowedBySCL |= NoSanitizeL->containsType(Mask, Ty.getAsString());
839+
840+
// Explicitly specifying "skip" is also possible "type:foo=skip"
841+
bool isSkippedBySCL =
842+
NoSanitizeL->containsType(Mask, Ty.getAsString(), "skip");
843+
844+
// Or "forbid", as there is currently no distinction between "skip" and
845+
// "forbid" for the purposes of the overflow/truncation sanitizer ignorelist.
846+
isSkippedBySCL |= NoSanitizeL->containsType(Mask, Ty.getAsString(), "forbid");
847+
848+
return isAllowedBySCL && !isSkippedBySCL;
849+
}
850+
820851
TargetCXXABI::Kind ASTContext::getCXXABIKind() const {
821852
auto Kind = getTargetInfo().getCXXABI().getKind();
822853
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
@@ -196,6 +196,18 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
196196
if (!Op.mayHaveIntegerOverflow())
197197
return true;
198198

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

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

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

12351260
CodeGenFunction::SanitizerScope SanScope(&CGF);
@@ -2770,10 +2795,11 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
27702795
return Builder.CreateNSWAdd(InVal, Amount, Name);
27712796
[[fallthrough]];
27722797
case LangOptions::SOB_Trapping:
2773-
if (!E->canOverflow())
2798+
BinOpInfo Info = createBinOpInfoFromIncDec(
2799+
E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
2800+
if (!E->canOverflow() || CanElideOverflowCheck(CGF.getContext(), Info))
27742801
return Builder.CreateNSWAdd(InVal, Amount, Name);
2775-
return EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(
2776-
E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts())));
2802+
return EmitOverflowCheckedBinOp(Info);
27772803
}
27782804
llvm_unreachable("Unknown SignedOverflowBehaviorTy");
27792805
}
@@ -2973,7 +2999,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
29732999
value = EmitIncDecConsiderOverflowBehavior(E, value, isInc);
29743000
} else if (E->canOverflow() && type->isUnsignedIntegerType() &&
29753001
CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
2976-
!excludeOverflowPattern) {
3002+
!excludeOverflowPattern &&
3003+
!CGF.getContext().isTypeIgnoredBySanitizer(
3004+
SanitizerKind::UnsignedIntegerOverflow, E->getType())) {
29773005
value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(
29783006
E, value, isInc, E->getFPFeaturesInEffect(CGF.getLangOpts())));
29793007
} 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)