[ 
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)

Reply via email to