authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-08 18:44:40-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-09 13:52:00-08:00
log4d6d2922b814d9f6885cfeec7603e73e9a852417
tree6bced54da3c8843b2906b960923d7593b5e6b2ac
parent9aaecde63f4d507b11de236c9eb6fc547f256b0e

std: move memory locking and memory protection to process

and introduce type safety for posix.PROT (mmap, mprotect) progress towards #6600

23 files changed, 311 insertions(+), 268 deletions(-)

lib/fuzzer.zig+1-1
...@@ -1320,7 +1320,7 @@ pub const MemoryMappedList = struct {...@@ -1320,7 +1320,7 @@ pub const MemoryMappedList = struct {
1320 const ptr = try std.posix.mmap(1320 const ptr = try std.posix.mmap(
1321 null,1321 null,
1322 capacity,1322 capacity,
1323 std.posix.PROT.READ | std.posix.PROT.WRITE,1323 .{ .READ = true, .WRITE = true },
1324 .{ .TYPE = .SHARED },1324 .{ .TYPE = .SHARED },
1325 file.handle,1325 file.handle,
1326 0,1326 0,
lib/std/Build/Fuzz.zig+1-1
...@@ -422,7 +422,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -422,7 +422,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
422 const mapped_memory = std.posix.mmap(422 const mapped_memory = std.posix.mmap(
423 null,423 null,
424 file_size,424 file_size,
425 std.posix.PROT.READ,425 .{ .READ = true },
426 .{ .TYPE = .SHARED },426 .{ .TYPE = .SHARED },
427 coverage_file.handle,427 coverage_file.handle,
428 0,428 0,
lib/std/Thread.zig+9-9
...@@ -1535,7 +1535,7 @@ const LinuxThreadImpl = struct {...@@ -1535,7 +1535,7 @@ const LinuxThreadImpl = struct {
1535 const mapped = posix.mmap(1535 const mapped = posix.mmap(
1536 null,1536 null,
1537 map_bytes,1537 map_bytes,
1538 posix.PROT.NONE,1538 .{},
1539 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },1539 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
1540 -1,1540 -1,
1541 0,1541 0,
...@@ -1551,14 +1551,14 @@ const LinuxThreadImpl = struct {...@@ -1551,14 +1551,14 @@ const LinuxThreadImpl = struct {
1551 assert(mapped.len >= map_bytes);1551 assert(mapped.len >= map_bytes);
1552 errdefer posix.munmap(mapped);1552 errdefer posix.munmap(mapped);
15531553
1554 // map everything but the guard page as read/write1554 // Map everything but the guard page as read/write.
1555 posix.mprotect(1555 const guarded: []align(std.heap.page_size_min) u8 = @alignCast(mapped[guard_offset..]);
1556 @alignCast(mapped[guard_offset..]),1556 const protection: posix.PROT = .{ .READ = true, .WRITE = true };
1557 posix.PROT.READ | posix.PROT.WRITE,1557 switch (posix.errno(posix.system.mprotect(guarded.ptr, guarded.len, protection))) {
1558 ) catch |err| switch (err) {1558 .SUCCESS => {},
1559 error.AccessDenied => unreachable,1559 .NOMEM => return error.OutOfMemory,
1560 else => |e| return e,1560 else => |err| return posix.unexpectedErrno(err),
1561 };1561 }
15621562
1563 // Prepare the TLS segment and prepare a user_desc struct when needed on x861563 // Prepare the TLS segment and prepare a user_desc struct when needed on x86
1564 var tls_ptr = linux.tls.prepareArea(mapped[tls_offset..]);1564 var tls_ptr = linux.tls.prepareArea(mapped[tls_offset..]);
lib/std/c.zig+14-33
...@@ -1672,10 +1672,10 @@ pub const MCL = switch (native_os) {...@@ -1672,10 +1672,10 @@ pub const MCL = switch (native_os) {
1672 // https://github.com/NetBSD/src/blob/fd2741deca927c18e3ba15acdf78b8b14b2abe36/sys/sys/mman.h#L1791672 // https://github.com/NetBSD/src/blob/fd2741deca927c18e3ba15acdf78b8b14b2abe36/sys/sys/mman.h#L179
1673 // https://github.com/openbsd/src/blob/39404228f6d36c0ca4be5f04ab5385568ebd6aa3/sys/sys/mman.h#L1291673 // https://github.com/openbsd/src/blob/39404228f6d36c0ca4be5f04ab5385568ebd6aa3/sys/sys/mman.h#L129
1674 // https://github.com/illumos/illumos-gate/blob/5280477614f83fea20fc938729df6adb3e44340d/usr/src/uts/common/sys/mman.h#L3431674 // https://github.com/illumos/illumos-gate/blob/5280477614f83fea20fc938729df6adb3e44340d/usr/src/uts/common/sys/mman.h#L343
1675 .freebsd, .dragonfly, .netbsd, .openbsd, .illumos => packed struct(c_int) {1675 .freebsd, .dragonfly, .netbsd, .openbsd, .illumos => packed struct(u32) {
1676 CURRENT: bool = 0,1676 CURRENT: bool = false,
1677 FUTURE: bool = 0,1677 FUTURE: bool = false,
1678 _: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 2) = 0,1678 _: u30 = 0,
1679 },1679 },
1680 else => void,1680 else => void,
1681};1681};
...@@ -1887,32 +1887,13 @@ pub const PROT = switch (native_os) {...@@ -1887,32 +1887,13 @@ pub const PROT = switch (native_os) {
1887 .linux => linux.PROT,1887 .linux => linux.PROT,
1888 .emscripten => emscripten.PROT,1888 .emscripten => emscripten.PROT,
1889 // https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L28-L311889 // https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L28-L31
1890 .openbsd, .haiku, .dragonfly, .netbsd, .illumos, .freebsd, .windows, .serenity => struct {1890 .openbsd, .haiku, .dragonfly, .netbsd, .illumos, .freebsd, .windows, .serenity => packed struct(u32) {
1891 /// page can not be accessed1891 READ: bool = false,
1892 pub const NONE = 0x0;1892 WRITE: bool = false,
1893 /// page can be read1893 EXEC: bool = false,
1894 pub const READ = 0x1;1894 _: u29 = 0,
1895 /// page can be written
1896 pub const WRITE = 0x2;
1897 /// page can be executed
1898 pub const EXEC = 0x4;
1899 },
1900 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => struct {
1901 /// [MC2] no permissions
1902 pub const NONE: vm_prot_t = 0x00;
1903 /// [MC2] pages can be read
1904 pub const READ: vm_prot_t = 0x01;
1905 /// [MC2] pages can be written
1906 pub const WRITE: vm_prot_t = 0x02;
1907 /// [MC2] pages can be executed
1908 pub const EXEC: vm_prot_t = 0x04;
1909 /// When a caller finds that they cannot obtain write permission on a
1910 /// mapped entry, the following flag can be used. The entry will be
1911 /// made "needs copy" effectively copying the object (using COW),
1912 /// and write permission will be added to the maximum protections for
1913 /// the associated entry.
1914 pub const COPY: vm_prot_t = 0x10;
1915 },1895 },
1896 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => vm_prot_t,
1916 else => void,1897 else => void,
1917};1898};
19181899
...@@ -10349,7 +10330,7 @@ pub extern "c" fn getgrgid(gid: gid_t) ?*group;...@@ -10349,7 +10330,7 @@ pub extern "c" fn getgrgid(gid: gid_t) ?*group;
10349pub extern "c" fn getgrgid_r(gid: gid_t, grp: *group, buf: [*]u8, buflen: usize, result: *?*group) c_int;10330pub extern "c" fn getgrgid_r(gid: gid_t, grp: *group, buf: [*]u8, buflen: usize, result: *?*group) c_int;
10350pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int;10331pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int;
10351pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64;10332pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64;
10352pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;10333pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: PROT, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
10353pub extern "c" fn open64(path: [*:0]const u8, oflag: O, ...) c_int;10334pub extern "c" fn open64(path: [*:0]const u8, oflag: O, ...) c_int;
10354pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: O, ...) c_int;10335pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: O, ...) c_int;
10355pub extern "c" fn pread64(fd: fd_t, buf: [*]u8, nbyte: usize, offset: i64) isize;10336pub extern "c" fn pread64(fd: fd_t, buf: [*]u8, nbyte: usize, offset: i64) isize;
...@@ -10478,7 +10459,7 @@ pub const mlock = switch (native_os) {...@@ -10478,7 +10459,7 @@ pub const mlock = switch (native_os) {
10478};10459};
1047910460
10480pub const mlock2 = switch (native_os) {10461pub const mlock2 = switch (native_os) {
10481 linux => private.mlock2,10462 .linux => private.mlock2,
10482 else => {},10463 else => {},
10483};10464};
1048410465
...@@ -10667,10 +10648,10 @@ pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) i...@@ -10667,10 +10648,10 @@ pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) i
10667pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: off_t) isize;10648pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: off_t) isize;
10668pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;10649pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;
10669pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: off_t) isize;10650pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: off_t) isize;
10670pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: MAP, fd: fd_t, offset: off_t) *anyopaque;10651pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: PROT, flags: MAP, fd: fd_t, offset: off_t) *anyopaque;
10671pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;10652pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;
10672pub extern "c" fn mremap(addr: ?*align(page_size) const anyopaque, old_len: usize, new_len: usize, flags: MREMAP, ...) *anyopaque;10653pub extern "c" fn mremap(addr: ?*align(page_size) const anyopaque, old_len: usize, new_len: usize, flags: MREMAP, ...) *anyopaque;
10673pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;10654pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: PROT) c_int;
10674pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8) c_int;10655pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8) c_int;
10675pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_uint) c_int;10656pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_uint) c_int;
10676pub extern "c" fn unlink(path: [*:0]const u8) c_int;10657pub extern "c" fn unlink(path: [*:0]const u8) c_int;
lib/std/c/darwin.zig+1-1
...@@ -808,7 +808,7 @@ pub const task_vm_info = extern struct {...@@ -808,7 +808,7 @@ pub const task_vm_info = extern struct {
808808
809pub const task_vm_info_data_t = task_vm_info;809pub const task_vm_info_data_t = task_vm_info;
810810
811pub const vm_prot_t = c_int;811pub const vm_prot_t = std.macho.vm_prot_t;
812pub const boolean_t = c_int;812pub const boolean_t = c_int;
813813
814pub extern "c" fn mach_vm_protect(814pub extern "c" fn mach_vm_protect(
lib/std/debug/ElfFile.zig+1-1
...@@ -442,7 +442,7 @@ fn loadInner(...@@ -442,7 +442,7 @@ fn loadInner(
442 break :mapped std.posix.mmap(442 break :mapped std.posix.mmap(
443 null,443 null,
444 file_len,444 file_len,
445 std.posix.PROT.READ,445 .{ .READ = true },
446 .{ .TYPE = .SHARED },446 .{ .TYPE = .SHARED },
447 elf_file.handle,447 elf_file.handle,
448 0,448 0,
lib/std/debug/MachOFile.zig+1-1
...@@ -526,7 +526,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c...@@ -526,7 +526,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c
526 return posix.mmap(526 return posix.mmap(
527 null,527 null,
528 file_len,528 file_len,
529 posix.PROT.READ,529 .{ .READ = true },
530 .{ .TYPE = .SHARED },530 .{ .TYPE = .SHARED },
531 file.handle,531 file.handle,
532 0,532 0,
lib/std/debug/SelfInfo/MachO.zig+1-1
...@@ -631,7 +631,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c...@@ -631,7 +631,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c
631 return posix.mmap(631 return posix.mmap(
632 null,632 null,
633 file_len,633 file_len,
634 posix.PROT.READ,634 .{ .READ = true },
635 .{ .TYPE = .SHARED },635 .{ .TYPE = .SHARED },
636 file.handle,636 file.handle,
637 0,637 0,
lib/std/dynamic_library.zig+9-9
...@@ -238,7 +238,7 @@ pub const ElfDynLib = struct {...@@ -238,7 +238,7 @@ pub const ElfDynLib = struct {
238 const file_bytes = try posix.mmap(238 const file_bytes = try posix.mmap(
239 null,239 null,
240 mem.alignForward(usize, size, page_size),240 mem.alignForward(usize, size, page_size),
241 posix.PROT.READ,241 .{ .READ = true },
242 .{ .TYPE = .PRIVATE },242 .{ .TYPE = .PRIVATE },
243 file.handle,243 file.handle,
244 0,244 0,
...@@ -276,7 +276,7 @@ pub const ElfDynLib = struct {...@@ -276,7 +276,7 @@ pub const ElfDynLib = struct {
276 const all_loaded_mem = try posix.mmap(276 const all_loaded_mem = try posix.mmap(
277 null,277 null,
278 virt_addr_end,278 virt_addr_end,
279 posix.PROT.NONE,279 .{},
280 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },280 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
281 -1,281 -1,
282 0,282 0,
...@@ -302,7 +302,7 @@ pub const ElfDynLib = struct {...@@ -302,7 +302,7 @@ pub const ElfDynLib = struct {
302 const extra_bytes = (base + ph.p_vaddr) - aligned_addr;302 const extra_bytes = (base + ph.p_vaddr) - aligned_addr;
303 const extended_memsz = mem.alignForward(usize, ph.p_memsz + extra_bytes, page_size);303 const extended_memsz = mem.alignForward(usize, ph.p_memsz + extra_bytes, page_size);
304 const ptr = @as([*]align(std.heap.page_size_min) u8, @ptrFromInt(aligned_addr));304 const ptr = @as([*]align(std.heap.page_size_min) u8, @ptrFromInt(aligned_addr));
305 const prot = elfToMmapProt(ph.p_flags);305 const prot = elfToProt(ph.p_flags);
306 if ((ph.p_flags & elf.PF_W) == 0) {306 if ((ph.p_flags & elf.PF_W) == 0) {
307 // If it does not need write access, it can be mapped from the fd.307 // If it does not need write access, it can be mapped from the fd.
308 _ = try posix.mmap(308 _ = try posix.mmap(
...@@ -531,12 +531,12 @@ pub const ElfDynLib = struct {...@@ -531,12 +531,12 @@ pub const ElfDynLib = struct {
531 return null;531 return null;
532 }532 }
533533
534 fn elfToMmapProt(elf_prot: u64) u32 {534 fn elfToProt(elf_prot: u64) posix.PROT {
535 var result: u32 = posix.PROT.NONE;535 return .{
536 if ((elf_prot & elf.PF_R) != 0) result |= posix.PROT.READ;536 .READ = (elf_prot & elf.PF_R) != 0,
537 if ((elf_prot & elf.PF_W) != 0) result |= posix.PROT.WRITE;537 .WRITE = (elf_prot & elf.PF_W) != 0,
538 if ((elf_prot & elf.PF_X) != 0) result |= posix.PROT.EXEC;538 .EXEC = (elf_prot & elf.PF_X) != 0,
539 return result;539 };
540 }540 }
541};541};
542542
lib/std/heap/PageAllocator.zig+1-1
...@@ -96,7 +96,7 @@ pub fn map(n: usize, alignment: mem.Alignment) ?[*]u8 {...@@ -96,7 +96,7 @@ pub fn map(n: usize, alignment: mem.Alignment) ?[*]u8 {
96 const slice = posix.mmap(96 const slice = posix.mmap(
97 hint,97 hint,
98 overalloc_len,98 overalloc_len,
99 posix.PROT.READ | posix.PROT.WRITE,99 .{ .READ = true, .WRITE = true },
100 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },100 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
101 -1,101 -1,
102 0,102 0,
lib/std/macho.zig+16-4
...@@ -9,7 +9,19 @@ const Allocator = mem.Allocator;...@@ -9,7 +9,19 @@ const Allocator = mem.Allocator;
99
10pub const cpu_type_t = c_int;10pub const cpu_type_t = c_int;
11pub const cpu_subtype_t = c_int;11pub const cpu_subtype_t = c_int;
12pub const vm_prot_t = c_int;12pub const vm_prot_t = packed struct(u32) {
13 READ: bool = false,
14 WRITE: bool = false,
15 EXEC: bool = false,
16 _: u1 = 0,
17 /// When a caller finds that they cannot obtain write permission on a
18 /// mapped entry, the following flag can be used. The entry will be
19 /// made "needs copy" effectively copying the object (using COW),
20 /// and write permission will be added to the maximum protections for
21 /// the associated entry.
22 COPY: bool = false,
23 __: u27 = 0,
24};
1325
14pub const mach_header = extern struct {26pub const mach_header = extern struct {
15 magic: u32,27 magic: u32,
...@@ -648,10 +660,10 @@ pub const segment_command_64 = extern struct {...@@ -648,10 +660,10 @@ pub const segment_command_64 = extern struct {
648 filesize: u64 = 0,660 filesize: u64 = 0,
649661
650 /// maximum VM protection662 /// maximum VM protection
651 maxprot: vm_prot_t = PROT.NONE,663 maxprot: vm_prot_t = .{},
652664
653 /// initial VM protection665 /// initial VM protection
654 initprot: vm_prot_t = PROT.NONE,666 initprot: vm_prot_t = .{},
655667
656 /// number of sections in segment668 /// number of sections in segment
657 nsects: u32 = 0,669 nsects: u32 = 0,
...@@ -662,7 +674,7 @@ pub const segment_command_64 = extern struct {...@@ -662,7 +674,7 @@ pub const segment_command_64 = extern struct {
662 }674 }
663675
664 pub fn isWriteable(seg: segment_command_64) bool {676 pub fn isWriteable(seg: segment_command_64) bool {
665 return seg.initprot & PROT.WRITE != 0;677 return seg.initprot.write;
666 }678 }
667};679};
668680
lib/std/os/emscripten.zig+8-7
...@@ -331,13 +331,14 @@ pub const POLL = struct {...@@ -331,13 +331,14 @@ pub const POLL = struct {
331 pub const RDBAND = 0x080;331 pub const RDBAND = 0x080;
332};332};
333333
334pub const PROT = struct {334pub const PROT = packed struct(u32) {
335 pub const NONE = 0x0;335 READ: bool = false,
336 pub const READ = 0x1;336 WRITE: bool = false,
337 pub const WRITE = 0x2;337 EXEC: bool = false,
338 pub const EXEC = 0x4;338 _: u21 = 0,
339 pub const GROWSDOWN = 0x01000000;339 GROWSDOWN: bool = false,
340 pub const GROWSUP = 0x02000000;340 GROWSUP: bool = false,
341 __: u6 = 0,
341};342};
342343
343pub const rlim_t = u64;344pub const rlim_t = u64;
lib/std/os/linux.zig+30-24
...@@ -986,13 +986,13 @@ pub fn pivot_root(new_root: [*:0]const u8, put_old: [*:0]const u8) usize {...@@ -986,13 +986,13 @@ pub fn pivot_root(new_root: [*:0]const u8, put_old: [*:0]const u8) usize {
986 return syscall2(.pivot_root, @intFromPtr(new_root), @intFromPtr(put_old));986 return syscall2(.pivot_root, @intFromPtr(new_root), @intFromPtr(put_old));
987}987}
988988
989pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, offset: i64) usize {989pub fn mmap(address: ?[*]u8, length: usize, prot: PROT, flags: MAP, fd: i32, offset: i64) usize {
990 if (@hasField(SYS, "mmap2")) {990 if (@hasField(SYS, "mmap2")) {
991 return syscall6(991 return syscall6(
992 .mmap2,992 .mmap2,
993 @intFromPtr(address),993 @intFromPtr(address),
994 length,994 length,
995 prot,995 @as(u32, @bitCast(prot)),
996 @as(u32, @bitCast(flags)),996 @as(u32, @bitCast(flags)),
997 @bitCast(@as(isize, fd)),997 @bitCast(@as(isize, fd)),
998 @truncate(@as(u64, @bitCast(offset)) / std.heap.pageSize()),998 @truncate(@as(u64, @bitCast(offset)) / std.heap.pageSize()),
...@@ -1005,7 +1005,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, of...@@ -1005,7 +1005,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, of
1005 @intFromPtr(&[_]usize{1005 @intFromPtr(&[_]usize{
1006 @intFromPtr(address),1006 @intFromPtr(address),
1007 length,1007 length,
1008 prot,1008 @as(u32, @bitCast(prot)),
1009 @as(u32, @bitCast(flags)),1009 @as(u32, @bitCast(flags)),
1010 @bitCast(@as(isize, fd)),1010 @bitCast(@as(isize, fd)),
1011 @as(u64, @bitCast(offset)),1011 @as(u64, @bitCast(offset)),
...@@ -1014,7 +1014,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, of...@@ -1014,7 +1014,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, of
1014 .mmap,1014 .mmap,
1015 @intFromPtr(address),1015 @intFromPtr(address),
1016 length,1016 length,
1017 prot,1017 @as(u32, @bitCast(prot)),
1018 @as(u32, @bitCast(flags)),1018 @as(u32, @bitCast(flags)),
1019 @bitCast(@as(isize, fd)),1019 @bitCast(@as(isize, fd)),
1020 @as(u64, @bitCast(offset)),1020 @as(u64, @bitCast(offset)),
...@@ -1022,8 +1022,8 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, of...@@ -1022,8 +1022,8 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, of
1022 }1022 }
1023}1023}
10241024
1025pub fn mprotect(address: [*]const u8, length: usize, protection: usize) usize {1025pub fn mprotect(address: [*]const u8, length: usize, protection: PROT) usize {
1026 return syscall3(.mprotect, @intFromPtr(address), length, protection);1026 return syscall3(.mprotect, @intFromPtr(address), length, @as(u32, @bitCast(protection)));
1027}1027}
10281028
1029pub fn mremap(old_addr: ?[*]const u8, old_len: usize, new_len: usize, flags: MREMAP, new_addr: ?[*]const u8) usize {1029pub fn mremap(old_addr: ?[*]const u8, old_len: usize, new_len: usize, flags: MREMAP, new_addr: ?[*]const u8) usize {
...@@ -3616,24 +3616,30 @@ pub const FUTEX2_FLAGS = packed struct(u32) {...@@ -3616,24 +3616,30 @@ pub const FUTEX2_FLAGS = packed struct(u32) {
3616 _undefined: u24 = 0,3616 _undefined: u24 = 0,
3617};3617};
36183618
3619pub const PROT = struct {3619pub const PROT = switch (native_arch) {
3620 /// page can not be accessed3620 .mips, .mipsel, .mips64, .mips64el, .xtensa, .xtensaeb => packed struct(u32) {
3621 pub const NONE = 0x0;3621 READ: bool = false,
3622 /// page can be read3622 WRITE: bool = false,
3623 pub const READ = 0x1;3623 EXEC: bool = false,
3624 /// page can be written3624 _: u1 = 0,
3625 pub const WRITE = 0x2;3625 /// Page may be used for atomic ops.
3626 /// page can be executed3626 SEM: bool = false,
3627 pub const EXEC = 0x4;3627 __: u19 = 0,
3628 /// page may be used for atomic ops3628 GROWSDOWN: bool = false,
3629 pub const SEM = switch (native_arch) {3629 GROWSUP: bool = false,
3630 .mips, .mipsel, .mips64, .mips64el, .xtensa, .xtensaeb => 0x10,3630 ___: u6 = 0,
3631 else => 0x8,3631 },
3632 };3632 else => packed struct(u32) {
3633 /// mprotect flag: extend change to start of growsdown vma3633 READ: bool = false,
3634 pub const GROWSDOWN = 0x01000000;3634 WRITE: bool = false,
3635 /// mprotect flag: extend change to end of growsup vma3635 EXEC: bool = false,
3636 pub const GROWSUP = 0x02000000;3636 /// Page may be used for atomic ops.
3637 SEM: bool = false,
3638 __: u20 = 0,
3639 GROWSDOWN: bool = false,
3640 GROWSUP: bool = false,
3641 ___: u6 = 0,
3642 },
3637};3643};
36383644
3639pub const FD_CLOEXEC = 1;3645pub const FD_CLOEXEC = 1;
lib/std/os/linux/IoUring.zig+3-3
...@@ -1526,7 +1526,7 @@ pub const SubmissionQueue = struct {...@@ -1526,7 +1526,7 @@ pub const SubmissionQueue = struct {
1526 const mmap = try posix.mmap(1526 const mmap = try posix.mmap(
1527 null,1527 null,
1528 size,1528 size,
1529 posix.PROT.READ | posix.PROT.WRITE,1529 .{ .READ = true, .WRITE = true },
1530 .{ .TYPE = .SHARED, .POPULATE = true },1530 .{ .TYPE = .SHARED, .POPULATE = true },
1531 fd,1531 fd,
1532 linux.IORING_OFF_SQ_RING,1532 linux.IORING_OFF_SQ_RING,
...@@ -1540,7 +1540,7 @@ pub const SubmissionQueue = struct {...@@ -1540,7 +1540,7 @@ pub const SubmissionQueue = struct {
1540 const mmap_sqes = try posix.mmap(1540 const mmap_sqes = try posix.mmap(
1541 null,1541 null,
1542 size_sqes,1542 size_sqes,
1543 posix.PROT.READ | posix.PROT.WRITE,1543 .{ .READ = true, .WRITE = true },
1544 .{ .TYPE = .SHARED, .POPULATE = true },1544 .{ .TYPE = .SHARED, .POPULATE = true },
1545 fd,1545 fd,
1546 linux.IORING_OFF_SQES,1546 linux.IORING_OFF_SQES,
...@@ -1747,7 +1747,7 @@ pub fn setup_buf_ring(...@@ -1747,7 +1747,7 @@ pub fn setup_buf_ring(
1747 const mmap = try posix.mmap(1747 const mmap = try posix.mmap(
1748 null,1748 null,
1749 mmap_size,1749 mmap_size,
1750 posix.PROT.READ | posix.PROT.WRITE,1750 .{ .READ = true, .WRITE = true },
1751 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },1751 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
1752 -1,1752 -1,
1753 0,1753 0,
lib/std/os/linux/tls.zig+4-4
...@@ -568,7 +568,7 @@ pub fn initStatic(phdrs: []elf.Phdr) void {...@@ -568,7 +568,7 @@ pub fn initStatic(phdrs: []elf.Phdr) void {
568}568}
569569
570inline fn mmap_tls(length: usize) usize {570inline fn mmap_tls(length: usize) usize {
571 const prot = linux.PROT.READ | linux.PROT.WRITE;571 const prot: linux.PROT = .{ .READ = true, .WRITE = true };
572 const flags: linux.MAP = .{ .TYPE = .PRIVATE, .ANONYMOUS = true };572 const flags: linux.MAP = .{ .TYPE = .PRIVATE, .ANONYMOUS = true };
573573
574 if (@hasField(linux.SYS, "mmap2")) {574 if (@hasField(linux.SYS, "mmap2")) {
...@@ -576,7 +576,7 @@ inline fn mmap_tls(length: usize) usize {...@@ -576,7 +576,7 @@ inline fn mmap_tls(length: usize) usize {
576 .mmap2,576 .mmap2,
577 0,577 0,
578 length,578 length,
579 prot,579 @as(u32, @bitCast(prot)),
580 @as(u32, @bitCast(flags)),580 @as(u32, @bitCast(flags)),
581 @as(usize, @bitCast(@as(isize, -1))),581 @as(usize, @bitCast(@as(isize, -1))),
582 0,582 0,
...@@ -589,7 +589,7 @@ inline fn mmap_tls(length: usize) usize {...@@ -589,7 +589,7 @@ inline fn mmap_tls(length: usize) usize {
589 @intFromPtr(&[_]usize{589 @intFromPtr(&[_]usize{
590 0,590 0,
591 length,591 length,
592 prot,592 @as(u32, @bitCast(prot)),
593 @as(u32, @bitCast(flags)),593 @as(u32, @bitCast(flags)),
594 @as(usize, @bitCast(@as(isize, -1))),594 @as(usize, @bitCast(@as(isize, -1))),
595 0,595 0,
...@@ -598,7 +598,7 @@ inline fn mmap_tls(length: usize) usize {...@@ -598,7 +598,7 @@ inline fn mmap_tls(length: usize) usize {
598 .mmap,598 .mmap,
599 0,599 0,
600 length,600 length,
601 prot,601 @as(u32, @bitCast(prot)),
602 @as(u32, @bitCast(flags)),602 @as(u32, @bitCast(flags)),
603 @as(usize, @bitCast(@as(isize, -1))),603 @as(usize, @bitCast(@as(isize, -1))),
604 0,604 0,
lib/std/os/windows.zig-34
...@@ -3765,40 +3765,6 @@ pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_...@@ -3765,40 +3765,6 @@ pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_
3765 };3765 };
3766}3766}
37673767
3768pub const VirtualProtectError = error{
3769 InvalidAddress,
3770 Unexpected,
3771};
3772
3773pub fn VirtualProtect(lpAddress: ?LPVOID, dwSize: SIZE_T, flNewProtect: DWORD, lpflOldProtect: *DWORD) VirtualProtectError!void {
3774 // ntdll takes an extra level of indirection here
3775 var addr = lpAddress;
3776 var size = dwSize;
3777 switch (ntdll.NtProtectVirtualMemory(GetCurrentProcess(), &addr, &size, flNewProtect, lpflOldProtect)) {
3778 .SUCCESS => {},
3779 .INVALID_ADDRESS => return error.InvalidAddress,
3780 else => |st| return unexpectedStatus(st),
3781 }
3782}
3783
3784pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: DWORD) VirtualProtectError!DWORD {
3785 var old_prot: DWORD = undefined;
3786 var out_addr = addr;
3787 var out_size = size;
3788 switch (ntdll.NtProtectVirtualMemory(
3789 handle,
3790 &out_addr,
3791 &out_size,
3792 new_prot,
3793 &old_prot,
3794 )) {
3795 .SUCCESS => return old_prot,
3796 .INVALID_ADDRESS => return error.InvalidAddress,
3797 // TODO: map errors
3798 else => |rc| return unexpectedStatus(rc),
3799 }
3800}
3801
3802pub const SetConsoleTextAttributeError = error{Unexpected};3768pub const SetConsoleTextAttributeError = error{Unexpected};
38033769
3804pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {3770pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
lib/std/posix.zig+2-117
...@@ -934,128 +934,14 @@ pub fn fanotify_markZ(...@@ -934,128 +934,14 @@ pub fn fanotify_markZ(
934 }934 }
935}935}
936936
937pub const MlockError = error{
938 PermissionDenied,
939 LockedMemoryLimitExceeded,
940 SystemResources,
941} || UnexpectedError;
942
943pub fn mlock(memory: []align(page_size_min) const u8) MlockError!void {
944 if (@TypeOf(system.mlock) == void)
945 @compileError("mlock not supported on this OS");
946 return switch (errno(system.mlock(memory.ptr, memory.len))) {
947 .SUCCESS => {},
948 .INVAL => unreachable, // unaligned, negative, runs off end of addrspace
949 .PERM => error.PermissionDenied,
950 .NOMEM => error.LockedMemoryLimitExceeded,
951 .AGAIN => error.SystemResources,
952 else => |err| unexpectedErrno(err),
953 };
954}
955
956pub fn mlock2(memory: []align(page_size_min) const u8, flags: MLOCK) MlockError!void {
957 if (@TypeOf(system.mlock2) == void)
958 @compileError("mlock2 not supported on this OS");
959 return switch (errno(system.mlock2(memory.ptr, memory.len, flags))) {
960 .SUCCESS => {},
961 .INVAL => unreachable, // bad memory or bad flags
962 .PERM => error.PermissionDenied,
963 .NOMEM => error.LockedMemoryLimitExceeded,
964 .AGAIN => error.SystemResources,
965 else => |err| unexpectedErrno(err),
966 };
967}
968
969pub fn munlock(memory: []align(page_size_min) const u8) MlockError!void {
970 if (@TypeOf(system.munlock) == void)
971 @compileError("munlock not supported on this OS");
972 return switch (errno(system.munlock(memory.ptr, memory.len))) {
973 .SUCCESS => {},
974 .INVAL => unreachable, // unaligned or runs off end of addr space
975 .PERM => return error.PermissionDenied,
976 .NOMEM => return error.LockedMemoryLimitExceeded,
977 .AGAIN => return error.SystemResources,
978 else => |err| unexpectedErrno(err),
979 };
980}
981
982pub fn mlockall(flags: MCL) MlockError!void {
983 if (@TypeOf(system.mlockall) == void)
984 @compileError("mlockall not supported on this OS");
985 return switch (errno(system.mlockall(flags))) {
986 .SUCCESS => {},
987 .INVAL => unreachable, // bad flags
988 .PERM => error.PermissionDenied,
989 .NOMEM => error.LockedMemoryLimitExceeded,
990 .AGAIN => error.SystemResources,
991 else => |err| unexpectedErrno(err),
992 };
993}
994
995pub fn munlockall() MlockError!void {
996 if (@TypeOf(system.munlockall) == void)
997 @compileError("munlockall not supported on this OS");
998 return switch (errno(system.munlockall())) {
999 .SUCCESS => {},
1000 .PERM => error.PermissionDenied,
1001 .NOMEM => error.LockedMemoryLimitExceeded,
1002 .AGAIN => error.SystemResources,
1003 else => |err| unexpectedErrno(err),
1004 };
1005}
1006
1007pub const MProtectError = error{
1008 /// The memory cannot be given the specified access. This can happen, for example, if you
1009 /// mmap(2) a file to which you have read-only access, then ask mprotect() to mark it
1010 /// PROT_WRITE.
1011 AccessDenied,
1012
1013 /// Changing the protection of a memory region would result in the total number of map‐
1014 /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the
1015 /// allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐
1016 /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐
1017 /// pings: two read/write mappings at each end and a read-only mapping in the middle.)
1018 OutOfMemory,
1019} || UnexpectedError;
1020
1021pub fn mprotect(memory: []align(page_size_min) u8, protection: u32) MProtectError!void {
1022 if (native_os == .windows) {
1023 const win_prot: windows.DWORD = switch (@as(u3, @truncate(protection))) {
1024 0b000 => windows.PAGE_NOACCESS,
1025 0b001 => windows.PAGE_READONLY,
1026 0b010 => unreachable, // +w -r not allowed
1027 0b011 => windows.PAGE_READWRITE,
1028 0b100 => windows.PAGE_EXECUTE,
1029 0b101 => windows.PAGE_EXECUTE_READ,
1030 0b110 => unreachable, // +w -r not allowed
1031 0b111 => windows.PAGE_EXECUTE_READWRITE,
1032 };
1033 var old: windows.DWORD = undefined;
1034 windows.VirtualProtect(memory.ptr, memory.len, win_prot, &old) catch |err| switch (err) {
1035 error.InvalidAddress => return error.AccessDenied,
1036 error.Unexpected => return error.Unexpected,
1037 };
1038 } else {
1039 switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
1040 .SUCCESS => return,
1041 .INVAL => unreachable,
1042 .ACCES => return error.AccessDenied,
1043 .NOMEM => return error.OutOfMemory,
1044 else => |err| return unexpectedErrno(err),
1045 }
1046 }
1047}
1048
1049pub const MMapError = error{937pub const MMapError = error{
1050 /// The underlying filesystem of the specified file does not support memory mapping.938 /// The underlying filesystem of the specified file does not support memory mapping.
1051 MemoryMappingNotSupported,939 MemoryMappingNotSupported,
1052
1053 /// A file descriptor refers to a non-regular file. Or a file mapping was requested,940 /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
1054 /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested941 /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
1055 /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.942 /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
1056 /// Or `PROT_WRITE` is set, but the file is append-only.943 /// Or `PROT_WRITE` is set, but the file is append-only.
1057 AccessDenied,944 AccessDenied,
1058
1059 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on945 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
1060 /// a filesystem that was mounted no-exec.946 /// a filesystem that was mounted no-exec.
1061 PermissionDenied,947 PermissionDenied,
...@@ -1063,7 +949,6 @@ pub const MMapError = error{...@@ -1063,7 +949,6 @@ pub const MMapError = error{
1063 ProcessFdQuotaExceeded,949 ProcessFdQuotaExceeded,
1064 SystemFdQuotaExceeded,950 SystemFdQuotaExceeded,
1065 OutOfMemory,951 OutOfMemory,
1066
1067 /// Using FIXED_NOREPLACE flag and the process has already mapped memory at the given address952 /// Using FIXED_NOREPLACE flag and the process has already mapped memory at the given address
1068 MappingAlreadyExists,953 MappingAlreadyExists,
1069} || UnexpectedError;954} || UnexpectedError;
...@@ -1076,8 +961,8 @@ pub const MMapError = error{...@@ -1076,8 +961,8 @@ pub const MMapError = error{
1076pub fn mmap(961pub fn mmap(
1077 ptr: ?[*]align(page_size_min) u8,962 ptr: ?[*]align(page_size_min) u8,
1078 length: usize,963 length: usize,
1079 prot: u32,964 prot: PROT,
1080 flags: system.MAP,965 flags: MAP,
1081 fd: fd_t,966 fd: fd_t,
1082 offset: u64,967 offset: u64,
1083) MMapError![]align(page_size_min) u8 {968) MMapError![]align(page_size_min) u8 {
lib/std/posix/test.zig+3-3
...@@ -172,7 +172,7 @@ test "mmap" {...@@ -172,7 +172,7 @@ test "mmap" {
172 const data = try posix.mmap(172 const data = try posix.mmap(
173 null,173 null,
174 1234,174 1234,
175 posix.PROT.READ | posix.PROT.WRITE,175 .{ .READ = true, .WRITE = true },
176 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },176 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
177 -1,177 -1,
178 0,178 0,
...@@ -214,7 +214,7 @@ test "mmap" {...@@ -214,7 +214,7 @@ test "mmap" {
214 const data = try posix.mmap(214 const data = try posix.mmap(
215 null,215 null,
216 alloc_size,216 alloc_size,
217 posix.PROT.READ,217 .{ .READ = true },
218 .{ .TYPE = .PRIVATE },218 .{ .TYPE = .PRIVATE },
219 file.handle,219 file.handle,
220 0,220 0,
...@@ -239,7 +239,7 @@ test "mmap" {...@@ -239,7 +239,7 @@ test "mmap" {
239 const data = try posix.mmap(239 const data = try posix.mmap(
240 null,240 null,
241 alloc_size / 2,241 alloc_size / 2,
242 posix.PROT.READ,242 .{ .READ = true },
243 .{ .TYPE = .PRIVATE },243 .{ .TYPE = .PRIVATE },
244 file.handle,244 file.handle,
245 alloc_size / 2,245 alloc_size / 2,
lib/std/process.zig+192
...@@ -886,3 +886,195 @@ pub const SetCurrentDirError = error{...@@ -886,3 +886,195 @@ pub const SetCurrentDirError = error{
886pub fn setCurrentDir(io: Io, dir: Io.Dir) !void {886pub fn setCurrentDir(io: Io, dir: Io.Dir) !void {
887 return io.vtable.processSetCurrentDir(io.userdata, dir);887 return io.vtable.processSetCurrentDir(io.userdata, dir);
888}888}
889
890pub const LockMemoryError = error{
891 UnsupportedOperation,
892 PermissionDenied,
893 LockedMemoryLimitExceeded,
894 SystemResources,
895} || Io.UnexpectedError;
896
897pub const LockMemoryOptions = struct {
898 /// Lock pages that are currently resident and mark the entire range so
899 /// that the remaining nonresident pages are locked when they are populated
900 /// by a page fault.
901 on_fault: bool = false,
902};
903
904/// Request part of the calling process's virtual address space to be in RAM,
905/// preventing that memory from being paged to the swap area.
906///
907/// Corresponds to "mlock" or "mlock2" in libc.
908///
909/// See also:
910/// * unlockMemory
911pub fn lockMemory(memory: []align(std.heap.page_size_min) const u8, options: LockMemoryOptions) LockMemoryError!void {
912 if (native_os == .windows) {
913 // TODO call VirtualLock
914 }
915 if (!options.on_fault and @TypeOf(posix.system.mlock) != void) {
916 switch (posix.errno(posix.system.mlock(memory.ptr, memory.len))) {
917 .SUCCESS => return,
918 .INVAL => |err| return std.Io.Threaded.errnoBug(err), // unaligned, negative, runs off end of addrspace
919 .PERM => return error.PermissionDenied,
920 .NOMEM => return error.LockedMemoryLimitExceeded,
921 .AGAIN => return error.SystemResources,
922 else => |err| return posix.unexpectedErrno(err),
923 }
924 }
925 if (@TypeOf(posix.system.mlock2) != void) {
926 const flags: posix.MLOCK = .{ .ONFAULT = options.on_fault };
927 switch (posix.errno(posix.system.mlock2(memory.ptr, memory.len, flags))) {
928 .SUCCESS => return,
929 .INVAL => |err| return std.Io.Threaded.errnoBug(err), // unaligned, negative, runs off end of addrspace
930 .PERM => return error.PermissionDenied,
931 .NOMEM => return error.LockedMemoryLimitExceeded,
932 .AGAIN => return error.SystemResources,
933 else => |err| return posix.unexpectedErrno(err),
934 }
935 }
936 return error.UnsupportedOperation;
937}
938
939pub const UnlockMemoryError = error{
940 PermissionDenied,
941 OutOfMemory,
942 SystemResources,
943} || Io.UnexpectedError;
944
945/// Withdraw request for process's virtual address space to be in RAM.
946///
947/// Corresponds to "munlock" in libc.
948///
949/// See also:
950/// * `lockMemory`
951pub fn unlockMemory(memory: []align(std.heap.page_size_min) const u8) UnlockMemoryError!void {
952 if (@TypeOf(posix.system.munlock) == void) return;
953 switch (posix.errno(posix.system.munlock(memory.ptr, memory.len))) {
954 .SUCCESS => return,
955 .INVAL => |err| return std.Io.Threaded.errnoBug(err), // unaligned or runs off end of addr space
956 .PERM => return error.PermissionDenied,
957 .NOMEM => return error.OutOfMemory,
958 .AGAIN => return error.SystemResources,
959 else => |err| return posix.unexpectedErrno(err),
960 }
961}
962
963pub const LockMemoryAllOptions = struct {
964 current: bool = false,
965 future: bool = false,
966 /// Asserted to be used together with `current` or `future`, or both.
967 on_fault: bool = false,
968};
969
970pub fn lockMemoryAll(options: LockMemoryAllOptions) LockMemoryError!void {
971 if (@TypeOf(posix.system.mlockall) == void) return error.UnsupportedOperation;
972 var flags: posix.MCL = .{
973 .CURRENT = options.current,
974 .FUTURE = options.future,
975 };
976 if (options.on_fault) {
977 assert(options.current or options.future);
978 if (@hasField(posix.MCL, "ONFAULT")) {
979 flags.ONFAULT = true;
980 } else {
981 return error.UnsupportedOperation;
982 }
983 }
984 switch (posix.errno(posix.system.mlockall(flags))) {
985 .SUCCESS => return,
986 .INVAL => |err| return std.Io.Threaded.errnoBug(err),
987 .PERM => return error.PermissionDenied,
988 .NOMEM => return error.LockedMemoryLimitExceeded,
989 .AGAIN => return error.SystemResources,
990 else => |err| return posix.unexpectedErrno(err),
991 }
992}
993
994pub fn unlockMemoryAll() UnlockMemoryError!void {
995 if (@TypeOf(posix.system.munlockall) == void) return;
996 switch (posix.errno(posix.system.munlockall())) {
997 .SUCCESS => return,
998 .PERM => return error.PermissionDenied,
999 .NOMEM => return error.OutOfMemory,
1000 .AGAIN => return error.SystemResources,
1001 else => |err| return posix.unexpectedErrno(err),
1002 }
1003}
1004
1005pub const ProtectMemoryError = error{
1006 UnsupportedOperation,
1007 /// The memory cannot be given the specified access. This can happen, for
1008 /// example, if you memory map a file to which you have read-only access,
1009 /// then use `protectMemory` to mark it writable.
1010 AccessDenied,
1011 /// Changing the protection of a memory region would result in the total
1012 /// number of mappings with distinct attributes exceeding the allowed
1013 /// maximum.
1014 OutOfMemory,
1015} || Io.UnexpectedError;
1016
1017pub const ProtectMemoryOptions = packed struct(u3) {
1018 read: bool = false,
1019 write: bool = false,
1020 execute: bool = false,
1021};
1022
1023pub fn protectMemory(
1024 memory: []align(std.heap.page_size_min) u8,
1025 options: ProtectMemoryOptions,
1026) ProtectMemoryError!void {
1027 if (native_os == .windows) {
1028 var addr = memory.ptr; // ntdll takes an extra level of indirection here
1029 var size = memory.len; // ntdll takes an extra level of indirection here
1030 var old: windows.PAGE = undefined;
1031 const current_process: windows.HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))));
1032 const new: windows.PAGE = switch (@as(u3, @bitCast(options))) {
1033 0b000 => .{ .NOACCESS = true },
1034 0b001 => .{ .READONLY = true },
1035 0b010 => return error.AccessDenied, // +w -r not allowed
1036 0b011 => .{ .READWRITE = true },
1037 0b100 => .{ .EXECUTE = true },
1038 0b101 => .{ .EXECUTE_READ = true },
1039 0b110 => return error.AccessDenied, // +w -r not allowed
1040 0b111 => .{ .EXECUTE_READWRITE = true },
1041 };
1042 switch (windows.ntdll.NtProtectVirtualMemory(current_process, @ptrCast(&addr), &size, new, &old)) {
1043 .SUCCESS => return,
1044 .INVALID_ADDRESS => return error.AccessDenied,
1045 else => |st| return windows.unexpectedStatus(st),
1046 }
1047 } else if (posix.PROT != void) {
1048 const flags: posix.PROT = .{
1049 .READ = options.read,
1050 .WRITE = options.write,
1051 .EXEC = options.execute,
1052 };
1053 switch (posix.errno(posix.system.mprotect(memory.ptr, memory.len, flags))) {
1054 .SUCCESS => return,
1055 .INVAL => |err| return std.Io.Threaded.errnoBug(err),
1056 .ACCES => return error.AccessDenied,
1057 .NOMEM => return error.OutOfMemory,
1058 else => |err| return posix.unexpectedErrno(err),
1059 }
1060 }
1061 return error.UnsupportedOperation;
1062}
1063
1064test lockMemory {
1065 var page: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined;
1066 lockMemory(&page, .{}) catch return error.SkipZigTest;
1067 unlockMemory(&page) catch return error.SkipZigTest;
1068}
1069
1070test lockMemoryAll {
1071 lockMemoryAll(.{ .current = true }) catch return error.SkipZigTest;
1072 unlockMemoryAll() catch return error.SkipZigTest;
1073}
1074
1075test protectMemory {
1076 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; // TODO
1077 var page: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined;
1078 protectMemory(&page, .{}) catch return error.SkipZigTest;
1079 protectMemory(&page, .{ .read = true, .write = true }) catch return error.SkipZigTest;
1080}
src/link/MachO.zig+10-10
...@@ -1758,10 +1758,10 @@ fn initSyntheticSections(self: *MachO) !void {...@@ -1758,10 +1758,10 @@ fn initSyntheticSections(self: *MachO) !void {
1758}1758}
17591759
1760fn getSegmentProt(segname: []const u8) macho.vm_prot_t {1760fn getSegmentProt(segname: []const u8) macho.vm_prot_t {
1761 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;1761 if (mem.eql(u8, segname, "__PAGEZERO")) return .{};
1762 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;1762 if (mem.eql(u8, segname, "__TEXT")) return .{ .READ = true, .EXEC = true };
1763 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;1763 if (mem.eql(u8, segname, "__LINKEDIT")) return .{ .READ = true };
1764 return macho.PROT.READ | macho.PROT.WRITE;1764 return .{ .READ = true, .WRITE = true };
1765}1765}
17661766
1767fn getSegmentRank(segname: []const u8) u8 {1767fn getSegmentRank(segname: []const u8) u8 {
...@@ -3348,7 +3348,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3348,7 +3348,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3348 .filesize = filesize,3348 .filesize = filesize,
3349 .vmaddr = base_vmaddr + 0x4000000,3349 .vmaddr = base_vmaddr + 0x4000000,
3350 .vmsize = filesize,3350 .vmsize = filesize,
3351 .prot = macho.PROT.READ | macho.PROT.EXEC,3351 .prot = .{ .READ = true, .EXEC = true },
3352 });3352 });
3353 }3353 }
33543354
...@@ -3360,7 +3360,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3360,7 +3360,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3360 .filesize = filesize,3360 .filesize = filesize,
3361 .vmaddr = base_vmaddr + 0xc000000,3361 .vmaddr = base_vmaddr + 0xc000000,
3362 .vmsize = filesize,3362 .vmsize = filesize,
3363 .prot = macho.PROT.READ | macho.PROT.WRITE,3363 .prot = .{ .READ = true, .WRITE = true },
3364 });3364 });
3365 }3365 }
33663366
...@@ -3372,7 +3372,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3372,7 +3372,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3372 .filesize = filesize,3372 .filesize = filesize,
3373 .vmaddr = base_vmaddr + 0x10000000,3373 .vmaddr = base_vmaddr + 0x10000000,
3374 .vmsize = filesize,3374 .vmsize = filesize,
3375 .prot = macho.PROT.READ | macho.PROT.WRITE,3375 .prot = .{ .READ = true, .WRITE = true },
3376 });3376 });
3377 }3377 }
33783378
...@@ -3381,7 +3381,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {...@@ -3381,7 +3381,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
3381 self.zig_bss_seg_index = try self.addSegment("__BSS_ZIG", .{3381 self.zig_bss_seg_index = try self.addSegment("__BSS_ZIG", .{
3382 .vmaddr = base_vmaddr + 0x14000000,3382 .vmaddr = base_vmaddr + 0x14000000,
3383 .vmsize = memsize,3383 .vmsize = memsize,
3384 .prot = macho.PROT.READ | macho.PROT.WRITE,3384 .prot = .{ .READ = true, .WRITE = true },
3385 });3385 });
3386 }3386 }
33873387
...@@ -3711,7 +3711,7 @@ pub fn addSegment(self: *MachO, name: []const u8, opts: struct {...@@ -3711,7 +3711,7 @@ pub fn addSegment(self: *MachO, name: []const u8, opts: struct {
3711 vmsize: u64 = 0,3711 vmsize: u64 = 0,
3712 fileoff: u64 = 0,3712 fileoff: u64 = 0,
3713 filesize: u64 = 0,3713 filesize: u64 = 0,
3714 prot: macho.vm_prot_t = macho.PROT.NONE,3714 prot: macho.vm_prot_t = .{},
3715}) error{OutOfMemory}!u8 {3715}) error{OutOfMemory}!u8 {
3716 const gpa = self.base.comp.gpa;3716 const gpa = self.base.comp.gpa;
3717 const index = @as(u8, @intCast(self.segments.items.len));3717 const index = @as(u8, @intCast(self.segments.items.len));
...@@ -4903,7 +4903,7 @@ pub const MachTask = extern struct {...@@ -4903,7 +4903,7 @@ pub const MachTask = extern struct {
4903 try task.setCurrProtection(4903 try task.setCurrProtection(
4904 address,4904 address,
4905 buf.len,4905 buf.len,
4906 std.c.PROT.READ | std.c.PROT.WRITE | std.c.PROT.COPY,4906 .{ .READ = true, .WRITE = true, .COPY = true },
4907 );4907 );
4908 defer {4908 defer {
4909 task.setCurrProtection(address, buf.len, curr_prot) catch {};4909 task.setCurrProtection(address, buf.len, curr_prot) catch {};
src/link/MachO/DebugSymbols.zig+2-2
...@@ -94,8 +94,8 @@ pub fn initMetadata(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -94,8 +94,8 @@ pub fn initMetadata(self: *DebugSymbols, macho_file: *MachO) !void {
94 self.linkedit_segment_cmd_index = @intCast(self.segments.items.len);94 self.linkedit_segment_cmd_index = @intCast(self.segments.items.len);
95 try self.segments.append(self.allocator, .{95 try self.segments.append(self.allocator, .{
96 .segname = makeStaticString("__LINKEDIT"),96 .segname = makeStaticString("__LINKEDIT"),
97 .maxprot = macho.PROT.READ,97 .maxprot = .{ .READ = true },
98 .initprot = macho.PROT.READ,98 .initprot = .{ .READ = true },
99 .cmdsize = @sizeOf(macho.segment_command_64),99 .cmdsize = @sizeOf(macho.segment_command_64),
100 });100 });
101}101}
src/link/MachO/relocatable.zig+1-1
...@@ -539,7 +539,7 @@ fn createSegment(macho_file: *MachO) !void {...@@ -539,7 +539,7 @@ fn createSegment(macho_file: *MachO) !void {
539 const gpa = macho_file.base.comp.gpa;539 const gpa = macho_file.base.comp.gpa;
540540
541 // For relocatable, we only ever need a single segment so create it now.541 // For relocatable, we only ever need a single segment so create it now.
542 const prot: macho.vm_prot_t = macho.PROT.READ | macho.PROT.WRITE | macho.PROT.EXEC;542 const prot: macho.vm_prot_t = .{ .READ = true, .WRITE = true, .EXEC = true };
543 try macho_file.segments.append(gpa, .{543 try macho_file.segments.append(gpa, .{
544 .cmdsize = @sizeOf(macho.segment_command_64),544 .cmdsize = @sizeOf(macho.segment_command_64),
545 .segname = MachO.makeStaticString(""),545 .segname = MachO.makeStaticString(""),
src/link/MappedFile.zig+1-1
...@@ -1049,7 +1049,7 @@ pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void {...@@ -1049,7 +1049,7 @@ pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void {
1049 } else mf.contents = try std.posix.mmap(1049 } else mf.contents = try std.posix.mmap(
1050 null,1050 null,
1051 aligned_capacity,1051 aligned_capacity,
1052 std.posix.PROT.READ | std.posix.PROT.WRITE,1052 .{ .READ = true, .WRITE = true },
1053 .{ .TYPE = if (is_linux) .SHARED_VALIDATE else .SHARED },1053 .{ .TYPE = if (is_linux) .SHARED_VALIDATE else .SHARED },
1054 mf.file.handle,1054 mf.file.handle,
1055 0,1055 0,