Skip to content

Commit 63cd5a3

Browse files
committed
crypto/rand: add randcrash=0 GODEBUG
For #66821 Change-Id: I525c308d6d6243a2bc805e819dcf40b67e52ade5 Reviewed-on: https://go-review.googlesource.com/c/go/+/608435 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Daniel McCarney <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]>
1 parent 55b930e commit 63cd5a3

File tree

6 files changed

+62
-7
lines changed

6 files changed

+62
-7
lines changed

doc/godebug.md

+5
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ For Go 1.24, it now defaults to multipathtcp="2", thus
168168
enabled by default on listerners. Using multipathtcp="0" reverts to the
169169
pre-Go 1.24 behavior.
170170

171+
Go 1.24 changed [`crypto/rand.Read`](/pkg/crypto/rand/#Read) to crash the
172+
program on any error. This setting is controlled by the `randcrash` setting.
173+
For Go 1.24 it defaults to `randcrash=1`.
174+
Using `randcrash=0` reverts to the pre-Go 1.24 behavior.
175+
171176
### Go 1.23
172177

173178
Go 1.23 changed the channels created by package time to be unbuffered

src/crypto/rand/rand.go

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package rand
88

99
import (
1010
"crypto/internal/boring"
11+
"internal/godebug"
1112
"io"
1213
"os"
1314
"sync"
@@ -64,6 +65,8 @@ func (r *reader) Read(b []byte) (n int, err error) {
6465
//go:linkname fatal
6566
func fatal(string)
6667

68+
var randcrash = godebug.New("randcrash")
69+
6770
// Read fills b with cryptographically secure random bytes. It never returns an
6871
// error, and always fills b entirely.
6972
//
@@ -83,6 +86,10 @@ func Read(b []byte) (n int, err error) {
8386
copy(b, bb)
8487
}
8588
if err != nil {
89+
if randcrash.Value() == "0" {
90+
randcrash.IncNonDefault()
91+
return 0, err
92+
}
8693
fatal("crypto/rand: failed to read random data (see https://go.dev/issue/66821): " + err.Error())
8794
panic("unreachable") // To be sure.
8895
}

src/crypto/rand/rand_linux_test.go

+5-7
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,18 @@ func TestNoGetrandom(t *testing.T) {
4848
return
4949
}
5050

51-
buf := &bytes.Buffer{}
5251
cmd := testenv.Command(t, os.Args[0], "-test.v")
53-
cmd.Stdout = buf
54-
cmd.Stderr = buf
5552
cmd.Env = append(os.Environ(), "GO_GETRANDOM_DISABLED=1")
56-
if err := cmd.Run(); err != nil {
57-
t.Errorf("subprocess failed: %v\n%s", err, buf.Bytes())
53+
out, err := cmd.CombinedOutput()
54+
if err != nil {
55+
t.Errorf("subprocess failed: %v\n%s", err, out)
5856
return
5957
}
6058

61-
if !bytes.Contains(buf.Bytes(), []byte("GetRandom returned ENOSYS")) {
59+
if !bytes.Contains(out, []byte("GetRandom returned ENOSYS")) {
6260
t.Errorf("subprocess did not disable getrandom")
6361
}
64-
if !bytes.Contains(buf.Bytes(), []byte("TestRead")) {
62+
if !bytes.Contains(out, []byte("TestRead")) {
6563
t.Errorf("subprocess did not run TestRead")
6664
}
6765
}()

src/crypto/rand/rand_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
"bytes"
99
"compress/flate"
1010
"crypto/internal/boring"
11+
"errors"
1112
"internal/race"
13+
"internal/testenv"
1214
"io"
1315
"os"
1416
"runtime"
@@ -189,6 +191,44 @@ func TestNoUrandomFallback(t *testing.T) {
189191
}
190192
}
191193

194+
func TestReadError(t *testing.T) {
195+
if testing.Short() {
196+
t.Skip("skipping test in short mode")
197+
}
198+
testenv.MustHaveExec(t)
199+
200+
// We run this test in a subprocess because it's expected to crash the
201+
// program unless the GODEBUG is set.
202+
if os.Getenv("GO_TEST_READ_ERROR") == "1" {
203+
defer func(r io.Reader) { Reader = r }(Reader)
204+
Reader = readerFunc(func([]byte) (int, error) {
205+
return 0, errors.New("error")
206+
})
207+
if _, err := Read(make([]byte, 32)); err == nil {
208+
t.Error("Read did not return error")
209+
}
210+
return
211+
}
212+
213+
cmd := testenv.Command(t, os.Args[0], "-test.run=TestReadError")
214+
cmd.Env = append(os.Environ(), "GO_TEST_READ_ERROR=1")
215+
out, err := cmd.CombinedOutput()
216+
if err == nil {
217+
t.Error("subprocess succeeded unexpectedly")
218+
}
219+
exp := "fatal error: crypto/rand: failed to read random data"
220+
if !bytes.Contains(out, []byte(exp)) {
221+
t.Errorf("subprocess output does not contain %q: %s", exp, out)
222+
}
223+
224+
cmd = testenv.Command(t, os.Args[0], "-test.run=TestReadError")
225+
cmd.Env = append(os.Environ(), "GO_TEST_READ_ERROR=1", "GODEBUG=randcrash=0")
226+
out, err = cmd.CombinedOutput()
227+
if err != nil {
228+
t.Errorf("subprocess failed: %v\n%s", err, out)
229+
}
230+
}
231+
192232
func BenchmarkRead(b *testing.B) {
193233
b.Run("4", func(b *testing.B) {
194234
benchmarkRead(b, 4)

src/internal/godebugs/table.go

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var All = []Info{
4747
{Name: "netedns0", Package: "net", Changed: 19, Old: "0"},
4848
{Name: "panicnil", Package: "runtime", Changed: 21, Old: "1"},
4949
{Name: "randautoseed", Package: "math/rand"},
50+
{Name: "randcrash", Package: "crypto/rand", Changed: 24, Old: "0"},
5051
{Name: "randseednop", Package: "math/rand", Changed: 24, Old: "0"},
5152
{Name: "tarinsecurepath", Package: "archive/tar"},
5253
{Name: "tls10server", Package: "crypto/tls", Changed: 22, Old: "1"},

src/runtime/metrics/doc.go

+4
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ Below is the full list of supported metrics, ordered lexicographically.
306306
The number of non-default behaviors executed by the math/rand
307307
package due to a non-default GODEBUG=randautoseed=... setting.
308308
309+
/godebug/non-default-behavior/randcrash:events
310+
The number of non-default behaviors executed by the crypto/rand
311+
package due to a non-default GODEBUG=randcrash=... setting.
312+
309313
/godebug/non-default-behavior/randseednop:events
310314
The number of non-default behaviors executed by the math/rand
311315
package due to a non-default GODEBUG=randseednop=... setting.

0 commit comments

Comments
 (0)