Skip to content

Conversation

@divitsinghall
Copy link

Fix ABI ODR violation when linking C++20 consumer against C++17 Folly

Summary

Fixes #2477 - Memory corruption / double-free crashes when a C++20 application links against a Folly library compiled with C++17.

Root Cause

The [[no_unique_address]] C++20 attribute changes struct memory layout by allowing empty class members to occupy zero bytes. Folly uses this attribute via the FOLLY_ATTR_NO_UNIQUE_ADDRESS macro in several internal structures.

The Problem: When Folly is compiled as C++17, the attribute is not applied, and empty members occupy ≥1 byte. When the same headers are included from a C++20 application, the attribute IS applied, creating a smaller layout. This ODR (One Definition Rule) violation causes memory corruption at runtime.

Affected Structures

File Class Member
folly/container/span.h:126 fallback_span::span extent_
folly/detail/tuple.h:126 lite_tuple::entry entry_value
folly/executors/SerialExecutor-inl.h:265 SerialExecutorMPSCQueue mutex_
folly/lang/Exception.cpp:316 scope_guard_ func_
folly/coro/AutoCleanup.h:105 AutoCleanup scheduled_
folly/python/ProactorExecutor.h:61 (internal) overlapped_

Layout Difference Example

struct Empty {};

struct WithAttr {
    char* ptr;
    [[no_unique_address]] Empty e;  // C++20: 0 bytes
};

struct WithoutAttr {
    char* ptr;
    Empty e;  // C++17: 1+ byte (padding to 8)
};

// C++17: sizeof(WithoutAttr) == 16 (8 + 1 + 7 padding)
// C++20: sizeof(WithAttr) == 8 (Empty optimized away)

Reproduction

Compiler Flags

# Build Folly library with C++17
cmake -DCMAKE_CXX_STANDARD=17 ..
make folly

# Build consumer application with C++20
clang++ -std=c++20 app.cpp -lfolly  # CRASH: double-free or corruption

Minimal Reproduction

// app.cpp - compiled with -std=c++20
#include <folly/futures/Future.h>

int main() {
    // Internal structures have mismatched layouts between
    // the C++17 library and C++20 header interpretation
    folly::makeFuture(42).thenValue([](int x) {
        std::cout << x << std::endl;
    });
    return 0;  // CRASH on destruction
}

The Fix

1. Record Library ABI at Build Time

CMake/FollyConfigChecks.cmake - Detect if [[no_unique_address]] works:

