Commit 2e4b409f31

Andrew Kelley <andrew@ziglang.org>
2020-12-18 06:51:53
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.
1 parent 228a093
Changed files (2)
lib
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;
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'";