From 594366a4824d7e65485033ca1002d8635ff6fbf9 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 28 May 2019 18:25:48 +0200 Subject: [PATCH 1/3] Fix os.mprotect signature --- std/os.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/os.zig b/std/os.zig index be2be92f7..14f205c1a 100644 --- a/std/os.zig +++ b/std/os.zig @@ -1889,14 +1889,14 @@ pub const MProtectError = error{ }; /// `memory.len` must be page-aligned. -pub fn mprotect(memory: [*]align(mem.page_size) u8, protection: u32) MProtectError!void { +pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void { assert(mem.isAligned(memory.len, mem.page_size)); switch (errno(system.mprotect(memory.ptr, memory.len, protection))) { 0 => return, EINVAL => unreachable, EACCES => return error.AccessDenied, ENOMEM => return error.OutOfMemory, - else => return unexpectedErrno(err), + else => |err| return unexpectedErrno(err), } } From 381f845287c8aef25a5351be81dfdfad055f0785 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 28 May 2019 18:26:38 +0200 Subject: [PATCH 2/3] Add a guard page for each thread --- std/thread.zig | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/std/thread.zig b/std/thread.zig index 49b2decb0..afed605b3 100644 --- a/std/thread.zig +++ b/std/thread.zig @@ -223,15 +223,17 @@ pub const Thread = struct { } }; - const MAP_GROWSDOWN = if (os.linux.is_the_target) os.linux.MAP_GROWSDOWN else 0; - + var guard_end_offset: usize = undefined; var stack_end_offset: usize = undefined; var thread_start_offset: usize = undefined; var context_start_offset: usize = undefined; var tls_start_offset: usize = undefined; const mmap_len = blk: { - // First in memory will be the stack, which grows downwards. - var l: usize = mem.alignForward(default_stack_size, mem.page_size); + var l: usize = mem.page_size; + // Allocate a guard page right after the end of the stack region + guard_end_offset = l; + // The stack itself, which grows downwards. + l = mem.alignForward(l + default_stack_size, mem.page_size); stack_end_offset = l; // Above the stack, so that it can be in the same mmap call, put the Thread object. l = mem.alignForward(l, @alignOf(Thread)); @@ -253,20 +255,33 @@ pub const Thread = struct { } break :blk l; }; + // Map the whole stack with no rw permissions to avoid committing the + // whole region right away const mmap_slice = os.mmap( null, mem.alignForward(mmap_len, mem.page_size), - os.PROT_READ | os.PROT_WRITE, - os.MAP_PRIVATE | os.MAP_ANONYMOUS | MAP_GROWSDOWN, + os.PROT_NONE, + os.MAP_PRIVATE | os.MAP_ANONYMOUS, -1, 0, ) catch |err| switch (err) { - error.MemoryMappingNotSupported => unreachable, // no file descriptor - error.AccessDenied => unreachable, // no file descriptor - error.PermissionDenied => unreachable, // no file descriptor + error.MemoryMappingNotSupported => unreachable, + error.AccessDenied => unreachable, + error.PermissionDenied => unreachable, else => |e| return e, }; errdefer os.munmap(mmap_slice); + + // Map everything but the guard page as rw + os.mprotect( + mmap_slice, + os.PROT_READ | os.PROT_WRITE, + ) catch |err| switch (err) { + error.OutOfMemory => unreachable, + error.AccessDenied => unreachable, + else => |e| return e, + }; + const mmap_addr = @ptrToInt(mmap_slice.ptr); const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); From c518b7b8bf71ad77adc96b5c9dabea277fe3ee43 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 May 2019 12:46:44 -0400 Subject: [PATCH 3/3] thread.spawn: bubble up mprotect error.OutOfMemory --- std/os.zig | 9 +++++++++ std/thread.zig | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/std/os.zig b/std/os.zig index 14f205c1a..6f1f0135c 100644 --- a/std/os.zig +++ b/std/os.zig @@ -1883,7 +1883,16 @@ pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void { } pub const MProtectError = error{ + /// The memory cannot be given the specified access. This can happen, for example, if you + /// mmap(2) a file to which you have read-only access, then ask mprotect() to mark it + /// PROT_WRITE. AccessDenied, + + /// Changing the protection of a memory region would result in the total number of map‐ + /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the + /// allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐ + /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐ + /// pings: two read/write mappings at each end and a read-only mapping in the middle.) OutOfMemory, Unexpected, }; diff --git a/std/thread.zig b/std/thread.zig index afed605b3..abf2f1cae 100644 --- a/std/thread.zig +++ b/std/thread.zig @@ -277,7 +277,6 @@ pub const Thread = struct { mmap_slice, os.PROT_READ | os.PROT_WRITE, ) catch |err| switch (err) { - error.OutOfMemory => unreachable, error.AccessDenied => unreachable, else => |e| return e, };