| author | |
| committer | |
| log | 53987c932c9d62cc9cdae3d523fb62756ce83ca9 |
| tree | 280817d390ef900cf590c40f47cca4230b354b0e |
| parent | 2b8dcc76eba92ad44bd88756218de8d2bd8a1c10 |
Everybody gets what they want!
* AT_RANDOM is completely ignored.
* On Linux, MADV_WIPEONFORK is used to provide fork safety.
* On pthread systems, `pthread_atfork` is used to provide fork safety.
* For systems that do not have the capability to provide fork safety,
the implementation falls back to calling getrandom() every time.
* If madvise is unavailable or returns an error, or pthread_atfork
fails for whatever reason, it falls back to calling getrandom() every
time.
* Applications may choose to opt-out of fork safety.
* Applications may choose to opt-in to unconditionally calling
getrandom() for every call to std.crypto.random.fillFn.
* Added `std.meta.globalOption`.
* Added `std.os.madvise` and related bits.
* Bumped up the size of the main thread TLS buffer. See the comment
there for justification.
* Simpler hot path in TLS initialization.10 files changed, 241 insertions(+), 71 deletions(-)
lib/std/c.zig+5| ... | @@ -264,6 +264,11 @@ pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: us | ... | @@ -264,6 +264,11 @@ pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: us |
| 264 | pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; | 264 | pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; |
| 265 | pub extern "c" fn pthread_self() pthread_t; | 265 | pub extern "c" fn pthread_self() pthread_t; |
| 266 | pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; | 266 | pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; |
| 267 | pub extern "c" fn pthread_atfork( | ||
| 268 | prepare: ?fn () callconv(.C) void, | ||
| 269 | parent: ?fn () callconv(.C) void, | ||
| 270 | child: ?fn () callconv(.C) void, | ||
| 271 | ) c_int; | ||
| 267 | 272 | ||
| 268 | pub extern "c" fn kqueue() c_int; | 273 | pub extern "c" fn kqueue() c_int; |
| 269 | pub extern "c" fn kevent( | 274 | pub extern "c" fn kevent( |
lib/std/c/linux.zig+6| ... | @@ -106,6 +106,12 @@ pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *con | ... | @@ -106,6 +106,12 @@ pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *con |
| 106 | pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; | 106 | pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; |
| 107 | pub extern "c" fn malloc_usable_size(?*const c_void) usize; | 107 | pub extern "c" fn malloc_usable_size(?*const c_void) usize; |
| 108 | 108 | ||
| 109 | pub extern "c" fn madvise( | ||
| 110 | addr: *align(std.mem.page_size) c_void, | ||
| 111 | length: usize, | ||
| 112 | advice: c_uint, | ||
| 113 | ) c_int; | ||
| 114 | |||
| 109 | pub const pthread_attr_t = extern struct { | 115 | pub const pthread_attr_t = extern struct { |
| 110 | __size: [56]u8, | 116 | __size: [56]u8, |
| 111 | __align: c_long, | 117 | __align: c_long, |
lib/std/crypto/tlcsprng.zig+123-29| ... | @@ -16,47 +16,141 @@ const mem = std.mem; | ... | @@ -16,47 +16,141 @@ const mem = std.mem; |
| 16 | /// We use this as a layer of indirection because global const pointers cannot | 16 | /// We use this as a layer of indirection because global const pointers cannot |
| 17 | /// point to thread-local variables. | 17 | /// point to thread-local variables. |
| 18 | pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill }; | 18 | pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill }; |
| 19 | pub threadlocal var csprng_state: std.crypto.core.Gimli = undefined; | 19 | |
| 20 | pub threadlocal var csprng_state_initialized = false; | 20 | const os_has_fork = switch (std.Target.current.os.tag) { |
| 21 | fn tlsCsprngFill(r: *const std.rand.Random, buf: []u8) void { | 21 | .dragonfly, |
| 22 | .freebsd, | ||
| 23 | .ios, | ||
| 24 | .kfreebsd, | ||
| 25 | .linux, | ||
| 26 | .macos, | ||
| 27 | .netbsd, | ||
| 28 | .openbsd, | ||
| 29 | .solaris, | ||
| 30 | .tvos, | ||
| 31 | .watchos, | ||
| 32 | => true, | ||
| 33 | |||
| 34 | else => false, | ||
| 35 | }; | ||
| 36 | const os_has_arc4random = std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf"); | ||
| 37 | const want_fork_safety = os_has_fork and !os_has_arc4random and | ||
| 38 | (std.meta.globalOption("crypto_fork_safety", bool) orelse true); | ||
| 39 | const maybe_have_wipe_on_fork = std.Target.current.os.isAtLeast(.linux, .{ | ||
| 40 | .major = 4, | ||
| 41 | .minor = 14, | ||
| 42 | }) orelse true; | ||
| 43 | |||
| 44 | const WipeMe = struct { | ||
| 45 | init_state: enum { uninitialized, initialized, failed }, | ||
| 46 | gimli: std.crypto.core.Gimli, | ||
| 47 | }; | ||
| 48 | const wipe_align = if (maybe_have_wipe_on_fork) mem.page_size else @alignOf(WipeMe); | ||
| 49 | |||
| 50 | threadlocal var wipe_me: WipeMe align(wipe_align) = .{ | ||
| 51 | .gimli = undefined, | ||
| 52 | .init_state = .uninitialized, | ||
| 53 | }; | ||
| 54 | |||
| 55 | fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | ||
| 22 | if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) { | 56 | if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) { |
| 23 | // arc4random is already a thread-local CSPRNG. | 57 | // arc4random is already a thread-local CSPRNG. |
| 24 | return std.c.arc4random_buf(buf.ptr, buf.len); | 58 | return std.c.arc4random_buf(buffer.ptr, buffer.len); |
| 25 | } | 59 | } |
| 26 | if (!csprng_state_initialized) { | 60 | // Allow applications to decide they would prefer to have every call to |
| 27 | var seed: [seed_len]u8 = undefined; | 61 | // std.crypto.random always make an OS syscall, rather than rely on an |
| 28 | // Because we panic on getrandom() failing, we provide the opportunity | 62 | // application implementation of a CSPRNG. |
| 29 | // to override the default seed function. This also makes | 63 | if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) { |
| 30 | // `std.crypto.random` available on freestanding targets, provided that | 64 | return fillWithOsEntropy(buffer); |
| 31 | // the `cryptoRandomSeed` function is provided. | ||
| 32 | if (@hasDecl(root, "cryptoRandomSeed")) { | ||
| 33 | root.cryptoRandomSeed(&seed); | ||
| 34 | } else { | ||
| 35 | defaultSeed(&seed); | ||
| 36 | } | ||
| 37 | init(seed); | ||
| 38 | } | 65 | } |
| 39 | if (buf.len != 0) { | 66 | switch (wipe_me.init_state) { |
| 40 | csprng_state.squeeze(buf); | 67 | .uninitialized => { |
| 68 | if (want_fork_safety) { | ||
| 69 | if (maybe_have_wipe_on_fork) { | ||
| 70 | if (std.os.madvise( | ||
| 71 | @ptrCast([*]align(mem.page_size) u8, &wipe_me), | ||
| 72 | @sizeOf(@TypeOf(wipe_me)), | ||
| 73 | std.os.MADV_WIPEONFORK, | ||
| 74 | )) |_| { | ||
| 75 | return initAndFill(buffer); | ||
| 76 | } else |_| if (std.Thread.use_pthreads) { | ||
| 77 | return setupPthreadAtforkAndFill(buffer); | ||
| 78 | } else { | ||
| 79 | // Since we failed to set up fork safety, we fall back to always | ||
| 80 | // calling getrandom every time. | ||
| 81 | wipe_me.init_state = .failed; | ||
| 82 | return fillWithOsEntropy(buffer); | ||
| 83 | } | ||
| 84 | } else if (std.Thread.use_pthreads) { | ||
| 85 | return setupPthreadAtforkAndFill(buffer); | ||
| 86 | } else { | ||
| 87 | // We have no mechanism to provide fork safety, but we want fork safety, | ||
| 88 | // so we fall back to calling getrandom every time. | ||
| 89 | wipe_me.init_state = .failed; | ||
| 90 | return fillWithOsEntropy(buffer); | ||
| 91 | } | ||
| 92 | } else { | ||
| 93 | return initAndFill(buffer); | ||
| 94 | } | ||
| 95 | }, | ||
| 96 | .initialized => { | ||
| 97 | return fillWithCsprng(buffer); | ||
| 98 | }, | ||
| 99 | .failed => { | ||
| 100 | if (want_fork_safety) { | ||
| 101 | return fillWithOsEntropy(buffer); | ||
| 102 | } else { | ||
| 103 | unreachable; | ||
| 104 | } | ||
| 105 | }, | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | fn setupPthreadAtforkAndFill(buffer: []u8) void { | ||
| 110 | const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0; | ||
| 111 | if (failed) { | ||
| 112 | wipe_me.init_state = .failed; | ||
| 113 | return fillWithOsEntropy(buffer); | ||
| 41 | } else { | 114 | } else { |
| 42 | csprng_state.permute(); | 115 | return initAndFill(buffer); |
| 43 | } | 116 | } |
| 44 | mem.set(u8, csprng_state.toSlice()[0..std.crypto.core.Gimli.RATE], 0); | ||
| 45 | } | 117 | } |
| 46 | 118 | ||
| 47 | fn defaultSeed(buffer: *[seed_len]u8) void { | 119 | fn childAtForkHandler() callconv(.C) void { |
| 48 | std.os.getrandom(buffer) catch @panic("getrandom() failed to seed thread-local CSPRNG"); | 120 | const wipe_slice = @ptrCast([*]u8, &wipe_me)[0..@sizeOf(@TypeOf(wipe_me))]; |
| 121 | std.crypto.utils.secureZero(u8, wipe_slice); | ||
| 49 | } | 122 | } |
| 50 | 123 | ||
| 51 | pub const seed_len = 16; | 124 | fn fillWithCsprng(buffer: []u8) void { |
| 125 | if (buffer.len != 0) { | ||
| 126 | wipe_me.gimli.squeeze(buffer); | ||
| 127 | } else { | ||
| 128 | wipe_me.gimli.permute(); | ||
| 129 | } | ||
| 130 | mem.set(u8, wipe_me.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0); | ||
| 131 | } | ||
| 132 | |||
| 133 | fn fillWithOsEntropy(buffer: []u8) void { | ||
| 134 | std.os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy"); | ||
| 135 | } | ||
| 52 | 136 | ||
| 53 | pub fn init(seed: [seed_len]u8) void { | 137 | fn initAndFill(buffer: []u8) void { |
| 54 | var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined; | 138 | var seed: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined; |
| 55 | mem.copy(u8, initial_state[0..seed_len], &seed); | 139 | // Because we panic on getrandom() failing, we provide the opportunity |
| 56 | mem.set(u8, initial_state[seed_len..], 0); | 140 | // to override the default seed function. This also makes |
| 57 | csprng_state = std.crypto.core.Gimli.init(initial_state); | 141 | // `std.crypto.random` available on freestanding targets, provided that |
| 142 | // the `cryptoRandomSeed` function is provided. | ||
| 143 | if (@hasDecl(root, "cryptoRandomSeed")) { | ||
| 144 | root.cryptoRandomSeed(&seed); | ||
| 145 | } else { | ||
| 146 | fillWithOsEntropy(&seed); | ||
| 147 | } | ||
| 148 | |||
| 149 | wipe_me.gimli = std.crypto.core.Gimli.init(seed); | ||
| 58 | 150 | ||
| 59 | // This is at the end so that accidental recursive dependencies result | 151 | // This is at the end so that accidental recursive dependencies result |
| 60 | // in stack overflows instead of invalid random data. | 152 | // in stack overflows instead of invalid random data. |
| 61 | csprng_state_initialized = true; | 153 | wipe_me.init_state = .initialized; |
| 154 | |||
| 155 | return fillWithCsprng(buffer); | ||
| 62 | } | 156 | } |
lib/std/meta.zig+8| ... | @@ -9,6 +9,7 @@ const debug = std.debug; | ... | @@ -9,6 +9,7 @@ const debug = std.debug; |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | const math = std.math; | 10 | const math = std.math; |
| 11 | const testing = std.testing; | 11 | const testing = std.testing; |
| 12 | const root = @import("root"); | ||
| 12 | 13 | ||
| 13 | pub const trait = @import("meta/trait.zig"); | 14 | pub const trait = @import("meta/trait.zig"); |
| 14 | pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; | 15 | pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; |
| ... | @@ -1085,3 +1086,10 @@ test "Tuple" { | ... | @@ -1085,3 +1086,10 @@ test "Tuple" { |
| 1085 | TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 })); | 1086 | TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 })); |
| 1086 | TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void })); | 1087 | TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void })); |
| 1087 | } | 1088 | } |
| 1089 | |||
| 1090 | /// TODO: https://github.com/ziglang/zig/issues/425 | ||
| 1091 | pub fn globalOption(comptime name: []const u8, comptime T: type) ?T { | ||
| 1092 | if (!@hasDecl(root, name)) | ||
| 1093 | return null; | ||
| 1094 | return @as(T, @field(root, name)); | ||
| 1095 | } |
lib/std/os.zig+48| ... | @@ -5845,3 +5845,51 @@ pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void | ... | @@ -5845,3 +5845,51 @@ pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void |
| 5845 | else => |err| return unexpectedErrno(err), | 5845 | else => |err| return unexpectedErrno(err), |
| 5846 | } | 5846 | } |
| 5847 | } | 5847 | } |
| 5848 | |||
| 5849 | pub const MadviseError = error{ | ||
| 5850 | /// advice is MADV_REMOVE, but the specified address range is not a shared writable mapping. | ||
| 5851 | AccessDenied, | ||
| 5852 | /// advice is MADV_HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability. | ||
| 5853 | PermissionDenied, | ||
| 5854 | /// A kernel resource was temporarily unavailable. | ||
| 5855 | SystemResources, | ||
| 5856 | /// One of the following: | ||
| 5857 | /// * addr is not page-aligned or length is negative | ||
| 5858 | /// * advice is not valid | ||
| 5859 | /// * advice is MADV_DONTNEED or MADV_REMOVE and the specified address range | ||
| 5860 | /// includes locked, Huge TLB pages, or VM_PFNMAP pages. | ||
| 5861 | /// * advice is MADV_MERGEABLE or MADV_UNMERGEABLE, but the kernel was not | ||
| 5862 | /// configured with CONFIG_KSM. | ||
| 5863 | /// * advice is MADV_FREE or MADV_WIPEONFORK but the specified address range | ||
| 5864 | /// includes file, Huge TLB, MAP_SHARED, or VM_PFNMAP ranges. | ||
| 5865 | InvalidSyscall, | ||
| 5866 | /// (for MADV_WILLNEED) Paging in this area would exceed the process's | ||
| 5867 | /// maximum resident set size. | ||
| 5868 | WouldExceedMaximumResidentSetSize, | ||
| 5869 | /// One of the following: | ||
| 5870 | /// * (for MADV_WILLNEED) Not enough memory: paging in failed. | ||
| 5871 | /// * Addresses in the specified range are not currently mapped, or | ||
| 5872 | /// are outside the address space of the process. | ||
| 5873 | OutOfMemory, | ||
| 5874 | /// The madvise syscall is not available on this version and configuration | ||
| 5875 | /// of the Linux kernel. | ||
| 5876 | MadviseUnavailable, | ||
| 5877 | /// The operating system returned an undocumented error code. | ||
| 5878 | Unexpected, | ||
| 5879 | }; | ||
| 5880 | |||
| 5881 | /// Give advice about use of memory. | ||
| 5882 | /// This syscall is optional and is sometimes configured to be disabled. | ||
| 5883 | pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void { | ||
| 5884 | switch (errno(system.madvise(ptr, length, advice))) { | ||
| 5885 | 0 => return, | ||
| 5886 | EACCES => return error.AccessDenied, | ||
| 5887 | EAGAIN => return error.SystemResources, | ||
| 5888 | EBADF => unreachable, // The map exists, but the area maps something that isn't a file. | ||
| 5889 | EINVAL => return error.InvalidSyscall, | ||
| 5890 | EIO => return error.WouldExceedMaximumResidentSetSize, | ||
| 5891 | ENOMEM => return error.OutOfMemory, | ||
| 5892 | ENOSYS => return error.MadviseUnavailable, | ||
| 5893 | else => |err| return unexpectedErrno(err), | ||
| 5894 | } | ||
| 5895 | } |
lib/std/os/bits/linux.zig+22| ... | @@ -2045,3 +2045,25 @@ pub const rlimit = extern struct { | ... | @@ -2045,3 +2045,25 @@ pub const rlimit = extern struct { |
| 2045 | /// Hard limit | 2045 | /// Hard limit |
| 2046 | max: rlim_t, | 2046 | max: rlim_t, |
| 2047 | }; | 2047 | }; |
| 2048 | |||
| 2049 | pub const MADV_NORMAL = 0; | ||
| 2050 | pub const MADV_RANDOM = 1; | ||
| 2051 | pub const MADV_SEQUENTIAL = 2; | ||
| 2052 | pub const MADV_WILLNEED = 3; | ||
| 2053 | pub const MADV_DONTNEED = 4; | ||
| 2054 | pub const MADV_FREE = 8; | ||
| 2055 | pub const MADV_REMOVE = 9; | ||
| 2056 | pub const MADV_DONTFORK = 10; | ||
| 2057 | pub const MADV_DOFORK = 11; | ||
| 2058 | pub const MADV_MERGEABLE = 12; | ||
| 2059 | pub const MADV_UNMERGEABLE = 13; | ||
| 2060 | pub const MADV_HUGEPAGE = 14; | ||
| 2061 | pub const MADV_NOHUGEPAGE = 15; | ||
| 2062 | pub const MADV_DONTDUMP = 16; | ||
| 2063 | pub const MADV_DODUMP = 17; | ||
| 2064 | pub const MADV_WIPEONFORK = 18; | ||
| 2065 | pub const MADV_KEEPONFORK = 19; | ||
| 2066 | pub const MADV_COLD = 20; | ||
| 2067 | pub const MADV_PAGEOUT = 21; | ||
| 2068 | pub const MADV_HWPOISON = 100; | ||
| 2069 | pub const MADV_SOFT_OFFLINE = 101; |
lib/std/os/linux.zig+4| ... | @@ -1351,6 +1351,10 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit, | ... | @@ -1351,6 +1351,10 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit, |
| 1351 | ); | 1351 | ); |
| 1352 | } | 1352 | } |
| 1353 | 1353 | ||
| 1354 | pub fn madvise(address: [*]u8, len: usize, advice: u32) usize { | ||
| 1355 | return syscall3(.madvise, @ptrToInt(address), len, advice); | ||
| 1356 | } | ||
| 1357 | |||
| 1354 | test "" { | 1358 | test "" { |
| 1355 | if (builtin.os.tag == .linux) { | 1359 | if (builtin.os.tag == .linux) { |
| 1356 | _ = @import("linux/test.zig"); | 1360 | _ = @import("linux/test.zig"); |
lib/std/os/linux/tls.zig+24-15| ... | @@ -327,34 +327,43 @@ pub fn prepareTLS(area: []u8) usize { | ... | @@ -327,34 +327,43 @@ pub fn prepareTLS(area: []u8) usize { |
| 327 | if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset; | 327 | if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset; |
| 328 | } | 328 | } |
| 329 | 329 | ||
| 330 | var main_thread_tls_buffer: [256]u8 = undefined; | 330 | // The main motivation for the size chosen here is this is how much ends up being |
| 331 | // requested for the thread local variables of the std.crypto.random implementation. | ||
| 332 | // I'm not sure why it ends up being so much; the struct itself is only 64 bytes. | ||
| 333 | // I think it has to do with being page aligned and LLVM or LLD is not smart enough | ||
| 334 | // to lay out the TLS data in a space conserving way. Anyway I think it's fine | ||
| 335 | // because it's less than 3 pages of memory, and putting it in the ELF like this | ||
| 336 | // is equivalent to moving the mmap call below into the kernel, avoiding syscall | ||
| 337 | // overhead. | ||
| 338 | var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined; | ||
| 331 | 339 | ||
| 332 | pub fn initStaticTLS() void { | 340 | pub fn initStaticTLS() void { |
| 333 | initTLS(); | 341 | initTLS(); |
| 334 | 342 | ||
| 335 | const alloc_tls_area: []u8 = blk: { | 343 | const tls_area = blk: { |
| 336 | const full_alloc_size = tls_image.alloc_size + tls_image.alloc_align - 1; | ||
| 337 | |||
| 338 | // Fast path for the common case where the TLS data is really small, | 344 | // Fast path for the common case where the TLS data is really small, |
| 339 | // avoid an allocation and use our local buffer | 345 | // avoid an allocation and use our local buffer. |
| 340 | if (full_alloc_size < main_thread_tls_buffer.len) | 346 | if (tls_image.alloc_align <= mem.page_size and |
| 341 | break :blk main_thread_tls_buffer[0..]; | 347 | tls_image.alloc_size <= main_thread_tls_buffer.len) |
| 348 | { | ||
| 349 | break :blk main_thread_tls_buffer[0..tls_image.alloc_size]; | ||
| 350 | } | ||
| 342 | 351 | ||
| 343 | break :blk os.mmap( | 352 | const alloc_tls_area = os.mmap( |
| 344 | null, | 353 | null, |
| 345 | full_alloc_size, | 354 | tls_image.alloc_size + tls_image.alloc_align - 1, |
| 346 | os.PROT_READ | os.PROT_WRITE, | 355 | os.PROT_READ | os.PROT_WRITE, |
| 347 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, | 356 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| 348 | -1, | 357 | -1, |
| 349 | 0, | 358 | 0, |
| 350 | ) catch os.abort(); | 359 | ) catch os.abort(); |
| 351 | }; | ||
| 352 | 360 | ||
| 353 | // Make sure the slice is correctly aligned | 361 | // Make sure the slice is correctly aligned. |
| 354 | const begin_addr = @ptrToInt(alloc_tls_area.ptr); | 362 | const begin_addr = @ptrToInt(alloc_tls_area.ptr); |
| 355 | const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align); | 363 | const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align); |
| 356 | const start = begin_aligned_addr - begin_addr; | 364 | const start = begin_aligned_addr - begin_addr; |
| 357 | const tls_area = alloc_tls_area[start .. start + tls_image.alloc_size]; | 365 | break :blk alloc_tls_area[start .. start + tls_image.alloc_size]; |
| 366 | }; | ||
| 358 | 367 | ||
| 359 | const tp_value = prepareTLS(tls_area); | 368 | const tp_value = prepareTLS(tls_area); |
| 360 | setThreadPointer(tp_value); | 369 | setThreadPointer(tp_value); |
lib/std/start.zig-26| ... | @@ -216,12 +216,6 @@ fn posixCallMainAndExit() noreturn { | ... | @@ -216,12 +216,6 @@ fn posixCallMainAndExit() noreturn { |
| 216 | std.os.linux.tls.initStaticTLS(); | 216 | std.os.linux.tls.initStaticTLS(); |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | if (!@hasDecl(root, "use_AT_RANDOM_auxval") or root.use_AT_RANDOM_auxval) { | ||
| 220 | // Initialize the per-thread CSPRNG since Linux gave us the handy-dandy | ||
| 221 | // AT_RANDOM. This depends on the TLS initialization above. | ||
| 222 | initCryptoSeedFromAuxVal(std.os.linux.getauxval(std.elf.AT_RANDOM)); | ||
| 223 | } | ||
| 224 | |||
| 225 | // TODO This is disabled because what should we do when linking libc and this code | 219 | // TODO This is disabled because what should we do when linking libc and this code |
| 226 | // does not execute? And also it's causing a test failure in stack traces in release modes. | 220 | // does not execute? And also it's causing a test failure in stack traces in release modes. |
| 227 | 221 | ||
| ... | @@ -257,32 +251,12 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { | ... | @@ -257,32 +251,12 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { |
| 257 | } | 251 | } |
| 258 | 252 | ||
| 259 | fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 { | 253 | fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 { |
| 260 | // By default, we do not attempt to initialize tlcsprng from AT_RANDOM here because | ||
| 261 | // libc owns the start code, not us, and therefore libc owns the random bytes | ||
| 262 | // from AT_RANDOM. | ||
| 263 | if (builtin.os.tag == .linux and | ||
| 264 | @hasDecl(root, "use_AT_RANDOM_auxval") and | ||
| 265 | root.use_AT_RANDOM_auxval) | ||
| 266 | { | ||
| 267 | initCryptoSeedFromAuxVal(std.c.getauxval(std.elf.AT_RANDOM)); | ||
| 268 | } | ||
| 269 | var env_count: usize = 0; | 254 | var env_count: usize = 0; |
| 270 | while (c_envp[env_count] != null) : (env_count += 1) {} | 255 | while (c_envp[env_count] != null) : (env_count += 1) {} |
| 271 | const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count]; | 256 | const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count]; |
| 272 | return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); | 257 | return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); |
| 273 | } | 258 | } |
| 274 | 259 | ||
| 275 | fn initCryptoSeedFromAuxVal(addr: usize) void { | ||
| 276 | if (addr == 0) return; | ||
| 277 | // "The address of sixteen bytes containing a random value." | ||
| 278 | const ptr = @intToPtr(*[16]u8, addr); | ||
| 279 | tlcsprng.init(ptr.*); | ||
| 280 | // Clear AT_RANDOM after we use it, otherwise our secure | ||
| 281 | // seed is sitting in memory ready for some other code in the | ||
| 282 | // program to reuse, and hence break our security. | ||
| 283 | std.crypto.utils.secureZero(u8, ptr); | ||
| 284 | } | ||
| 285 | |||
| 286 | // General error message for a malformed return type | 260 | // General error message for a malformed return type |
| 287 | const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'"; | 261 | const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'"; |
| 288 | 262 |
test/stack_traces.zig+1-1| ... | @@ -282,7 +282,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { | ... | @@ -282,7 +282,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { |
| 282 | \\source.zig:10:8: [address] in main (test) | 282 | \\source.zig:10:8: [address] in main (test) |
| 283 | \\ foo(); | 283 | \\ foo(); |
| 284 | \\ ^ | 284 | \\ ^ |
| 285 | \\start.zig:377:29: [address] in std.start.posixCallMainAndExit (test) | 285 | \\start.zig:342:29: [address] in std.start.posixCallMainAndExit (test) |
| 286 | \\ return root.main(); | 286 | \\ return root.main(); |
| 287 | \\ ^ | 287 | \\ ^ |
| 288 | \\start.zig:163:5: [address] in std.start._start (test) | 288 | \\start.zig:163:5: [address] in std.start._start (test) |