mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Publish unwind plans without a plain write to their pc
A new plan was copied into its slot as a whole struct, which wrote the slot's pc with a plain store while other threads' lookups read it atomically: a data race. The insert now copies the fields after pc and publishes pc only with the release store. The unwinder test also runs its threaded cases under ThreadSanitizer, which reports the race without this change.
This commit is contained in:
@@ -566,8 +566,10 @@ static __attribute__((noinline)) const ScrUnwindPlan *scr_unwind_insert(uint64_t
|
||||
scr_unwind_plan_count++;
|
||||
ScrUnwindPlan built;
|
||||
scr_unwind_build(&built, ra);
|
||||
built.pc = 0;
|
||||
*slot = built;
|
||||
/* Lookups read pc atomically, so the plan's other fields are written
|
||||
* first and pc only by the release store that publishes them. */
|
||||
memcpy((char *)slot + sizeof slot->pc, (const char *)&built + sizeof built.pc,
|
||||
sizeof *slot - sizeof slot->pc);
|
||||
__atomic_store_n(&slot->pc, ra, __ATOMIC_RELEASE);
|
||||
result = slot;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ test.runIf(process.platform === "linux" && process.arch === "x64")(
|
||||
// .eh_frame_hdr.
|
||||
const library = join(buildDir, "libtest_unwind_frames.so");
|
||||
await execFileAsync("clang", ["-O1", "-shared", "-fPIC", "-o", library, ...frames]);
|
||||
for (const build of ["plain", "asan", "shared"] as const) {
|
||||
for (const build of ["plain", "asan", "tsan", "shared"] as const) {
|
||||
const bin = join(buildDir, `test_unwind_${build}`);
|
||||
await execFileAsync("clang", [
|
||||
"-std=c11",
|
||||
@@ -28,6 +28,7 @@ test.runIf(process.platform === "linux" && process.arch === "x64")(
|
||||
"-D_GNU_SOURCE",
|
||||
"-DSCR_UNWIND_TESTING",
|
||||
...(build === "asan" ? ["-fsanitize=address"] : []),
|
||||
...(build === "tsan" ? ["-fsanitize=thread"] : []),
|
||||
"-pthread",
|
||||
"-o",
|
||||
bin,
|
||||
@@ -37,8 +38,14 @@ test.runIf(process.platform === "linux" && process.arch === "x64")(
|
||||
// The library's landing pads and the program share libgcc_s's unwinder.
|
||||
...(build === "shared" ? ["-shared-libgcc"] : ["-lgcc_eh"]),
|
||||
]);
|
||||
const env = { ...process.env, ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1" };
|
||||
for (const args of [[], ["threads"]]) {
|
||||
const env = {
|
||||
...process.env,
|
||||
ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1",
|
||||
TSAN_OPTIONS: "halt_on_error=1",
|
||||
};
|
||||
// ThreadSanitizer checks the threads' concurrent plan inserts and
|
||||
// lookups; the libgcc fallback cases run in the other builds.
|
||||
for (const args of build === "tsan" ? [["threads"]] : [[], ["threads"]]) {
|
||||
const run = await execFileAsync(bin, args, { env });
|
||||
expect(run.stdout).toBe("unwind checks passed\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user