Skip to content

[Clang] Introduce OverflowBehaviorType for fine-grained overflow control #148914

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
434 changes: 434 additions & 0 deletions clang/docs/OverflowBehaviorTypes.rst

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ Non-comprehensive list of changes in this release
New Compiler Flags
------------------

- New option ``-Wundef-true`` added and enabled by default to warn when `true` is used in the C preprocessor without being defined before C23.

- New option ``-fprofile-continuous`` added to enable continuous profile syncing to file (#GH124353, `docs <https://clang.llvm.org/docs/UsersManual.html#cmdoption-fprofile-continuous>`_).
The feature has `existed <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program>`_)
for a while and this is just a user facing option.

- New option ``-ftime-report-json`` added which outputs the same timing data as ``-ftime-report`` but formatted as JSON.

- New option ``-Wnrvo`` added and disabled by default to warn about missed NRVO opportunities.

- New option ``-ignore-pch`` added to disable precompiled headers. It overrides ``-emit-pch`` and ``-include-pch``. (#GH142409, `PCHDocs <https://clang.llvm.org/docs/UsersManual.html#ignoring-a-pch-file>`_).

- New options ``-g[no-]key-instructions`` added, disabled by default. Reduces jumpiness of debug stepping for optimized code in some debuggers (not LLDB at this time). Not recommended for use without optimizations. DWARF only. Note both the positive and negative flags imply ``-g``.

- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute.

Deprecated Compiler Flags
-------------------------

Expand All @@ -105,6 +121,10 @@ Removed Compiler Flags
Attribute Changes in Clang
--------------------------

- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
currently accepts either ``wrap`` or ``no_wrap`` as an argument, enabling
type-level control over overflow behavior.

Improvements to Clang's diagnostics
-----------------------------------
- Added a separate diagnostic group ``-Wfunction-effect-redeclarations``, for the more pedantic
Expand Down
34 changes: 34 additions & 0 deletions clang/docs/SanitizerSpecialCaseList.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,40 @@ precedence. Here are a few examples.
fun:*bar
fun:bad_bar=sanitize

Interaction with Overflow Behavior Types
----------------------------------------

The ``overflow_behavior`` attribute provides a more granular, source-level
control that takes precedence over the Sanitizer Special Case List. If a type
is given an ``overflow_behavior`` attribute, it will override any matching
``type:`` entry in a special case list.

This allows developers to enforce a specific overflow behavior for a critical
type, even if a broader rule in the special case list would otherwise disable
instrumentation for it.

.. code-block:: bash

$ cat ignorelist.txt
# Disable signed overflow checks for all types by default.
[signed-integer-overflow]
type:*

$ cat foo.c
// Force 'critical_type' to always have overflow checks,
// overriding the ignorelist.
typedef int __attribute__((overflow_behavior(no_wrap))) critical_type;

void foo(int x) {
critical_type a = x;
a++; // Overflow is checked here due to the 'no_wrap' attribute.

int b = x;
b++; // Overflow is NOT checked here due to the ignorelist.
}

For more details on overflow behavior types, see :doc:`OverflowBehaviorTypes`.

Format
======

Expand Down
36 changes: 36 additions & 0 deletions clang/docs/UndefinedBehaviorSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,42 @@ This attribute may not be
supported by other compilers, so consider using it together with
``#if defined(__clang__)``.

Disabling Overflow Instrumentation with ``__attribute__((overflow_behavior(wrap)))``
------------------------------------------------------------------------------------

For more fine-grained control over how integer overflow is handled, you can use
the ``__attribute__((overflow_behavior(wrap)))`` attribute. This attribute can
be applied to ``typedef`` declarations and integer types to specify that
arithmetic operations on that type should wrap on overflow. This can be used to
disable overflow sanitization for specific types, while leaving it enabled for
all other types.

The ``overflow_behavior`` attribute not only affects UBSan instrumentation
but also changes the fundamental overflow behavior of arithmetic operations
on the annotated type. Operations on types marked with ``wrap`` will have
well-defined wrapping semantics, while operations on types marked with
``no_wrap`` will be checked for overflow (regardless of global flags like
``-fwrapv``).

The attribute also affects implicit type promotion rules: when an overflow
behavior type participates in arithmetic operations with standard integer
types, the result maintains the overflow behavior type's characteristics,
including its bit-width. This means annotated types can preserve narrower
widths that would normally be promoted, allowing operations to stay within
the constraints of the smallest annotated type in the expression.

For more information, see :doc:`OverflowBehaviorTypes`.

Enforcing Overflow Instrumentation with ``__attribute__((overflow_behavior(no_wrap)))``
---------------------------------------------------------------------------------------

Conversely, you can use ``__attribute__((overflow_behavior(no_wrap)))`` to
enforce overflow checks for a specific type, even when ``-fwrapv`` is enabled
globally. This is useful for ensuring that critical calculations are always
checked for overflow, regardless of the global compiler settings.

For more information, see :doc:`OverflowBehaviorTypes`.

Suppressing Errors in Recompiled Code (Ignorelist)
--------------------------------------------------

Expand Down
1 change: 1 addition & 0 deletions clang/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Using Clang as a Compiler
SanitizerCoverage
SanitizerStats
SanitizerSpecialCaseList
OverflowBehaviorTypes
BoundsSafety
BoundsSafetyAdoptionGuide
BoundsSafetyImplPlans
Expand Down
20 changes: 20 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_ASTCONTEXT_H
#define LLVM_CLANG_AST_ASTCONTEXT_H

#include "Type.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/CommentCommandTraits.h"
Expand Down Expand Up @@ -259,6 +260,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::ContextualFoldingSet<DependentBitIntType, ASTContext &>
DependentBitIntTypes;
mutable llvm::FoldingSet<BTFTagAttributedType> BTFTagAttributedTypes;
mutable llvm::FoldingSet<OverflowBehaviorType> OverflowBehaviorTypes;
llvm::FoldingSet<HLSLAttributedResourceType> HLSLAttributedResourceTypes;
llvm::FoldingSet<HLSLInlineSpirvType> HLSLInlineSpirvTypes;

Expand Down Expand Up @@ -904,6 +906,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
const QualType &Ty) const;

