Skip to content

Commit db6fb19

Browse files
committed
[Clang] implement OverflowBehaviorTypes
Signed-off-by: Justin Stitt <[email protected]>
1 parent 6752369 commit db6fb19

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+2031
-130
lines changed

clang/docs/OverflowBehaviorTypes.rst

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
=====================
2+
OverflowBehaviorTypes
3+
=====================
4+
5+
.. contents::
6+
:local:
7+
8+
Introduction
9+
============
10+
11+
Clang provides a type attribute that allows developers to have fine-grained control
12+
over the overflow behavior of integer types. The ``overflow_behavior``
13+
attribute can be used to specify how arithmetic operations on a given integer
14+
type should behave upon overflow. This is particularly useful for projects that
15+
need to balance performance and safety, allowing developers to enable or
16+
disable overflow checks for specific types.
17+
18+
The attribute can be enabled using the compiler option
19+
``-foverflow-behavior-types``.
20+
21+
The attribute syntax is as follows:
22+
23+
.. code-block:: c++
24+
25+
__attribute__((overflow_behavior(behavior)))
26+
27+
Where ``behavior`` can be one of the following:
28+
29+
* ``wrap``: Specifies that arithmetic operations on the integer type should
30+
wrap on overflow. This is equivalent to the behavior of ``-fwrapv``, but it
31+
applies only to the attributed type and may be used with both signed and
32+
unsigned types. When this is enabled, UBSan's integer overflow and integer
33+
truncation checks (``signed-integer-overflow``,
34+
``unsigned-integer-overflow``, ``implicit-signed-integer-truncation``, and
35+
``implicit-unsigned-integer-truncation``) are suppressed for the attributed
36+
type.
37+
38+
* ``no_wrap``: Specifies that arithmetic operations on the integer type should
39+
be checked for overflow. When using the ``signed-integer-overflow`` sanitizer
40+
or when using ``-ftrapv`` alongside a signed type, this is the default
41+
behavior. Using this, one may enforce overflow checks for a type even when
42+
``-fwrapv`` is enabled globally.
43+
44+
This attribute can be applied to ``typedef`` declarations and to integer types directly.
45+
46+
Examples
47+
========
48+
49+
Here is an example of how to use the ``overflow_behavior`` attribute with a ``typedef``:
50+
51+
.. code-block:: c++
52+
53+
typedef unsigned int __attribute__((overflow_behavior(no_wrap))) non_wrapping_uint;
54+
55+
non_wrapping_uint add_one(non_wrapping_uint a) {
56+
return a + 1; // Overflow is checked for this operation.
57+
}
58+
59+
Here is an example of how to use the ``overflow_behavior`` attribute with a type directly:
60+
61+
.. code-block:: c++
62+
63+
int mul_alot(int n) {
64+
int __attribute__((overflow_behavior(wrap))) a = n;
65+
return a * 1337; // Potential overflow is not checked and is well-defined
66+
}
67+
68+
"Well-defined" overflow is consistent with two's complement wrap-around
69+
semantics and won't be removed via eager compiler optimizations (like some
70+
undefined behavior might).
71+
72+
Overflow behavior types are implicitly convertible to and from built-in
73+
integral types.
74+
75+
Note that C++ overload set formation rules treat promotions to and from
76+
overflow behavior types the same as normal integral promotions and conversions.
77+
78+
Interaction with Command-Line Flags and Sanitizer Special Case Lists
79+
====================================================================
80+
81+
The ``overflow_behavior`` attribute interacts with sanitizers, ``-ftrapv``,
82+
``-fwrapv``, and Sanitizer Special Case Lists (SSCL) by wholly overriding these
83+
global flags. The following table summarizes the interactions:
84+
85+
.. list-table:: Overflow Behavior Precedence
86+
:widths: 15 15 15 15 20 15
87+
:header-rows: 1
88+
89+
* - Behavior
90+
- Default(No Flags)
91+
- -ftrapv
92+
- -fwrapv
93+
- Sanitizers
94+
- SSCL
95+
* - ``overflow_behavior(wrap)``
96+
- Wraps
97+
- No trap
98+
- Wraps
99+
- No report
100+
- Overrides SSCL
101+
* - ``overflow_behavior(no_wrap)``
102+
- Traps
103+
- Traps
104+
- Traps
105+
- Reports
106+
- Overrides SSCL
107+
108+
It is important to note the distinction between signed and unsigned types. For
109+
unsigned integers, which wrap on overflow by default, ``overflow_behavior(no_wrap)``
110+
is particularly useful for enabling overflow checks. For signed integers, whose
111+
overflow behavior is undefined by default, ``overflow_behavior(wrap)`` provides
112+
a guaranteed wrapping behavior.
113+
114+
The ``overflow_behavior`` attribute can be used to override the behavior of
115+
entries from a :doc:`SanitizerSpecialCaseList`. This is useful for allowlisting
116+
specific types into overflow instrumentation.
117+
118+
Promotion Rules
119+
===============
120+
121+
The promotion rules for overflow behavior types are designed to preserve the
122+
specified overflow behavior throughout an arithmetic expression. They differ
123+
from standard C/C++ integer promotions but in a predictable way, similar to
124+
how ``_Complex`` and ``_BitInt`` have their own promotion rules.
125+
126+
* **OBT and Standard Integer Type**: In an operation involving an overflow
127+
behavior type (OBT) and a standard integer type, the result will have the
128+
type of the OBT, including its overflow behavior, sign, and bit-width. The
129+
standard integer type is implicitly converted to match the OBT.
130+
131+
.. code-block:: c++
132+
133+
typedef char __attribute__((overflow_behavior(no_wrap))) no_wrap_char;
134+
// The result of this expression is no_wrap_char.
135+
no_wrap_char c;
136+
unsigned long ul;
137+
auto result = c + ul;
138+
139+
* **Two OBTs of the Same Kind**: When an operation involves two OBTs of the
140+
same kind (e.g., both ``wrap``), the result will have the larger of the two
141+
bit-widths. If the bit-widths are the same, an unsigned type is favored over
142+
a signed one.
143+
144+
.. code-block:: c++
145+
146+
typedef unsigned char __attribute__((overflow_behavior(wrap))) u8_wrap;
147+
typedef unsigned short __attribute__((overflow_behavior(wrap))) u16_wrap;
148+
// The result of this expression is u16_wrap.
149+
u8_wrap a;
150+
u16_wrap b;
151+
auto result = a + b;
152+
153+
* **Two OBTs of Different Kinds**: In an operation between a ``wrap`` and a
154+
``no_wrap`` type, a ``no_wrap`` is produced. It is recommended to avoid such
155+
operations, as Clang may emit a warning for such cases in the future.
156+
Regardless, the resulting type matches the bit-width, sign and behavior of
157+
the ``no_wrap`` type.
158+
159+
Diagnostics
160+
===========
161+
162+
Clang provides diagnostics to help developers manage overflow behavior types.
163+
164+
-Wimplicitly-discarded-overflow-behavior
165+
----------------------------------------
166+
167+
This warning is issued when an overflow behavior type is implicitly converted
168+
to a standard integer type, which may lead to the loss of the specified
169+
overflow behavior.
170+
171+
.. code-block:: c++
172+
173+
typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
174+
175+
void some_function(int);
176+
177+
void another_function(wrapping_int w) {
178+
some_function(w); // warning: implicit conversion from 'wrapping_int' to
179+
// 'int' discards overflow behavior
180+
}
181+
182+
To fix this, you can explicitly cast the overflow behavior type to a standard
183+
integer type.
184+
185+
.. code-block:: c++
186+
187+
typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
188+
189+
void some_function(int);
190+
191+
void another_function(wrapping_int w) {
192+
some_function(static_cast<int>(w)); // OK
193+
}
194+
195+
This warning acts as a group that includes
196+
``-Wimplicitly-discarded-overflow-behavior-pedantic`` and
197+
``-Wimplicitly-discarded-overflow-behavior-assignment``.
198+
199+
-Wimplicitly-discarded-overflow-behavior-pedantic
200+
-------------------------------------------------
201+
202+
A less severe version of the warning, ``-Wimplicitly-discarded-overflow-behavior-pedantic``,
203+
is issued for implicit conversions from an unsigned wrapping type to a standard
204+
unsigned integer type. This is considered less problematic because both types
205+
have well-defined wrapping behavior, but the conversion still discards the
206+
explicit ``overflow_behavior`` attribute.
207+
208+
.. code-block:: c++
209+
210+
typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;
211+
212+
void some_function(unsigned int);
213+
214+
void another_function(wrapping_uint w) {
215+
some_function(w); // warning: implicit conversion from 'wrapping_uint' to
216+
// 'unsigned int' discards overflow behavior
217+
// [-Wimplicitly-discarded-overflow-behavior-pedantic]
218+
}
219+
220+
-Wimplicitly-discarded-overflow-behavior-assignment
221+
---------------------------------------------------
222+
223+
This warning is issued when an overflow behavior type is implicitly converted
224+
to a standard integer type as part of an assignment, which may lead to the
225+
loss of the specified overflow behavior. This is a more specific version of
226+
the ``-Wimplicitly-discarded-overflow-behavior`` warning, and it is off by
227+
default.
228+
229+
.. code-block:: c++
230+
231+
typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
232+
233+
void some_function() {
234+
wrapping_int w = 1;
235+
int i = w; // warning: implicit conversion from 'wrapping_int' to 'int'
236+
// discards overflow behavior
237+
// [-Wimplicitly-discarded-overflow-behavior-assignment]
238+
}
239+
240+
To fix this, you can explicitly cast the overflow behavior type to a standard
241+
integer type.
242+
243+
.. code-block:: c++
244+
245+
typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
246+
247+
void some_function() {
248+
wrapping_int w = 1;
249+
int i = static_cast<int>(w); // OK
250+
int j = (int)w; // C-style OK
251+
}
252+
253+
254+
-Woverflow-behavior-attribute-ignored
255+
-------------------------------------
256+
257+
This warning is issued when the ``overflow_behavior`` attribute is applied to
258+
a type that is not an integer type.
259+
260+
.. code-block:: c++
261+
262+
typedef float __attribute__((overflow_behavior(wrap))) wrapping_float;
263+
// warning: 'overflow_behavior' attribute only applies to integer types;
264+
// attribute is ignored [-Woverflow-behavior-attribute-ignored]
265+
266+
typedef struct S { int i; } __attribute__((overflow_behavior(wrap))) S_t;
267+
// warning: 'overflow_behavior' attribute only applies to integer types;
268+
// attribute is ignored [-Woverflow-behavior-attribute-ignored]
269+

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ New Compiler Flags
381381

382382
- 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``.
383383

384+
- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute.
385+
384386
Deprecated Compiler Flags
385387
-------------------------
386388

@@ -481,6 +483,10 @@ related warnings within the method body.
481483
- Clang will print the "reason" string argument passed on to
482484
``[[clang::warn_unused_result("reason")]]`` as part of the warning diagnostic.
483485

486+
- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
487+
currently accepts either ``wrap`` or ``no_wrap`` as an argument, enabling
488+
type-level control over overflow behavior.
489+
484490
Improvements to Clang's diagnostics
485491
-----------------------------------
486492

clang/docs/SanitizerSpecialCaseList.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,40 @@ precedence. Here are a few examples.
134134
fun:*bar
135135
fun:bad_bar=sanitize
136136
137+
Interaction with Overflow Behavior Types
138+
----------------------------------------
139+
140+
The ``overflow_behavior`` attribute provides a more granular, source-level
141+
control that takes precedence over the Sanitizer Special Case List. If a type
142+
is given an ``overflow_behavior`` attribute, it will override any matching
143+
``type:`` entry in a special case list.
144+
145+
This allows developers to enforce a specific overflow behavior for a critical
146+
type, even if a broader rule in the special case list would otherwise disable
147+
instrumentation for it.
148+
149+
.. code-block:: bash
150+
151+
$ cat ignorelist.txt
152+
# Disable signed overflow checks for all types by default.
153+
[signed-integer-overflow]
154+
type:*
155+
156+
$ cat foo.c
157+
// Force 'critical_type' to always have overflow checks,
158+
// overriding the ignorelist.
159+
typedef int __attribute__((overflow_behavior(no_wrap))) critical_type;
160+
161+
void foo(int x) {
162+
critical_type a = x;
163+
a++; // Overflow is checked here due to the 'no_wrap' attribute.
164+
165+
int b = x;
166+
b++; // Overflow is NOT checked here due to the ignorelist.
167+
}
168+
169+
For more details on overflow behavior types, see :doc:`OverflowBehaviorTypes`.
170+
137171
Format
138172
======
139173

