| ... | ... | @@ -65,7 +65,7 @@ argv0: Argv0, |
| 65 | 65 | environ: Environ, |
| 66 | 66 | |
| 67 | 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 | 70 | pub const Argv0 = switch (native_os) { |
| 71 | 71 | .openbsd, .haiku => struct { |
| ... | ... | @@ -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 | 164 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { |
| 156 | 165 | unknown = 0, |
| 157 | 166 | _, |
| ... | ... | @@ -586,9 +595,7 @@ const Thread = struct { |
| 586 | 595 | /// Always released when `Status.cancelation` is set to `.parked`. |
| 587 | 596 | futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn, |
| 588 | 597 | |
| 589 | | random_buffer: [128]u8, |
| 590 | | /// How many bytes of `random_buffer` are filled. |
| 591 | | random_i: usize, |
| 598 | csprng: std.Random.DefaultCsprng, |
| 592 | 599 | |
| 593 | 600 | const Handle = Handle: { |
| 594 | 601 | if (std.Thread.use_pthreads) break :Handle std.c.pthread_t; |
| ... | ... | @@ -1290,9 +1297,7 @@ pub fn deinit(t: *Threaded) void { |
| 1290 | 1297 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); |
| 1291 | 1298 | } |
| 1292 | 1299 | t.null_file.deinit(); |
| 1293 | | if (use_dev_urandom and t.dev_urandom_fd != -1) { |
| 1294 | | posix.close(t.dev_urandom_fd); |
| 1295 | | } |
| 1300 | t.random_file.deinit(); |
| 1296 | 1301 | t.* = undefined; |
| 1297 | 1302 | } |
| 1298 | 1303 | |
| ... | ... | @@ -1321,6 +1326,10 @@ fn worker(t: *Threaded) void { |
| 1321 | 1326 | }), |
| 1322 | 1327 | .cancel_protection = .unblocked, |
| 1323 | 1328 | .futex_waiter = undefined, |
| 1329 | .csprng = .{ |
| 1330 | .state = undefined, |
| 1331 | .offset = std.math.maxInt(usize), |
| 1332 | }, |
| 1324 | 1333 | }; |
| 1325 | 1334 | Thread.current = &thread; |
| 1326 | 1335 | |
| ... | ... | @@ -1734,8 +1743,6 @@ const getrandom_use_libc = @TypeOf(posix.system.getrandom) != void and (native_o |
| 1734 | 1743 | |
| 1735 | 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 | 1746 | fn async( |
| 1740 | 1747 | userdata: ?*anyopaque, |
| 1741 | 1748 | result: []u8, |
| ... | ... | @@ -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 | 13939 | fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 13877 | 13940 | { |
| 13878 | 13941 | t.mutex.lock(); |
| ... | ... | @@ -14959,28 +15022,48 @@ fn random(userdata: ?*anyopaque, buffer: []u8) Io.RandomError!void { |
| 14959 | 15022 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 14960 | 15023 | |
| 14961 | 15024 | if (is_windows) { |
| 14962 | | // Call RtlGenRandom() instead of CryptGetRandom() on Windows |
| 14963 | | // https://github.com/rust-lang-nursery/rand/issues/111 |
| 14964 | | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 |
| 14965 | | const max_read_size: windows.ULONG = std.math.maxInt(windows.ULONG); |
| 15025 | if (buffer.len == 0) return; |
| 15026 | // ProcessPrng from bcryptprimitives.dll has the following properties: |
| 15027 | // * introduces a dependency on bcryptprimitives.dll, which apparently |
| 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 | 15035 | var i: usize = 0; |
| 14967 | | while (i < buffer.len) { |
| 14968 | | const buf = buffer[i..]; |
| 14969 | | const request_n: windows.ULONG = @min(buf.len, max_read_size); |
| 14970 | | const syscall: Syscall = try .start(); |
| 14971 | | const result = windows.advapi32.RtlGenRandom(buf.ptr, request_n); |
| 14972 | | syscall.finish(); |
| 14973 | | if (result == 0) { |
| 14974 | | // `RtlGenRandom` has been observed to fail in situations where |
| 14975 | | // the system is under heavy load. Unfortunately, it does not |
| 14976 | | // call `SetLastError`, so it is not possible to get more |
| 14977 | | // specific error information; it could actually be due to an |
| 14978 | | // out-of-memory condition, for example. |
| 14979 | | return error.EntropyUnavailable; |
| 15036 | const syscall: Syscall = try .start(); |
| 15037 | while (true) { |
| 15038 | const remaining_len = std.math.lossyCast(u32, buffer.len - i); |
| 15039 | switch (windows.ntdll.NtDeviceIoControlFile( |
| 15040 | cng_device, |
| 15041 | null, |
| 15042 | null, |
| 15043 | null, |
| 15044 | &io_status_block, |
| 15045 | windows.IOCTL.KSEC.GEN_RANDOM, |
| 15046 | null, |
| 15047 | 0, |
| 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 | 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 | 15115 | } |
| 15033 | 15116 | } |
| 15034 | 15117 | |
| 15118 | if (buffer.len == 0) return; |
| 15035 | 15119 | const urandom_fd = try getRandomFd(t); |
| 15036 | 15120 | |
| 15037 | 15121 | var i: usize = 0; |
| ... | ... | @@ -15066,8 +15150,8 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15066 | 15150 | t.mutex.lock(); |
| 15067 | 15151 | defer t.mutex.unlock(); |
| 15068 | 15152 | |
| 15069 | | if (t.dev_urandom_fd == -2) return error.EntropyUnavailable; |
| 15070 | | if (t.dev_urandom_fd != -1) return t.dev_urandom_fd; |
| 15153 | if (t.random_file.fd == -2) return error.EntropyUnavailable; |
| 15154 | if (t.random_file.fd != -1) return t.random_file.fd; |
| 15071 | 15155 | } |
| 15072 | 15156 | |
| 15073 | 15157 | const fd: posix.fd_t = fd: { |
| ... | ... | @@ -15088,7 +15172,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15088 | 15172 | }, |
| 15089 | 15173 | else => { |
| 15090 | 15174 | syscall.endSyscall(); |
| 15091 | | t.dev_urandom_fd = -2; |
| 15175 | t.random_file.fd = -2; |
| 15092 | 15176 | return error.EntropyUnavailable; |
| 15093 | 15177 | }, |
| 15094 | 15178 | } |
| ... | ... | @@ -15108,14 +15192,14 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15108 | 15192 | if (!statx.mask.TYPE) return error.Unexpected; |
| 15109 | 15193 | t.mutex.lock(); // Another thread might have won the race. |
| 15110 | 15194 | defer t.mutex.unlock(); |
| 15111 | | if (t.dev_urandom_fd >= 0) { |
| 15195 | if (t.random_file.fd >= 0) { |
| 15112 | 15196 | posix.close(fd); |
| 15113 | | return t.dev_urandom_fd; |
| 15197 | return t.random_file.fd; |
| 15114 | 15198 | } else if (!posix.S.ISCHR(statx.mode)) { |
| 15115 | | t.dev_urandom_fd = -2; |
| 15199 | t.random_file.fd = -2; |
| 15116 | 15200 | return error.EntropyUnavailable; |
| 15117 | 15201 | } else { |
| 15118 | | t.dev_urandom_fd = fd; |
| 15202 | t.random_file.fd = fd; |
| 15119 | 15203 | return fd; |
| 15120 | 15204 | } |
| 15121 | 15205 | }, |
| ... | ... | @@ -15124,7 +15208,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15124 | 15208 | continue; |
| 15125 | 15209 | }, |
| 15126 | 15210 | else => { |
| 15127 | | t.dev_urandom_fd = -2; |
| 15211 | t.random_file.fd = -2; |
| 15128 | 15212 | return error.EntropyUnavailable; |
| 15129 | 15213 | }, |
| 15130 | 15214 | } |
| ... | ... | @@ -15137,14 +15221,14 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15137 | 15221 | switch (posix.errno(fstat_sym(fd, &stat))) { |
| 15138 | 15222 | .SUCCESS => { |
| 15139 | 15223 | syscall.finish(); |
| 15140 | | if (t.dev_urandom_fd >= 0) { |
| 15224 | if (t.random_file.fd >= 0) { |
| 15141 | 15225 | posix.close(fd); |
| 15142 | | return t.dev_urandom_fd; |
| 15226 | return t.random_file.fd; |
| 15143 | 15227 | } else if (!posix.S.ISCHR(stat.mode)) { |
| 15144 | | t.dev_urandom_fd = -2; |
| 15228 | t.random_file.fd = -2; |
| 15145 | 15229 | return error.EntropyUnavailable; |
| 15146 | 15230 | } else { |
| 15147 | | t.dev_urandom_fd = fd; |
| 15231 | t.random_file.fd = fd; |
| 15148 | 15232 | return fd; |
| 15149 | 15233 | } |
| 15150 | 15234 | }, |
| ... | ... | @@ -15153,7 +15237,7 @@ fn getRandomFd(t: *Threaded) posix.fd_t { |
| 15153 | 15237 | continue; |
| 15154 | 15238 | }, |
| 15155 | 15239 | else => { |
| 15156 | | t.dev_urandom_fd = -2; |
| 15240 | t.random_file.fd = -2; |
| 15157 | 15241 | return error.EntropyUnavailable; |
| 15158 | 15242 | }, |
| 15159 | 15243 | } |