bool isUnaryOverflowPatternExcluded(const UnaryOperator *UO);

const XRayFunctionFilter &getXRayFilter() const {
return *XRayFilter;
}
Expand Down Expand Up @@ -1004,6 +1008,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
comments::FullComment *getCommentForDecl(const Decl *D,
const Preprocessor *PP) const;

/// Attempts to merge two types that may be OverflowBehaviorTypes.
///
/// \returns A QualType if the types were handled, std::nullopt otherwise.
/// A null QualType indicates an incompatible merge.
std::optional<QualType>
tryMergeOverflowBehaviorTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
bool Unqualified, bool BlockReturnType,
bool IsConditionalOperator);

/// Return parsed documentation comment attached to a given declaration.
/// Returns nullptr if no comment is attached. Does not look at any
/// redeclarations of the declaration.
Expand Down Expand Up @@ -1852,6 +1865,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
QualType Wrapped) const;

QualType getOverflowBehaviorType(const OverflowBehaviorAttr *Attr,
QualType Wrapped) const;

QualType
getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind Kind,
QualType Wrapped) const;

QualType getHLSLAttributedResourceType(
QualType Wrapped, QualType Contained,
const HLSLAttributedResourceType::Attributes &Attrs);
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ class ASTNodeTraverser
void VisitBTFTagAttributedType(const BTFTagAttributedType *T) {
Visit(T->getWrappedType());
}
void VisitOverflowBehaviorType(const OverflowBehaviorType *T) {
Visit(T->getUnderlyingType());
}
void VisitHLSLAttributedResourceType(const HLSLAttributedResourceType *T) {
QualType Contained = T->getContainedType();
if (!Contained.isNull())
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,14 @@ class DeclRefExpr final
return DeclRefExprBits.IsImmediateEscalating;
}

