diff options
author | Brad Fitzpatrick <bradfitz@golang.org> | 2014-08-12 14:35:27 -0700 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@golang.org> | 2014-08-12 14:35:27 -0700 |
commit | c3374e3eaf4ec6bc10cb6637f089fd5845bb1270 (patch) | |
tree | cb0a025dbbe72900d2e36dfb439ab81a07a75749 /src/pkg | |
parent | da708d8620611d27dc1789b778fc66ec262cf155 (diff) | |
download | go-c3374e3eaf4ec6bc10cb6637f089fd5845bb1270.tar.gz |
crypto/rand: use getrandom system call on Linux
Adds internal/syscall package.
Fixes Issue 8520
LGTM=r, agl
R=agl, rsc, r
CC=golang-codereviews, iant
https://codereview.appspot.com/123260044
Diffstat (limited to 'src/pkg')
-rw-r--r-- | src/pkg/crypto/rand/rand_linux.go | 39 | ||||
-rw-r--r-- | src/pkg/crypto/rand/rand_unix.go | 11 | ||||
-rw-r--r-- | src/pkg/go/build/deps_test.go | 2 | ||||
-rw-r--r-- | src/pkg/internal/syscall/getrandom_linux.go | 55 |
4 files changed, 105 insertions, 2 deletions
diff --git a/src/pkg/crypto/rand/rand_linux.go b/src/pkg/crypto/rand/rand_linux.go new file mode 100644 index 000000000..8cb59c75d --- /dev/null +++ b/src/pkg/crypto/rand/rand_linux.go @@ -0,0 +1,39 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "internal/syscall" + "sync" +) + +func init() { + altGetRandom = getRandomLinux +} + +var ( + once sync.Once + useSyscall bool +) + +func pickStrategy() { + // Test whether we should use the system call or /dev/urandom. + // We'll fall back to urandom if: + // - the kernel is too old (before 3.17) + // - the machine has no entropy available (early boot + no hardware + // entropy source?) and we want to avoid blocking later. + var buf [1]byte + n, err := syscall.GetRandom(buf[:], syscall.GRND_NONBLOCK) + useSyscall = n == 1 && err == nil +} + +func getRandomLinux(p []byte) (ok bool) { + once.Do(pickStrategy) + if !useSyscall { + return false + } + n, err := syscall.GetRandom(p, 0) + return n == len(p) && err == nil +} diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go index 1e741fda1..62d0fbdb3 100644 --- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.go @@ -20,6 +20,8 @@ import ( "time" ) +const urandomDevice = "/dev/urandom" + // Easy implementation: read from /dev/urandom. // This is sufficient on Linux, OS X, and FreeBSD. @@ -27,7 +29,7 @@ func init() { if runtime.GOOS == "plan9" { Reader = newReader(nil) } else { - Reader = &devReader{name: "/dev/urandom"} + Reader = &devReader{name: urandomDevice} } } @@ -38,7 +40,14 @@ type devReader struct { mu sync.Mutex } +// altGetRandom if non-nil specifies an OS-specific function to get +// urandom-style randomness. +var altGetRandom func([]byte) (ok bool) + func (r *devReader) Read(b []byte) (n int, err error) { + if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) { + return len(b), nil + } r.mu.Lock() defer r.mu.Unlock() if r.f == nil { diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go index 99b985b51..2a7173ba4 100644 --- a/src/pkg/go/build/deps_test.go +++ b/src/pkg/go/build/deps_test.go @@ -279,7 +279,7 @@ var pkgDeps = map[string][]string{ // Random byte, number generation. // This would be part of core crypto except that it imports // math/big, which imports fmt. - "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall"}, + "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall"}, // Mathematical crypto: dependencies on fmt (L4) and math/big. // We could avoid some of the fmt, but math/big imports fmt anyway. diff --git a/src/pkg/internal/syscall/getrandom_linux.go b/src/pkg/internal/syscall/getrandom_linux.go new file mode 100644 index 000000000..1c586ec9b --- /dev/null +++ b/src/pkg/internal/syscall/getrandom_linux.go @@ -0,0 +1,55 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +import ( + "runtime" + "sync/atomic" + stdsyscall "syscall" + "unsafe" +) + +var randomTrap = map[string]uintptr{ + "amd64": 318, + "386": 355, +}[runtime.GOARCH] + +var randomUnsupported int32 // atomic + +// GetRandomFlag is a flag supported by the getrandom system call. +type GetRandomFlag uintptr + +const ( + // GRND_NONBLOCK means return EAGAIN rather than blocking. + GRND_NONBLOCK GetRandomFlag = 0x0001 + + // GRND_RANDOM means use the /dev/random pool instead of /dev/urandom. + GRND_RANDOM GetRandomFlag = 0x0002 +) + +// GetRandom calls the Linux getrandom system call. +// See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6e9d6f38894798696f23c8084ca7edbf16ee895 +func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) { + if randomTrap == 0 { + return 0, stdsyscall.ENOSYS + } + if len(p) == 0 { + return 0, nil + } + if atomic.LoadInt32(&randomUnsupported) != 0 { + return 0, stdsyscall.ENOSYS + } + r1, _, errno := stdsyscall.Syscall(randomTrap, + uintptr(unsafe.Pointer(&p[0])), + uintptr(len(p)), + uintptr(flags)) + if errno != 0 { + if errno == stdsyscall.ENOSYS { + atomic.StoreInt32(&randomUnsupported, 1) + } + return 0, errno + } + return int(r1), nil +} |