mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
std.debug: fix segfault/panic race condition
closes #7859 closes #12207
This commit is contained in:
parent
2ee328995a
commit
95ac94b7ac
@ -334,6 +334,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
|
|||||||
resetSegfaultHandler();
|
resetSegfaultHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note there is similar logic in handleSegfaultPosix and handleSegfaultWindowsExtra.
|
||||||
nosuspend switch (panic_stage) {
|
nosuspend switch (panic_stage) {
|
||||||
0 => {
|
0 => {
|
||||||
panic_stage = 1;
|
panic_stage = 1;
|
||||||
@ -359,16 +360,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
|
|||||||
dumpCurrentStackTrace(first_trace_addr);
|
dumpCurrentStackTrace(first_trace_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panicking.fetchSub(1, .SeqCst) != 1) {
|
waitForOtherThreadToFinishPanicking();
|
||||||
// Another thread is panicking, wait for the last one to finish
|
|
||||||
// and call abort()
|
|
||||||
if (builtin.single_threaded) unreachable;
|
|
||||||
|
|
||||||
// Sleep forever without hammering the CPU
|
|
||||||
var futex = std.atomic.Atomic(u32).init(0);
|
|
||||||
while (true) std.Thread.Futex.wait(&futex, 0);
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
1 => {
|
1 => {
|
||||||
panic_stage = 2;
|
panic_stage = 2;
|
||||||
@ -387,6 +379,20 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
|
|||||||
os.abort();
|
os.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Must be called only after adding 1 to `panicking`. There are three callsites.
|
||||||
|
fn waitForOtherThreadToFinishPanicking() void {
|
||||||
|
if (panicking.fetchSub(1, .SeqCst) != 1) {
|
||||||
|
// Another thread is panicking, wait for the last one to finish
|
||||||
|
// and call abort()
|
||||||
|
if (builtin.single_threaded) unreachable;
|
||||||
|
|
||||||
|
// Sleep forever without hammering the CPU
|
||||||
|
var futex = std.atomic.Atomic(u32).init(0);
|
||||||
|
while (true) std.Thread.Futex.wait(&futex, 0);
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn writeStackTrace(
|
pub fn writeStackTrace(
|
||||||
stack_trace: std.builtin.StackTrace,
|
stack_trace: std.builtin.StackTrace,
|
||||||
out_stream: anytype,
|
out_stream: anytype,
|
||||||
@ -1971,17 +1977,41 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
|
|||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't use std.debug.print() as stderr_mutex may still be locked.
|
nosuspend switch (panic_stage) {
|
||||||
nosuspend {
|
0 => {
|
||||||
const stderr = io.getStdErr().writer();
|
panic_stage = 1;
|
||||||
_ = switch (sig) {
|
_ = panicking.fetchAdd(1, .SeqCst);
|
||||||
os.SIG.SEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
|
|
||||||
os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
|
{
|
||||||
os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
|
panic_mutex.lock();
|
||||||
os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
|
defer panic_mutex.unlock();
|
||||||
else => unreachable,
|
|
||||||
} catch os.abort();
|
dumpSegfaultInfoPosix(sig, addr, ctx_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
waitForOtherThreadToFinishPanicking();
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
// panic mutex already locked
|
||||||
|
dumpSegfaultInfoPosix(sig, addr, ctx_ptr);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// We cannot allow the signal handler to return because when it runs the original instruction
|
||||||
|
// again, the memory may be mapped and undefined behavior would occur rather than repeating
|
||||||
|
// the segfault. So we simply abort here.
|
||||||
|
os.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dumpSegfaultInfoPosix(sig: i32, addr: usize, ctx_ptr: ?*const anyopaque) void {
|
||||||
|
const stderr = io.getStdErr().writer();
|
||||||
|
_ = switch (sig) {
|
||||||
|
os.SIG.SEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
|
||||||
|
os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
|
||||||
|
os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
|
||||||
|
os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
|
||||||
|
else => unreachable,
|
||||||
|
} catch os.abort();
|
||||||
|
|
||||||
switch (native_arch) {
|
switch (native_arch) {
|
||||||
.x86 => {
|
.x86 => {
|
||||||
@ -2033,11 +2063,6 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
|
|||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot allow the signal handler to return because when it runs the original instruction
|
|
||||||
// again, the memory may be mapped and undefined behavior would occur rather than repeating
|
|
||||||
// the segfault. So we simply abort here.
|
|
||||||
os.abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long {
|
fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long {
|
||||||
@ -2050,27 +2075,36 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WIN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// zig won't let me use an anon enum here https://github.com/ziglang/zig/issues/3707
|
fn handleSegfaultWindowsExtra(
|
||||||
fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u8, comptime format: ?[]const u8) noreturn {
|
info: *windows.EXCEPTION_POINTERS,
|
||||||
|
msg: u8,
|
||||||
|
label: ?[]const u8,
|
||||||
|
) noreturn {
|
||||||
const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress);
|
const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress);
|
||||||
if (@hasDecl(windows, "CONTEXT")) {
|
if (@hasDecl(windows, "CONTEXT")) {
|
||||||
const regs = info.ContextRecord.getRegs();
|
nosuspend switch (panic_stage) {
|
||||||
// Don't use std.debug.print() as stderr_mutex may still be locked.
|
0 => {
|
||||||
nosuspend {
|
panic_stage = 1;
|
||||||
const stderr = io.getStdErr().writer();
|
_ = panicking.fetchAdd(1, .SeqCst);
|
||||||
_ = switch (msg) {
|
|
||||||
0 => stderr.print("{s}\n", .{format.?}),
|
|
||||||
1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
|
|
||||||
2 => stderr.print("Illegal instruction at address 0x{x}\n", .{regs.ip}),
|
|
||||||
else => unreachable,
|
|
||||||
} catch os.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
dumpStackTraceFromBase(regs.bp, regs.ip);
|
{
|
||||||
|
panic_mutex.lock();
|
||||||
|
defer panic_mutex.unlock();
|
||||||
|
|
||||||
|
dumpSegfaultInfoWindows(info, msg, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForOtherThreadToFinishPanicking();
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
// panic mutex already locked
|
||||||
|
dumpSegfaultInfoWindows(info, msg, label);
|
||||||
|
},
|
||||||
|
};
|
||||||
os.abort();
|
os.abort();
|
||||||
} else {
|
} else {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
0 => panicImpl(null, exception_address, format.?),
|
0 => panicImpl(null, exception_address, "{s}", label.?),
|
||||||
1 => {
|
1 => {
|
||||||
const format_item = "Segmentation fault at address 0x{x}";
|
const format_item = "Segmentation fault at address 0x{x}";
|
||||||
var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large
|
var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large
|
||||||
@ -2083,6 +2117,19 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void {
|
||||||
|
const regs = info.ContextRecord.getRegs();
|
||||||
|
const stderr = io.getStdErr().writer();
|
||||||
|
_ = switch (msg) {
|
||||||
|
0 => stderr.print("{s}\n", .{label.?}),
|
||||||
|
1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
|
||||||
|
2 => stderr.print("Illegal instruction at address 0x{x}\n", .{regs.ip}),
|
||||||
|
else => unreachable,
|
||||||
|
} catch os.abort();
|
||||||
|
|
||||||
|
dumpStackTraceFromBase(regs.bp, regs.ip);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dumpStackPointerAddr(prefix: []const u8) void {
|
pub fn dumpStackPointerAddr(prefix: []const u8) void {
|
||||||
const sp = asm (""
|
const sp = asm (""
|
||||||
: [argc] "={rsp}" (-> usize),
|
: [argc] "={rsp}" (-> usize),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user