authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-07-09 12:51:48-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-07-09 12:53:19-04:00
log1b34ae19beffcaa988de636c05264b3e9357a66f
tree04f148404568fc0c92c3e9a74c4e46c9f6237325
parent47846bc17c54d50886b702b5994c00ca8670c82b

debug: prevent segfaults on linux


2 files changed, 93 insertions(+), 31 deletions(-)

lib/std/debug.zig+89-27
...@@ -570,6 +570,7 @@ pub const StackIterator = struct {...@@ -570,6 +570,7 @@ pub const StackIterator = struct {
570 first_address: ?usize,570 first_address: ?usize,
571 // Last known value of the frame pointer register.571 // Last known value of the frame pointer register.
572 fp: usize,572 fp: usize,
573 ma: MemoryAccessor = MemoryAccessor.init,
573574
574 // When DebugInfo and a register context is available, this iterator can unwind575 // When DebugInfo and a register context is available, this iterator can unwind
575 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer),576 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer),
...@@ -616,16 +617,16 @@ pub const StackIterator = struct {...@@ -616,16 +617,16 @@ pub const StackIterator = struct {
616 }617 }
617 }618 }
618619
619 pub fn deinit(self: *StackIterator) void {620 pub fn deinit(it: *StackIterator) void {
620 if (have_ucontext and self.unwind_state != null) self.unwind_state.?.dwarf_context.deinit();621 if (have_ucontext and it.unwind_state != null) it.unwind_state.?.dwarf_context.deinit();
621 }622 }
622623
623 pub fn getLastError(self: *StackIterator) ?struct {624 pub fn getLastError(it: *StackIterator) ?struct {
624 err: UnwindError,625 err: UnwindError,
625 address: usize,626 address: usize,
626 } {627 } {
627 if (!have_ucontext) return null;628 if (!have_ucontext) return null;
628 if (self.unwind_state) |*unwind_state| {629 if (it.unwind_state) |*unwind_state| {
629 if (unwind_state.last_error) |err| {630 if (unwind_state.last_error) |err| {
630 unwind_state.last_error = null;631 unwind_state.last_error = null;
631 return .{632 return .{
...@@ -662,14 +663,14 @@ pub const StackIterator = struct {...@@ -662,14 +663,14 @@ pub const StackIterator = struct {
662 else663 else
663 @sizeOf(usize);664 @sizeOf(usize);
664665
665 pub fn next(self: *StackIterator) ?usize {666 pub fn next(it: *StackIterator) ?usize {
666 var address = self.next_internal() orelse return null;667 var address = it.next_internal() orelse return null;
667668
668 if (self.first_address) |first_address| {669 if (it.first_address) |first_address| {
669 while (address != first_address) {670 while (address != first_address) {
670 address = self.next_internal() orelse return null;671 address = it.next_internal() orelse return null;
671 }672 }
672 self.first_address = null;673 it.first_address = null;
673 }674 }
674675
675 return address;676 return address;
...@@ -718,8 +719,74 @@ pub const StackIterator = struct {...@@ -718,8 +719,74 @@ pub const StackIterator = struct {
718 }719 }
719 }720 }
720721
721 fn next_unwind(self: *StackIterator) !usize {722 pub const MemoryAccessor = struct {
722 const unwind_state = &self.unwind_state.?;723 var cached_pid: posix.pid_t = -1;
724
725 mem: switch (native_os) {
726 .linux => File,
727 else => void,
728 },
729
730 pub const init: MemoryAccessor = .{
731 .mem = switch (native_os) {
732 .linux => .{ .handle = -1 },
733 else => {},
734 },
735 };
736
737 fn read(ma: *MemoryAccessor, address: usize, buf: []u8) bool {
738 switch (native_os) {
739 .linux => while (true) switch (ma.mem.handle) {
740 -2 => break,
741 -1 => {
742 const linux = std.os.linux;
743 const pid = switch (@atomicLoad(posix.pid_t, &cached_pid, .monotonic)) {
744 -1 => pid: {
745 const pid = linux.getpid();
746 @atomicStore(posix.pid_t, &cached_pid, pid, .monotonic);
747 break :pid pid;
748 },
749 else => |pid| pid,
750 };
751 const bytes_read = linux.process_vm_readv(
752 pid,
753 &.{.{ .base = buf.ptr, .len = buf.len }},
754 &.{.{ .base = @ptrFromInt(address), .len = buf.len }},
755 0,
756 );
757 switch (linux.E.init(bytes_read)) {
758 .SUCCESS => return bytes_read == buf.len,
759 .FAULT => return false,
760 .INVAL, .PERM, .SRCH => unreachable, // own pid is always valid
761 .NOMEM, .NOSYS => {},
762 else => unreachable, // unexpected
763 }
764 var path_buf: [
765 std.fmt.count("/proc/{d}/mem", .{math.minInt(posix.pid_t)})
766 ]u8 = undefined;
767 const path = std.fmt.bufPrint(&path_buf, "/proc/{d}/mem", .{pid}) catch
768 unreachable;
769 ma.mem = std.fs.openFileAbsolute(path, .{}) catch {
770 ma.mem.handle = -2;
771 break;
772 };
773 },
774 else => return (ma.mem.pread(buf, address) catch return false) == buf.len,
775 },
776 else => {},
777 }
778 if (!isValidMemory(address)) return false;
779 @memcpy(buf, @as([*]const u8, @ptrFromInt(address)));
780 return true;
781 }
782 pub fn load(ma: *MemoryAccessor, comptime Type: type, address: usize) ?Type {
783 var result: Type = undefined;
784 return if (ma.read(address, std.mem.asBytes(&result))) result else null;
785 }
786 };
787
788 fn next_unwind(it: *StackIterator) !usize {
789 const unwind_state = &it.unwind_state.?;
723 const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);790 const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);
724 switch (native_os) {791 switch (native_os) {
725 .macos, .ios, .watchos, .tvos, .visionos => {792 .macos, .ios, .watchos, .tvos, .visionos => {
...@@ -741,13 +808,13 @@ pub const StackIterator = struct {...@@ -741,13 +808,13 @@ pub const StackIterator = struct {
741 } else return error.MissingDebugInfo;808 } else return error.MissingDebugInfo;
742 }809 }
743810
744 fn next_internal(self: *StackIterator) ?usize {811 fn next_internal(it: *StackIterator) ?usize {
745 if (have_ucontext) {812 if (have_ucontext) {
746 if (self.unwind_state) |*unwind_state| {813 if (it.unwind_state) |*unwind_state| {
747 if (!unwind_state.failed) {814 if (!unwind_state.failed) {
748 if (unwind_state.dwarf_context.pc == 0) return null;815 if (unwind_state.dwarf_context.pc == 0) return null;
749 defer self.fp = unwind_state.dwarf_context.getFp() catch 0;816 defer it.fp = unwind_state.dwarf_context.getFp() catch 0;
750 if (self.next_unwind()) |return_address| {817 if (it.next_unwind()) |return_address| {
751 return return_address;818 return return_address;
752 } else |err| {819 } else |err| {
753 unwind_state.last_error = err;820 unwind_state.last_error = err;
...@@ -763,29 +830,24 @@ pub const StackIterator = struct {...@@ -763,29 +830,24 @@ pub const StackIterator = struct {
763830
764 const fp = if (comptime native_arch.isSPARC())831 const fp = if (comptime native_arch.isSPARC())
765 // On SPARC the offset is positive. (!)832 // On SPARC the offset is positive. (!)
766 math.add(usize, self.fp, fp_offset) catch return null833 math.add(usize, it.fp, fp_offset) catch return null
767 else834 else
768 math.sub(usize, self.fp, fp_offset) catch return null;835 math.sub(usize, it.fp, fp_offset) catch return null;
769836
770 // Sanity check.837 // Sanity check.
771 if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)) or !isValidMemory(fp))838 if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) return null;
839 const new_fp = math.add(usize, it.ma.load(usize, fp) orelse return null, fp_bias) catch
772 return null;840 return null;
773841
774 const new_fp = math.add(usize, @as(*const usize, @ptrFromInt(fp)).*, fp_bias) catch return null;
775
776 // Sanity check: the stack grows down thus all the parent frames must be842 // Sanity check: the stack grows down thus all the parent frames must be
777 // be at addresses that are greater (or equal) than the previous one.843 // be at addresses that are greater (or equal) than the previous one.
778 // A zero frame pointer often signals this is the last frame, that case844 // A zero frame pointer often signals this is the last frame, that case
779 // is gracefully handled by the next call to next_internal.845 // is gracefully handled by the next call to next_internal.
780 if (new_fp != 0 and new_fp < self.fp)846 if (new_fp != 0 and new_fp < it.fp) return null;
847 const new_pc = it.ma.load(usize, math.add(usize, fp, pc_offset) catch return null) orelse
781 return null;848 return null;
782849
783 const new_pc = @as(850 it.fp = new_fp;
784 *const usize,
785 @ptrFromInt(math.add(usize, fp, pc_offset) catch return null),
786 ).*;
787
788 self.fp = new_fp;
789851
790 return new_pc;852 return new_pc;
791 }853 }
lib/std/os/linux.zig+4-4
...@@ -1519,15 +1519,15 @@ pub fn setgroups(size: usize, list: [*]const gid_t) usize {...@@ -1519,15 +1519,15 @@ pub fn setgroups(size: usize, list: [*]const gid_t) usize {
1519}1519}
15201520
1521pub fn setsid() pid_t {1521pub fn setsid() pid_t {
1522 return @as(pid_t, @bitCast(@as(u32, @truncate(syscall0(.setsid)))));1522 return @bitCast(@as(u32, @truncate(syscall0(.setsid))));
1523}1523}
15241524
1525pub fn getpid() pid_t {1525pub fn getpid() pid_t {
1526 return @as(pid_t, @bitCast(@as(u32, @truncate(syscall0(.getpid)))));1526 return @bitCast(@as(u32, @truncate(syscall0(.getpid))));
1527}1527}
15281528
1529pub fn gettid() pid_t {1529pub fn gettid() pid_t {
1530 return @as(pid_t, @bitCast(@as(u32, @truncate(syscall0(.gettid)))));1530 return @bitCast(@as(u32, @truncate(syscall0(.gettid))));
1531}1531}
15321532
1533pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) usize {1533pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) usize {
...@@ -2116,7 +2116,7 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u...@@ -2116,7 +2116,7 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u
2116 );2116 );
2117}2117}
21182118
2119pub fn process_vm_readv(pid: pid_t, local: []iovec, remote: []const iovec_const, flags: usize) usize {2119pub fn process_vm_readv(pid: pid_t, local: []const iovec, remote: []const iovec_const, flags: usize) usize {
2120 return syscall6(2120 return syscall6(
2121 .process_vm_readv,2121 .process_vm_readv,
2122 @as(usize, @bitCast(@as(isize, pid))),2122 @as(usize, @bitCast(@as(isize, pid))),