Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
8 changes: 8 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,14 @@ Sanitizers
This new flag should allow those projects to enable integer sanitizers with
less noise.

- Arithmetic overflow sanitizers ``-fsanitize=signed-integer-overflow`` and
``-fsanitize=unsigned-integer-overflow`` as well as the implicit integer
truncation sanitizers ``-fsanitize=implicit-signed-integer-truncation`` and
``-fsanitize=implicit-unsigned-integer-truncation`` now properly support the
"type" prefix within `Sanitizer Special Case Lists (SSCL)
<https://clang.llvm.org/docs/SanitizerSpecialCaseList.html>`_. See that link
for examples.

Python Binding Changes
----------------------
- Fixed an issue that led to crashes when calling ``Type.get_exception_specification_kind``.
Expand Down
64 changes: 61 additions & 3 deletions clang/docs/SanitizerSpecialCaseList.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ file at compile-time.
Goal and usage
==============

Users of sanitizer tools, such as :doc:`AddressSanitizer`, :doc:`ThreadSanitizer`
or :doc:`MemorySanitizer` may want to disable or alter some checks for
certain source-level entities to:
Users of sanitizer tools, such as :doc:`AddressSanitizer`,
:doc:`ThreadSanitizer`, :doc:`MemorySanitizer` or :doc:`UndefinedBehaviorSanitizer`
may want to disable or alter some checks for certain source-level entities to:

* speedup hot function, which is known to be correct;
* ignore a function that does some low-level magic (e.g. walks through the
Expand Down Expand Up @@ -48,6 +48,64 @@ Example
$ clang -fsanitize=address -fsanitize-ignorelist=ignorelist.txt foo.c ; ./a.out
# No error report here.

Usage with UndefinedBehaviorSanitizer
=====================================

The arithmetic overflow sanitizers ``unsigned-integer-overflow`` and
``signed-integer-overflow`` as well as the implicit integer truncation
sanitizers ``implicit-signed-integer-truncation`` and
``implicit-unsigned-integer-truncation`` support the ability to adjust
instrumentation based on type.

.. code-block:: bash

$ cat foo.c
void foo() {
int a = 2147483647; // INT_MAX
++a; // Normally, an overflow with -fsanitize=signed-integer-overflow
}
$ cat ignorelist.txt
[signed-integer-overflow]
type:int
$ clang -fsanitize=signed-integer-overflow -fsanitize-ignorelist=ignorelist.txt foo.c ; ./a.out
# no signed-integer-overflow error

For example, supplying the above ``ignorelist.txt`` to
``-fsanitize-ignorelist=ignorelist.txt`` disables overflow sanitizer
instrumentation for arithmetic operations containing values of type ``int``.

The following SCL categories are supported: ``=no_sanitize`` and ``=sanitize``.
The ``no_sanitize`` category is the default for any entry within an ignorelist
and specifies that the query, if matched, will have its sanitizer
instrumentation ignored. Conversely, ``sanitize`` causes its queries, if
matched, to be left out of the ignorelist -- essentially ensuring sanitizer
instrumentation remains for those types. This is useful for whitelisting
specific types. If multiple entries for the same type exist, those with the
``sanitize`` category take precedence.

With this, one may disable instrumentation for some or all types and
specifically allow instrumentation for one or many types -- including types
created via ``typedef``.

The example below shows how one may control the signed truncation sanitizer for
various types.

.. code-block:: bash

$ cat ignorelist.txt
[implicit-signed-integer-truncation]
type:*=no_sanitize
type:T=sanitize

$ cat foo.c
typedef char T;
typedef char U;
void foo(int toobig) {
T a = toobig; // instrumented
U b = toobig; // not instrumented
char c = toobig; // also not instrumented
}

Format
======

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,9 @@ class ASTContext : public RefCountedBase<ASTContext> {

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

bool isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
const QualType &Ty) const;

const XRayFunctionFilter &getXRayFilter() const {
return *XRayFilter;
}
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,25 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
return CanonTTP;
}

