name: '🧵 ThreadSanitizer' # Reusable workflow: builds the channel stress suite with ThreadSanitizer and # runs it. This is the dynamic half of verifying the lock-free SPSC Channel # (the static half is the CDSChecker model-check harness in verify/). # # Triggering and path filtering are owned by ci.yaml (the orchestrator), which # calls this only when code changed. workflow_dispatch is kept for manual runs. # # Runs in the prebuilt builder image (gcc:14), which already ships libtsan — no # package installs at job time. on: workflow_call: workflow_dispatch: jobs: tsan: runs-on: linux/amd64 container: image: gitea.tourolle.paris/dtourolle/kpnpp-builder:latest # This runner is Docker nested in an unprivileged LXC container, whose # kernel randomizes mmap addresses beyond the range TSan's fixed shadow # mapping expects, so TSan aborts at init with "unexpected memory # mapping". The fix is to disable ASLR per-process with `setarch -R` # (below), which needs the personality(2) syscall that Docker's default # seccomp profile blocks. seccomp=unconfined permits it. Verified on the # runner: setarch -R alone gets EPERM, seccomp alone still aborts, both # together run clean. Scoped to this job, which runs only our own tests. options: --security-opt seccomp=unconfined steps: - name: Checkout repository uses: actions/checkout@v4 with: path: tsan-${{ github.run_id }} - name: Cache FetchContent dependencies uses: actions/cache@v3 with: path: ~/.cmake/fetchcontent key: cmake-fetchcontent-${{ hashFiles('**/CMakeLists.txt') }} restore-keys: cmake-fetchcontent- - name: Configure (TSan) working-directory: tsan-${{ github.run_id }} run: | cmake -S . -B build \ -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DKPN_SANITIZER=thread \ -DKPN_BUILD_TESTS=ON \ -DKPN_BUILD_EXAMPLES=OFF \ -DKPN_BUILD_PYTHON=OFF \ -DFETCHCONTENT_BASE_DIR=$HOME/.cmake/fetchcontent - name: Build (TSan) working-directory: tsan-${{ github.run_id }} run: cmake --build build --parallel --target kpn_tests kpn_tests_stress - name: Run stress suite under TSan working-directory: tsan-${{ github.run_id }} # halt_on_error=1 makes the first detected race fail the job; the report # (with both stacks) is printed to the log. second_deadlock_stack gives # the full picture for lock-order issues. env: TSAN_OPTIONS: "halt_on_error=1 second_deadlock_stack=1" # setarch -R disables ASLR for this process; see the container comment. run: setarch -R ./build/tests/kpn_tests_stress - name: Run unit tests under TSan working-directory: tsan-${{ github.run_id }} env: TSAN_OPTIONS: "halt_on_error=1 second_deadlock_stack=1" run: setarch -R ./build/tests/kpn_tests - name: Cleanup if: always() run: rm -rf tsan-${{ github.run_id }}