|
| 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 | + |
0 commit comments