/// Check if a type can have its sanitizer instrumentation elided.
/// Determine this by its presence in a SCL alongside its specified categories.
/// For example:
/// ignorelist.txt>
/// [{unsigned-integer-overflow,signed-integer-overflow}]
/// type:*=no_sanitize
/// type:size_t=sanitize
/// <ignorelist.txt
/// Supplying the above ignorelist.txt will disable overflow sanitizer
/// instrumentation for all types except "size_t".
bool ASTContext::isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
const QualType &Ty) const {
std::string TyName = Ty.getUnqualifiedType().getAsString(getPrintingPolicy());

return (NoSanitizeL->containsType(Mask, TyName) ||
NoSanitizeL->containsType(Mask, TyName, "no_sanitize")) &&
!NoSanitizeL->containsType(Mask, TyName, "sanitize");
}

TargetCXXABI::Kind ASTContext::getCXXABIKind() const {
auto Kind = getTargetInfo().getCXXABI().getKind();
return getLangOpts().CXXABI.value_or(Kind);
Expand Down
36 changes: 32 additions & 4 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,18 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
if (!Op.mayHaveIntegerOverflow())
return true;

if (Op.Ty->isSignedIntegerType() &&
Ctx.isTypeIgnoredBySanitizer(SanitizerKind::SignedIntegerOverflow,
Op.Ty)) {
return true;
}

if (Op.Ty->isUnsignedIntegerType() &&
Ctx.isTypeIgnoredBySanitizer(SanitizerKind::UnsignedIntegerOverflow,
Op.Ty)) {
return true;
}

const UnaryOperator *UO = dyn_cast<UnaryOperator>(Op.E);

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

// Does some SSCL ignore this type?
if (CGF.getContext().isTypeIgnoredBySanitizer(Check.second.second, DstType))
return;

llvm::Constant *StaticArgs[] = {
CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
CGF.EmitCheckTypeDescriptor(DstType),
Expand Down Expand Up @@ -1231,6 +1247,15 @@ void ScalarExprEmitter::EmitIntegerSignChangeCheck(Value *Src, QualType SrcType,
// Because here sign change check is interchangeable with truncation check.
return;
}
// Does an SSCL have an entry for the DstType under its respective sanitizer
// section?
if (DstSigned && CGF.getContext().isTypeIgnoredBySanitizer(
SanitizerKind::ImplicitSignedIntegerTruncation, DstType))
return;
if (!DstSigned &&
CGF.getContext().isTypeIgnoredBySanitizer(
SanitizerKind::ImplicitUnsignedIntegerTruncation, DstType))
return;
// That's it. We can't rule out any more cases with the data we have.

CodeGenFunction::SanitizerScope SanScope(&CGF);
Expand Down Expand Up @@ -2780,10 +2805,11 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
return Builder.CreateNSWAdd(InVal, Amount, Name);
[[fallthrough]];
case LangOptions::SOB_Trapping:
if (!E->canOverflow())
BinOpInfo Info = createBinOpInfoFromIncDec(
E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
if (!E->canOverflow() || CanElideOverflowCheck(CGF.getContext(), Info))
return Builder.CreateNSWAdd(InVal, Amount, Name);
return EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(
E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts())));
return EmitOverflowCheckedBinOp(Info);
}
llvm_unreachable("Unknown SignedOverflowBehaviorTy");
}
Expand Down Expand Up @@ -2986,7 +3012,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
value = EmitIncDecConsiderOverflowBehavior(E, value, isInc);
} else if (E->canOverflow() && type->isUnsignedIntegerType() &&
CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
!excludeOverflowPattern) {
!excludeOverflowPattern &&
!CGF.getContext().isTypeIgnoredBySanitizer(
SanitizerKind::UnsignedIntegerOverflow, E->getType())) {
value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(
E, value, isInc, E->getFPFeaturesInEffect(CGF.getLangOpts())));
} else {
Expand Down
58 changes: 58 additions & 0 deletions clang/test/CodeGen/ubsan-type-ignorelist-category-2.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// RUN: rm -rf %t
// RUN: split-file %s %t

// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-0.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-1.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-2.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-3.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-4.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-5.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-6.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/order-7.ignorelist -emit-llvm %t/test.c -o - | FileCheck %s

// The same type can appear multiple times within an ignorelist. This is a test
// to make sure "=sanitize" has priority regardless of the order in which
// duplicate type entries appear. This is a precautionary measure; we would
// much rather eagerly sanitize than silently forgo sanitization.

//--- order-0.ignorelist
type:int=no_sanitize
type:int=sanitize

//--- order-1.ignorelist
type:int=sanitize
type:int=no_sanitize

//--- order-2.ignorelist
type:in*=no_sanitize
type:int=sanitize

//--- order-3.ignorelist
type:in*=sanitize
type:int=no_sanitize

//--- order-4.ignorelist
type:int=no_sanitize
type:in*=sanitize

//--- order-5.ignorelist
type:int=sanitize
type:in*=no_sanitize

//--- order-6.ignorelist
type:int=sanitize
type:in*

//--- order-7.ignorelist
type:int
type:int=sanitize




//--- test.c
// CHECK-LABEL: @test
void test(int A) {
// CHECK: @llvm.sadd.with.overflow.i32
++A;
}
77 changes: 77 additions & 0 deletions clang/test/CodeGen/ubsan-type-ignorelist-category.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// RUN: rm -rf %t
// RUN: split-file %s %t

// Verify ubsan doesn't emit checks for ignorelisted types
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/int.ignorelist -emit-llvm %t/test.cpp -o - | FileCheck %s --check-prefix=INT
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/nosection.ignorelist -emit-llvm %t/test.cpp -o - | FileCheck %s --check-prefix=INT
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/nosan-same-as-no-category.ignorelist -emit-llvm %t/test.cpp -o - | FileCheck %s --check-prefix=INT
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-ignorelist=%t/myty.ignorelist -emit-llvm %t/test.cpp -o - | FileCheck %s --check-prefix=MYTY
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=implicit-signed-integer-truncation,implicit-unsigned-integer-truncation -fsanitize-ignorelist=%t/trunc.ignorelist -emit-llvm %t/test.cpp -o - | FileCheck %s --check-prefix=TRUNC

//--- int.ignorelist
[{unsigned-integer-overflow,signed-integer-overflow}]
type:int

//--- nosection.ignorelist
type:int

//--- nosan-same-as-no-category.ignorelist
type:int=no_sanitize

//--- myty.ignorelist
[{unsigned-integer-overflow,signed-integer-overflow}]
type:*
type:myty=sanitize

//--- trunc.ignorelist
[{implicit-signed-integer-truncation,implicit-unsigned-integer-truncation}]
type:char
type:unsigned char

//--- test.cpp
// INT-LABEL: ignore_int
void ignore_int(int A, int B, unsigned C, unsigned D, long E) {
// INT: llvm.uadd.with.overflow.i32
(void)(C+D);
// INT-NOT: llvm.sadd.with.overflow.i32
(void)(A+B);
// INT: llvm.sadd.with.overflow.i64
(void)(++E);
}


typedef unsigned long myty;
typedef myty derivative;
// INT-LABEL: ignore_all_except_myty
// MYTY-LABEL: ignore_all_except_myty
void ignore_all_except_myty(myty A, myty B, int C, unsigned D, derivative E) {
// MYTY-NOT: llvm.sadd.with.overflow.i32
(void)(++C);

// MYTY-NOT: llvm.uadd.with.overflow.i32
(void)(D+D);

// MYTY-NOT: llvm.umul.with.overflow.i64
(void)(E*2);

// MYTY: llvm.uadd.with.overflow.i64
(void)(A+B);
}

// INT-LABEL: truncation
// MYTY-LABEL: truncation
// TRUNC-LABEL: truncation
void truncation(char A, int B, unsigned char C, short D) {
// TRUNC-NOT: %handler.implicit_conversion
A = B;
// TRUNC-NOT: %handler.implicit_conversion
A = C;
// TRUNC-NOT: %handler.implicit_conversion
C = B;

// TRUNC: %handler.implicit_conversion
D = B;

(void)A;
(void)D;
}