Skip to content

tsan: intermittent lock-order-inversion in the full morph_tests run, observed once in six #578

Description

@Yaraslaut

Noticed while running the full test suite under clang-tsan as a pre-flight for #567's PR. Filed rather than folded in, per AGENTS.md — it is unrelated to that change and I could not reproduce it.

Verification status

Observed once, not reproduced. One lock-order-inversion warning in 1 of 6 full morph_tests runs under the clang-tsan preset, on branch core-structural-registration-surface (base cab05413), Arch Linux, clang 22.1.8, TSAN_OPTIONS unset (so cmake/tsan.supp was not active — that file suppresses race: entries only, none of which would match a lock-order report anyway).

I did not establish whether it is present on origin/master: reproducing it would need a TSan build of master and, at 1-in-6, several runs of each to say anything. I also did not capture the full report — the run that produced it was being read through grep -A 60 | head -70, and every subsequent run came back clean, so the rest of the stack is gone.

Real output

The whole of what was captured:

==================
WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) (pid=266676)
  Cycle in lock order graph: M0 (0x55f8a7f684e0) => M1 (0x7ffd74360a48) => M2 (0x7ffd74360cb0) => M0

  Mutex M1 acquired here while holding mutex M0 in thread T2719:
    #0 pthread_mutex_lock <null> (morph_tests+0x1c7b8b) (BuildId: b8e27091b137a43db2ec07f9cd046ef40d76714f)
    #1 __gthread_mutex_lock(pthread_mutex_t*) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/x86_64-pc-linux-gnu/bits/gthr-default.h:795:21 (morph_tests+0x26cef7) (BuildId: b8e27091b137a43db2ec07f9cd046ef40d76714f)
    #2 std::mutex::lock() /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/bits/std_mutex.h:116:17 (morph_tests+0x26cef7)

What little the addresses say: M0 (0x55f8…) is in the binary's static/heap range, while M1 and M2 (0x7ffd…) are stack addresses — so two of the three mutexes in the cycle are locals, which points at test fixtures or short-lived objects rather than three long-lived framework locks. No morph frame survives in the captured portion.

Run tallies on that build:

Selection Runs Warnings
Full suite 6 1
Full suite, exclude:[registration-surface] 3 0
[registration-surface] alone 5 0

The last two rows are why I do not think it belongs to the PR's new tests, but 3 and 5 runs against a 1-in-6 event is not evidence of anything, and I am not claiming it is.

Why it is worth a record anyway

A lock-order-inversion is TSan's potential deadlock report, not a materialised one — it fires when TSan observes two acquisition orders over the same mutexes, whether or not they ever raced. This repository already treats a sanitizer leg that cannot fail as worse than none (#541), and the sanitizer legs have been able to fail since ecc2f6c. A cycle that shows up one run in six will therefore eventually turn a CI leg red on a PR that did not cause it, and whoever gets that run will start from nothing unless there is a record.

What would change the verdict

  • Close it if a deliberate reproduction attempt — morph_tests under clang-tsan with --order rand, 50+ runs on master, with the full report captured to a file rather than grepped — produces no warning. At 1-in-6 that is a strong negative.
  • Re-scope it into a real bug as soon as any run captures the full report: the two stack-allocated mutexes should name a test or a framework type directly, at which point this stops being a sighting and becomes a lock-ordering defect with an owner.
  • If it reproduces only on the branch above, it is mine and belongs on core: give IBackend a registration surface whose threading contract is structural, not prose #567's PR instead.

Anyone reproducing: capture with ./build/clang-tsan/tests/morph_tests --order rand > log 2>&1 and read the file. Piping into grep/head is how this report was lost.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreSubsystem: corebugSomething isn't workingtriage: unverifiedPremise plausible but untested; blocked on an experiment

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions