Skip to content
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

Improve -fno-strict-overflow description #694

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ Table 2: Recommended compiler options that enable run-time protection mechanisms
| [`-fPIE -pie`](#-fPIE_-pie) | Binutils 2.16.0<br/>Clang 5.0.0 | Build as position-independent executable. Can impact performance on 32-bit architectures. |
| [`-fPIC -shared`](#-fPIC_-shared) | < Binutils 2.6.0<br/>Clang 5.0.0 | Build as position-independent code. Can impact performance on 32-bit architectures. |
| [`-fno-delete-null-pointer-checks`](#-fno-delete-null-pointer-checks) | GCC 3.0.0<br/>Clang 7.0.0 | Force retention of null pointer checks |
| [`-fno-strict-overflow`](#-fno-strict-overflow) | GCC 4.2.0 | Integer overflow may occur |
| [`-fno-strict-overflow`](#-fno-strict-overflow) | GCC 4.2.0 | Define behavior for signed integer and pointer arithmetic overflows |
| [`-fno-strict-aliasing`](#-fno-strict-aliasing) | GCC 2.95.3<br/>Clang 2.9.0 | Do not assume strict aliasing |
| [`-ftrivial-auto-var-init`](#-ftrivial-auto-var-init) | GCC 12.0.0<br/>Clang 8.0.0 | Perform trivial auto variable initialization |
| [`-fexceptions`](#-fexceptions) | GCC 2.95.3<br/>Clang 2.6.0 | Enable exception propagation to harden multi-threaded C code |
Expand Down Expand Up @@ -945,15 +945,18 @@ There are normally no significant performance implications. Null pointer checks

---

### Integer overflow may occur
### Define behavior for signed integer and pointer arithmetic overflows
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these don't define behavior, they complain about it, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The options we cover here make signed-integers-arithmetic and pointer-arithmetic overflows either wrap-around or trap so I think it's justifiable to characterize that as defined behavior (as opposed to the default undefined behavior which allows for the optimizations that may case problems). We don't cover warnings here, so I'd be inclined to leave "defined" as it is.


| Compiler Flag | Supported since | Description |
|:------------------------------------------------------------- |:---------------:|:----------------------------------------------------------------- |
| <span id="-fno-strict-overflow">`-fno-strict-overflow`</span> | GCC 4.2.0 | Integer overflow may occur |
| Compiler Flag | Supported since | Description |
|:------------------------------------------------------------- |:---------------:|:---------------------------------------------------------------------------------------------------------------------------------------------- |
| <span id="-fno-strict-overflow">`-fno-strict-overflow`</span> | GCC 8.5.0 | Signed integer overflows on addition, subtraction, multiplication, and pointer arithmetic wraps around using two's-completment representation |
| <span id="-fwrapv">`-fwrapv`</span> | GCC 3.4.0 | Signed integer overflows on addition, subtraction, and multiplication wraps around using twos-completment representation |
| <span id="-fwrapv-pointer">`-fwrapv-pointer`</span> | GCC 8.5.0 | Pointer arithmetic and multiplication wraps around using two's-complement representation |
| <span id="-ftrapv">`-ftrapv`</span> | GCC 3.3.0 | Signed integer overflows on addition, subtraction and multiplication trap with `SIGABRT` |

#### Synopsis

In C and C++ unsigned integers have long been defined as "wrapping around". However, for many years C and C++ have assumed that overflows do not occur in many other circumstances. Overflow when doing arithmetic with signed numbers is considered undefined by many versions of the official specifications, This approach also allows the compiler to assume strict pointer semantics: if adding an offset to a pointer does not produce a pointer to the same object. In practice, this means that important security checks written in the source code may be silently ignored when generating executable code.
In C and C++ unsigned integers have long been defined as "wrapping around". However, C and C++ compilers, by default, assume that overflows do not occur in other circumstances. Overflow when doing arithmetic with signed numbers is considered undefined in the language specifications. This allows the compiler to assume strict pointer semantics: if adding an offset to a pointer does not produce a pointer to the same object, the addition is undefined. In practice, this means that important security checks written in the source code may be silently ignored when generating executable code.

For example, here is some code from `fs/open.c` of the Linux kernel [^Wang2012]:

Expand All @@ -975,9 +978,33 @@ A developer *might* expect that the computation `offset + len` would produce a u

The Linux kernel enables `-no-strict-overflow` to reduce the likelihood that important security checks in the source code will be silently ignored by the compiler.

An alternative option is to use the `-fwrapv` option. With `-fwrapv`, integer signed overflow wraps (and is thus defined).
Alternatives to `-no-strict-overflow` are the `-fwrapv` and `-ftrapv` options. With `-fwrapv`, integer signed overflow wraps (and is thus defined). With `-ftrapv`, signed integer overflows trap, e.g., on x86 an overflow causes a `SIGABRT` signal to the application.

Note that GCC and Clang interpret this option slightly differently. On clang, this option is considered a synonym for `-fwrapv`. On GCC, this option does not fully enforce two's complement on signed integers, allowing for additional optimizations. [^Wang2012]
Since GCC 8.5 `-no-strict-overflow` is equivalent to `-fwrapv -fwrapv-pointer` while GCC documentation recommends `-fsanitize=signed-integer-overflow` for diagnosing signed integer overflow issues during testing and debugging. In prior GCC versions `-no-strict-overflow` does not fully enforce two's complement on signed integers, allowing for additional optimizations[^Wang2012]. In Clang, `-no-strict-overflow` option is considered a synonym for `-fwrapv`.

#### When not to use?

The C and C++ standards since C23[^C2023] and C++20[^CPP2020] only support two’s complement representation for signed integer types[^Bastien2024]. Previous editions of these standards additionally allowed other sign representations. Code targeting one these previous language editions and requires a specific signed integer representation becomes less portable in a very subtle way. However, in practice, neither GCC nor Clang support other representations [^Bastien2018]. This means that even prior to C23 and C++20 the GCC and Clang implementations of these langauges are effectively two’s complement. Consequently we believe most code will benefit from `-fno-strict-overflow` or its alternatives.

#### Performance implications

Each of these options gives the compiler less freedom for optimizing the resulting machine code compared to the default `-fstrict-overflow` behavior. For example, under `-fstrict-overflow` semantics, expressions such as `i + 10 > i` will always be true for signed `i`, allowing the expression to be replaced at compile time with a constant value. As discussed above, if such expressions occur in condition checks the compiler may optimize away entire code paths when the expression can be evaluated at compile time. In contrast, under `-fno-strict-overflow` those expression must be evaluated at run-time in case of overflows that wrap around the value, thus preventing some optimizations. On the other hand, treating overflows as undefined behavior will only yield optimal behavior if the programmer can be certain the program will never accept inputs that cause overflows.

The `-ftrapv` option requires the compiler to emit checks to detect and trap overflows on signed integers unless it has compile time information of the value range to prove the operation doesn't overflow. As a result, `-ftrapv` is expected to have the largest performance impact out the options covered in this section.

### Other considerations

During link-time optimization (LTO), different compilation units may have been built with different arithmetic overflow behavior. The `-fno-strict-overflow`, `-fwrapv`, `-fno-trapv` and `-fno-strict-aliasing` are passed through to the link stage and take precedece over `-fstrict-overflow` semantics for compilation units with conflicting behavior[^gcc-flto]. In practice this means that software where certain modules benefit from `-fstrict-overflow` for perormance, but others use `-fno-strict-overflow` to improve security, may loose out on the performance benefits with `-fno-strict-overflow` taking precedence during LTO.

[^gcc-flto]: GCC team, [Options That Control Optimization: -flto](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-flto), GCC Manual, 2024-05-07.

[^C2023]: ISO/IEC, [Programming languages — C ("C23")](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf), ISO/IEC ISO/IEC 9899:2024, 2024. Note: The official ISO/IEC specification is paywalled and therefore not publicly available. The final specification draft is publicly available.

[^CPP2020]: ISO/IEC, [Programming languages — C++ ("C++20")](https://isocpp.org/files/papers/N4860.pdf), ISO/IEC ISO/IEC 14882:2020, 2022. Note: The official ISO/IEC specification is paywalled and therefore not publicly available. The final specification draft is publicly available.

[^Bastien2024]: Bastien, JF, [P3477R0: There are exactly 8 bits in a byte](https://open-std.org/JTC1/SC22/WG21/docs/papers/2024/p3477r0.html), Published ISO/IEC JTC1/SC22/WG21 Proposal, 2024-10-15.

[^Bastien2018]: Bastien, JF, [P0907R4: Signed Integers are Two’s Complement](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r4.html), Published ISO/IEC JTC1/SC22/WG21 Proposal, 2018-10-06

---

Expand Down
Loading