From 5308c6d9325735ab4e69620e99aaa8d119e02941 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 16 Dec 2014 15:11:43 +1100 Subject: [PATCH] crypto/rand: handle EAGAIN reads from /dev/urandom Fixes #9205 Change-Id: Iacd608ba43332008984aa8ece17dcb5757f27b3f Reviewed-on: https://go-review.googlesource.com/1611 Reviewed-by: Ian Lance Taylor --- src/crypto/rand/eagain.go | 27 +++++++++++++++++++++++++++ src/crypto/rand/rand_unix.go | 18 +++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/crypto/rand/eagain.go diff --git a/src/crypto/rand/eagain.go b/src/crypto/rand/eagain.go new file mode 100644 index 00000000000..2c853d0a134 --- /dev/null +++ b/src/crypto/rand/eagain.go @@ -0,0 +1,27 @@ +// 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package rand + +import ( + "os" + "syscall" +) + +func init() { + isEAGAIN = unixIsEAGAIN +} + +// unixIsEAGAIN reports whether err is a syscall.EAGAIN wrapped in a PathError. +// See golang.org/issue/9205 +func unixIsEAGAIN(err error) bool { + if pe, ok := err.(*os.PathError); ok { + if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN { + return true + } + } + return false +} diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go index 62d0fbdb350..75c36e05b34 100644 --- a/src/crypto/rand/rand_unix.go +++ b/src/crypto/rand/rand_unix.go @@ -58,12 +58,28 @@ func (r *devReader) Read(b []byte) (n int, err error) { if runtime.GOOS == "plan9" { r.f = f } else { - r.f = bufio.NewReader(f) + r.f = bufio.NewReader(hideAgainReader{f}) } } return r.f.Read(b) } +var isEAGAIN func(error) bool // set by eagain.go on unix systems + +// hideAgainReader masks EAGAIN reads from /dev/urandom. +// See golang.org/issue/9205 +type hideAgainReader struct { + r io.Reader +} + +func (hr hideAgainReader) Read(p []byte) (n int, err error) { + n, err = hr.r.Read(p) + if err != nil && isEAGAIN != nil && isEAGAIN(err) { + err = nil + } + return +} + // Alternate pseudo-random implementation for use on // systems without a reliable /dev/urandom.