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 3 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
269 changes: 269 additions & 0 deletions clang/docs/OverflowBehaviorTypes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
=====================
OverflowBehaviorTypes
=====================

.. contents::
:local:

Introduction
============

Clang provides a type attribute that allows developers to have fine-grained control
over the overflow behavior of integer types. The ``overflow_behavior``
attribute can be used to specify how arithmetic operations on a given integer
type should behave upon overflow. This is particularly useful for projects that
need to balance performance and safety, allowing developers to enable or
disable overflow checks for specific types.

The attribute can be enabled using the compiler option
``-foverflow-behavior-types``.

The attribute syntax is as follows:

.. code-block:: c++

__attribute__((overflow_behavior(behavior)))

Where ``behavior`` can be one of the following:

* ``wrap``: Specifies that arithmetic operations on the integer type should
wrap on overflow. This is equivalent to the behavior of ``-fwrapv``, but it
applies only to the attributed type and may be used with both signed and
unsigned types. When this is enabled, UBSan's integer overflow and integer
truncation checks (``signed-integer-overflow``,
``unsigned-integer-overflow``, ``implicit-signed-integer-truncation``, and
``implicit-unsigned-integer-truncation``) are suppressed for the attributed
type.

* ``no_wrap``: Specifies that arithmetic operations on the integer type should
be checked for overflow. When using the ``signed-integer-overflow`` sanitizer
or when using ``-ftrapv`` alongside a signed type, this is the default
behavior. Using this, one may enforce overflow checks for a type even when
``-fwrapv`` is enabled globally.

This attribute can be applied to ``typedef`` declarations and to integer types directly.

Examples
========

Here is an example of how to use the ``overflow_behavior`` attribute with a ``typedef``:

.. code-block:: c++

typedef unsigned int __attribute__((overflow_behavior(no_wrap))) non_wrapping_uint;

non_wrapping_uint add_one(non_wrapping_uint a) {
return a + 1; // Overflow is checked for this operation.
}

Here is an example of how to use the ``overflow_behavior`` attribute with a type directly:

.. code-block:: c++

int mul_alot(int n) {
int __attribute__((overflow_behavior(wrap))) a = n;
return a * 1337; // Potential overflow is not checked and is well-defined
}

"Well-defined" overflow is consistent with two's complement wrap-around
semantics and won't be removed via eager compiler optimizations (like some
undefined behavior might).

Overflow behavior types are implicitly convertible to and from built-in
integral types.

Note that C++ overload set formation rules treat promotions to and from
overflow behavior types the same as normal integral promotions and conversions.

Interaction with Command-Line Flags and Sanitizer Special Case Lists
====================================================================

The ``overflow_behavior`` attribute interacts with sanitizers, ``-ftrapv``,
``-fwrapv``, and Sanitizer Special Case Lists (SSCL) by wholly overriding these
global flags. The following table summarizes the interactions:

.. list-table:: Overflow Behavior Precedence
:widths: 15 15 15 15 20 15
:header-rows: 1

* - Behavior
- Default(No Flags)
- -ftrapv
- -fwrapv
- Sanitizers
- SSCL
* - ``overflow_behavior(wrap)``
- Wraps
- No trap
- Wraps
- No report
- Overrides SSCL
* - ``overflow_behavior(no_wrap)``
- Traps
- Traps
- Traps
- Reports
- Overrides SSCL

It is important to note the distinction between signed and unsigned types. For
unsigned integers, which wrap on overflow by default, ``overflow_behavior(no_wrap)``
is particularly useful for enabling overflow checks. For signed integers, whose
overflow behavior is undefined by default, ``overflow_behavior(wrap)`` provides
a guaranteed wrapping behavior.

The ``overflow_behavior`` attribute can be used to override the behavior of
entries from a :doc:`SanitizerSpecialCaseList`. This is useful for allowlisting
specific types into overflow instrumentation.

Promotion Rules
===============

The promotion rules for overflow behavior types are designed to preserve the
specified overflow behavior throughout an arithmetic expression. They differ
from standard C/C++ integer promotions but in a predictable way, similar to
how ``_Complex`` and ``_BitInt`` have their own promotion rules.

* **OBT and Standard Integer Type**: In an operation involving an overflow
behavior type (OBT) and a standard integer type, the result will have the
type of the OBT, including its overflow behavior, sign, and bit-width. The
standard integer type is implicitly converted to match the OBT.

.. code-block:: c++

