[Bug 277047] /usr/lib/libcompiler_rt.a(atomic.o) hidden visibility causes non-atomic atomics

From: <bugzilla-noreply_at_freebsd.org>
Date: Wed, 14 Feb 2024 08:21:13 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=277047

            Bug ID: 277047
           Summary: /usr/lib/libcompiler_rt.a(atomic.o) hidden visibility
                    causes non-atomic atomics
           Product: Base System
           Version: Unspecified
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: misc
          Assignee: bugs@FreeBSD.org
          Reporter: i@maskray.me

lib/libcompiler_rt/ builds llvm-project/compiler-rt including atomics (for arm
there is additional sys/arm/arm/stdatomic.c).
Many packages have a dependency on lib/libcompiler_rt. lib/libcompiler_rt is
installed to /usr/lib/libcompiler_rt.a. 
The symbols are hidden, which usually lead to benign ODR violations if both an
executable and its DT_NEEDED get a copy of libcompiler_rt.a. 

However, atomic.o has a global lock pool, and having different lock pools would
lead to non-synchronized atomic operations.

Prepare input files as attached at the end of this post.

% zsh a.sh
/tmp/b-9aaa82.o: reference to __atomic_compare_exchange
/usr/lib/libcompiler_rt.a(atomic.o): definition of __atomic_compare_exchange
/tmp/a-864004.o: reference to __atomic_compare_exchange
/usr/lib/libcompiler_rt.a(atomic.o): definition of __atomic_compare_exchange
9470401
/tmp/b-28139a.o: reference to __atomic_compare_exchange
/usr/lib/libgcc.a(atomic.o): definition of __atomic_compare_exchange
/tmp/a-1dceb9.o: reference to __atomic_compare_exchange
/usr/lib/libgcc.a(atomic.o): definition of __atomic_compare_exchange
9481088

The expected result is 10000000. libgcc.a has the same issue.
In practice it is extremely rare when two pieces of code operating on the same
atomic lives in different link units.

---

cat > a.cc <<'eof'
#include <cstdint>
#include <iostream>
#include <thread>
#include <vector>
using namespace std;

#define REP(i, n) for (int i = 0; i < (n); i++)

constexpr int kIterations = 500'000;
__int128_t g;

void loop_b();
void loop_a() {
  REP(i, kIterations) {
    for(;;) {
      auto e = __atomic_load_n(&g, __ATOMIC_RELAXED), desired = e+1;
      if (__atomic_compare_exchange_n(&g, &e, desired, 0, __ATOMIC_RELAXED,
__ATOMIC_RELAXED))
        break;
    }
  }
}

int main() {
  vector<thread> ts;
  REP(i, 10) ts.emplace_back(loop_a), ts.emplace_back(loop_b);
  for (auto &t : ts) t.join();
  cout << (int64_t)g << '\n';
}
eof
cat > a.sh <<'eof'
clang++ -O1 -fpic -Wno-atomic-alignment b.cc -shared -lcompiler_rt -o b.so
-Wl,-y,__atomic_compare_exchange
clang++ -O1 -Wno-atomic-alignment a.cc ./b.so -lcompiler_rt -pthread -o a
-Wl,-y,__atomic_compare_exchange
./a

clang++ -O1 -fpic -Wno-atomic-alignment b.cc -shared -o b.gcc.so
-Wl,-y,__atomic_compare_exchange
clang++ -O1 -Wno-atomic-alignment a.cc ./b.gcc.so -pthread -o a.gcc
-Wl,-y,__atomic_compare_exchange
./a.gcc
eof
cat > b.cc <<'eof'
#define REP(i, n) for (int i = 0; i < (n); i++)
extern __int128_t g;
constexpr int kIterations = 500'000;
void loop_b() {
  REP(i, kIterations) {
    for(;;) {
      auto e = __atomic_load_n(&g, __ATOMIC_RELAXED);
      auto desired = e + 1;
      if (__atomic_compare_exchange_n(&g, &e, desired, 0, __ATOMIC_RELAXED,
__ATOMIC_RELAXED))
        break;
    }
  }
}
eof

-- 
You are receiving this mail because:
You are the assignee for the bug.