Just in case: Debian Bookworm comes with a buggy GCC

pgradot

Pierre Gradot

Posted on December 15, 2023

Just in case: Debian Bookworm comes with a buggy GCC

Last month, I embarked on a new project. I set up a new computer with the latest Debian version, installed my favorites tools, and was all set to code. My first task was to migrate all the repositories from C++14 to C++20. While it might seem as straightforward as updating all the CMakeLists.txt to replace set (CMAKE_CXX_STANDARD 14) with set (CMAKE_CXX_STANDARD 20) reality proved otherwise (and I knew it would).

Among the funny things I encountered (such as std::experimental::basic_string_view::to_string() being missing in std::basic_string_view), the most cryptic error was something like that:

/usr/include/c++/12/bits/char_traits.h:431:56: warning: ‘void* __builtin_memcpy(void*, const void*, long unsigned int)’ accessing 9223372036854775810 or more bytes at offsets -4611686018427387902 and [-4611686018427387903, 4611686018427387904] may overlap up to 9223372036854775813 bytes at offset -3 [-Wrestrict]

  431 |         return static_cast<char_type*>(__builtin_memcpy(__s1, __s2, __n));

Enter fullscreen mode Exit fullscreen mode

This is a known bug in GCC and it affects only the 12.x branch. It's (Bug 105329 - [12/13/14 Regression] Bogus restrict warning when assigning 1-char string literal to std::string since r12-3347-g8af8abfbbace49e6).

Encountering this bug might not be an everyday occurrence, but it's quite easy to trigger it. Unfortunately, as you have guessed, Debian Bookworm ships with this problematic version of GCC. Bookworm is the latest stable version of Debian, released in June this year. Previous stables version, Buster and Bulleye, came with GCC 8 and GCC 10 respectively. Since Debian releases a new stable version every two years, if you are using Debian like me, we may have to deal with this bug for an extended period.

In this article, I will explain how you can trigger this bug and how you can avoid it. It's also an opportunity to revisit various techniques for circumventing spurious warnings.

Conditions to Trigger the Bug

Beyond from the source code itself, specific build conditions are necessary to trigger the bug.

  1. You must use GCC 12, obviously. GCC 11 wasn't affected and the bug is fixed in GCC 13.
  2. Optimizations must be enabled. You must use either -O2 or -O3. -O0, -O1 and -Os don't seem to concerned. If you set CMAKE_BUILD_TYPE to Release, CMake will add -O3. Your release builds will then be susceptible, unlike debug builds.
  3. You must use C++20 and C++23. C++17 is unaffected.
  4. You must pass the -Wrestrict flag since it causes the warning. It is included in -Wall.

This means your build may break simply by:

  • Updating GCC (it happened to GoogleTest).
  • Changing from debug to release mode (but I guess your CI always builds in release mode, right?).
  • Switching for C++17 or below to C++20 or above (and I encourage you to regularly upgrade to the new standard version).
  • Enabling warnings (no, I can't believe you had lived without -Wall so far).

I use the word "break" because most release builds also use -Werror to turn warnings into errors, ensuring they are absolutely noticeable.

The Code Itself

Oh, poor boyz and girlz... The code is so simple.

Here is a CMake CMakeLists.txt:

cmake_minimum_required(VERSION 3.27)
project(buggy)

add_executable(buggy main.cpp)
target_compile_options(buggy PRIVATE -Wall)
target_compile_features(buggy PRIVATE cxx_std_20)
Enter fullscreen mode Exit fullscreen mode

And here a main.cpp that will raise the dreaded warning:

#include <string>

std::string s;

int main() {
    s = "a"; // oopsi...
}
Enter fullscreen mode Exit fullscreen mode

Here is another case that emits the warning:

#include <string>

std::string s;

int main() {
    s = "a" + std::string("b"); // oopsi...
}
Enter fullscreen mode Exit fullscreen mode

The string literal must be single byte and must be the left-hand-side operand. The following lines are fine:

s = "ab";
s = "ab" + std::string("c");
s = std::string("a") + "b";
Enter fullscreen mode Exit fullscreen mode

Circumvention Measures

The discussion for bug 105329 clearly indicates, in comments 26 and 27, than the 12 branch won't be fixed. The solution is hence to move to GCC 13. Alas! Debian will probably not change the major version of GCC in Bookworm. The next release, Trixie, is currently in testing mode and comes with GCC 13, so perhaps one day it will be available in the backports.

Fortunately, there are easy workarounds.

Disable -Wrestrict globally

As a temporary solution, you may add -Wno-restrict to your build:

target_compile_options(buggy PRIVATE -Wall -Wno-restrict)
Enter fullscreen mode Exit fullscreen mode

This is extreme and I highly discourage pushing such a change to a develop or a master/main branch.

Disable -Wrestrict for some files

Another solution I also consider temporary is to disable the warning but only for a particular set of files.

You can use CMake for this, especially if you can't change these sources files:

set_source_files_properties(
        main.cpp
        PROPERTIES
        COMPILE_FLAGS -Wno-restrict)
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can use a pragma at the beginning of each file:

#pragma GCC diagnostic ignored "-Wrestrict"
#include <string>

std::string s;

int main() {
    s = "a";
}
Enter fullscreen mode Exit fullscreen mode

Disable -Wrestrict locally

Disabling the warning only for a couple of lines is a more acceptable solution:

#include <string>

std::string s;

int main() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wrestrict"
    s = "a";
#pragma GCC diagnostic pop
}
Enter fullscreen mode Exit fullscreen mode

I had to use this technique around a #include <boost/test/data/test_case.hpp> because BOOST_DATA_TEST_CASE triggered the bug. This solution is very verbose and reduce your code's readability. Think twice before choosing it.

Note about Clang

Clang does support #pragma GCC but it doesn't support -Wrestrict... It means that the previous codes will raise a warning or error:

$ clang++ main.cpp -Wall -Werror
main.cpp:7:32: error: unknown warning group '-Wrestrict', ignored [-Werror,-Wunknown-warning-option]
#pragma GCC diagnostic ignored "-Wrestrict"
Enter fullscreen mode Exit fullscreen mode

If you want your code to compile both with GCC and Clang, you will need some extra preprocessor tricks:

#ifndef __clang__
#pragma GCC diagnostic ignored "-Wrestrict"
#endif
Enter fullscreen mode Exit fullscreen mode

See Clang's builtin-macros.

The preferred solution: change the code

The solution with set_source_files_properties() may be acceptable if you plan to switch to GCC 13 soon. Otherwise, if you plan to use GCC 12 for a long time, just change your code.

Remember the two faulty lines:

 s = "a";
 s = "a" + std::string("b");
Enter fullscreen mode Exit fullscreen mode

Simply change them to:

s = std::string("a");
s = std::string("a") + std::string("b");
Enter fullscreen mode Exit fullscreen mode

This simply makes implicit conversions explicit.

Conclusion

At the end of the day, the warning message is quite scary, but the code can easily be changed to avoid the bug. Apart from changing the code to avoid this particular issue with GCC 12, we also discussed several techniques you may use to mitigate spurious warnings in your code.

This is not the first time I run into a compiler's bug. It's always so much fun (sarcasm indented) 😆

💖 💪 🙅 🚩
pgradot
Pierre Gradot

Posted on December 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related