clang/docs/UndefinedBehaviorSanitizer.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,28 @@ This attribute may not be
380380
supported by other compilers, so consider using it together with
381381
``#if defined(__clang__)``.
382382

383+
Disabling Overflow Instrumentation with ``__attribute__((overflow_behavior(wrap)))``
384+
------------------------------------------------------------------------------------
385+
386+
For more fine-grained control over how integer overflow is handled, you can use
387+
the ``__attribute__((overflow_behavior(wrap)))`` attribute. This attribute can
388+
be applied to ``typedef`` declarations and integer types to specify that
389+
arithmetic operations on that type should wrap on overflow. This can be used to
390+
disable overflow sanitization for specific types, while leaving it enabled for
391+
all other types.
392+
393+
For more information, see :doc:`OverflowBehaviorTypes`.
394+
395+
Enforcing Overflow Instrumentation with ``__attribute__((overflow_behavior(no_wrap)))``
396+
---------------------------------------------------------------------------------------
397+
398+
Conversely, you can use ``__attribute__((overflow_behavior(no_wrap)))`` to
399+
enforce overflow checks for a specific type, even when ``-fwrapv`` is enabled
400+
globally. This is useful for ensuring that critical calculations are always
401+
checked for overflow, regardless of the global compiler settings.
402+
403+
For more information, see :doc:`OverflowBehaviorTypes`.
404+
383405
Suppressing Errors in Recompiled Code (Ignorelist)
384406
--------------------------------------------------
385407

clang/docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Using Clang as a Compiler
4040
SanitizerCoverage
4141
SanitizerStats
4242
SanitizerSpecialCaseList
43+
OverflowBehaviorTypes
4344
BoundsSafety
4445
BoundsSafetyAdoptionGuide
4546
BoundsSafetyImplPlans

0 commit comments

Comments
 (0)