| ... | @@ -223,15 +223,17 @@ pub const Thread = struct { | ... | @@ -223,15 +223,17 @@ pub const Thread = struct { |
| 223 | } | 223 | } |
| 224 | }; | 224 | }; |
| 225 | | 225 | |
| 226 | const MAP_GROWSDOWN = if (os.linux.is_the_target) os.linux.MAP_GROWSDOWN else 0; | 226 | var guard_end_offset: usize = undefined; |
| 227 | | | |
| 228 | var stack_end_offset: usize = undefined; | 227 | var stack_end_offset: usize = undefined; |
| 229 | var thread_start_offset: usize = undefined; | 228 | var thread_start_offset: usize = undefined; |
| 230 | var context_start_offset: usize = undefined; | 229 | var context_start_offset: usize = undefined; |
| 231 | var tls_start_offset: usize = undefined; | 230 | var tls_start_offset: usize = undefined; |
| 232 | const mmap_len = blk: { | 231 | const mmap_len = blk: { |
| 233 | // First in memory will be the stack, which grows downwards. | 232 | var l: usize = mem.page_size; |
| 234 | var l: usize = mem.alignForward(default_stack_size, mem.page_size); | 233 | // Allocate a guard page right after the end of the stack region |
| | 234 | guard_end_offset = l; |
| | 235 | // The stack itself, which grows downwards. |
| | 236 | l = mem.alignForward(l + default_stack_size, mem.page_size); |
| 235 | stack_end_offset = l; | 237 | stack_end_offset = l; |
| 236 | // Above the stack, so that it can be in the same mmap call, put the Thread object. | 238 | // Above the stack, so that it can be in the same mmap call, put the Thread object. |
| 237 | l = mem.alignForward(l, @alignOf(Thread)); | 239 | l = mem.alignForward(l, @alignOf(Thread)); |
| ... | @@ -253,20 +255,33 @@ pub const Thread = struct { | ... | @@ -253,20 +255,33 @@ pub const Thread = struct { |
| 253 | } | 255 | } |
| 254 | break :blk l; | 256 | break :blk l; |
| 255 | }; | 257 | }; |
| | 258 | // Map the whole stack with no rw permissions to avoid committing the |
| | 259 | // whole region right away |
| 256 | const mmap_slice = os.mmap( | 260 | const mmap_slice = os.mmap( |
| 257 | null, | 261 | null, |
| 258 | mem.alignForward(mmap_len, mem.page_size), | 262 | mem.alignForward(mmap_len, mem.page_size), |
| 259 | os.PROT_READ | os.PROT_WRITE, | 263 | os.PROT_NONE, |
| 260 | os.MAP_PRIVATE | os.MAP_ANONYMOUS | MAP_GROWSDOWN, | 264 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| 261 | -1, | 265 | -1, |
| 262 | 0, | 266 | 0, |
| 263 | ) catch |err| switch (err) { | 267 | ) catch |err| switch (err) { |
| 264 | error.MemoryMappingNotSupported => unreachable, // no file descriptor | 268 | error.MemoryMappingNotSupported => unreachable, |
| 265 | error.AccessDenied => unreachable, // no file descriptor | 269 | error.AccessDenied => unreachable, |
| 266 | error.PermissionDenied => unreachable, // no file descriptor | 270 | error.PermissionDenied => unreachable, |
| 267 | else => |e| return e, | 271 | else => |e| return e, |
| 268 | }; | 272 | }; |
| 269 | errdefer os.munmap(mmap_slice); | 273 | errdefer os.munmap(mmap_slice); |
| | 274 | |
| | 275 | // Map everything but the guard page as rw |
| | 276 | os.mprotect( |
| | 277 | mmap_slice, |
| | 278 | os.PROT_READ | os.PROT_WRITE, |
| | 279 | ) catch |err| switch (err) { |
| | 280 | error.OutOfMemory => unreachable, |
| | 281 | error.AccessDenied => unreachable, |
| | 282 | else => |e| return e, |
| | 283 | }; |
| | 284 | |
| 270 | const mmap_addr = @ptrToInt(mmap_slice.ptr); | 285 | const mmap_addr = @ptrToInt(mmap_slice.ptr); |
| 271 | | 286 | |
| 272 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); | 287 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); |