Skip to content

Commit

Permalink
crypto/rand: use getrandom system call on Linux
Browse files Browse the repository at this point in the history
Adds internal/syscall package.

Fixes #8520

LGTM=r, agl
R=agl, rsc, r
CC=golang-codereviews, iant
https://golang.org/cl/123260044
  • Loading branch information
bradfitz committed Aug 12, 2014
1 parent 1837419 commit 67e1d40
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 2 deletions.
39 changes: 39 additions & 0 deletions 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
}
11 changes: 10 additions & 1 deletion src/pkg/crypto/rand/rand_unix.go
Expand Up @@ -20,14 +20,16 @@ import (
"time"
)

const urandomDevice = "/dev/urandom"

// Easy implementation: read from /dev/urandom.
// This is sufficient on Linux, OS X, and FreeBSD.

func init() {
if runtime.GOOS == "plan9" {
Reader = newReader(nil)
} else {
Reader = &devReader{name: "/dev/urandom"}
Reader = &devReader{name: urandomDevice}
}
}

Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/go/build/deps_test.go
Expand Up @@ -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.
Expand Down
55 changes: 55 additions & 0 deletions 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
}

0 comments on commit 67e1d40

Please sign in to comment.