Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
57 changes: 55 additions & 2 deletions clang/docs/SanitizerSpecialCaseList.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +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
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;
Expand Down Expand Up @@ -48,6 +49,58 @@ 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

Supplying ``ignorelist.txt`` with ``-fsanitize-ignorelist=ignorelist.txt``
disables overflow sanitizer instrumentation for arithmetic operations
containing values of type ``int``, for example. Custom types may be used.

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.

With this, one may disable instrumentation for all types and specifically allow
instrumentation for one or many 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
22 changes: 22 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,28 @@ 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 {
bool sanitizeType =
NoSanitizeL->containsType(Mask, Ty.getAsString(), "sanitize");

bool noSanitizeType =
NoSanitizeL->containsType(Mask, Ty.getAsString(), "no_sanitize") ||
NoSanitizeL->containsType(Mask, Ty.getAsString());

return noSanitizeType && !sanitizeType;
}

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
23 changes: 0 additions & 23 deletions clang/test/CodeGen/ubsan-type-ignorelist.cpp

This file was deleted.

105 changes: 105 additions & 0 deletions clang/test/CodeGen/ubsan-type-ignorelist.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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

// Verify ubsan vptr does not check down-casts on ignorelisted types.
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -emit-llvm %t/test.cpp -o - | FileCheck %s --check-prefix=VPTR
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -fsanitize-ignorelist=%t/vptr.ignorelist -emit-llvm %t/test.cpp -o - | FileCheck %s --check-prefix=VPTR-TYPE


//--- 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


//--- vptr.ignorelist
type:_ZTI3Foo

//--- test.cpp
class Bar {
public:
virtual ~Bar() {}
};
class Foo : public Bar {};

Bar bar;

// VPTR: @_Z7checkmev
// VPTR-TYPE: @_Z7checkmev
void checkme() {
// VPTR: call void @__ubsan_handle_dynamic_type_cache_miss({{.*}} (ptr @bar to
// VPTR-TYPE-NOT: @__ubsan_handle_dynamic_type_cache_miss
Foo* foo = static_cast<Foo*>(&bar); // down-casting
// VPTR: ret void
// VPTR-TYPE: ret void
return;
}

// 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;
}
Loading