TL;DR
A production bug in a C++ payment service produced responses that claimed both error and success. The root cause was reading uninitialized bool fields in a struct; changing the declaration to value-initialize the object or adding defaults prevents the problem and tools like clang-tidy and sanitizers can help detect it.
What happened
A bug report showed an HTTP endpoint returning a response that indicated both error=true and succeeded=true despite the code having only one assignment site for each field. The Response type contained two bools and a std::string. Because the struct is non-trivial (it has a std::string), the compiler generated a default constructor and called it, which default-initialized the string but left the primitive bool fields with indeterminate values. Reading those uninitialized booleans produced undefined behavior that manifested in production. The author fixed the problem at the call site by changing the declaration to value-initialize the Response object (Response response{}), which zero-initializes the bools and default-initializes the string. Other fixes described include adding a default constructor or field initializers. The author also notes that compilers did not warn, clang-tidy can detect instances, and runtime sanitizers (ASan/UBSan) reported the undefined behavior.
Why it matters
- Undefined behavior in seemingly simple code can surface in production and produce incorrect responses.
- Structs that mix primitive and non-trivial members can leave primitive fields indeterminate unless explicitly initialized.
- Compilers may not emit warnings for these cases; relying on syntax and initialization discipline is safer.
- Static and runtime analysis tools can catch these bugs but coverage and behavior vary by tool and configuration.
Key facts
- The affected product processed large volumes of payments (billion-euro scale mentioned in the article).
- Response contained: bool error; bool succeeded; std::string data; and only one place in the function set each boolean.
- Declaring Response response; in this context led to a compiler-generated default constructor being called, which did not initialize the primitive bool fields.
- Reading an indeterminate bool is undefined behavior; that allowed a response with both error and succeeded set to true to be produced.
- Fixes that were applied or recommended: use Response response{} to value-initialize, define a default constructor initializing fields, or give fields inline default values (e.g., bool error = false).
- The author recommends always zero-initializing local objects to avoid this class of bugs.
- clang (the compiler) did not warn about the issue with warnings enabled; clang-tidy flagged the pattern and has improved over time to report more locations.
- AddressSanitizer / UndefinedBehaviorSanitizer reported the invalid boolean load at runtime in tests, demonstrating runtime detection is effective.
What to watch next
- Audit structs that combine primitive types and non-trivial members; ensure primitives get explicit initializers.
- Prefer brace/value-initialization (Type var{};) for local objects when fields must start with a known value.
- Enable static analysis (clang-tidy) and runtime sanitizers (ASan/UBSan) in CI to catch uninitialized reads early.
- Whether cppcheck reliably detects this pattern in your toolchain is not confirmed in the source.
Quick glossary
- Undefined behavior: Program behavior that the language standard does not define; results can be unpredictable and may vary between compilers or runs.
- Default initialization: Initialization that occurs when an object is declared without an explicit initializer; rules differ by type and language version.
- Value initialization: Initialization form (e.g., T obj{};) that zero-initializes primitives and calls default constructors for non-trivial members.
- POD / Trivially default constructible: Types that the language treats as plain data (pre-C++11 POD) or that can be default constructed without custom logic; they may remain uninitialized with certain declarations.
- Address Sanitizer (ASan) / UBSan: Runtime tools that detect memory errors and certain undefined behaviors during program execution.
Reader FAQ
How did both error and succeeded become true?
Reading uninitialized bool fields in the Response object produced undefined behavior, which allowed both fields to appear true even though code only assigned each field in exclusive places.
What immediate fix stopped the bug?
Changing the local declaration to value-initialize the object (Response response{}) so the bools are zeroed and the string is default-initialized.
Would adding default values in the struct help?
Yes. Giving fields inline initializers (e.g., bool error = false) ensures the generated default constructor initializes them.
Did the compiler warn about this?
Not confirmed in the source that the compiler emitted a warning; the author stated clang did not catch it but clang-tidy reported the issue and sanitizers detected it at runtime.
⏴ Back to all articles Published on 2025-12-27 The production bug that made me care about undefined behavior C++ Undefined behavior Bug Table of contents The bug report Investigating Just…
Sources
- The production bug that made me care about undefined behavior
- Is it undefined behavior to return an uninitialized, ultimately …
- Why would accessing uninitialized memory necessarily be …
- The production bug that made me care about undefined …
Related posts
- Static memory allocation in Zig: building a Redis-compatible kv server
- How to adjust what online platforms show you: tuning algorithms
- Experimental Carnegie Mellon camera lens can focus every object at once