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 {
570570 first_address: ?usize,
571571 // Last known value of the frame pointer register.
572572 fp: usize,
573 ma: MemoryAccessor = MemoryAccessor.init,
573574
574575 // When DebugInfo and a register context is available, this iterator can unwind
575576 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer),
......@@ -616,16 +617,16 @@ pub const StackIterator = struct {
616617 }
617618 }
618619
619 pub fn deinit(self: *StackIterator) void {
620 if (have_ucontext and self.unwind_state != null) self.unwind_state.?.dwarf_context.deinit();
620 pub fn deinit(it: *StackIterator) void {
621 if (have_ucontext and it.unwind_state != null) it.unwind_state.?.dwarf_context.deinit();
621622 }
622623
623 pub fn getLastError(self: *StackIterator) ?struct {
624 pub fn getLastError(it: *StackIterator) ?struct {
624625 err: UnwindError,
625626 address: usize,
626627 } {
627628 if (!have_ucontext) return null;
628 if (self.unwind_state) |*unwind_state| {
629 if (it.unwind_state) |*unwind_state| {
629630 if (unwind_state.last_error) |err| {
630631 unwind_state.last_error = null;
631632 return .{
......@@ -662,14 +663,14 @@ pub const StackIterator = struct {
662663 else
663664 @sizeOf(usize);
664665
665 pub fn next(self: *StackIterator) ?usize {
666 var address = self.next_internal() orelse return null;
666 pub fn next(it: *StackIterator) ?usize {
667 var address = it.next_internal() orelse return null;
667668
668 if (self.first_address) |first_address| {
669 if (it.first_address) |first_address| {
669670 while (address != first_address) {
670 address = self.next_internal() orelse return null;
671 address = it.next_internal() orelse return null;
671672 }
672 self.first_address = null;
673 it.first_address = null;
673674 }
674675
675676 return address;
......@@ -718,8 +719,74 @@ pub const StackIterator = struct {
718719 }
719720 }
720721
721 fn next_unwind(self: *StackIterator) !usize {
722 const unwind_state = &self.unwind_state.?;
722 pub const MemoryAccessor = struct {
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.?;
723790 const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);
724791 switch (native_os) {
725792 .macos, .ios, .watchos, .tvos, .visionos => {
......@@ -741,13 +808,13 @@ pub const StackIterator = struct {
741808 } else return error.MissingDebugInfo;
742809 }
743810
744 fn next_internal(self: *StackIterator) ?usize {
811 fn next_internal(it: *StackIterator) ?usize {
745812 if (have_ucontext) {
746 if (self.unwind_state) |*unwind_state| {
813 if (it.unwind_state) |*unwind_state| {
747814 if (!unwind_state.failed) {
748815 if (unwind_state.dwarf_context.pc == 0) return null;
749 defer self.fp = unwind_state.dwarf_context.getFp() catch 0;
750 if (self.next_unwind()) |return_address| {
816 defer it.fp = unwind_state.dwarf_context.getFp() catch 0;
817 if (it.next_unwind()) |return_address| {
751818 return return_address;
752819 } else |err| {
753820 unwind_state.last_error = err;
......@@ -763,29 +830,24 @@ pub const StackIterator = struct {
763830
764831 const fp = if (comptime native_arch.isSPARC())
765832 // On SPARC the offset is positive. (!)
766 math.add(usize, self.fp, fp_offset) catch return null
833 math.add(usize, it.fp, fp_offset) catch return null
767834 else
768 math.sub(usize, self.fp, fp_offset) catch return null;
835 math.sub(usize, it.fp, fp_offset) catch return null;
769836
770837 // 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
772840 return null;
773841
774 const new_fp = math.add(usize, @as(*const usize, @ptrFromInt(fp)).*, fp_bias) catch return null;
775
776842 // Sanity check: the stack grows down thus all the parent frames must be
777843 // be at addresses that are greater (or equal) than the previous one.
778844 // A zero frame pointer often signals this is the last frame, that case
779845 // 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
781848 return null;
782849
783 const new_pc = @as(
784 *const usize,
785 @ptrFromInt(math.add(usize, fp, pc_offset) catch return null),
786 ).*;
787
788 self.fp = new_fp;
850 it.fp = new_fp;
789851
790852 return new_pc;
791853 }
lib/std/os/linux.zig+4-4
......@@ -1519,15 +1519,15 @@ pub fn setgroups(size: usize, list: [*]const gid_t) usize {
15191519}
15201520
15211521pub fn setsid() pid_t {
1522 return @as(pid_t, @bitCast(@as(u32, @truncate(syscall0(.setsid)))));
1522 return @bitCast(@as(u32, @truncate(syscall0(.setsid))));
15231523}
15241524
15251525pub fn getpid() pid_t {
1526 return @as(pid_t, @bitCast(@as(u32, @truncate(syscall0(.getpid)))));
1526 return @bitCast(@as(u32, @truncate(syscall0(.getpid))));
15271527}
15281528
15291529pub fn gettid() pid_t {
1530 return @as(pid_t, @bitCast(@as(u32, @truncate(syscall0(.gettid)))));
1530 return @bitCast(@as(u32, @truncate(syscall0(.gettid))));
15311531}
15321532
15331533pub 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
21162116 );
21172117}
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 {
21202120 return syscall6(
21212121 .process_vm_readv,
21222122 @as(usize, @bitCast(@as(isize, pid))),