The ThreadSanitizer job runs on Docker nested in an unprivileged LXC container, whose kernel randomizes mmap addresses beyond the range TSan's fixed shadow mapping expects. TSan aborted at init with "unexpected memory mapping" before any test ran. Disable ASLR per-process with `setarch -R`, which needs the personality(2) syscall that Docker's default seccomp profile blocks; seccomp=unconfined on the container permits it. Verified on the runner that both are required: setarch -R alone gets EPERM, seccomp alone still aborts, both together run clean. Scoped to the tsan job, which runs only our own test binaries. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
78 lines
3.1 KiB
YAML
78 lines
3.1 KiB
YAML
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<T>
|
|
# (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 }}
|