bool isOverflowBehaviorDiscarded() const {
return DeclRefExprBits.IsOverflwBehaviorDiscarded;
}

void setOverflowBehaviorDiscarded(bool Set) {
DeclRefExprBits.IsOverflwBehaviorDiscarded = Set;
}

void setIsImmediateEscalating(bool Set) {
DeclRefExprBits.IsImmediateEscalating = Set;
}
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def AutoTypeKeyword : EnumPropertyType;
def Bool : PropertyType<"bool">;
def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">;
def BTFTypeTagAttr : PropertyType<"const BTFTypeTagAttr *">;
def OverflowBehaviorKind
: EnumPropertyType<"OverflowBehaviorType::OverflowBehaviorKind">;
def CallingConv : EnumPropertyType;
def DeclarationName : PropertyType;
def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,9 @@ DEF_TRAVERSE_TYPE(CountAttributedType, {
DEF_TRAVERSE_TYPE(BTFTagAttributedType,
{ TRY_TO(TraverseType(T->getWrappedType())); })

DEF_TRAVERSE_TYPE(OverflowBehaviorType,
{ TRY_TO(TraverseType(T->getUnderlyingType())); })

DEF_TRAVERSE_TYPE(HLSLAttributedResourceType,
{ TRY_TO(TraverseType(T->getWrappedType())); })

Expand Down Expand Up @@ -1462,6 +1465,9 @@ DEF_TRAVERSE_TYPELOC(CountAttributedType,
DEF_TRAVERSE_TYPELOC(BTFTagAttributedType,
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })

DEF_TRAVERSE_TYPELOC(OverflowBehaviorType,
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })

DEF_TRAVERSE_TYPELOC(HLSLAttributedResourceType,
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,9 @@ class alignas(void *) Stmt {
LLVM_PREFERRED_TYPE(bool)
unsigned IsImmediateEscalating : 1;

LLVM_PREFERRED_TYPE(bool)
unsigned IsOverflwBehaviorDiscarded : 1;

/// The location of the declaration name itself.
SourceLocation Loc;
};
Expand Down
75 changes: 74 additions & 1 deletion clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class TagDecl;
class TemplateParameterList;
class Type;
class Attr;
class NoSanitizeAttr;

enum {
TypeAlignmentInBits = 4,
Expand Down Expand Up @@ -1148,6 +1149,12 @@ class QualType {
/// Returns true if it is a WebAssembly Funcref Type.
bool isWebAssemblyFuncrefType() const;

/// Returns true if it is a OverflowBehaviorType of Wrap kind.
bool isWrapType() const;

/// Returns true if it is a OverflowBehaviorType of NoWrap kind.
bool isNoWrapType() const;

// Don't promise in the API that anything besides 'const' can be
// easily added.

Expand Down Expand Up @@ -2632,6 +2639,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isSubscriptableVectorType() const;
bool isMatrixType() const; // Matrix type.
bool isConstantMatrixType() const; // Constant matrix type.
bool isOverflowBehaviorType() const; // __attribute__((no_sanitize))
bool isDependentAddressSpaceType() const; // value-dependent address space qualifier
bool isObjCObjectPointerType() const; // pointer to ObjC object
bool isObjCRetainableType() const; // ObjC object or block pointer
Expand Down Expand Up @@ -3100,6 +3108,10 @@ template <> const BoundsAttributedType *Type::getAs() const;
/// sugar until it reaches an CountAttributedType or a non-sugared type.
template <> const CountAttributedType *Type::getAs() const;

/// This will check for a OverflowBehaviorType by removing any existing
/// sugar until it reaches an OverflowBehaviorType or a non-sugared type.
template <> const OverflowBehaviorType *Type::getAs() const;

// We can do canonical leaf types faster, because we don't have to
// worry about preserving child type decoration.
#define TYPE(Class, Base)
Expand Down Expand Up @@ -6334,6 +6346,51 @@ class BTFTagAttributedType : public Type, public llvm::FoldingSetNode {
}
};

class OverflowBehaviorType : public Type, public llvm::FoldingSetNode {
public:
enum OverflowBehaviorKind { Wrap, NoWrap };

private:
friend class ASTContext; // ASTContext creates these

QualType UnderlyingType;
OverflowBehaviorKind BehaviorKind;

OverflowBehaviorType(QualType Canon, QualType Underlying,
OverflowBehaviorKind Kind);

public:
QualType getUnderlyingType() const { return UnderlyingType; }
OverflowBehaviorKind getBehaviorKind() const { return BehaviorKind; }

bool isWrapKind() const { return BehaviorKind == OverflowBehaviorKind::Wrap; }
bool isNoWrapKind() const {
return BehaviorKind == OverflowBehaviorKind::NoWrap;
}

OverflowBehaviorKind setBehaviorKind(OverflowBehaviorKind Kind) {
BehaviorKind = Kind;
return BehaviorKind;
}

bool isSugared() const { return false; }
QualType desugar() const { return getUnderlyingType(); }

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, UnderlyingType, BehaviorKind);
}

static void Profile(llvm::FoldingSetNodeID &ID, QualType Underlying,
OverflowBehaviorKind Kind) {
ID.AddPointer(Underlying.getAsOpaquePtr());
ID.AddInteger((int)Kind);
}

static bool classof(const Type *T) {
return T->getTypeClass() == OverflowBehavior;
}
};

class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode {
public:
struct Attributes {
Expand Down Expand Up @@ -8575,6 +8632,10 @@ inline bool Type::isConstantMatrixType() const {
return isa<ConstantMatrixType>(CanonicalType);
}

inline bool Type::isOverflowBehaviorType() const {
return isa<OverflowBehaviorType>(CanonicalType);
}

inline bool Type::isDependentAddressSpaceType() const {
return isa<DependentAddressSpaceType>(CanonicalType);
}
Expand Down Expand Up @@ -8819,6 +8880,12 @@ inline bool Type::isIntegerType() const {
return IsEnumDeclComplete(ET->getDecl()) &&
!IsEnumDeclScoped(ET->getDecl());
}

if (isOverflowBehaviorType())
return cast<OverflowBehaviorType>(CanonicalType)
->getUnderlyingType()
->isIntegerType();

return isBitIntType();
}

Expand Down Expand Up @@ -8881,7 +8948,7 @@ inline bool Type::isScalarType() const {
isa<MemberPointerType>(CanonicalType) ||
isa<ComplexType>(CanonicalType) ||
isa<ObjCObjectPointerType>(CanonicalType) ||
isBitIntType();
isa<OverflowBehaviorType>(CanonicalType) || isBitIntType();
}

inline bool Type::isIntegralOrEnumerationType() const {
Expand All @@ -8893,6 +8960,10 @@ inline bool Type::isIntegralOrEnumerationType() const {
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
return IsEnumDeclComplete(ET->getDecl());

if (const OverflowBehaviorType *OBT =
dyn_cast<OverflowBehaviorType>(CanonicalType))
return OBT->getUnderlyingType()->isIntegralOrEnumerationType();

return isBitIntType();
}

Expand Down Expand Up @@ -9025,6 +9096,8 @@ template <typename T> const T *Type::getAsAdjusted() const {
Ty = A->getModifiedType().getTypePtr();
else if (const auto *A = dyn_cast<BTFTagAttributedType>(Ty))
Ty = A->getWrappedType().getTypePtr();
// else if (const auto *A = dyn_cast<OverflowBehaviorType>(Ty))
// Ty = A->getWrappedType().getTypePtr();
else if (const auto *A = dyn_cast<HLSLAttributedResourceType>(Ty))
Ty = A->getWrappedType().getTypePtr();
else if (const auto *E = dyn_cast<ElaboratedType>(Ty))
Expand Down
Loading
Loading