[
https://issues.apache.org/jira/browse/THRIFT-6076?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Justin Caratzas updated THRIFT-6076:
------------------------------------
Description:
Summary
Building the Thrift compiler (`compiler/cpp`) with GCC 14 and link-time
optimisation enabled (`-flto=auto -ffat-lto-objects`) fails at the final link
step with false-positive `-Werror=stringop-overflow` /
`-Werror=stringop-overread` diagnostics in ten generator source files. The code
is correct; GCC 14's LTO inter-procedural analysis incorrectly estimates
intermediate buffer sizes when chained `std::string() + "literal"` expressions
are inlined.
Environment
- *{*}GCC{*}*: 14.3.1 (Red Hat 14.3.1-4), also reproducible with upstream GCC
14.x
- *{*}Build flags{*}*: `-flto=auto -ffat-lto-objects` (default RPM/distro
hardened flags on RHEL 10 / Fedora 40+)
- *{*}Thrift{*}*: `master` branch and 0.24.0 release
Affected files
|File|Function|Line (master)|
|------|----------|--------------|
|`compiler/cpp/src/thrift/generate/t_javame_generator.cc`|`java_type_imports()`|292|
|`compiler/cpp/src/thrift/generate/t_javame_generator.cc`|`java_thrift_imports()`|302|
|`compiler/cpp/src/thrift/generate/t_haxe_generator.cc`|`haxe_type_imports()`|321|
|`compiler/cpp/src/thrift/generate/t_haxe_generator.cc`|`haxe_thrift_imports()`|341|
|`compiler/cpp/src/thrift/generate/t_js_generator.cc`|`autogen_comment()`|272|
|`compiler/cpp/src/thrift/generate/t_ocaml_generator.cc`|`ocaml_autogen_comment()`|252|
|`compiler/cpp/src/thrift/generate/t_perl_generator.cc`|`autogen_comment()`|155|
|`compiler/cpp/src/thrift/generate/t_erl_generator.cc`|`erl_autogen_comment()`|396|
|`compiler/cpp/src/thrift/generate/t_go_generator.cc`|`go_autogen_comment()`|695|
|`compiler/cpp/src/thrift/generate/t_delphi_generator.cc`|`autogen_comment()`|345|
|`compiler/cpp/src/thrift/generate/t_rb_generator.cc`|`rb_autogen_comment()`|345|
|`compiler/cpp/src/thrift/generate/t_xsd_generator.cc`|`xml_autogen_comment()`|97|
Error output
```
/usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
writing 41 bytes into a region of size 16 overflows the destination
[-Werror=stringop-overflow=]
427 | return static_cast<char_type*>(_{_}builtin_memcpy({_}_s1, __s2, __n));
src/thrift/generate/t_javame_generator.cc: In member function
'java_type_imports':
src/thrift/generate/t_javame_generator.cc:292:21: note: at offset 16 into
destination object '<anonymous>' of size 32
src/thrift/generate/t_haxe_generator.cc: In member function
'haxe_thrift_imports':
/usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
writing 67 bytes into a region of size 16 overflows the destination
[-Werror=stringop-overflow=]
src/thrift/generate/t_js_generator.cc: In member function 'autogen_comment':
/usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
writing 47 bytes into a region of size 16 overflows the destination
[-Werror=stringop-overflow=]
lto1: all warnings being treated as errors
lto-wrapper: fatal error: make returned 2 exit status
collect2: error: ld returned 1 exit status
```
Root cause
All affected sites use one of these patterns where chained `operator+` calls on
temporary `std::string` objects trigger GCC 14's LTO false positive:
```cpp
// Pattern 1 — empty string() seed
return string() + "import java.util.Hashtable;\n" + "import java.util.Vector;\n"
+ "import java.util.Enumeration;\n\n";
// Pattern 2 — non-empty literal seed + THRIFT_VERSION variable
return std::string("//\n") + "// Autogenerated by Thrift Compiler (" +
THRIFT_VERSION + ")\n"
+ "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE
DOING\n"
+ "//\n";
```
Proposed fix
*{*}For pure-literal returns{*}* (no `THRIFT_VERSION`): replace with adjacent
string literal concatenation, which is resolved at compile time and produces no
temporary objects:
```cpp
// t_javame_generator.cc java_type_imports() — before
return string() + "import java.util.Hashtable;\n" + "import java.util.Vector;\n"
+ "import java.util.Enumeration;\n\n";
// after
return "import java.util.Hashtable;\n"
"import java.util.Vector;\n"
"import java.util.Enumeration;\n\n";
```
*{*}For returns that embed `THRIFT_VERSION`{*}*: use adjacent literals up to
the variable insertion point, so that only one `operator+` call with a
correctly-sized left operand remains:
```cpp
// t_js_generator.cc autogen_comment() — before
return std::string("//\n") + "// Autogenerated by Thrift Compiler (" +
THRIFT_VERSION + ")\n"
+ "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE
DOING\n"
+ "//\n";
// after
return "//\n"
"// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
"//\n"
"// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
"//\n";
```
This pattern is safe: adjacent string literals are concatenated by the compiler
before `operator+` is evaluated, so the left operand of the first `+` is always
a correctly-sized compile-time constant.
A workaround for downstream packagers is to add `-Wno-stringop-overflow
-Wno-stringop-overread` to `CXXFLAGS` at build time, but fixing the source is
the correct long-term solution.
was:
## Summary
Building the Thrift compiler (`compiler/cpp`) with GCC 14 and link-time
optimisation enabled (`-flto=auto -ffat-lto-objects`) fails at the final link
step with false-positive `-Werror=stringop-overflow` /
`-Werror=stringop-overread` diagnostics in ten generator source files. The code
is correct; GCC 14's LTO inter-procedural analysis incorrectly estimates
intermediate buffer sizes when chained `std::string() + "literal"` expressions
are inlined.
## Environment
- **GCC**: 14.3.1 (Red Hat 14.3.1-4), also reproducible with upstream GCC 14.x
- **Build flags**: `-flto=auto -ffat-lto-objects` (default RPM/distro hardened
flags on RHEL 10 / Fedora 40+)
- **Thrift**: `master` branch and 0.24.0 release
## Affected files
| File | Function | Line (master) |
|------|----------|--------------|
| `compiler/cpp/src/thrift/generate/t_javame_generator.cc` |
`java_type_imports()` | 292 |
| `compiler/cpp/src/thrift/generate/t_javame_generator.cc` |
`java_thrift_imports()` | 302 |
| `compiler/cpp/src/thrift/generate/t_haxe_generator.cc` |
`haxe_type_imports()` | 321 |
| `compiler/cpp/src/thrift/generate/t_haxe_generator.cc` |
`haxe_thrift_imports()` | 341 |
| `compiler/cpp/src/thrift/generate/t_js_generator.cc` | `autogen_comment()` |
272 |
| `compiler/cpp/src/thrift/generate/t_ocaml_generator.cc` |
`ocaml_autogen_comment()` | 252 |
| `compiler/cpp/src/thrift/generate/t_perl_generator.cc` | `autogen_comment()`
| 155 |
| `compiler/cpp/src/thrift/generate/t_erl_generator.cc` |
`erl_autogen_comment()` | 396 |
| `compiler/cpp/src/thrift/generate/t_go_generator.cc` | `go_autogen_comment()`
| 695 |
| `compiler/cpp/src/thrift/generate/t_delphi_generator.cc` |
`autogen_comment()` | 345 |
| `compiler/cpp/src/thrift/generate/t_rb_generator.cc` | `rb_autogen_comment()`
| 345 |
| `compiler/cpp/src/thrift/generate/t_xsd_generator.cc` |
`xml_autogen_comment()` | 97 |
## Error output
```
/usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
writing 41 bytes into a region of size 16 overflows the destination
[-Werror=stringop-overflow=]
427 | return static_cast<char_type*>(__builtin_memcpy(__s1, __s2,
__n));
src/thrift/generate/t_javame_generator.cc: In member function
'java_type_imports':
src/thrift/generate/t_javame_generator.cc:292:21: note: at offset 16 into
destination object '<anonymous>' of size 32
src/thrift/generate/t_haxe_generator.cc: In member function
'haxe_thrift_imports':
/usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
writing 67 bytes into a region of size 16 overflows the destination
[-Werror=stringop-overflow=]
src/thrift/generate/t_js_generator.cc: In member function 'autogen_comment':
/usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
writing 47 bytes into a region of size 16 overflows the destination
[-Werror=stringop-overflow=]
lto1: all warnings being treated as errors
lto-wrapper: fatal error: make returned 2 exit status
collect2: error: ld returned 1 exit status
```
## Root cause
All affected sites use one of these patterns where chained `operator+` calls on
temporary `std::string` objects trigger GCC 14's LTO false positive:
```cpp
// Pattern 1 — empty string() seed
return string() + "import java.util.Hashtable;\n" + "import java.util.Vector;\n"
+ "import java.util.Enumeration;\n\n";
// Pattern 2 — non-empty literal seed + THRIFT_VERSION variable
return std::string("//\n") + "// Autogenerated by Thrift Compiler (" +
THRIFT_VERSION + ")\n"
+ "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU
ARE DOING\n"
+ "//\n";
```
## Proposed fix
**For pure-literal returns** (no `THRIFT_VERSION`): replace with adjacent
string literal concatenation, which is resolved at compile time and produces no
temporary objects:
```cpp
// t_javame_generator.cc java_type_imports() — before
return string() + "import java.util.Hashtable;\n" + "import java.util.Vector;\n"
+ "import java.util.Enumeration;\n\n";
// after
return "import java.util.Hashtable;\n"
"import java.util.Vector;\n"
"import java.util.Enumeration;\n\n";
```
**For returns that embed `THRIFT_VERSION`**: use adjacent literals up to the
variable insertion point, so that only one `operator+` call with a
correctly-sized left operand remains:
```cpp
// t_js_generator.cc autogen_comment() — before
return std::string("//\n") + "// Autogenerated by Thrift Compiler (" +
THRIFT_VERSION + ")\n"
+ "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU
ARE DOING\n"
+ "//\n";
// after
return "//\n"
"// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
"//\n"
"// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
"//\n";
```
This pattern is safe: adjacent string literals are concatenated by the compiler
before `operator+` is evaluated, so the left operand of the first `+` is always
a correctly-sized compile-time constant.
A workaround for downstream packagers is to add `-Wno-stringop-overflow
-Wno-stringop-overread` to `CXXFLAGS` at build time, but fixing the source is
the correct long-term solution.
> Compiler build fails with GCC 14 LTO
> ------------------------------------
>
> Key: THRIFT-6076
> URL: https://issues.apache.org/jira/browse/THRIFT-6076
> Project: Thrift
> Issue Type: Bug
> Reporter: Justin Caratzas
> Priority: Major
>
> Summary
> Building the Thrift compiler (`compiler/cpp`) with GCC 14 and link-time
> optimisation enabled (`-flto=auto -ffat-lto-objects`) fails at the final link
> step with false-positive `-Werror=stringop-overflow` /
> `-Werror=stringop-overread` diagnostics in ten generator source files. The
> code is correct; GCC 14's LTO inter-procedural analysis incorrectly estimates
> intermediate buffer sizes when chained `std::string() + "literal"`
> expressions are inlined.
> Environment
> - *{*}GCC{*}*: 14.3.1 (Red Hat 14.3.1-4), also reproducible with upstream
> GCC 14.x
> - *{*}Build flags{*}*: `-flto=auto -ffat-lto-objects` (default RPM/distro
> hardened flags on RHEL 10 / Fedora 40+)
> - *{*}Thrift{*}*: `master` branch and 0.24.0 release
> Affected files
> |File|Function|Line (master)|
> |------|----------|--------------|
> |`compiler/cpp/src/thrift/generate/t_javame_generator.cc`|`java_type_imports()`|292|
> |`compiler/cpp/src/thrift/generate/t_javame_generator.cc`|`java_thrift_imports()`|302|
> |`compiler/cpp/src/thrift/generate/t_haxe_generator.cc`|`haxe_type_imports()`|321|
> |`compiler/cpp/src/thrift/generate/t_haxe_generator.cc`|`haxe_thrift_imports()`|341|
> |`compiler/cpp/src/thrift/generate/t_js_generator.cc`|`autogen_comment()`|272|
> |`compiler/cpp/src/thrift/generate/t_ocaml_generator.cc`|`ocaml_autogen_comment()`|252|
> |`compiler/cpp/src/thrift/generate/t_perl_generator.cc`|`autogen_comment()`|155|
> |`compiler/cpp/src/thrift/generate/t_erl_generator.cc`|`erl_autogen_comment()`|396|
> |`compiler/cpp/src/thrift/generate/t_go_generator.cc`|`go_autogen_comment()`|695|
> |`compiler/cpp/src/thrift/generate/t_delphi_generator.cc`|`autogen_comment()`|345|
> |`compiler/cpp/src/thrift/generate/t_rb_generator.cc`|`rb_autogen_comment()`|345|
> |`compiler/cpp/src/thrift/generate/t_xsd_generator.cc`|`xml_autogen_comment()`|97|
> Error output
> ```
> /usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
> writing 41 bytes into a region of size 16 overflows the destination
> [-Werror=stringop-overflow=]
> 427 | return static_cast<char_type*>(_{_}builtin_memcpy({_}_s1, __s2, __n));
> src/thrift/generate/t_javame_generator.cc: In member function
> 'java_type_imports':
> src/thrift/generate/t_javame_generator.cc:292:21: note: at offset 16 into
> destination object '<anonymous>' of size 32
> src/thrift/generate/t_haxe_generator.cc: In member function
> 'haxe_thrift_imports':
> /usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
> writing 67 bytes into a region of size 16 overflows the destination
> [-Werror=stringop-overflow=]
> src/thrift/generate/t_js_generator.cc: In member function 'autogen_comment':
> /usr/include/c++/14/bits/char_traits.h:427:56: error: '__builtin_memcpy'
> writing 47 bytes into a region of size 16 overflows the destination
> [-Werror=stringop-overflow=]
> lto1: all warnings being treated as errors
> lto-wrapper: fatal error: make returned 2 exit status
> collect2: error: ld returned 1 exit status
> ```
> Root cause
> All affected sites use one of these patterns where chained `operator+` calls
> on temporary `std::string` objects trigger GCC 14's LTO false positive:
> ```cpp
> // Pattern 1 — empty string() seed
> return string() + "import java.util.Hashtable;\n" + "import
> java.util.Vector;\n"
> + "import java.util.Enumeration;\n\n";
> // Pattern 2 — non-empty literal seed + THRIFT_VERSION variable
> return std::string("//\n") + "// Autogenerated by Thrift Compiler (" +
> THRIFT_VERSION + ")\n"
> + "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE
> DOING\n"
> + "//\n";
> ```
> Proposed fix
> *{*}For pure-literal returns{*}* (no `THRIFT_VERSION`): replace with adjacent
> string literal concatenation, which is resolved at compile time and produces
> no temporary objects:
> ```cpp
> // t_javame_generator.cc java_type_imports() — before
> return string() + "import java.util.Hashtable;\n" + "import
> java.util.Vector;\n"
> + "import java.util.Enumeration;\n\n";
> // after
> return "import java.util.Hashtable;\n"
> "import java.util.Vector;\n"
> "import java.util.Enumeration;\n\n";
> ```
> *{*}For returns that embed `THRIFT_VERSION`{*}*: use adjacent literals up to
> the variable insertion point, so that only one `operator+` call with a
> correctly-sized left operand remains:
> ```cpp
> // t_js_generator.cc autogen_comment() — before
> return std::string("//\n") + "// Autogenerated by Thrift Compiler (" +
> THRIFT_VERSION + ")\n"
> + "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE
> DOING\n"
> + "//\n";
> // after
> return "//\n"
> "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
> "//\n"
> "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
> "//\n";
> ```
> This pattern is safe: adjacent string literals are concatenated by the
> compiler before `operator+` is evaluated, so the left operand of the first
> `+` is always a correctly-sized compile-time constant.
> A workaround for downstream packagers is to add `-Wno-stringop-overflow
> -Wno-stringop-overread` to `CXXFLAGS` at build time, but fixing the source is
> the correct long-term solution.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)