Justin Caratzas created THRIFT-6076:
---------------------------------------

             Summary: 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


## 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