authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-17 22:51:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-18 12:22:46-07:00
log2e4b409f31352ff08dbabf350519ba0f5212218a
treee0107a3c5dc9aa81a0538c48c7adb798364df8f2
parent228a0937a2bb761b3a63d98ddda5402a1f594fe8

std: tlcsprng: cleanups & improvements

* get rid of the pointless fences * make seed_len 16 instead of 32, which is accurate since it was already padding the rest anyway; now we do 1 pad instead of 2. * secureZero to clear the AT_RANDOM auxval * add a flag root source files can use to disable the start code. This is in case people want to opt out of the initialization when they don't depend on it.

2 files changed, 22 insertions(+), 20 deletions(-)

lib/std/crypto/tlcsprng.zig+2-2
...@@ -18,7 +18,7 @@ const mem = std.mem;...@@ -18,7 +18,7 @@ const mem = std.mem;
18pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill };18pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill };
19pub threadlocal var csprng_state: std.crypto.core.Gimli = undefined;19pub threadlocal var csprng_state: std.crypto.core.Gimli = undefined;
20pub threadlocal var csprng_state_initialized = false;20pub threadlocal var csprng_state_initialized = false;
21fn tlsCsprngFill(r: *std.rand.Random, buf: []u8) void {21fn tlsCsprngFill(r: *const std.rand.Random, buf: []u8) void {
22 if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) {22 if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) {
23 // arc4random is already a thread-local CSPRNG.23 // arc4random is already a thread-local CSPRNG.
24 return std.c.arc4random_buf(buf.ptr, buf.len);24 return std.c.arc4random_buf(buf.ptr, buf.len);
...@@ -48,7 +48,7 @@ fn defaultSeed(buffer: *[seed_len]u8) void {...@@ -48,7 +48,7 @@ fn defaultSeed(buffer: *[seed_len]u8) void {
48 std.os.getrandom(buffer) catch @panic("getrandom() failed to seed thread-local CSPRNG");48 std.os.getrandom(buffer) catch @panic("getrandom() failed to seed thread-local CSPRNG");
49}49}
5050
51pub const seed_len = 32;51pub const seed_len = 16;
5252
53pub fn init(seed: [seed_len]u8) void {53pub fn init(seed: [seed_len]u8) void {
54 var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;54 var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
lib/std/start.zig+20-18
...@@ -206,7 +206,6 @@ fn posixCallMainAndExit() noreturn {...@@ -206,7 +206,6 @@ fn posixCallMainAndExit() noreturn {
206 // Do this as early as possible, the aux vector is needed206 // Do this as early as possible, the aux vector is needed
207 if (builtin.position_independent_executable) {207 if (builtin.position_independent_executable) {
208 @import("os/linux/start_pie.zig").apply_relocations();208 @import("os/linux/start_pie.zig").apply_relocations();
209 @fence(.SeqCst);
210 }209 }
211210
212 // Initialize the TLS area. We do a runtime check here to make sure211 // Initialize the TLS area. We do a runtime check here to make sure
...@@ -215,10 +214,9 @@ fn posixCallMainAndExit() noreturn {...@@ -215,10 +214,9 @@ fn posixCallMainAndExit() noreturn {
215 const is_dynamic = @import("dynamic_library.zig").get_DYNAMIC() != null;214 const is_dynamic = @import("dynamic_library.zig").get_DYNAMIC() != null;
216 if (!is_dynamic) {215 if (!is_dynamic) {
217 std.os.linux.tls.initStaticTLS();216 std.os.linux.tls.initStaticTLS();
218 @fence(.SeqCst);
219 }217 }
220218
221 {219 if (!@hasDecl(root, "use_AT_RANDOM_auxval") or root.use_AT_RANDOM_auxval) {
222 // Initialize the per-thread CSPRNG since Linux gave us the handy-dandy220 // Initialize the per-thread CSPRNG since Linux gave us the handy-dandy
223 // AT_RANDOM. This depends on the TLS initialization above.221 // AT_RANDOM. This depends on the TLS initialization above.
224 var i: usize = 0;222 var i: usize = 0;
...@@ -226,19 +224,7 @@ fn posixCallMainAndExit() noreturn {...@@ -226,19 +224,7 @@ fn posixCallMainAndExit() noreturn {
226 switch (auxv[i].a_type) {224 switch (auxv[i].a_type) {
227 std.elf.AT_RANDOM => {225 std.elf.AT_RANDOM => {
228 // "The address of sixteen bytes containing a random value."226 // "The address of sixteen bytes containing a random value."
229 const addr = auxv[i].a_un.a_val;227 initCryptoSeedFromAuxVal(auxv[i].a_un.a_val);
230 if (addr == 0) break;
231 const ptr = @intToPtr(*[16]u8, addr);
232 var seed: [32]u8 = undefined;
233 seed[0..16].* = ptr.*;
234 seed[16..].* = ptr.*;
235 tlcsprng.init(seed);
236 // Overwrite AT_RANDOM after we use it, otherwise our secure
237 // seed is sitting in memory ready for some other code in the
238 // program to reuse, and hence break our security.
239 // We play nice by refreshing it with fresh random bytes
240 // rather than clearing it.
241 std.crypto.random.bytes(ptr);
242 break;228 break;
243 },229 },
244 else => continue,230 else => continue,
...@@ -281,15 +267,31 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {...@@ -281,15 +267,31 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
281}267}
282268
283fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 {269fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 {
284 // We do not attempt to initialize tlcsprng from AT_RANDOM here because270 // By default, we do not attempt to initialize tlcsprng from AT_RANDOM here because
285 // libc owns the start code, not us, and therefore libc ows the random bytes271 // libc owns the start code, not us, and therefore libc owns the random bytes
286 // from AT_RANDOM.272 // from AT_RANDOM.
273 if (builtin.os.tag == .linux and
274 @hasDecl(root, "use_AT_RANDOM_auxval") and
275 root.use_AT_RANDOM_auxval)
276 {
277 initCryptoSeedFromAuxVal(std.c.getauxval(std.elf.AT_RANDOM));
278 }
287 var env_count: usize = 0;279 var env_count: usize = 0;
288 while (c_envp[env_count] != null) : (env_count += 1) {}280 while (c_envp[env_count] != null) : (env_count += 1) {}
289 const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count];281 const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count];
290 return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });282 return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
291}283}
292284
285fn initCryptoSeedFromAuxVal(addr: usize) void {
286 if (addr == 0) return;
287 const ptr = @intToPtr(*[16]u8, addr);
288 tlcsprng.init(ptr.*);
289 // Clear AT_RANDOM after we use it, otherwise our secure
290 // seed is sitting in memory ready for some other code in the
291 // program to reuse, and hence break our security.
292 std.crypto.utils.secureZero(u8, ptr);
293}
294
293// General error message for a malformed return type295// General error message for a malformed return type
294const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";296const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";
295297