| ... | @@ -65,7 +65,7 @@ argv0: Argv0, | ... | @@ -65,7 +65,7 @@ argv0: Argv0, |
| 65 | environ: Environ, | 65 | environ: Environ, |
| 66 | | 66 | |
| 67 | null_file: NullFile = .{}, | 67 | null_file: NullFile = .{}, |
| 68 | dev_urandom_fd: dev_urandom_fd_t = if (use_dev_urandom) -1 else {}, | 68 | random_file: RandomFile = .{}, |
| 69 | | 69 | |
| 70 | pub const Argv0 = switch (native_os) { | 70 | pub const Argv0 = switch (native_os) { |
| 71 | .openbsd, .haiku => struct { | 71 | .openbsd, .haiku => struct { |
| ... | @@ -152,6 +152,15 @@ pub const NullFile = switch (native_os) { | ... | @@ -152,6 +152,15 @@ pub const NullFile = switch (native_os) { |
| 152 | }, | 152 | }, |
| 153 | }; | 153 | }; |
| 154 | | 154 | |
| | 155 | pub const RandomFile = switch (native_os) { |
| | 156 | .windows => NullFile, |
| | 157 | else => if (use_dev_urandom) NullFile else struct { |
| | 158 | fn deinit(this: @This()) void { |
| | 159 | _ = this; |
| | 160 | } |
| | 161 | }, |
| | 162 | }; |
| | 163 | |
| 155 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { | 164 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { |
| 156 | unknown = 0, | 165 | unknown = 0, |
| 157 | _, | 166 | _, |
| ... | @@ -586,9 +595,7 @@ const Thread = struct { | ... | @@ -586,9 +595,7 @@ const Thread = struct { |
| 586 | /// Always released when `Status.cancelation` is set to `.parked`. | 595 | /// Always released when `Status.cancelation` is set to `.parked`. |
| 587 | futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn, | 596 | futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn, |
| 588 | | 597 | |
| 589 | random_buffer: [128]u8, | 598 | csprng: std.Random.DefaultCsprng, |
| 590 | /// How many bytes of `random_buffer` are filled. | | |
| 591 | random_i: usize, | | |
| 592 | | 599 | |
| 593 | const Handle = Handle: { | 600 | const Handle = Handle: { |
| 594 | if (std.Thread.use_pthreads) break :Handle std.c.pthread_t; | 601 | if (std.Thread.use_pthreads) break :Handle std.c.pthread_t; |
| ... | @@ -1290,9 +1297,7 @@ pub fn deinit(t: *Threaded) void { | ... | @@ -1290,9 +1297,7 @@ pub fn deinit(t: *Threaded) void { |
| 1290 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); | 1297 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); |
| 1291 | } | 1298 | } |
| 1292 | t.null_file.deinit(); | 1299 | t.null_file.deinit(); |
| 1293 | if (use_dev_urandom and t.dev_urandom_fd != -1) { | 1300 | t.random_file.deinit(); |
| 1294 | posix.close(t.dev_urandom_fd); | | |
| 1295 | } | | |
| 1296 | t.* = undefined; | 1301 | t.* = undefined; |
| 1297 | } | 1302 | } |
| 1298 | | 1303 | |
| ... | @@ -1321,6 +1326,10 @@ fn worker(t: *Threaded) void { | ... | @@ -1321,6 +1326,10 @@ fn worker(t: *Threaded) void { |
| 1321 | }), | 1326 | }), |
| 1322 | .cancel_protection = .unblocked, | 1327 | .cancel_protection = .unblocked, |
| 1323 | .futex_waiter = undefined, | 1328 | .futex_waiter = undefined, |
| | 1329 | .csprng = .{ |
| | 1330 | .state = undefined, |
| | 1331 | .offset = std.math.maxInt(usize), |
| | 1332 | }, |
| 1324 | }; | 1333 | }; |
| 1325 | Thread.current = &thread; | 1334 | Thread.current = &thread; |
| 1326 | | 1335 | |
| ... | @@ -1734,8 +1743,6 @@ const getrandom_use_libc = @TypeOf(posix.system.getrandom) != void and (native_o | ... | @@ -1734,8 +1743,6 @@ const getrandom_use_libc = @TypeOf(posix.system.getrandom) != void and (native_o |
| 1734 | | 1743 | |
| 1735 | const use_dev_urandom = getrandom_use_libc and native_os == .linux; | 1744 | const use_dev_urandom = getrandom_use_libc and native_os == .linux; |
| 1736 | | 1745 | |
| 1737 | const dev_urandom_fd_t = if (use_dev_urandom) posix.fd_t else void; | | |
| 1738 | | | |
| 1739 | fn async( | 1746 | fn async( |
| 1740 | userdata: ?*anyopaque, | 1747 | userdata: ?*anyopaque, |
| 1741 | result: []u8, | 1748 | result: []u8, |
| ... | @@ -13873,6 +13880,62 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -13873,6 +13880,62 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 13873 | }; | 13880 | }; |
| 13874 | } | 13881 | } |
| 13875 | | 13882 | |
| | 13883 | fn getCngHandle(t: *Threaded) !windows.HANDLE { |
| | 13884 | { |
| | 13885 | t.mutex.lock(); |
| | 13886 | defer t.mutex.unlock(); |
| | 13887 | if (t.random_file.handle) |handle| return handle; |
| | 13888 | } |
| | 13889 | |
| | 13890 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }; |
| | 13891 | |
| | 13892 | var nt_name: windows.UNICODE_STRING = .{ |
| | 13893 | .Length = device_path.len * 2, |
| | 13894 | .MaximumLength = 0, |
| | 13895 | .Buffer = @constCast(&device_path), |
| | 13896 | }; |
| | 13897 | var fresh_handle: windows.HANDLE = undefined; |
| | 13898 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| | 13899 | var syscall: Syscall = try .start(); |
| | 13900 | while (true) switch (windows.ntdll.NtOpenFile( |
| | 13901 | &fresh_handle, |
| | 13902 | .{ |
| | 13903 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| | 13904 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } }, |
| | 13905 | }, |
| | 13906 | &.{ |
| | 13907 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), |
| | 13908 | .RootDirectory = null, |
| | 13909 | .ObjectName = &nt_name, |
| | 13910 | .Attributes = .{}, |
| | 13911 | .SecurityDescriptor = null, |
| | 13912 | .SecurityQualityOfService = null, |
| | 13913 | }, |
| | 13914 | &io_status_block, |
| | 13915 | .VALID_FLAGS, |
| | 13916 | .{ .IO = .SYNCHRONOUS_NONALERT }, |
| | 13917 | )) { |
| | 13918 | .SUCCESS => { |
| | 13919 | syscall.finish(); |
| | 13920 | t.mutex.lock(); // Another thread might have won the race. |
| | 13921 | defer t.mutex.unlock(); |
| | 13922 | if (t.random_file.handle) |prev_handle| { |
| | 13923 | _ = windows.ntdll.NtClose(fresh_handle); |
| | 13924 | return prev_handle; |
| | 13925 | } else { |
| | 13926 | t.random_file.handle = fresh_handle; |
| | 13927 | return fresh_handle; |
| | 13928 | } |
| | 13929 | }, |
| | 13930 | .CANCELLED => { |
| | 13931 | try syscall.checkCancel(); |
| | 13932 | continue; |
| | 13933 | }, |
| | 13934 | .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.Unexpected), // Observed on wine 10.0 |
| | 13935 | else => |status| return syscall.unexpectedNtstatus(status), |
| | 13936 | }; |
| | 13937 | } |
| | 13938 | |
| 13876 | fn getNulHandle(t: *Threaded) !windows.HANDLE { | 13939 | fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 13877 | { | 13940 | { |
| 13878 | t.mutex.lock(); | 13941 | t.mutex.lock(); |
| ... | @@ -14959,28 +15022,48 @@ fn random(userdata: ?*anyopaque, buffer: []u8) Io.RandomError!void { | ... | @@ -14959,28 +15022,48 @@ fn random(userdata: ?*anyopaque, buffer: []u8) Io.RandomError!void { |
| 14959 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 15022 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 14960 | | 15023 | |
| 14961 | if (is_windows) { | 15024 | if (is_windows) { |
| 14962 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows | 15025 | if (buffer.len == 0) return; |
| 14963 | // https://github.com/rust-lang-nursery/rand/issues/111 | 15026 | // ProcessPrng from bcryptprimitives.dll has the following properties: |
| 14964 | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 | 15027 | // * introduces a dependency on bcryptprimitives.dll, which apparently |
| 14965 | const max_read_size: windows.ULONG = std.math.maxInt(windows.ULONG); | 15028 | // runs a test suite every time it is loaded |
| | 15029 | // * heap allocates a 48-byte buffer, handling failure by returning NO_MEMORY in a BOOL |
| | 15030 | // despite the function being documented to always return TRUE |
| | 15031 | // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG |
| | 15032 | // Therefore, that function is avoided in favor of using the device directly. |
| | 15033 | const cng_device = try getCngHandle(t); |
| | 15034 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 14966 | var i: usize = 0; | 15035 | var i: usize = 0; |
| 14967 | while (i < buffer.len) { | 15036 | const syscall: Syscall = try .start(); |
| 14968 | const buf = buffer[i..]; | 15037 | while (true) { |
| 14969 | const request_n: windows.ULONG = @min(buf.len, max_read_size); | 15038 | const remaining_len = std.math.lossyCast(u32, buffer.len - i); |
| 14970 | const syscall: Syscall = try .start(); | 15039 | switch (windows.ntdll.NtDeviceIoControlFile( |
| 14971 | const result = windows.advapi32.RtlGenRandom(buf.ptr, request_n); | 15040 | cng_device, |
| 14972 | syscall.finish(); | 15041 | null, |
| 14973 | if (result == 0) { | 15042 | null, |
| 14974 | // `RtlGenRandom` has been observed to fail in situations where | 15043 | null, |
| 14975 | // the system is under heavy load. Unfortunately, it does not | 15044 | &io_status_block, |
| 14976 | // call `SetLastError`, so it is not possible to get more | 15045 | windows.IOCTL.KSEC.GEN_RANDOM, |
| 14977 | // specific error information; it could actually be due to an | 15046 | null, |
| 14978 | // out-of-memory condition, for example. | 15047 | 0, |
| 14979 | return error.EntropyUnavailable; | 15048 | buffer[i..].ptr, |
| | 15049 | remaining_len, |
| | 15050 | )) { |
| | 15051 | .SUCCESS => { |
| | 15052 | i += remaining_len; |
| | 15053 | if (buffer.len - i == 0) { |
| | 15054 | return syscall.finish(); |
| | 15055 | } else { |
| | 15056 | try syscall.checkCancel(); |
| | 15057 | continue; |
| | 15058 | } |
| | 15059 | }, |
| | 15060 | .CANCELLED => { |
| | 15061 | try syscall.checkCancel(); |
| | 15062 | continue; |
| | 15063 | }, |
| | 15064 | else => return syscall.fail(error.EntropyUnavailable), |
| 14980 | } | 15065 | } |
| 14981 | i += request_n; | | |
| 14982 | } | 15066 | } |
| 14983 | return; | | |
| 14984 | } | 15067 | } |
| 14985 | | 15068 | |
| 14986 | if (builtin.link_libc and @TypeOf(posix.system.arc4random_buf) != void) { | 15069 | if (builtin.link_libc and @TypeOf(posix.system.arc4random_buf) != void) { |
| ... | @@ -15032,6 +15115,7 @@ fn random(userdata: ?*anyopaque, buffer: []u8) Io.RandomError!void { | ... | @@ -15032,6 +15115,7 @@ fn random(userdata: ?*anyopaque, buffer: []u8) Io.RandomError!void { |
| 15032 | } | 15115 | } |
| 15033 | } | 15116 | } |
| 15034 | | 15117 | |
| | 15118 | if (buffer.len == 0) return; |
| 15035 | const urandom_fd = try getRandomFd(t); | 15119 | const urandom_fd = try getRandomFd(t); |
| 15036 | | 15120 | |
| 15037 | var i: usize = 0; | 15121 | var i: usize = 0; |
| ... | @@ -15066,8 +15150,8 @@ fn getRandomFd(t: *Threaded) posix.fd_t { | ... | @@ -15066,8 +15150,8 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15066 | t.mutex.lock(); | 15150 | t.mutex.lock(); |
| 15067 | defer t.mutex.unlock(); | 15151 | defer t.mutex.unlock(); |
| 15068 | | 15152 | |
| 15069 | if (t.dev_urandom_fd == -2) return error.EntropyUnavailable; | 15153 | if (t.random_file.fd == -2) return error.EntropyUnavailable; |
| 15070 | if (t.dev_urandom_fd != -1) return t.dev_urandom_fd; | 15154 | if (t.random_file.fd != -1) return t.random_file.fd; |
| 15071 | } | 15155 | } |
| 15072 | | 15156 | |
| 15073 | const fd: posix.fd_t = fd: { | 15157 | const fd: posix.fd_t = fd: { |
| ... | @@ -15088,7 +15172,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { | ... | @@ -15088,7 +15172,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15088 | }, | 15172 | }, |
| 15089 | else => { | 15173 | else => { |
| 15090 | syscall.endSyscall(); | 15174 | syscall.endSyscall(); |
| 15091 | t.dev_urandom_fd = -2; | 15175 | t.random_file.fd = -2; |
| 15092 | return error.EntropyUnavailable; | 15176 | return error.EntropyUnavailable; |
| 15093 | }, | 15177 | }, |
| 15094 | } | 15178 | } |
| ... | @@ -15108,14 +15192,14 @@ fn getRandomFd(t: *Threaded) posix.fd_t { | ... | @@ -15108,14 +15192,14 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15108 | if (!statx.mask.TYPE) return error.Unexpected; | 15192 | if (!statx.mask.TYPE) return error.Unexpected; |
| 15109 | t.mutex.lock(); // Another thread might have won the race. | 15193 | t.mutex.lock(); // Another thread might have won the race. |
| 15110 | defer t.mutex.unlock(); | 15194 | defer t.mutex.unlock(); |
| 15111 | if (t.dev_urandom_fd >= 0) { | 15195 | if (t.random_file.fd >= 0) { |
| 15112 | posix.close(fd); | 15196 | posix.close(fd); |
| 15113 | return t.dev_urandom_fd; | 15197 | return t.random_file.fd; |
| 15114 | } else if (!posix.S.ISCHR(statx.mode)) { | 15198 | } else if (!posix.S.ISCHR(statx.mode)) { |
| 15115 | t.dev_urandom_fd = -2; | 15199 | t.random_file.fd = -2; |
| 15116 | return error.EntropyUnavailable; | 15200 | return error.EntropyUnavailable; |
| 15117 | } else { | 15201 | } else { |
| 15118 | t.dev_urandom_fd = fd; | 15202 | t.random_file.fd = fd; |
| 15119 | return fd; | 15203 | return fd; |
| 15120 | } | 15204 | } |
| 15121 | }, | 15205 | }, |
| ... | @@ -15124,7 +15208,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { | ... | @@ -15124,7 +15208,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15124 | continue; | 15208 | continue; |
| 15125 | }, | 15209 | }, |
| 15126 | else => { | 15210 | else => { |
| 15127 | t.dev_urandom_fd = -2; | 15211 | t.random_file.fd = -2; |
| 15128 | return error.EntropyUnavailable; | 15212 | return error.EntropyUnavailable; |
| 15129 | }, | 15213 | }, |
| 15130 | } | 15214 | } |
| ... | @@ -15137,14 +15221,14 @@ fn getRandomFd(t: *Threaded) posix.fd_t { | ... | @@ -15137,14 +15221,14 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15137 | switch (posix.errno(fstat_sym(fd, &stat))) { | 15221 | switch (posix.errno(fstat_sym(fd, &stat))) { |
| 15138 | .SUCCESS => { | 15222 | .SUCCESS => { |
| 15139 | syscall.finish(); | 15223 | syscall.finish(); |
| 15140 | if (t.dev_urandom_fd >= 0) { | 15224 | if (t.random_file.fd >= 0) { |
| 15141 | posix.close(fd); | 15225 | posix.close(fd); |
| 15142 | return t.dev_urandom_fd; | 15226 | return t.random_file.fd; |
| 15143 | } else if (!posix.S.ISCHR(stat.mode)) { | 15227 | } else if (!posix.S.ISCHR(stat.mode)) { |
| 15144 | t.dev_urandom_fd = -2; | 15228 | t.random_file.fd = -2; |
| 15145 | return error.EntropyUnavailable; | 15229 | return error.EntropyUnavailable; |
| 15146 | } else { | 15230 | } else { |
| 15147 | t.dev_urandom_fd = fd; | 15231 | t.random_file.fd = fd; |
| 15148 | return fd; | 15232 | return fd; |
| 15149 | } | 15233 | } |
| 15150 | }, | 15234 | }, |
| ... | @@ -15153,7 +15237,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { | ... | @@ -15153,7 +15237,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15153 | continue; | 15237 | continue; |
| 15154 | }, | 15238 | }, |
| 15155 | else => { | 15239 | else => { |
| 15156 | t.dev_urandom_fd = -2; | 15240 | t.random_file.fd = -2; |
| 15157 | return error.EntropyUnavailable; | 15241 | return error.EntropyUnavailable; |
| 15158 | }, | 15242 | }, |
| 15159 | } | 15243 | } |