typedef char __attribute__((overflow_behavior(no_wrap))) no_wrap_char;
// The result of this expression is no_wrap_char.
no_wrap_char c;
unsigned long ul;
auto result = c + ul;

* **Two OBTs of the Same Kind**: When an operation involves two OBTs of the
same kind (e.g., both ``wrap``), the result will have the larger of the two
bit-widths. If the bit-widths are the same, an unsigned type is favored over
a signed one.

.. code-block:: c++

typedef unsigned char __attribute__((overflow_behavior(wrap))) u8_wrap;
typedef unsigned short __attribute__((overflow_behavior(wrap))) u16_wrap;
// The result of this expression is u16_wrap.
u8_wrap a;
u16_wrap b;
auto result = a + b;

* **Two OBTs of Different Kinds**: In an operation between a ``wrap`` and a
``no_wrap`` type, a ``no_wrap`` is produced. It is recommended to avoid such
operations, as Clang may emit a warning for such cases in the future.
Regardless, the resulting type matches the bit-width, sign and behavior of
the ``no_wrap`` type.

Diagnostics
===========

Clang provides diagnostics to help developers manage overflow behavior types.

-Wimplicitly-discarded-overflow-behavior
----------------------------------------

This warning is issued when an overflow behavior type is implicitly converted
to a standard integer type, which may lead to the loss of the specified
overflow behavior.

.. code-block:: c++

typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;

void some_function(int);

void another_function(wrapping_int w) {
some_function(w); // warning: implicit conversion from 'wrapping_int' to
// 'int' discards overflow behavior
}

To fix this, you can explicitly cast the overflow behavior type to a standard
integer type.

.. code-block:: c++

typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;

void some_function(int);

void another_function(wrapping_int w) {
some_function(static_cast<int>(w)); // OK
}

This warning acts as a group that includes
``-Wimplicitly-discarded-overflow-behavior-pedantic`` and
``-Wimplicitly-discarded-overflow-behavior-assignment``.

-Wimplicitly-discarded-overflow-behavior-pedantic
-------------------------------------------------

A less severe version of the warning, ``-Wimplicitly-discarded-overflow-behavior-pedantic``,
is issued for implicit conversions from an unsigned wrapping type to a standard
unsigned integer type. This is considered less problematic because both types
have well-defined wrapping behavior, but the conversion still discards the
explicit ``overflow_behavior`` attribute.

.. code-block:: c++

typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;

void some_function(unsigned int);

void another_function(wrapping_uint w) {
some_function(w); // warning: implicit conversion from 'wrapping_uint' to
// 'unsigned int' discards overflow behavior
// [-Wimplicitly-discarded-overflow-behavior-pedantic]
}

-Wimplicitly-discarded-overflow-behavior-assignment
---------------------------------------------------

This warning is issued when an overflow behavior type is implicitly converted
to a standard integer type as part of an assignment, which may lead to the
loss of the specified overflow behavior. This is a more specific version of
the ``-Wimplicitly-discarded-overflow-behavior`` warning, and it is off by
default.

.. code-block:: c++

typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;

void some_function() {
wrapping_int w = 1;
int i = w; // warning: implicit conversion from 'wrapping_int' to 'int'
// discards overflow behavior
// [-Wimplicitly-discarded-overflow-behavior-assignment]
}

To fix this, you can explicitly cast the overflow behavior type to a standard
integer type.

.. code-block:: c++

typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;

void some_function() {
wrapping_int w = 1;
int i = static_cast<int>(w); // OK
int j = (int)w; // C-style OK
}


-Woverflow-behavior-attribute-ignored
-------------------------------------

This warning is issued when the ``overflow_behavior`` attribute is applied to
a type that is not an integer type.

.. code-block:: c++

typedef float __attribute__((overflow_behavior(wrap))) wrapping_float;
// warning: 'overflow_behavior' attribute only applies to integer types;
// attribute is ignored [-Woverflow-behavior-attribute-ignored]

typedef struct S { int i; } __attribute__((overflow_behavior(wrap))) S_t;
// warning: 'overflow_behavior' attribute only applies to integer types;
// attribute is ignored [-Woverflow-behavior-attribute-ignored]

6 changes: 6 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ New Compiler Flags

- 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 Down Expand Up @@ -481,6 +483,10 @@ related warnings within the method body.
- Clang will print the "reason" string argument passed on to
``[[clang::warn_unused_result("reason")]]`` as part of the warning diagnostic.

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

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
22 changes: 22 additions & 0 deletions clang/docs/UndefinedBehaviorSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,28 @@ 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.

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