| ... | @@ -48,6 +48,16 @@ const Context = struct { | ... | @@ -48,6 +48,16 @@ const Context = struct { |
| 48 | gimli: std.crypto.core.Gimli, | 48 | gimli: std.crypto.core.Gimli, |
| 49 | }; | 49 | }; |
| 50 | | 50 | |
| | 51 | var install_atfork_handler = std.once(struct { |
| | 52 | // Install the global handler only once. |
| | 53 | // The same handler is shared among threads and is inherinted by fork()-ed |
| | 54 | // processes. |
| | 55 | fn do() void { |
| | 56 | const r = std.c.pthread_atfork(null, null, childAtForkHandler); |
| | 57 | std.debug.assert(r == 0); |
| | 58 | } |
| | 59 | }.do); |
| | 60 | |
| 51 | threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{}; | 61 | threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{}; |
| 52 | | 62 | |
| 53 | fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | 63 | fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| ... | @@ -107,13 +117,9 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | ... | @@ -107,13 +117,9 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| 107 | break :wof; | 117 | break :wof; |
| 108 | } else |_| {} | 118 | } else |_| {} |
| 109 | | 119 | |
| 110 | os.madvise( | 120 | if (os.madvise(wipe_mem.ptr, wipe_mem.len, os.MADV_WIPEONFORK)) |_| { |
| 111 | wipe_mem.ptr, | | |
| 112 | wipe_mem.len, | | |
| 113 | os.MADV_WIPEONFORK, | | |
| 114 | ) catch { | | |
| 115 | return initAndFill(buffer); | 121 | return initAndFill(buffer); |
| 116 | }; | 122 | } else |_| {} |
| 117 | } | 123 | } |
| 118 | | 124 | |
| 119 | if (std.Thread.use_pthreads) { | 125 | if (std.Thread.use_pthreads) { |
| ... | @@ -139,17 +145,14 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | ... | @@ -139,17 +145,14 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| 139 | } | 145 | } |
| 140 | | 146 | |
| 141 | fn setupPthreadAtforkAndFill(buffer: []u8) void { | 147 | fn setupPthreadAtforkAndFill(buffer: []u8) void { |
| 142 | const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0; | 148 | install_atfork_handler.call(); |
| 143 | if (failed) { | 149 | return initAndFill(buffer); |
| 144 | const ctx = @ptrCast(*Context, wipe_mem.ptr); | | |
| 145 | ctx.init_state = .failed; | | |
| 146 | return fillWithOsEntropy(buffer); | | |
| 147 | } else { | | |
| 148 | return initAndFill(buffer); | | |
| 149 | } | | |
| 150 | } | 150 | } |
| 151 | | 151 | |
| 152 | fn childAtForkHandler() callconv(.C) void { | 152 | fn childAtForkHandler() callconv(.C) void { |
| | 153 | // The atfork handler is global, this function may be called after |
| | 154 | // fork()-ing threads that never initialized the CSPRNG context. |
| | 155 | if (wipe_mem.len == 0) return; |
| 153 | std.crypto.utils.secureZero(u8, wipe_mem); | 156 | std.crypto.utils.secureZero(u8, wipe_mem); |
| 154 | } | 157 | } |
| 155 | | 158 | |