Commit bd8d6a8342

m <m@pop-os.localdomain>
2022-02-11 15:28:36
std: validate frame-pointer address in stack walking
1 parent e1a5353
Changed files (5)
lib/std/c/linux.zig
@@ -30,6 +30,7 @@ pub const MAP = struct {
     /// Only used by libc to communicate failure.
     pub const FAILED = @intToPtr(*anyopaque, maxInt(usize));
 };
+pub const MSF = linux.MSF;
 pub const MMAP2_UNIT = linux.MMAP2_UNIT;
 pub const MSG = linux.MSG;
 pub const NAME_MAX = linux.NAME_MAX;
lib/std/os/linux.zig
@@ -406,6 +406,16 @@ pub fn mprotect(address: [*]const u8, length: usize, protection: usize) usize {
     return syscall3(.mprotect, @ptrToInt(address), length, protection);
 }
 
+pub const MSF = struct {
+    pub const ASYNC = 1;
+    pub const INVALIDATE = 2;
+    pub const SYNC = 4;
+};
+
+pub fn msync(address: [*]const u8, length: usize, flags: u32) usize {
+    return syscall3(.msync, @ptrToInt(address), length, flags);
+}
+
 pub fn munmap(address: [*]const u8, length: usize) usize {
     return syscall2(.munmap, @ptrToInt(address), length);
 }
lib/std/c.zig
@@ -123,6 +123,7 @@ pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize;
 pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize;
 pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: c.fd_t, offset: c.off_t) *anyopaque;
 pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;
+pub extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
 pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;
 pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int;
 pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, newpath: [*:0]const u8, flags: c_int) c_int;
lib/std/debug.zig
@@ -424,6 +424,28 @@ pub const StackIterator = struct {
         return address;
     }
 
+    fn isValidMemory(address: u64) bool {
+        if (native_os != .windows) {
+            var res = true;
+            const length = 2 * mem.page_size;
+            const aligned_address = address & ~@intCast(u64, (mem.page_size - 1));
+            const aligned_memory = @intToPtr([*]align(mem.page_size) u8, aligned_address)[0..length];
+
+            os.msync(aligned_memory, os.MSF.ASYNC) catch |err| {
+                switch (err) {
+                    os.MSyncError.UnmappedMemory => {
+                        res = false;
+                    },
+                    else => unreachable,
+                }
+            };
+            return res;
+        } else {
+            // TODO: Using windows memory API check if a page is mapped
+            return true;
+        }
+    }
+
     fn next_internal(self: *StackIterator) ?usize {
         const fp = if (comptime native_arch.isSPARC())
             // On SPARC the offset is positive. (!)
@@ -432,7 +454,7 @@ pub const StackIterator = struct {
             math.sub(usize, self.fp, fp_offset) catch return null;
 
         // Sanity check.
-        if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)))
+        if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)) or !isValidMemory(fp))
             return null;
 
         const new_fp = math.add(usize, @intToPtr(*const usize, fp).*, fp_bias) catch return null;
lib/std/os.zig
@@ -88,6 +88,7 @@ pub const Kevent = system.Kevent;
 pub const LOCK = system.LOCK;
 pub const MADV = system.MADV;
 pub const MAP = system.MAP;
+pub const MSF = system.MSF;
 pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
 pub const MMAP2_UNIT = system.MMAP2_UNIT;
 pub const MSG = system.MSG;
@@ -4016,6 +4017,19 @@ pub fn munmap(memory: []align(mem.page_size) const u8) void {
     }
 }
 
+pub const MSyncError = error{
+    UnmappedMemory,
+} || UnexpectedError;
+
+pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
+    switch (errno(system.msync(memory.ptr, memory.len, flags))) {
+        .SUCCESS => return,
+        .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
+        .INVAL => unreachable, // Invalid parameters.
+        else => unreachable,
+    }
+}
+
 pub const AccessError = error{
     PermissionDenied,
     FileNotFound,