Skip to content

Commit 67e1d40

Browse files
committed
crypto/rand: use getrandom system call on Linux
Adds internal/syscall package. Fixes #8520 LGTM=r, agl R=agl, rsc, r CC=golang-codereviews, iant https://golang.org/cl/123260044
1 parent 1837419 commit 67e1d40

File tree

4 files changed

+105
-2
lines changed

4 files changed

+105
-2
lines changed

src/pkg/crypto/rand/rand_linux.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2014 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package rand
6+
7+
import (
8+
"internal/syscall"
9+
"sync"
10+
)
11+
12+
func init() {
13+
altGetRandom = getRandomLinux
14+
}
15+
16+
var (
17+
once sync.Once
18+
useSyscall bool
19+
)
20+
21+
func pickStrategy() {
22+
// Test whether we should use the system call or /dev/urandom.
23+
// We'll fall back to urandom if:
24+
// - the kernel is too old (before 3.17)
25+
// - the machine has no entropy available (early boot + no hardware
26+
// entropy source?) and we want to avoid blocking later.
27+
var buf [1]byte
28+
n, err := syscall.GetRandom(buf[:], syscall.GRND_NONBLOCK)
29+
useSyscall = n == 1 && err == nil
30+
}
31+
32+
func getRandomLinux(p []byte) (ok bool) {
33+
once.Do(pickStrategy)
34+
if !useSyscall {
35+
return false
36+
}
37+
n, err := syscall.GetRandom(p, 0)
38+
return n == len(p) && err == nil
39+
}

src/pkg/crypto/rand/rand_unix.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ import (
2020
"time"
2121
)
2222

23+
const urandomDevice = "/dev/urandom"
24+
2325
// Easy implementation: read from /dev/urandom.
2426
// This is sufficient on Linux, OS X, and FreeBSD.
2527

2628
func init() {
2729
if runtime.GOOS == "plan9" {
2830
Reader = newReader(nil)
2931
} else {
30-
Reader = &devReader{name: "/dev/urandom"}
32+
Reader = &devReader{name: urandomDevice}
3133
}
3234
}
3335

@@ -38,7 +40,14 @@ type devReader struct {
3840
mu sync.Mutex
3941
}
4042

43+
// altGetRandom if non-nil specifies an OS-specific function to get
44+
// urandom-style randomness.
45+
var altGetRandom func([]byte) (ok bool)
46+
4147
func (r *devReader) Read(b []byte) (n int, err error) {
48+
if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) {
49+
return len(b), nil
50+
}
4251
r.mu.Lock()
4352
defer r.mu.Unlock()
4453
if r.f == nil {

src/pkg/go/build/deps_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ var pkgDeps = map[string][]string{
279279
// Random byte, number generation.
280280
// This would be part of core crypto except that it imports
281281
// math/big, which imports fmt.
282-
"crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall"},
282+
"crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall"},
283283

284284
// Mathematical crypto: dependencies on fmt (L4) and math/big.
285285
// We could avoid some of the fmt, but math/big imports fmt anyway.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2014 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package syscall
6+
7+
import (
8+
"runtime"
9+
"sync/atomic"
10+
stdsyscall "syscall"
11+
"unsafe"
12+
)
13+
14+
var randomTrap = map[string]uintptr{
15+
"amd64": 318,
16+
"386": 355,
17+
}[runtime.GOARCH]
18+
19+
var randomUnsupported int32 // atomic
20+
21+
// GetRandomFlag is a flag supported by the getrandom system call.
22+
type GetRandomFlag uintptr
23+
24+
const (
25+
// GRND_NONBLOCK means return EAGAIN rather than blocking.
26+
GRND_NONBLOCK GetRandomFlag = 0x0001
27+
28+
// GRND_RANDOM means use the /dev/random pool instead of /dev/urandom.
29+
GRND_RANDOM GetRandomFlag = 0x0002
30+
)
31+
32+
// GetRandom calls the Linux getrandom system call.
33+
// See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6e9d6f38894798696f23c8084ca7edbf16ee895
34+
func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
35+
if randomTrap == 0 {
36+
return 0, stdsyscall.ENOSYS
37+
}
38+
if len(p) == 0 {
39+
return 0, nil
40+
}
41+
if atomic.LoadInt32(&randomUnsupported) != 0 {
42+
return 0, stdsyscall.ENOSYS
43+
}
44+
r1, _, errno := stdsyscall.Syscall(randomTrap,
45+
uintptr(unsafe.Pointer(&p[0])),
46+
uintptr(len(p)),
47+
uintptr(flags))
48+
if errno != 0 {
49+
if errno == stdsyscall.ENOSYS {
50+
atomic.StoreInt32(&randomUnsupported, 1)
51+
}
52+
return 0, errno
53+
}
54+
return int(r1), nil
55+
}

0 commit comments

Comments
 (0)