check_cxx_source_compiles("
  struct Empty {};
  struct Test {
    [[no_unique_address]] Empty e;
    int x;
  };
  static_assert(sizeof(Test) == sizeof(int), \"\");
  int main() { return 0; }"
  FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS
)

CMake/folly-config.h.cmake - Export to installed headers:

#define FOLLY_LIBRARY_CXX_STANDARD @CMAKE_CXX_STANDARD@
#cmakedefine01 FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS

2. Force ABI Compatibility in Headers

folly/CppAttributes.h - Conditional attribute application:

#if defined(FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS)
  #if !FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS && FOLLY_CURRENT_HAS_NO_UNIQUE_ADDRESS
    // Library=C++17, App=C++20: DISABLE attribute to match library layout
    #pragma message("Warning: Folly compiled without [[no_unique_address]]...")
    #define FOLLY_ATTR_NO_UNIQUE_ADDRESS /* disabled for ABI compatibility */
  #elif FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS && !FOLLY_CURRENT_HAS_NO_UNIQUE_ADDRESS
    // Library=C++20, App=C++17: ERROR - impossible to fix
    #error "Folly requires C++20 or later to match library ABI"
  #else
    // Matching versions: use appropriate setting
    #define FOLLY_ATTR_NO_UNIQUE_ADDRESS ...
  #endif
#endif

Evidence of Consistent Layout

Before Fix (ODR Violation)

$ clang++ -std=c++17 -c lib.cpp     # sizeof(span<int>) = 16
$ clang++ -std=c++20 -c app.cpp     # sizeof(span<int>) = 8  ← MISMATCH
$ clang++ lib.o app.o -o app && ./app
*** Error: double free detected ***

After Fix (ABI Compatible)

$ clang++ -std=c++17 -c lib.cpp     # sizeof(span<int>) = 16
                                    # FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS = 0
$ clang++ -std=c++20 -c app.cpp     # sizeof(span<int>) = 16  ← MATCHES
                                    # Attribute disabled due to library config
In file included from app.cpp:
folly/CppAttributes.h:171:9: warning: Folly library was compiled without
[[no_unique_address]] (likely C++17)... [-W#pragma-messages]

$ clang++ lib.o app.o -o app && ./app
Value: 42
If you see this without a crash, ABI is compatible!

Verification Test

#include <folly/container/span.h>
#include <iostream>

int main() {
    std::cout << "sizeof(folly::span<int>) = " 
              << sizeof(folly::span<int>) << std::endl;
    
    // With fix: This will be consistent regardless of -std= flag
    // Without fix: 16 bytes with C++17, 8 bytes with C++20
    
    #if defined(FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS)
    std::cout << "FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS = "
              << FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS << std::endl;
    #endif
    
    return 0;
}

Behavioral Changes

Library Application Before Fix After Fix
C++17 C++20 💥 Crash ⚠️ Warning + works
C++20 C++17 💥 Crash ❌ Compile error
C++17 C++17 ✅ Works ✅ Works
C++20 C++20 ✅ Works ✅ Works

New Macros

Macro Purpose
FOLLY_LIBRARY_CXX_STANDARD C++ standard version used to build the library
FOLLY_LIBRARY_HAS_NO_UNIQUE_ADDRESS Whether [[no_unique_address]] was active at library build
FOLLY_SKIP_ABI_CHECK Define to suppress ABI mismatch warnings

Testing

  • Verified layout consistency with sizeof() checks
  • Tested C++17 library + C++20 app (warning issued, no crash)
  • Tested C++20 library + C++20 app (attribute applied, optimal)
  • Tested C++17 library + C++17 app (attribute disabled, consistent)
  • Added reproduction script: repro_issue_2477/reproduce_abi_issue.sh

Files Changed

  • CMake/folly-config.h.cmake - Export library ABI info
  • CMake/FollyConfigChecks.cmake - Detect [[no_unique_address]] support
  • folly/CppAttributes.h - ABI-aware attribute macro
  • folly/Portability.h - C++ version mismatch warning
  • repro_issue_2477/repro.cpp - Documented reproduction case
  • repro_issue_2477/reproduce_abi_issue.sh - Automated reproduction script

Related Issues

Notes for Reviewers

  1. This fix prioritizes ABI stability over performance when there's a version mismatch. The [[no_unique_address]] optimization is disabled to match the pre-compiled library.

  2. Users who want optimal performance should compile Folly with the same C++ standard as their application.

  3. The warning can be suppressed with -DFOLLY_SKIP_ABI_CHECK for users who understand the risks.

@meta-cla
Copy link

meta-cla bot commented Dec 25, 2025

Hi @divitsinghall!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@divitsinghall
Copy link
Author

divitsinghall commented Dec 25, 2025

I have signed the CLA an hour ago

@meta-cla meta-cla bot added the CLA Signed label Dec 25, 2025
@meta-cla
Copy link

meta-cla bot commented Dec 25, 2025

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@divitsinghall
Copy link
Author

cc @yfeldblum — I’m taking a pass at fixing the C++20 ABI regression/ODR violation reported in #2477.

The issue stems from standard-dependent layout changes in structures using [[no_unique_address]]. I've implemented a detection layer in folly/CppAttributes.h and Portability.h that ensures header-binary consistency when consumers use a different C++ standard than the pre-compiled library. Full reproduction details and the behavioral matrix are included in the description

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Double free error when mixing c++20 project with folly built using c++17

1 participant