aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-05-28 12:47:10 -0400
committerAndrew Kelley <andrew@ziglang.org>2019-05-28 12:47:10 -0400
commit568dc56232d8a1bf53495dd1c742ffae383ceaab (patch)
treecc5d15a0a272ac17b50ad98eb8a22fb3131049d9
parentd1b6f29d225bbedd0afb97fce64f5d042df4d9c6 (diff)
parentc518b7b8bf71ad77adc96b5c9dabea277fe3ee43 (diff)
downloadzig-568dc56232d8a1bf53495dd1c742ffae383ceaab.tar.gz
zig-568dc56232d8a1bf53495dd1c742ffae383ceaab.zip
Merge branch 'LemonBoy-guard-pages-in-threads'
-rw-r--r--std/os.zig13
-rw-r--r--std/thread.zig32
2 files changed, 34 insertions, 11 deletions
diff --git a/std/os.zig b/std/os.zig
index be2be92f7d..6f1f0135ce 100644
--- a/std/os.zig
+++ b/std/os.zig
@@ -1883,20 +1883,29 @@ 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,
};
/// `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),
}
}
diff --git a/std/thread.zig b/std/thread.zig
index 49b2decb09..abf2f1cae1 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,32 @@ 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.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));