lib/wasm/wasm_exec.js | 2 +- src/bufio/bufio.go | 7 ++++--- src/cmd/compile/internal/ssa/_gen/rulegen.go | 3 ++- src/cmd/go/alldocs.go | 6 +++++- src/cmd/go/internal/help/helpdoc.go | 6 +++++- src/cmd/go/testdata/script/goauth_userauth.txt | 94 ++++++++++++++++++++++++++++++++++++++++++----------- src/cmd/internal/objfile/disasm.go => src/cmd/internal/disasm/disasm.go | 28 +++++++++++++++------------- src/cmd/internal/objfile/objfile.go | 10 ++++++---- src/cmd/objdump/main.go | 3 ++- src/cmd/pprof/pprof.go | 9 +++++---- src/crypto/ecdh/nist.go | 5 +++++ src/crypto/ecdsa/ecdsa.go | 7 +++++++ src/crypto/internal/fips140/drbg/rand.go | 37 +++++++++++++++++++++++++++++++++++++ src/crypto/internal/fips140/ecdh/ecdh.go | 20 ++++++-------------- src/crypto/internal/fips140/ecdsa/cast.go | 3 ++- src/crypto/internal/fips140/ecdsa/ecdsa.go | 23 +++-------------------- src/crypto/internal/fips140/ecdsa/hmacdrbg.go | 2 ++ src/crypto/internal/fips140/ed25519/ed25519.go | 19 ++++--------------- src/crypto/internal/fips140/rsa/keygen.go | 14 +++----------- src/crypto/internal/fips140/rsa/pkcs1v22.go | 21 ++++----------------- src/crypto/internal/fips140only/fips140only.go | 7 +++++++ src/crypto/internal/fips140test/cast_test.go | 2 +- src/crypto/rand/rand.go | 4 +++- src/crypto/rsa/fips.go | 9 +++++++++ src/crypto/rsa/rsa.go | 3 +++ src/crypto/rsa/rsa_test.go | 4 ---- src/html/template/js.go | 11 +++++++---- src/html/template/js_test.go | 4 ++-- src/internal/goos/gengoos.go | 2 +- src/internal/synctest/synctest_test.go | 41 +++++++++++++++++++++++++++++++++++++++-- src/log/slog/example_discard_test.go | 2 +- src/net/http/http.go | 1 + src/net/interface_linux.go | 21 +++------------------ src/reflect/map_noswiss.go | 2 ++ src/reflect/map_swiss.go | 2 ++ src/reflect/value.go | 36 +++++++++++++++++++++++------------- src/runtime/mcleanup.go | 37 ++++++++++++++++++++++++++++++------- src/runtime/mfinal.go | 3 +++ src/runtime/mheap.go | 4 ++-- src/runtime/pinner.go | 2 +- src/runtime/proflabel.go | 2 -- src/runtime/symtab.go | 13 +++++++++---- src/runtime/time.go | 10 +++++++--- src/syscall/env_unix.go | 2 +- src/weak/doc.go | 27 ++------------------------- src/weak/pointer.go | 42 +++++++++++++++++++++++++++++++++++------- src/weak/pointer_test.go | 17 +++++++++++++++++ diff --git a/lib/wasm/wasm_exec.js b/lib/wasm/wasm_exec.js index ec96d42db52cc7639e6817e27395317900e63640..d71af9e97e8ca961f816f753afcf3738a50fd6a2 100644 --- a/lib/wasm/wasm_exec.js +++ b/lib/wasm/wasm_exec.js @@ -14,7 +14,7 @@ if (!globalThis.fs) { let outputBuf = ""; globalThis.fs = { - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused writeSync(fd, buf) { outputBuf += decoder.decode(buf); const nl = outputBuf.lastIndexOf("\n"); diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index 160bd8adb325741d080d5b0ff34bd8df0d3602f9..d589701e19a85c19c860707843dfac8c645e4bb0 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -133,9 +133,10 @@ return err } // Peek returns the next n bytes without advancing the reader. The bytes stop -// being valid at the next read call. If Peek returns fewer than n bytes, it -// also returns an error explaining why the read is short. The error is -// [ErrBufferFull] if n is larger than b's buffer size. +// being valid at the next read call. If necessary, Peek will read more bytes +// into the buffer in order to make n bytes available. If Peek returns fewer +// than n bytes, it also returns an error explaining why the read is short. +// The error is [ErrBufferFull] if n is larger than b's buffer size. // // Calling Peek prevents a [Reader.UnreadByte] or [Reader.UnreadRune] call from succeeding // until the next read operation. diff --git a/src/cmd/compile/internal/ssa/_gen/rulegen.go b/src/cmd/compile/internal/ssa/_gen/rulegen.go index b6356315014411d5287dc4fab89700e07e086a9f..4374d3e153f69e96c14d367181cef430c586392c 100644 --- a/src/cmd/compile/internal/ssa/_gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/_gen/rulegen.go @@ -5,7 +5,8 @@ // This program generates Go code that applies rewrite rules to a Value. // The generated code implements a function of type func (v *Value) bool // which reports whether if did something. -// Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html +// Ideas stolen from the Swift Java compiler: +// https://bitsavers.org/pdf/dec/tech_reports/WRL-2000-2.pdf package main diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index ced43b6d5bb8537831e3e11a9aad84c9159c2d11..3a4473902cb8f1c33bc5a8465ea4ca58d929932f 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -2210,7 +2210,10 @@ // -buildmode=c-shared // Build the listed main package, plus all packages it imports, // into a C shared library. The only callable symbols will // be those functions exported using a cgo //export comment. -// Requires exactly one main package to be listed. +// On wasip1, this mode builds it to a WASI reactor/library, +// of which the callable symbols are those functions exported +// using a //go:wasmexport directive. Requires exactly one +// main package to be listed. // // -buildmode=default // Listed main packages are built into executables and listed @@ -2261,6 +2264,7 @@ // // The go command caches build outputs for reuse in future builds. // The default location for cache data is a subdirectory named go-build // in the standard user cache directory for the current operating system. +// The cache is safe for concurrent invocations of the go command. // Setting the GOCACHE environment variable overrides this default, // and running 'go env GOCACHE' prints the current cache directory. // diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index a64f980e5ee610a5834a041dc0542fe9acebdc83..e1240de710b7a98244fc003b7783e1479c9eabc0 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -769,7 +769,10 @@ -buildmode=c-shared Build the listed main package, plus all packages it imports, into a C shared library. The only callable symbols will be those functions exported using a cgo //export comment. - Requires exactly one main package to be listed. + On wasip1, this mode builds it to a WASI reactor/library, + of which the callable symbols are those functions exported + using a //go:wasmexport directive. Requires exactly one + main package to be listed. -buildmode=default Listed main packages are built into executables and listed @@ -806,6 +809,7 @@ Long: ` The go command caches build outputs for reuse in future builds. The default location for cache data is a subdirectory named go-build in the standard user cache directory for the current operating system. +The cache is safe for concurrent invocations of the go command. Setting the GOCACHE environment variable overrides this default, and running 'go env GOCACHE' prints the current cache directory. diff --git a/src/cmd/go/testdata/script/goauth_userauth.txt b/src/cmd/go/testdata/script/goauth_userauth.txt index 8403c37125d050a504c480e746604fe7cc785ecc..036573e07a2a3ff733ba93fee35146d96d5f45b2 100644 --- a/src/cmd/go/testdata/script/goauth_userauth.txt +++ b/src/cmd/go/testdata/script/goauth_userauth.txt @@ -3,13 +3,8 @@ # See golang.org/issue/26232 env GOPROXY=direct env GOSUMDB=off - -# Use a custom authenticator to provide custom credentials mkdir $WORK/bin env PATH=$WORK/bin${:}$PATH -cd auth -go build -o $WORK/bin/my-auth$GOEXE . -cd .. # Without credentials, downloading a module from a path that requires HTTPS # basic auth should fail. @@ -21,8 +16,32 @@ # go imports should fail as well. ! go mod tidy stderr '^\tserver response: ACCESS DENIED, buddy$' -# With credentials from the my-auth binary, it should succeed. -env GOAUTH='my-auth'$GOEXE' --arg1 "value with spaces"' +# Initial invocation of authenticator is successful. +go build -o $WORK/bin/basic$GOEXE scripts/basic.go +# With credentials from the binary, it should succeed. +env GOAUTH='basic'$GOEXE +cp go.mod.orig go.mod +go get vcs-test.golang.org/auth/or401 +# go imports should resolve correctly as well. +go mod tidy +go list all +stdout vcs-test.golang.org/auth/or401 + +# Second invocation of authenticator is successful. +go build -o $WORK/bin/reinvocation$GOEXE scripts/reinvocation.go +# With credentials from the binary, it should succeed. +env GOAUTH='reinvocation'$GOEXE +cp go.mod.orig go.mod +go get vcs-test.golang.org/auth/or401 +# go imports should resolve correctly as well. +go mod tidy +go list all +stdout vcs-test.golang.org/auth/or401 + +# Authenticator can parse arguments correctly. +go build -o $WORK/bin/arguments$GOEXE scripts/arguments.go +# With credentials from the binary, it should succeed. +env GOAUTH='arguments'$GOEXE' --arg1 "value with spaces"' cp go.mod.orig go.mod go get vcs-test.golang.org/auth/or401 # go imports should resolve correctly as well. @@ -30,7 +49,32 @@ go mod tidy go list all stdout vcs-test.golang.org/auth/or401 --- auth/main.go -- +# Authenticator provides bad credentials. +go build -o $WORK/bin/invalid$GOEXE scripts/invalid.go +# With credentials from the binary, it should fail. +env GOAUTH='invalid'$GOEXE +cp go.mod.orig go.mod +! go get vcs-test.golang.org/auth/or401 +stderr '^\tserver response: ACCESS DENIED, buddy$' +# go imports should fail as well. +! go mod tidy +stderr '^\tserver response: ACCESS DENIED, buddy$' + +-- go.mod.orig -- +module private.example.com +-- main.go -- +package useprivate + +import "vcs-test.golang.org/auth/or401" +-- scripts/basic.go -- +package main + +import "fmt" + +func main() { + fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l\n\n") +} +-- scripts/reinvocation.go -- package main import( @@ -45,11 +89,7 @@ "strings" ) func main() { - arg1 := flag.String("arg1", "", "") flag.Parse() - if *arg1 != "value with spaces" { - log.Fatal("argument with spaces does not work") - } // wait for re-invocation if !strings.HasPrefix(flag.Arg(0), "https://vcs-test.golang.org") { return @@ -68,12 +108,28 @@ log.Fatal("expected 401 error code") } fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l\n\n") } +-- scripts/arguments.go -- +package main --- auth/go.mod -- -module my-auth --- go.mod.orig -- -module private.example.com --- main.go -- -package useprivate +import( + "flag" + "fmt" + "log" +) + +func main() { + arg1 := flag.String("arg1", "", "") + flag.Parse() + if *arg1 != "value with spaces" { + log.Fatal("argument with spaces does not work") + } + fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l\n\n") +} +-- scripts/invalid.go -- +package main + +import "fmt" -import "vcs-test.golang.org/auth/or401" +func main() { + fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic invalid\n\n") +} \ No newline at end of file diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/disasm/disasm.go rename from src/cmd/internal/objfile/disasm.go rename to src/cmd/internal/disasm/disasm.go index 99f54143fa986331e07749bbfc18d9623c78b625..c317effa900f60d3080b387a13463152b379e121 100644 --- a/src/cmd/internal/objfile/disasm.go +++ b/src/cmd/internal/disasm/disasm.go @@ -2,13 +2,16 @@ // 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 objfile +// Package disasm provides disassembly routines. +// +// It is broken out from cmd/internal/objfile so tools that don't need +// disassembling don't need to depend on x/arch disassembler code. +package disasm import ( "bufio" "bytes" "container/list" - "debug/gosym" "encoding/binary" "fmt" "io" @@ -19,6 +22,7 @@ "sort" "strings" "text/tabwriter" + "cmd/internal/objfile" "cmd/internal/src" "golang.org/x/arch/arm/armasm" @@ -32,8 +36,8 @@ ) // Disasm is a disassembler for a given File. type Disasm struct { - syms []Sym //symbols in file, sorted by address - pcln Liner // pcln table + syms []objfile.Sym // symbols in file, sorted by address + pcln objfile.Liner // pcln table text []byte // bytes of text segment (actual instructions) textStart uint64 // start PC of text textEnd uint64 // end PC of text @@ -42,8 +46,12 @@ disasm disasmFunc // disassembler function for goarch byteOrder binary.ByteOrder // byte order for goarch } -// Disasm returns a disassembler for the file f. -func (e *Entry) Disasm() (*Disasm, error) { +// DisasmForFile returns a disassembler for the file f. +func DisasmForFile(f *objfile.File) (*Disasm, error) { + return disasmForEntry(f.Entries()[0]) +} + +func disasmForEntry(e *objfile.Entry) (*Disasm, error) { syms, err := e.Symbols() if err != nil { return nil, err @@ -269,7 +277,7 @@ bw.Flush() } // Decode disassembles the text segment range [start, end), calling f for each instruction. -func (d *Disasm) Decode(start, end uint64, relocs []Reloc, gnuAsm bool, f func(pc, size uint64, file string, line int, text string)) { +func (d *Disasm) Decode(start, end uint64, relocs []objfile.Reloc, gnuAsm bool, f func(pc, size uint64, file string, line int, text string)) { if start < d.textStart { start = d.textStart } @@ -452,9 +460,3 @@ "ppc64le": binary.LittleEndian, "riscv64": binary.LittleEndian, "s390x": binary.BigEndian, } - -type Liner interface { - // Given a pc, returns the corresponding file, line, and function data. - // If unknown, returns "",0,nil. - PCToLine(uint64) (string, int, *gosym.Func) -} diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go index 2f2d7718132d5741ee4c26e95b6516ef8e93a537..ed9aae280e55795bd78a1215675d79bfde74df0e 100644 --- a/src/cmd/internal/objfile/objfile.go +++ b/src/cmd/internal/objfile/objfile.go @@ -119,10 +119,6 @@ func (f *File) DWARF() (*dwarf.Data, error) { return f.entries[0].DWARF() } -func (f *File) Disasm() (*Disasm, error) { - return f.entries[0].Disasm() -} - func (e *Entry) Name() string { return e.name } @@ -181,3 +177,9 @@ // This is for cmd/pprof to locate cgo functions. func (e *Entry) DWARF() (*dwarf.Data, error) { return e.raw.dwarf() } + +type Liner interface { + // Given a pc, returns the corresponding file, line, and function data. + // If unknown, returns "",0,nil. + PCToLine(uint64) (string, int, *gosym.Func) +} diff --git a/src/cmd/objdump/main.go b/src/cmd/objdump/main.go index b5b0d7f5178d5bf4aa13ba81c7a29ae25a222640..c98551e6b857b0840d814c07461dbec7bae8ac7c 100644 --- a/src/cmd/objdump/main.go +++ b/src/cmd/objdump/main.go @@ -40,6 +40,7 @@ "regexp" "strconv" "strings" + "cmd/internal/disasm" "cmd/internal/objfile" "cmd/internal/telemetry/counter" ) @@ -82,7 +83,7 @@ log.Fatal(err) } defer f.Close() - dis, err := f.Disasm() + dis, err := disasm.DisasmForFile(f) if err != nil { log.Fatalf("disassemble %s: %v", flag.Arg(0), err) } diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go index a1c2cd210f8dbaf2c3d4d49739af962e6793120f..bfc2911b69cd396013aea4a7215e17feb27a9386 100644 --- a/src/cmd/pprof/pprof.go +++ b/src/cmd/pprof/pprof.go @@ -24,6 +24,7 @@ "strings" "sync" "time" + "cmd/internal/disasm" "cmd/internal/objfile" "cmd/internal/telemetry/counter" @@ -162,7 +163,7 @@ // objTool implements driver.ObjTool using Go libraries // (instead of invoking GNU binutils). type objTool struct { mu sync.Mutex - disasmCache map[string]*objfile.Disasm + disasmCache map[string]*disasm.Disasm } func (*objTool) Open(name string, start, limit, offset uint64, relocationSymbol string) (driver.ObjFile, error) { @@ -202,11 +203,11 @@ }) return asm, nil } -func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) { +func (t *objTool) cachedDisasm(file string) (*disasm.Disasm, error) { t.mu.Lock() defer t.mu.Unlock() if t.disasmCache == nil { - t.disasmCache = make(map[string]*objfile.Disasm) + t.disasmCache = make(map[string]*disasm.Disasm) } d := t.disasmCache[file] if d != nil { @@ -216,7 +217,7 @@ f, err := objfile.Open(file) if err != nil { return nil, err } - d, err = f.Disasm() + d, err = disasm.DisasmForFile(f) f.Close() if err != nil { return nil, err diff --git a/src/crypto/ecdh/nist.go b/src/crypto/ecdh/nist.go index 0f4a65e5affb800c2c9f17cb4bbc160472ef62aa..acef8298943c2bb07ed395367e39005efd343d2b 100644 --- a/src/crypto/ecdh/nist.go +++ b/src/crypto/ecdh/nist.go @@ -8,6 +8,7 @@ import ( "bytes" "crypto/internal/boring" "crypto/internal/fips140/ecdh" + "crypto/internal/fips140only" "errors" "io" ) @@ -41,6 +42,10 @@ publicKey: &PublicKey{curve: c, publicKey: pub.Bytes(), boring: pub}, boring: key, } return k, nil + } + + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode") } privateKey, err := c.generate(rand) diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index 0ad669795c56b28baeb0985cf68d270f299ac9d7..77727aaf96befb1d8718ddea5124c1b85ac7de35 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -21,6 +21,7 @@ "crypto/elliptic" "crypto/internal/boring" "crypto/internal/boring/bbig" "crypto/internal/fips140/ecdsa" + "crypto/internal/fips140only" "crypto/internal/randutil" "crypto/sha512" "crypto/subtle" @@ -182,6 +183,9 @@ } } func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], rand io.Reader) (*PrivateKey, error) { + if fips140only.Enabled && fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } privateKey, err := ecdsa.GenerateKey(c, rand) if err != nil { return nil, err @@ -228,6 +232,9 @@ } } func signFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey, rand io.Reader, hash []byte) ([]byte, error) { + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } // privateKeyToFIPS is very slow in FIPS mode because it performs a // Sign+Verify cycle per FIPS 140-3 IG 10.3.A. We should find a way to cache // it or attach it to the PrivateKey. diff --git a/src/crypto/internal/fips140/drbg/rand.go b/src/crypto/internal/fips140/drbg/rand.go index 736a4b0cc0f4b3529cbe3dce0965805e97cd9d69..967fb0673ef1a18f158b69425c44ed3cdb300eba 100644 --- a/src/crypto/internal/fips140/drbg/rand.go +++ b/src/crypto/internal/fips140/drbg/rand.go @@ -7,7 +7,9 @@ import ( "crypto/internal/entropy" "crypto/internal/fips140" + "crypto/internal/randutil" "crypto/internal/sysrand" + "io" "sync" ) @@ -56,3 +58,38 @@ } b = b[size:] } } + +// DefaultReader is a sentinel type, embedded in the default +// [crypto/rand.Reader], used to recognize it when passed to +// APIs that accept a rand io.Reader. +type DefaultReader interface{ defaultReader() } + +// ReadWithReader uses Reader to fill b with cryptographically secure random +// bytes. It is intended for use in APIs that expose a rand io.Reader. +// +// If Reader is not the default Reader from crypto/rand, +// [randutil.MaybeReadByte] and [fips140.RecordNonApproved] are called. +func ReadWithReader(r io.Reader, b []byte) error { + if _, ok := r.(DefaultReader); ok { + Read(b) + return nil + } + + fips140.RecordNonApproved() + randutil.MaybeReadByte(r) + _, err := io.ReadFull(r, b) + return err +} + +// ReadWithReaderDeterministic is like ReadWithReader, but it doesn't call +// [randutil.MaybeReadByte] on non-default Readers. +func ReadWithReaderDeterministic(r io.Reader, b []byte) error { + if _, ok := r.(DefaultReader); ok { + Read(b) + return nil + } + + fips140.RecordNonApproved() + _, err := io.ReadFull(r, b) + return err +} diff --git a/src/crypto/internal/fips140/ecdh/ecdh.go b/src/crypto/internal/fips140/ecdh/ecdh.go index 19a45c00db9ce972dd30fd70b9e3e14aeb0569aa..bf71c75a92c6eb1662e38a38eaed742fad729d3a 100644 --- a/src/crypto/internal/fips140/ecdh/ecdh.go +++ b/src/crypto/internal/fips140/ecdh/ecdh.go @@ -10,7 +10,6 @@ "crypto/internal/fips140" "crypto/internal/fips140/drbg" "crypto/internal/fips140/nistec" "crypto/internal/fips140deps/byteorder" - "crypto/internal/randutil" "errors" "io" "math/bits" @@ -137,8 +136,6 @@ 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38, 0x64, 0x09, } // GenerateKey generates a new ECDSA private key pair for the specified curve. -// -// In FIPS mode, rand is ignored. func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) { fips140.RecordApproved() // This procedure is equivalent to Key Pair Generation by Testing @@ -146,18 +143,13 @@ // Candidates, specified in NIST SP 800-56A Rev. 3, Section 5.6.1.2.2. for { key := make([]byte, len(c.N)) - if fips140.Enabled { - drbg.Read(key) - } else { - randutil.MaybeReadByte(rand) - if _, err := io.ReadFull(rand, key); err != nil { - return nil, err - } - // In tests, rand will return all zeros and NewPrivateKey will reject - // the zero key as it generates the identity as a public key. This also - // makes this function consistent with crypto/elliptic.GenerateKey. - key[1] ^= 0x42 + if err := drbg.ReadWithReader(rand, key); err != nil { + return nil, err } + // In tests, rand will return all zeros and NewPrivateKey will reject + // the zero key as it generates the identity as a public key. This also + // makes this function consistent with crypto/elliptic.GenerateKey. + key[1] ^= 0x42 // Mask off any excess bits if the size of the underlying field is not a // whole number of bytes, which is only the case for P-521. diff --git a/src/crypto/internal/fips140/ecdsa/cast.go b/src/crypto/internal/fips140/ecdsa/cast.go index a324cf929d8bf21f5b0a1a3a96054392a04f20f4..219b7211e74e92c4d0b144a092c584c802e36e08 100644 --- a/src/crypto/internal/fips140/ecdsa/cast.go +++ b/src/crypto/internal/fips140/ecdsa/cast.go @@ -54,7 +54,8 @@ func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error { return fips140.PCT("ECDSA PCT", func() error { hash := testHash() - sig, err := Sign(c, sha512.New, k, nil, hash) + drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil) + sig, err := sign(c, k, drbg, hash) if err != nil { return err } diff --git a/src/crypto/internal/fips140/ecdsa/ecdsa.go b/src/crypto/internal/fips140/ecdsa/ecdsa.go index 61b40122a0fab4b769d7bdd1b5bf0b37c8bdd5de..9459b03de764844328494df613705706056bb98e 100644 --- a/src/crypto/internal/fips140/ecdsa/ecdsa.go +++ b/src/crypto/internal/fips140/ecdsa/ecdsa.go @@ -10,7 +10,6 @@ "crypto/internal/fips140" "crypto/internal/fips140/bigmod" "crypto/internal/fips140/drbg" "crypto/internal/fips140/nistec" - "crypto/internal/randutil" "errors" "io" "sync" @@ -187,20 +186,11 @@ return &PublicKey{curve: c.curve, q: Q}, nil } // GenerateKey generates a new ECDSA private key pair for the specified curve. -// -// In FIPS mode, rand is ignored. func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) { fips140.RecordApproved() k, Q, err := randomPoint(c, func(b []byte) error { - if fips140.Enabled { - drbg.Read(b) - return nil - } else { - randutil.MaybeReadByte(rand) - _, err := io.ReadFull(rand, b) - return err - } + return drbg.ReadWithReader(rand, b) }) if err != nil { return nil, err @@ -281,8 +271,6 @@ // Sign signs a hash (which shall be the result of hashing a larger message with // the hash function H) using the private key, priv. If the hash is longer than // the bit-length of the private key's curve order, the hash will be truncated // to that length. -// -// The signature is randomized. If FIPS mode is enabled, rand is ignored. func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) { if priv.pub.curve != c.curve { return nil, errors.New("ecdsa: private key does not match curve") @@ -296,13 +284,8 @@ // in draft-irtf-cfrg-det-sigs-with-noise-04, Section 4. This has also the // advantage of closely resembling Deterministic ECDSA. Z := make([]byte, len(priv.d)) - if fips140.Enabled { - drbg.Read(Z) - } else { - randutil.MaybeReadByte(rand) - if _, err := io.ReadFull(rand, Z); err != nil { - return nil, err - } + if err := drbg.ReadWithReader(rand, Z); err != nil { + return nil, err } // See https://github.com/cfrg/draft-irtf-cfrg-det-sigs-with-noise/issues/6 diff --git a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go index 6fd7ac697473d4bbdd66563850e09d8b20b9a8b9..4f085e2801b79dab307f831b34ec4d19f78fc299 100644 --- a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go +++ b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go @@ -160,4 +160,6 @@ // V = HMAC (K, V) d.hK = d.newHMAC(K) d.hK.Write(d.V) d.V = d.hK.Sum(d.V[:0]) + + d.reseedCounter++ } diff --git a/src/crypto/internal/fips140/ed25519/ed25519.go b/src/crypto/internal/fips140/ed25519/ed25519.go index 9824cbdf8149266a90e88e765c240788bef6f42c..bbdc5b4a8ba2f9016a4b12de384c77aae73569e0 100644 --- a/src/crypto/internal/fips140/ed25519/ed25519.go +++ b/src/crypto/internal/fips140/ed25519/ed25519.go @@ -11,7 +11,6 @@ "crypto/internal/fips140/drbg" "crypto/internal/fips140/edwards25519" "crypto/internal/fips140/sha512" "errors" - "io" "strconv" ) @@ -61,24 +60,14 @@ return a[:] } // GenerateKey generates a new Ed25519 private key pair. -// -// In FIPS mode, rand is ignored. Otherwise, the output of this function is -// deterministic, and equivalent to reading 32 bytes from rand, and passing them -// to [NewKeyFromSeed]. -func GenerateKey(rand io.Reader) (*PrivateKey, error) { +func GenerateKey() (*PrivateKey, error) { priv := &PrivateKey{} - return generateKey(priv, rand) + return generateKey(priv) } -func generateKey(priv *PrivateKey, rand io.Reader) (*PrivateKey, error) { +func generateKey(priv *PrivateKey) (*PrivateKey, error) { fips140.RecordApproved() - if fips140.Enabled { - drbg.Read(priv.seed[:]) - } else { - if _, err := io.ReadFull(rand, priv.seed[:]); err != nil { - return nil, err - } - } + drbg.Read(priv.seed[:]) precomputePrivateKey(priv) if err := fipsPCT(priv); err != nil { // This clearly can't happen, but FIPS 140-3 requires that we check. diff --git a/src/crypto/internal/fips140/rsa/keygen.go b/src/crypto/internal/fips140/rsa/keygen.go index a9e12eb1e8e920a3dcdc184c5aa62dec8ebd93ac..df76772ef5878d7443f792c68c8e73e2ce742ca2 100644 --- a/src/crypto/internal/fips140/rsa/keygen.go +++ b/src/crypto/internal/fips140/rsa/keygen.go @@ -8,15 +8,12 @@ import ( "crypto/internal/fips140" "crypto/internal/fips140/bigmod" "crypto/internal/fips140/drbg" - "crypto/internal/randutil" "errors" "io" ) // GenerateKey generates a new RSA key pair of the given bit size. // bits must be at least 128. -// -// When operating in FIPS mode, rand is ignored. func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) { if bits < 128 { return nil, errors.New("rsa: key too small") @@ -94,7 +91,7 @@ } } // randomPrime returns a random prime number of the given bit size following -// the process in FIPS 186-5, Appendix A.1.3. rand is ignored in FIPS mode. +// the process in FIPS 186-5, Appendix A.1.3. func randomPrime(rand io.Reader, bits int) ([]byte, error) { if bits < 64 { return nil, errors.New("rsa: prime size must be at least 32-bit") @@ -102,13 +99,8 @@ } b := make([]byte, (bits+7)/8) for { - if fips140.Enabled { - drbg.Read(b) - } else { - randutil.MaybeReadByte(rand) - if _, err := io.ReadFull(rand, b); err != nil { - return nil, err - } + if err := drbg.ReadWithReader(rand, b); err != nil { + return nil, err } if excess := len(b)*8 - bits; excess != 0 { b[0] >>= excess diff --git a/src/crypto/internal/fips140/rsa/pkcs1v22.go b/src/crypto/internal/fips140/rsa/pkcs1v22.go index a62d7e485f6df7736a73b4f4ea2d58daf27a0f5c..a5bc56dafcd9ff75117b0114f1fac24032ed7afa 100644 --- a/src/crypto/internal/fips140/rsa/pkcs1v22.go +++ b/src/crypto/internal/fips140/rsa/pkcs1v22.go @@ -264,8 +264,6 @@ return saltLength, nil } // SignPSS calculates the signature of hashed using RSASSA-PSS. -// -// In FIPS mode, rand is ignored and can be nil. func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte, saltLength int) ([]byte, error) { fipsSelfTest() fips140.RecordApproved() @@ -286,12 +284,8 @@ if saltLength > hash.Size() { fips140.RecordNonApproved() } salt := make([]byte, saltLength) - if fips140.Enabled { - drbg.Read(salt) - } else { - if _, err := io.ReadFull(rand, salt); err != nil { - return nil, err - } + if err := drbg.ReadWithReaderDeterministic(rand, salt); err != nil { + return nil, err } emBits := priv.pub.N.BitLen() - 1 @@ -374,8 +368,6 @@ } } // EncryptOAEP encrypts the given message with RSAES-OAEP. -// -// In FIPS mode, random is ignored and can be nil. func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) { // Note that while we don't commit to deterministic execution with respect // to the random stream, we also don't apply MaybeReadByte, so per Hyrum's @@ -408,13 +400,8 @@ copy(db[0:hash.Size()], lHash) db[len(db)-len(msg)-1] = 1 copy(db[len(db)-len(msg):], msg) - if fips140.Enabled { - drbg.Read(seed) - } else { - _, err := io.ReadFull(random, seed) - if err != nil { - return nil, err - } + if err := drbg.ReadWithReaderDeterministic(random, seed); err != nil { + return nil, err } mgf1XOR(db, mgfHash, seed) diff --git a/src/crypto/internal/fips140only/fips140only.go b/src/crypto/internal/fips140only/fips140only.go index 6ad97befbe06daa704f8ab34be87d3fea80fbf0e..7126781af0d8bc64516464eb201bcd846b7bd683 100644 --- a/src/crypto/internal/fips140only/fips140only.go +++ b/src/crypto/internal/fips140only/fips140only.go @@ -5,11 +5,13 @@ package fips140only import ( + "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha256" "crypto/internal/fips140/sha3" "crypto/internal/fips140/sha512" "hash" "internal/godebug" + "io" ) // Enabled reports whether FIPS 140-only mode is enabled, in which non-approved @@ -24,3 +26,8 @@ default: return false } } + +func ApprovedRandomReader(r io.Reader) bool { + _, ok := r.(drbg.DefaultReader) + return ok +} diff --git a/src/crypto/internal/fips140test/cast_test.go b/src/crypto/internal/fips140test/cast_test.go index c6e3212f3f064f9beed9d4ef47f05af102122262..b2aee15eab70f50691e693177483cd3cf8de8b5f 100644 --- a/src/crypto/internal/fips140test/cast_test.go +++ b/src/crypto/internal/fips140test/cast_test.go @@ -85,7 +85,7 @@ if err != nil { t.Fatal(err) } ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32)) - k25519, err := ed25519.GenerateKey(rand.Reader) + k25519, err := ed25519.GenerateKey() if err != nil { t.Fatal(err) } diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index 5dd875e6e725751144aabb37c467c58621c44850..1ca16caa9563e68c5684256cbd92c0ea6d16ac6b 100644 --- a/src/crypto/rand/rand.go +++ b/src/crypto/rand/rand.go @@ -38,7 +38,9 @@ } Reader = &reader{} } -type reader struct{} +type reader struct { + drbg.DefaultReader +} func (r *reader) Read(b []byte) (n int, err error) { boring.Unreachable() diff --git a/src/crypto/rsa/fips.go b/src/crypto/rsa/fips.go index bc23d597098f0c3f08ae54ffd1d1ea0a95b71052..24dfb38cf625bda5f87f003c140175cd7763bae4 100644 --- a/src/crypto/rsa/fips.go +++ b/src/crypto/rsa/fips.go @@ -17,6 +17,9 @@ const ( // PSSSaltLengthAuto causes the salt in a PSS signature to be as large // as possible when signing, and to be auto-detected when verifying. + // + // When signing in FIPS 140-3 mode, the salt length is capped at the length + // of the hash function used in the signature. PSSSaltLengthAuto = 0 // PSSSaltLengthEqualsHash causes the salt length to equal the length // of the hash used in the signature. @@ -66,6 +69,9 @@ return nil, err } if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") } if opts != nil && opts.Hash != 0 { @@ -187,6 +193,9 @@ return nil, err } if fips140only.Enabled && !fips140only.ApprovedHash(hash) { return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") } defer hash.Reset() diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index 0f58f2226f848e53c42b33d20086fea2f48e361f..fb23f003a6f2177ed6a1d1c0dfc1220d80425a9c 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -322,6 +322,9 @@ } if fips140only.Enabled && bits%2 == 1 { return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode") } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } k, err := rsa.GenerateKey(random, bits) if err != nil { diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go index c395732c8b27b8eeaeae056b954f821606ae67df..2474ab82dfa207eca2a77fdad664c2e841d90cb2 100644 --- a/src/crypto/rsa/rsa_test.go +++ b/src/crypto/rsa/rsa_test.go @@ -10,7 +10,6 @@ "bytes" "crypto" "crypto/internal/boring" "crypto/internal/cryptotest" - "crypto/internal/fips140" "crypto/rand" . "crypto/rsa" "crypto/sha1" @@ -782,9 +781,6 @@ msgs []testEncryptOAEPMessage } func TestEncryptOAEP(t *testing.T) { - if fips140.Enabled { - t.Skip("FIPS mode overrides the deterministic random source") - } sha1 := sha1.New() n := new(big.Int) for i, test := range testEncryptOAEPData { diff --git a/src/html/template/js.go b/src/html/template/js.go index d1463dee149101418f7c1ccc85a61f7113e10073..b3bf94801b238a034b59050dba0260aa7f360a57 100644 --- a/src/html/template/js.go +++ b/src/html/template/js.go @@ -9,6 +9,7 @@ "bytes" "encoding/json" "fmt" "reflect" + "regexp" "strings" "unicode/utf8" ) @@ -144,6 +145,8 @@ } return v.Interface() } +var scriptTagRe = regexp.MustCompile("(?i)<(/?)script") + // jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has // neither side-effects nor free variables outside (NaN, Infinity). func jsValEscaper(args ...any) string { @@ -181,9 +184,9 @@ // // In particular we: // * replace "*/" comment end tokens with "* /", which does not // terminate the comment - // * replace "", `"]]\u003e"`, false}, {" entry { - // We store the pc of the start of the instruction following - // the instruction in question (the call or the inline mark). - // This is done for historical reasons, and to make FuncForPC - // work correctly for entries in the result of runtime.Callers. pc-- } // It's important that interpret pc non-strictly as cgoTraceback may diff --git a/src/runtime/time.go b/src/runtime/time.go index 7c6d7988726f069b1601b470fafd32c71443335c..c22d39c0894e35c0d4c8c383d38371cb28da71c0 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -1370,15 +1370,19 @@ // maybeRunChan checks whether the timer needs to run // to send a value to its associated channel. If so, it does. // The timer must not be locked. func (t *timer) maybeRunChan() { - if sg := getg().syncGroup; sg != nil || t.isFake { + if t.isFake { t.lock() var timerGroup *synctestGroup if t.ts != nil { timerGroup = t.ts.syncGroup } t.unlock() - if sg == nil || !t.isFake || sg != timerGroup { - panic(plainError("timer moved between synctest groups")) + sg := getg().syncGroup + if sg == nil { + panic(plainError("synctest timer accessed from outside bubble")) + } + if timerGroup != nil && sg != timerGroup { + panic(plainError("timer moved between synctest bubbles")) } // No need to do anything here. // synctest.Run will run the timer when it advances its fake clock. diff --git a/src/syscall/env_unix.go b/src/syscall/env_unix.go index 8e87e018e82de37d90c064df3bb6d8d7d3c57f8b..1144ed1416c0ee5e36b36f59ca870931efee5ee9 100644 --- a/src/syscall/env_unix.go +++ b/src/syscall/env_unix.go @@ -124,7 +124,7 @@ return nil } func Clearenv() { - envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv + envOnce.Do(copyenv) envLock.Lock() defer envLock.Unlock() diff --git a/src/weak/doc.go b/src/weak/doc.go index e66d5ab5ac72d289a2c37c2d08afdfa3410627b3..1af8e4c69b2f86ecbddc3c2aa82c7556565146fe 100644 --- a/src/weak/doc.go +++ b/src/weak/doc.go @@ -3,30 +3,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* -Package weak provides weak pointers with the goal of memory efficiency. -The primary use-cases for weak pointers are for implementing caches, -canonicalization maps (like the unique package), and for tying together -the lifetimes of separate values (for example, through a map with weak -keys). - -# Advice - -This package is intended to target niche use-cases like the unique -package, and the structures inside are not intended to be general -replacements for regular Go pointers, maps, etc. -Misuse of the structures in this package may generate unexpected and -hard-to-reproduce bugs. -Using the facilities in this package to try and resolve out-of-memory -issues requires careful consideration, and even so, will likely be the -wrong answer if the solution does not fall into one of the listed -use-cases above. - -The structures in this package are intended to be an implementation -detail of the package they are used by (again, see the unique package). -If you're writing a package intended to be used by others, as a rule of -thumb, avoid exposing the behavior of any weak structures in your package's -API. -Doing so will almost certainly make your package more difficult to use -correctly. +Package weak provides ways to safely reference memory weakly, +that is, without preventing its reclamation. */ package weak diff --git a/src/weak/pointer.go b/src/weak/pointer.go index f6d20530ab5deed695c5502de7910581a1a82c5b..fb10bc2d699ab3693b2b5377b810c654728e1bd8 100644 --- a/src/weak/pointer.go +++ b/src/weak/pointer.go @@ -12,11 +12,21 @@ ) // Pointer is a weak pointer to a value of type T. // -// Two Pointer values compare equal if the pointers -// that they were created from compare equal. This property is retained even -// after the object referenced by the pointer used to create a weak reference -// is reclaimed. +// Just like regular pointers, Pointer may reference any part of an +// object, such as the field of a struct or an element of an array. +// Objects that are only pointed to by weak pointers are not considered +// reachable and once the object becomes unreachable [Pointer.Value] +// may return nil. +// +// The primary use-cases for weak pointers are for implementing caches, +// canonicalization maps (like the unique package), and for tying together +// the lifetimes of separate values (for example, through a map with weak +// keys). // +// Two Pointer values always compare equal if the pointers that they were +// created from compare equal. This property is retained even after the +// object referenced by the pointer used to create a weak reference is +// reclaimed. // If multiple weak pointers are made to different offsets within same object // (for example, pointers to different fields of the same struct), those pointers // will not compare equal. @@ -24,14 +34,32 @@ // If a weak pointer is created from an object that becomes unreachable, but is // then resurrected due to a finalizer, that weak pointer will not compare equal // with weak pointers created after resurrection. // -// Calling Make with a nil pointer returns a weak pointer whose Value method +// Calling [Make] with a nil pointer returns a weak pointer whose [Pointer.Value] // always returns nil. The zero value of a Pointer behaves as if it was created -// by passing nil to Make and compares equal with such pointers. +// by passing nil to [Make] and compares equal with such pointers. +// +// [Pointer.Value] is not guaranteed to eventually return nil. +// [Pointer.Value] may return nil as soon as the object becomes +// unreachable. +// Values stored in global variables, or that can be found by tracing +// pointers from a global variable, are reachable. A function argument or +// receiver may become unreachable at the last point where the function +// mentions it. To ensure [Pointer.Value] does not return nil, +// pass a pointer to the object to the [runtime.KeepAlive] function after +// the last point where the object must remain reachable. +// +// Note that because [Pointer.Value] is not guaranteed to eventually return +// nil, even after an object is no longer referenced, the runtime is allowed to +// perform a space-saving optimization that batches objects together in a single +// allocation slot. The weak pointer for an unreferenced object in such an +// allocation may never be called if it always exists in the same batch as a +// referenced object. Typically, this batching only happens for tiny +// (on the order of 16 bytes or less) and pointer-free objects. type Pointer[T any] struct { u unsafe.Pointer } -// Make creates a weak pointer from a strong pointer to some value of type T. +// Make creates a weak pointer from a pointer to some value of type T. func Make[T any](ptr *T) Pointer[T] { // Explicitly force ptr to escape to the heap. ptr = abi.Escape(ptr) diff --git a/src/weak/pointer_test.go b/src/weak/pointer_test.go index 213dde8c4059f84f5a1c20eaae9bac0236fe599b..002b4130f0b5534f4de4bb1b1edb61b9f20b2967 100644 --- a/src/weak/pointer_test.go +++ b/src/weak/pointer_test.go @@ -43,9 +43,11 @@ func TestPointerEquality(t *testing.T) { bt := make([]*T, 10) wt := make([]weak.Pointer[T], 10) + wo := make([]weak.Pointer[int], 10) for i := range bt { bt[i] = new(T) wt[i] = weak.Make(bt[i]) + wo[i] = weak.Make(&bt[i].a) } for i := range bt { st := wt[i].Value() @@ -55,6 +57,9 @@ } if wp := weak.Make(st); wp != wt[i] { t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wt[i]) } + if wp := weak.Make(&st.a); wp != wo[i] { + t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wo[i]) + } if i == 0 { continue } @@ -71,6 +76,9 @@ t.Fatalf("weak pointer is not the same as strong pointer: %p vs. %p", st, bt[i]) } if wp := weak.Make(st); wp != wt[i] { t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wt[i]) + } + if wp := weak.Make(&st.a); wp != wo[i] { + t.Fatalf("new weak pointer not equal to existing weak pointer: %v vs. %v", wp, wo[i]) } if i == 0 { continue @@ -210,3 +218,12 @@ }() } wg.Wait() } + +func TestIssue70739(t *testing.T) { + x := make([]*int, 4<<16) + wx1 := weak.Make(&x[1<<16]) + wx2 := weak.Make(&x[1<<16]) + if wx1 != wx2 { + t.Fatal("failed to look up special and made duplicate weak handle; see issue #70739") + } +}