From 2e4b409f31352ff08dbabf350519ba0f5212218a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 Dec 2020 22:51:53 -0700 Subject: [PATCH] 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. --- lib/std/crypto/tlcsprng.zig | 4 ++-- lib/std/start.zig | 38 +++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/std/crypto/tlcsprng.zig b/lib/std/crypto/tlcsprng.zig index 0105cf0ce8..4e3036ecde 100644 --- a/lib/std/crypto/tlcsprng.zig +++ b/lib/std/crypto/tlcsprng.zig @@ -18,7 +18,7 @@ const mem = std.mem; pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill }; pub threadlocal var csprng_state: std.crypto.core.Gimli = undefined; pub threadlocal var csprng_state_initialized = false; -fn tlsCsprngFill(r: *std.rand.Random, buf: []u8) void { +fn tlsCsprngFill(r: *const std.rand.Random, buf: []u8) void { if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) { // arc4random is already a thread-local CSPRNG. return std.c.arc4random_buf(buf.ptr, buf.len); @@ -48,7 +48,7 @@ fn defaultSeed(buffer: *[seed_len]u8) void { std.os.getrandom(buffer) catch @panic("getrandom() failed to seed thread-local CSPRNG"); } -pub const seed_len = 32; +pub const seed_len = 16; pub fn init(seed: [seed_len]u8) void { var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined; diff --git a/lib/std/start.zig b/lib/std/start.zig index d3065c7719..25c578c0ef 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -206,7 +206,6 @@ fn posixCallMainAndExit() noreturn { // Do this as early as possible, the aux vector is needed if (builtin.position_independent_executable) { @import("os/linux/start_pie.zig").apply_relocations(); - @fence(.SeqCst); } // Initialize the TLS area. We do a runtime check here to make sure @@ -215,10 +214,9 @@ fn posixCallMainAndExit() noreturn { const is_dynamic = @import("dynamic_library.zig").get_DYNAMIC() != null; if (!is_dynamic) { std.os.linux.tls.initStaticTLS(); - @fence(.SeqCst); } - { + if (!@hasDecl(root, "use_AT_RANDOM_auxval") or root.use_AT_RANDOM_auxval) { // Initialize the per-thread CSPRNG since Linux gave us the handy-dandy // AT_RANDOM. This depends on the TLS initialization above. var i: usize = 0; @@ -226,19 +224,7 @@ fn posixCallMainAndExit() noreturn { switch (auxv[i].a_type) { std.elf.AT_RANDOM => { // "The address of sixteen bytes containing a random value." - const addr = auxv[i].a_un.a_val; - if (addr == 0) break; - const ptr = @intToPtr(*[16]u8, addr); - var seed: [32]u8 = undefined; - seed[0..16].* = ptr.*; - seed[16..].* = ptr.*; - tlcsprng.init(seed); - // Overwrite AT_RANDOM after we use it, otherwise our secure - // seed is sitting in memory ready for some other code in the - // program to reuse, and hence break our security. - // We play nice by refreshing it with fresh random bytes - // rather than clearing it. - std.crypto.random.bytes(ptr); + initCryptoSeedFromAuxVal(auxv[i].a_un.a_val); break; }, else => continue, @@ -281,15 +267,31 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { } fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 { - // We do not attempt to initialize tlcsprng from AT_RANDOM here because - // libc owns the start code, not us, and therefore libc ows the random bytes + // By default, we do not attempt to initialize tlcsprng from AT_RANDOM here because + // libc owns the start code, not us, and therefore libc owns the random bytes // from AT_RANDOM. + if (builtin.os.tag == .linux and + @hasDecl(root, "use_AT_RANDOM_auxval") and + root.use_AT_RANDOM_auxval) + { + initCryptoSeedFromAuxVal(std.c.getauxval(std.elf.AT_RANDOM)); + } var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count]; return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); } +fn initCryptoSeedFromAuxVal(addr: usize) void { + if (addr == 0) return; + const ptr = @intToPtr(*[16]u8, addr); + tlcsprng.init(ptr.*); + // Clear AT_RANDOM after we use it, otherwise our secure + // seed is sitting in memory ready for some other code in the + // program to reuse, and hence break our security. + std.crypto.utils.secureZero(u8, ptr); +} + // General error message for a malformed return type const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";