diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go index f1c30f8f9a2b2a..2ce705adba44f3 100644 --- a/src/cmd/cgo/internal/testcshared/cshared_test.go +++ b/src/cmd/cgo/internal/testcshared/cshared_test.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "cmd/cgo/internal/cgotest" + "cmp" "debug/elf" "debug/pe" "encoding/binary" @@ -272,7 +273,7 @@ func createHeaders() error { // which results in the linkers output implib getting overwritten at each step. So instead build the // import library the traditional way, using a def file. err = os.WriteFile("libgo.def", - []byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n\t_cgo_dummy_export\n"), + []byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n"), 0644) if err != nil { return fmt.Errorf("unable to write def file: %v", err) @@ -375,9 +376,23 @@ func TestExportedSymbols(t *testing.T) { } } -func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFunctions int, wantAll bool) { +func checkNumberOfExportedSymbolsWindows(t *testing.T, exportedSymbols int, wantAll bool) { + t.Parallel() tmpdir := t.TempDir() + prog := ` +package main +import "C" +func main() {} +` + + for i := range exportedSymbols { + prog += fmt.Sprintf(` +//export GoFunc%d +func GoFunc%d() {} +`, i, i) + } + srcfile := filepath.Join(tmpdir, "test.go") objfile := filepath.Join(tmpdir, "test.dll") if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil { @@ -443,18 +458,19 @@ func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFu t.Fatalf("binary.Read failed: %v", err) } - // Only the two exported functions and _cgo_dummy_export should be exported. + exportedSymbols = cmp.Or(exportedSymbols, 1) // _cgo_stub_export is exported if there are no other symbols exported + // NumberOfNames is the number of functions exported with a unique name. // NumberOfFunctions can be higher than that because it also counts // functions exported only by ordinal, a unique number asigned by the linker, // and linkers might add an unknown number of their own ordinal-only functions. if wantAll { - if e.NumberOfNames <= uint32(exportedFunctions) { - t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedFunctions) + if e.NumberOfNames <= uint32(exportedSymbols) { + t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedSymbols) } } else { - if e.NumberOfNames > uint32(exportedFunctions) { - t.Errorf("got %d exported names, want <= %d", e.NumberOfNames, exportedFunctions) + if e.NumberOfNames != uint32(exportedSymbols) { + t.Errorf("got %d exported names, want %d", e.NumberOfNames, exportedSymbols) } } } @@ -470,43 +486,14 @@ func TestNumberOfExportedFunctions(t *testing.T) { t.Parallel() - const prog0 = ` -package main - -import "C" - -func main() { -} -` - - const prog2 = ` -package main - -import "C" - -//export GoFunc -func GoFunc() { - println(42) -} - -//export GoFunc2 -func GoFunc2() { - println(24) -} - -func main() { -} -` - // All programs export _cgo_dummy_export, so add 1 to the expected counts. - t.Run("OnlyExported/0", func(t *testing.T) { - checkNumberOfExportedFunctionsWindows(t, prog0, 0+1, false) - }) - t.Run("OnlyExported/2", func(t *testing.T) { - checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, false) - }) - t.Run("All", func(t *testing.T) { - checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, true) - }) + for i := range 3 { + t.Run(fmt.Sprintf("OnlyExported/%d", i), func(t *testing.T) { + checkNumberOfExportedSymbolsWindows(t, i, false) + }) + t.Run(fmt.Sprintf("All/%d", i), func(t *testing.T) { + checkNumberOfExportedSymbolsWindows(t, i, true) + }) + } } // test1: shared library can be dynamically loaded and exported symbols are accessible. diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index dfa54e41d33399..622d35ac7b3bab 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1005,12 +1005,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { } // Build the wrapper function compiled by gcc. - gccExport := "" - if goos == "windows" { - gccExport = "__declspec(dllexport) " - } var s strings.Builder - fmt.Fprintf(&s, "%s%s %s(", gccExport, gccResult, exp.ExpName) + fmt.Fprintf(&s, "%s %s(", gccResult, exp.ExpName) if fn.Recv != nil { s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String()) s.WriteString(" recv") diff --git a/src/cmd/link/dwarf_test.go b/src/cmd/link/dwarf_test.go index 6a60a746a5b48b..56a076002a92d4 100644 --- a/src/cmd/link/dwarf_test.go +++ b/src/cmd/link/dwarf_test.go @@ -364,6 +364,10 @@ func TestFlagW(t *testing.T) { if runtime.GOOS == "aix" { t.Skip("internal/xcoff cannot parse file without symbol table") } + if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) { + t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH) + } + t.Parallel() tmpdir := t.TempDir() @@ -382,7 +386,7 @@ func TestFlagW(t *testing.T) { {"-s", false}, // -s implies -w {"-s -w=0", true}, // -w=0 negates the implied -w } - if testenv.HasCGO() { + if testenv.HasCGO() && runtime.GOOS != "solaris" { // Solaris linker doesn't support the -S flag tests = append(tests, testCase{"-w -linkmode=external", false}, testCase{"-s -linkmode=external", false}, diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 8d2763bb57f31a..79d3d37835e9ad 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1452,7 +1452,7 @@ func (ctxt *Link) hostlink() { argv = append(argv, "-s") } } else if *FlagW { - if !ctxt.IsAIX() { // The AIX linker's -S has different meaning + if !ctxt.IsAIX() && !ctxt.IsSolaris() { // The AIX and Solaris linkers' -S has different meaning argv = append(argv, "-Wl,-S") // suppress debugging symbols } } @@ -1772,7 +1772,8 @@ func (ctxt *Link) hostlink() { } // Force global symbols to be exported for dlopen, etc. - if ctxt.IsELF { + switch { + case ctxt.IsELF: if ctxt.DynlinkingGo() || ctxt.BuildMode == BuildModeCShared || !linkerFlagSupported(ctxt.Arch, argv[0], altLinker, "-Wl,--export-dynamic-symbol=main") { argv = append(argv, "-rdynamic") } else { @@ -1783,10 +1784,12 @@ func (ctxt *Link) hostlink() { sort.Strings(exports) argv = append(argv, exports...) } - } - if ctxt.HeadType == objabi.Haix { + case ctxt.IsAIX(): fileName := xcoffCreateExportFile(ctxt) argv = append(argv, "-Wl,-bE:"+fileName) + case ctxt.IsWindows() && !slices.Contains(flagExtldflags, "-Wl,--export-all-symbols"): + fileName := peCreateExportFile(ctxt, filepath.Base(outopt)) + argv = append(argv, fileName) } const unusedArguments = "-Qunused-arguments" diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index 5219a98dd47cf4..0f0650e5e149e3 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -8,6 +8,7 @@ package ld import ( + "bytes" "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/loader" @@ -17,6 +18,8 @@ import ( "fmt" "internal/buildcfg" "math" + "os" + "path/filepath" "slices" "sort" "strconv" @@ -1748,3 +1751,44 @@ func asmbPe(ctxt *Link) { pewrite(ctxt) } + +// peCreateExportFile creates a file with exported symbols for Windows .def files. +// ld will export all symbols, even those not marked for export, unless a .def file is provided. +func peCreateExportFile(ctxt *Link, libName string) (fname string) { + fname = filepath.Join(*flagTmpdir, "export_file.def") + var buf bytes.Buffer + + fmt.Fprintf(&buf, "LIBRARY %s\n", libName) + buf.WriteString("EXPORTS\n") + + ldr := ctxt.loader + var exports []string + for s := range ldr.ForAllCgoExportStatic() { + extname := ldr.SymExtname(s) + if !strings.HasPrefix(extname, "_cgoexp_") { + continue + } + if ldr.IsFileLocal(s) { + continue // Only export non-static symbols + } + // Retrieve the name of the initial symbol + // exported by cgo. + // The corresponding Go symbol is: + // _cgoexp_hashcode_symname. + name := strings.SplitN(extname, "_", 4)[3] + exports = append(exports, name) + } + if len(exports) == 0 { + // See runtime/cgo/windows.go for details. + exports = append(exports, "_cgo_stub_export") + } + sort.Strings(exports) + buf.WriteString(strings.Join(exports, "\n")) + + err := os.WriteFile(fname, buf.Bytes(), 0666) + if err != nil { + Errorf("WriteFile %s failed: %v", fname, err) + } + + return fname +} diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 759262286d39d8..2c999ccc4e3a19 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -645,7 +645,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { sliceSym(pcln.funcnametab) // The cutab slice - sliceSym(pcln.cutab) + slice(pcln.cutab, uint64(ldr.SymSize(pcln.cutab))/4) // The filetab slice sliceSym(pcln.filetab) @@ -654,7 +654,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { sliceSym(pcln.pctab) // The pclntab slice - slice(pcln.pclntab, uint64(ldr.SymSize(pcln.pclntab))) + sliceSym(pcln.pclntab) // The ftab slice slice(pcln.pclntab, uint64(pcln.nfunc+1)) diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index 1bce2cf9b6124d..da728e25455618 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -1779,10 +1779,7 @@ func xcoffCreateExportFile(ctxt *Link) (fname string) { var buf bytes.Buffer ldr := ctxt.loader - for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ { - if !ldr.AttrCgoExport(s) { - continue - } + for s := range ldr.ForAllCgoExportStatic() { extname := ldr.SymExtname(s) if !strings.HasPrefix(extname, "._cgoexp_") { continue diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 9f3ea3e553dd7c..103dad03001263 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -16,6 +16,7 @@ import ( "fmt" "internal/abi" "io" + "iter" "log" "math/bits" "os" @@ -1109,6 +1110,18 @@ func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) { } } +// ForAllCgoExportStatic returns an iterator over all symbols +// marked with the "cgo_export_static" compiler directive. +func (l *Loader) ForAllCgoExportStatic() iter.Seq[Sym] { + return func(yield func(Sym) bool) { + for s := range l.attrCgoExportStatic { + if !yield(s) { + break + } + } + } +} + // IsGeneratedSym returns true if a symbol's been previously marked as a // generator symbol through the SetIsGeneratedSym. The functions for generator // symbols are kept in the Link context. diff --git a/src/crypto/internal/entropy/entropy.go b/src/crypto/internal/entropy/entropy.go index 5319e9e47a7455..73fd5298007a11 100644 --- a/src/crypto/internal/entropy/entropy.go +++ b/src/crypto/internal/entropy/entropy.go @@ -3,9 +3,11 @@ // license that can be found in the LICENSE file. // Package entropy provides the passive entropy source for the FIPS 140-3 -// module. It is only used in FIPS mode by [crypto/internal/fips140/drbg.Read]. +// module. It is only used in FIPS mode by [crypto/internal/fips140/drbg.Read] +// from the FIPS 140-3 Go Cryptographic Module v1.0.0. Later versions of the +// module have an internal CPU jitter-based entropy source. // -// This complies with IG 9.3.A, Additional Comment 12, which until January 1, +// This complied with IG 9.3.A, Additional Comment 12, which until January 1, // 2026 allows new modules to meet an [earlier version] of Resolution 2(b): // "A software module that contains an approved DRBG that receives a LOAD // command (or its logical equivalent) with entropy obtained from [...] inside diff --git a/src/crypto/internal/fips140/drbg/rand.go b/src/crypto/internal/fips140/drbg/rand.go index c1a3ea0ae658ff..3ccb018e326047 100644 --- a/src/crypto/internal/fips140/drbg/rand.go +++ b/src/crypto/internal/fips140/drbg/rand.go @@ -9,21 +9,53 @@ package drbg import ( - "crypto/internal/entropy" "crypto/internal/fips140" + "crypto/internal/fips140/entropy" "crypto/internal/randutil" "crypto/internal/sysrand" "io" "sync" + "sync/atomic" ) -var drbgs = sync.Pool{ +// memory is a scratch buffer that is accessed between samples by the entropy +// source to expose it to memory access timings. +// +// We reuse it and share it between Seed calls to avoid the significant (~500µs) +// cost of zeroing a new allocation every time. The entropy source accesses it +// using atomics (and doesn't care about its contents). +// +// It should end up in the .noptrbss section, and become backed by physical pages +// at first use. This ensures that programs that do not use the FIPS 140-3 module +// do not incur any memory use or initialization penalties. +var memory entropy.ScratchBuffer + +func getEntropy() *[SeedSize]byte { + var retries int + seed, err := entropy.Seed(&memory) + for err != nil { + // The CPU jitter-based SP 800-90B entropy source has a non-negligible + // chance of failing the startup health tests. + // + // Each time it does, it enters a permanent failure state, and we + // restart it anew. This is not expected to happen more than a few times + // in a row. + if retries++; retries > 100 { + panic("fips140/drbg: failed to obtain initial entropy") + } + seed, err = entropy.Seed(&memory) + } + return &seed +} + +// getEntropy is very slow (~500µs), so we don't want it on the hot path. +// We keep both a persistent DRBG instance and a pool of additional instances. +// Occasional uses will use drbgInstance, even if the pool was emptied since the +// last use. Frequent concurrent uses will fill the pool and use it. +var drbgInstance atomic.Pointer[Counter] +var drbgPool = sync.Pool{ New: func() any { - var c *Counter - entropy.Depleted(func(seed *[48]byte) { - c = NewCounter(seed) - }) - return c + return NewCounter(getEntropy()) }, } @@ -44,8 +76,15 @@ func Read(b []byte) { additionalInput := new([SeedSize]byte) sysrand.Read(additionalInput[:16]) - drbg := drbgs.Get().(*Counter) - defer drbgs.Put(drbg) + drbg := drbgInstance.Swap(nil) + if drbg == nil { + drbg = drbgPool.Get().(*Counter) + } + defer func() { + if !drbgInstance.CompareAndSwap(nil, drbg) { + drbgPool.Put(drbg) + } + }() for len(b) > 0 { size := min(len(b), maxRequestSize) @@ -54,9 +93,7 @@ func Read(b []byte) { // Section 9.3.2: if Generate reports a reseed is required, the // additional input is passed to Reseed along with the entropy and // then nulled before the next Generate call. - entropy.Depleted(func(seed *[48]byte) { - drbg.Reseed(seed, additionalInput) - }) + drbg.Reseed(getEntropy(), additionalInput) additionalInput = nil continue } diff --git a/src/crypto/internal/fips140/entropy/entropy.go b/src/crypto/internal/fips140/entropy/entropy.go new file mode 100644 index 00000000000000..273f05c817aff8 --- /dev/null +++ b/src/crypto/internal/fips140/entropy/entropy.go @@ -0,0 +1,202 @@ +// Copyright 2025 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 entropy implements a CPU jitter-based SP 800-90B entropy source. +package entropy + +import ( + "crypto/internal/fips140deps/time" + "errors" + "sync/atomic" + "unsafe" +) + +// Version returns the version of the entropy source. +// +// This is independent of the FIPS 140-3 module version, in order to reuse the +// ESV certificate across module versions. +func Version() string { + return "v1.0.0" +} + +// ScratchBuffer is a large buffer that will be written to using atomics, to +// generate noise from memory access timings. Its contents do not matter. +type ScratchBuffer [1 << 25]byte + +// Seed returns a 384-bit seed with full entropy. +// +// memory is passed in to allow changing the allocation strategy without +// modifying the frozen and certified entropy source in this package. +// +// Seed returns an error if the entropy source startup health tests fail, which +// has a non-negligible chance of happening. +func Seed(memory *ScratchBuffer) ([48]byte, error) { + // Collect w = 1024 samples, each certified to provide no less than h = 0.5 + // bits of entropy, for a total of hᵢₙ = w × h = 512 bits of entropy, over + // nᵢₙ = w × n = 8192 bits of input data. + var samples [1024]byte + if err := Samples(samples[:], memory); err != nil { + return [48]byte{}, err + } + + // Use a vetted unkeyed conditioning component, SHA-384, with nw = 384 and + // nₒᵤₜ = 384. Per the formula in SP 800-90B Section 3.1.5.1.2, the output + // entropy hₒᵤₜ is: + // + // sage: n_in = 8192 + // sage: n_out = 384 + // sage: nw = 384 + // sage: h_in = 512 + // sage: P_high = 2^(-h_in) + // sage: P_low = (1 - P_high) / (2^n_in - 1) + // sage: n = min(n_out, nw) + // sage: ψ = 2^(n_in - n) * P_low + P_high + // sage: U = 2^(n_in - n) + sqrt(2 * n * 2^(n_in - n) * ln(2)) + // sage: ω = U * P_low + // sage: h_out = -log(max(ψ, ω), 2) + // sage: h_out.n() + // 384.000000000000 + // + // According to Implementation Guidance D.K, Resolution 19, since + // + // - the conditioning component is vetted, + // - hᵢₙ = 512 ≥ nₒᵤₜ + 64 = 448, and + // - nₒᵤₜ ≤ security strength of SHA-384 = 384 (per SP 800-107 Rev. 1, Table 1), + // + // we can claim the output has full entropy. + return SHA384(&samples), nil +} + +// Samples starts a new entropy source, collects the requested number of +// samples, conducts startup health tests, and returns the samples or an error +// if the health tests fail. +// +// The health tests have a non-negligible chance of failing. +func Samples(samples []uint8, memory *ScratchBuffer) error { + if len(samples) < 1024 { + return errors.New("entropy: at least 1024 samples are required for startup health tests") + } + s := newSource(memory) + for range 4 { + // Warm up the source to avoid any initial bias. + _ = s.Sample() + } + for i := range samples { + samples[i] = s.Sample() + } + if err := RepetitionCountTest(samples); err != nil { + return err + } + if err := AdaptiveProportionTest(samples); err != nil { + return err + } + return nil +} + +type source struct { + memory *ScratchBuffer + lcgState uint32 + previous int64 +} + +func newSource(memory *ScratchBuffer) *source { + return &source{ + memory: memory, + lcgState: uint32(time.HighPrecisionNow()), + previous: time.HighPrecisionNow(), + } +} + +// touchMemory performs a write to memory at the given index. +// +// The memory slice is passed in and may be shared across sources e.g. to avoid +// the significant (~500µs) cost of zeroing a new allocation on every [Seed] call. +func touchMemory(memory *ScratchBuffer, idx uint32) { + idx = idx / 4 * 4 // align to 32 bits + u32 := (*uint32)(unsafe.Pointer(&memory[idx])) + last := atomic.LoadUint32(u32) + atomic.SwapUint32(u32, last+13) +} + +func (s *source) Sample() uint8 { + // Perform a few memory accesses in an unpredictable pattern to expose the + // next measurement to as much system noise as possible. + memory, lcgState := s.memory, s.lcgState + _ = memory[0] // hoist the nil check out of touchMemory + for range 64 { + lcgState = 1664525*lcgState + 1013904223 + // Discard the lower bits, which tend to fall into short cycles. + idx := (lcgState >> 6) & (1<<25 - 1) + touchMemory(memory, idx) + } + s.lcgState = lcgState + + t := time.HighPrecisionNow() + sample := t - s.previous + s.previous = t + + // Reduce the symbol space to 256 values, assuming most of the entropy is in + // the least-significant bits, which represent the highest-resolution timing + // differences. + return uint8(sample) +} + +// RepetitionCountTest implements the repetition count test from SP 800-90B +// Section 4.4.1. It returns an error if any symbol is repeated C = 41 or more +// times in a row. +// +// This C value is calculated from a target failure probability α = 2⁻²⁰ and a +// claimed min-entropy per symbol h = 0.5 bits, using the formula in SP 800-90B +// Section 4.4.1. +// +// sage: α = 2^-20 +// sage: H = 0.5 +// sage: 1 + ceil(-log(α, 2) / H) +// 41 +func RepetitionCountTest(samples []uint8) error { + x := samples[0] + count := 1 + for _, y := range samples[1:] { + if y == x { + count++ + if count >= 41 { + return errors.New("entropy: repetition count health test failed") + } + } else { + x = y + count = 1 + } + } + return nil +} + +// AdaptiveProportionTest implements the adaptive proportion test from SP 800-90B +// Section 4.4.2. It returns an error if any symbol appears C = 410 or more +// times in the last W = 512 samples. +// +// This C value is calculated from a target failure probability α = 2⁻²⁰, a +// window size W = 512, and a claimed min-entropy per symbol h = 0.5 bits, using +// the formula in SP 800-90B Section 4.4.2, equivalent to the Microsoft Excel +// formula 1+CRITBINOM(W, power(2,(−H)),1−α). +// +// sage: from scipy.stats import binom +// sage: α = 2^-20 +// sage: H = 0.5 +// sage: W = 512 +// sage: C = 1 + binom.ppf(1 - α, W, 2**(-H)) +// sage: ceil(C) +// 410 +func AdaptiveProportionTest(samples []uint8) error { + var counts [256]int + for i, x := range samples { + counts[x]++ + if i >= 512 { + counts[samples[i-512]]-- + } + if counts[x] >= 410 { + return errors.New("entropy: adaptive proportion health test failed") + } + } + return nil +} diff --git a/src/crypto/internal/fips140/entropy/sha384.go b/src/crypto/internal/fips140/entropy/sha384.go new file mode 100644 index 00000000000000..ec23cfc9ad3661 --- /dev/null +++ b/src/crypto/internal/fips140/entropy/sha384.go @@ -0,0 +1,191 @@ +// Copyright 2025 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 entropy + +import "math/bits" + +// This file includes a SHA-384 implementation to insulate the entropy source +// from any changes in the FIPS 140-3 module's crypto/internal/fips140/sha512 +// package. We only support 1024-byte inputs. + +func SHA384(p *[1024]byte) [48]byte { + h := [8]uint64{ + 0xcbbb9d5dc1059ed8, + 0x629a292a367cd507, + 0x9159015a3070dd17, + 0x152fecd8f70e5939, + 0x67332667ffc00b31, + 0x8eb44a8768581511, + 0xdb0c2e0d64f98fa7, + 0x47b5481dbefa4fa4, + } + + sha384Block(&h, (*[128]byte)(p[0:128])) + sha384Block(&h, (*[128]byte)(p[128:256])) + sha384Block(&h, (*[128]byte)(p[256:384])) + sha384Block(&h, (*[128]byte)(p[384:512])) + sha384Block(&h, (*[128]byte)(p[512:640])) + sha384Block(&h, (*[128]byte)(p[640:768])) + sha384Block(&h, (*[128]byte)(p[768:896])) + sha384Block(&h, (*[128]byte)(p[896:1024])) + + var padlen [128]byte + padlen[0] = 0x80 + bePutUint64(padlen[112+8:], 1024*8) + sha384Block(&h, &padlen) + + var digest [48]byte + bePutUint64(digest[0:], h[0]) + bePutUint64(digest[8:], h[1]) + bePutUint64(digest[16:], h[2]) + bePutUint64(digest[24:], h[3]) + bePutUint64(digest[32:], h[4]) + bePutUint64(digest[40:], h[5]) + return digest +} + +var _K = [...]uint64{ + 0x428a2f98d728ae22, + 0x7137449123ef65cd, + 0xb5c0fbcfec4d3b2f, + 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, + 0x59f111f1b605d019, + 0x923f82a4af194f9b, + 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, + 0x12835b0145706fbe, + 0x243185be4ee4b28c, + 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, + 0x80deb1fe3b1696b1, + 0x9bdc06a725c71235, + 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, + 0xefbe4786384f25e3, + 0x0fc19dc68b8cd5b5, + 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, + 0x4a7484aa6ea6e483, + 0x5cb0a9dcbd41fbd4, + 0x76f988da831153b5, + 0x983e5152ee66dfab, + 0xa831c66d2db43210, + 0xb00327c898fb213f, + 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, + 0xd5a79147930aa725, + 0x06ca6351e003826f, + 0x142929670a0e6e70, + 0x27b70a8546d22ffc, + 0x2e1b21385c26c926, + 0x4d2c6dfc5ac42aed, + 0x53380d139d95b3df, + 0x650a73548baf63de, + 0x766a0abb3c77b2a8, + 0x81c2c92e47edaee6, + 0x92722c851482353b, + 0xa2bfe8a14cf10364, + 0xa81a664bbc423001, + 0xc24b8b70d0f89791, + 0xc76c51a30654be30, + 0xd192e819d6ef5218, + 0xd69906245565a910, + 0xf40e35855771202a, + 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, + 0x1e376c085141ab53, + 0x2748774cdf8eeb99, + 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, + 0x4ed8aa4ae3418acb, + 0x5b9cca4f7763e373, + 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, + 0x78a5636f43172f60, + 0x84c87814a1f0ab72, + 0x8cc702081a6439ec, + 0x90befffa23631e28, + 0xa4506cebde82bde9, + 0xbef9a3f7b2c67915, + 0xc67178f2e372532b, + 0xca273eceea26619c, + 0xd186b8c721c0c207, + 0xeada7dd6cde0eb1e, + 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, + 0x0a637dc5a2c898a6, + 0x113f9804bef90dae, + 0x1b710b35131c471b, + 0x28db77f523047d84, + 0x32caab7b40c72493, + 0x3c9ebe0a15c9bebc, + 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, + 0x597f299cfc657e2a, + 0x5fcb6fab3ad6faec, + 0x6c44198c4a475817, +} + +func sha384Block(dh *[8]uint64, p *[128]byte) { + var w [80]uint64 + for i := range 80 { + if i < 16 { + w[i] = beUint64(p[i*8:]) + } else { + v1 := w[i-2] + t1 := bits.RotateLeft64(v1, -19) ^ bits.RotateLeft64(v1, -61) ^ (v1 >> 6) + v2 := w[i-15] + t2 := bits.RotateLeft64(v2, -1) ^ bits.RotateLeft64(v2, -8) ^ (v2 >> 7) + + w[i] = t1 + w[i-7] + t2 + w[i-16] + } + } + + a, b, c, d, e, f, g, h := dh[0], dh[1], dh[2], dh[3], dh[4], dh[5], dh[6], dh[7] + + for i := range 80 { + t1 := h + (bits.RotateLeft64(e, -14) ^ bits.RotateLeft64(e, -18) ^ + bits.RotateLeft64(e, -41)) + ((e & f) ^ (^e & g)) + _K[i] + w[i] + t2 := (bits.RotateLeft64(a, -28) ^ bits.RotateLeft64(a, -34) ^ + bits.RotateLeft64(a, -39)) + ((a & b) ^ (a & c) ^ (b & c)) + + h = g + g = f + f = e + e = d + t1 + d = c + c = b + b = a + a = t1 + t2 + } + + dh[0] += a + dh[1] += b + dh[2] += c + dh[3] += d + dh[4] += e + dh[5] += f + dh[6] += g + dh[7] += h +} + +func beUint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func bePutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} diff --git a/src/crypto/internal/fips140/fips140.go b/src/crypto/internal/fips140/fips140.go index d03219b540e27f..76054b00684e2b 100644 --- a/src/crypto/internal/fips140/fips140.go +++ b/src/crypto/internal/fips140/fips140.go @@ -48,6 +48,8 @@ func Supported() error { } // See EnableFIPS in cmd/internal/obj/fips.go for commentary. + // Also, js/wasm and windows/386 don't have good enough timers + // for the CPU jitter entropy source. switch { case runtime.GOARCH == "wasm", runtime.GOOS == "windows" && runtime.GOARCH == "386", diff --git a/src/crypto/internal/fips140/mlkem/cast.go b/src/crypto/internal/fips140/mlkem/cast.go index a432d1fdab0e2b..ea089c1b76c0c0 100644 --- a/src/crypto/internal/fips140/mlkem/cast.go +++ b/src/crypto/internal/fips140/mlkem/cast.go @@ -9,9 +9,10 @@ import ( "crypto/internal/fips140" _ "crypto/internal/fips140/check" "errors" + "sync" ) -func init() { +var fipsSelfTest = sync.OnceFunc(func() { fips140.CAST("ML-KEM-768", func() error { var d = &[32]byte{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, @@ -40,14 +41,12 @@ func init() { dk := &DecapsulationKey768{} kemKeyGen(dk, d, z) ek := dk.EncapsulationKey() - Ke, c := ek.EncapsulateInternal(m) - Kd, err := dk.Decapsulate(c) - if err != nil { - return err - } + var cc [CiphertextSize768]byte + Ke, _ := kemEncaps(&cc, ek, m) + Kd := kemDecaps(dk, &cc) if !bytes.Equal(Ke, K) || !bytes.Equal(Kd, K) { return errors.New("unexpected result") } return nil }) -} +}) diff --git a/src/crypto/internal/fips140/mlkem/mlkem1024.go b/src/crypto/internal/fips140/mlkem/mlkem1024.go index 1419cf20fa9c67..edde161422cb6f 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem1024.go +++ b/src/crypto/internal/fips140/mlkem/mlkem1024.go @@ -113,6 +113,7 @@ func GenerateKey1024() (*DecapsulationKey1024, error) { } func generateKey1024(dk *DecapsulationKey1024) (*DecapsulationKey1024, error) { + fipsSelfTest() var d [32]byte drbg.Read(d[:]) var z [32]byte @@ -126,6 +127,7 @@ func generateKey1024(dk *DecapsulationKey1024) (*DecapsulationKey1024, error) { // GenerateKeyInternal1024 is a derandomized version of GenerateKey1024, // exclusively for use in tests. func GenerateKeyInternal1024(d, z *[32]byte) *DecapsulationKey1024 { + fipsSelfTest() dk := &DecapsulationKey1024{} kemKeyGen1024(dk, d, z) return dk @@ -278,6 +280,7 @@ func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) { } func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (sharedKey, ciphertext []byte) { + fipsSelfTest() var m [messageSize]byte drbg.Read(m[:]) // Note that the modulus check (step 2 of the encapsulation key check from @@ -289,6 +292,7 @@ func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (share // EncapsulateInternal is a derandomized version of Encapsulate, exclusively for // use in tests. func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) { + fipsSelfTest() cc := &[CiphertextSize1024]byte{} return kemEncaps1024(cc, ek, m) } @@ -394,6 +398,7 @@ func pkeEncrypt1024(cc *[CiphertextSize1024]byte, ex *encryptionKey1024, m *[mes // // The shared key must be kept secret. func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + fipsSelfTest() if len(ciphertext) != CiphertextSize1024 { return nil, errors.New("mlkem: invalid ciphertext length") } diff --git a/src/crypto/internal/fips140/mlkem/mlkem768.go b/src/crypto/internal/fips140/mlkem/mlkem768.go index 298660e4e977dd..088c2954de6a5c 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem768.go +++ b/src/crypto/internal/fips140/mlkem/mlkem768.go @@ -172,6 +172,7 @@ func GenerateKey768() (*DecapsulationKey768, error) { } func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) { + fipsSelfTest() var d [32]byte drbg.Read(d[:]) var z [32]byte @@ -185,6 +186,7 @@ func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) { // GenerateKeyInternal768 is a derandomized version of GenerateKey768, // exclusively for use in tests. func GenerateKeyInternal768(d, z *[32]byte) *DecapsulationKey768 { + fipsSelfTest() dk := &DecapsulationKey768{} kemKeyGen(dk, d, z) return dk @@ -337,6 +339,7 @@ func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) { } func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (sharedKey, ciphertext []byte) { + fipsSelfTest() var m [messageSize]byte drbg.Read(m[:]) // Note that the modulus check (step 2 of the encapsulation key check from @@ -348,6 +351,7 @@ func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (sharedK // EncapsulateInternal is a derandomized version of Encapsulate, exclusively for // use in tests. func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) { + fipsSelfTest() cc := &[CiphertextSize768]byte{} return kemEncaps(cc, ek, m) } @@ -453,6 +457,7 @@ func pkeEncrypt(cc *[CiphertextSize768]byte, ex *encryptionKey, m *[messageSize] // // The shared key must be kept secret. func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + fipsSelfTest() if len(ciphertext) != CiphertextSize768 { return nil, errors.New("mlkem: invalid ciphertext length") } diff --git a/src/crypto/internal/fips140deps/fipsdeps_test.go b/src/crypto/internal/fips140deps/fipsdeps_test.go index 2c3bc8184e71bc..97552dc1ce10f1 100644 --- a/src/crypto/internal/fips140deps/fipsdeps_test.go +++ b/src/crypto/internal/fips140deps/fipsdeps_test.go @@ -88,7 +88,8 @@ func TestImports(t *testing.T) { } } - // Ensure that all packages except check and check's dependencies import check. + // Ensure that all packages except check, check's dependencies, and the + // entropy source (which is used only from .../fips140/drbg) import check. for pkg := range allPackages { switch pkg { case "crypto/internal/fips140/check": @@ -99,6 +100,7 @@ func TestImports(t *testing.T) { case "crypto/internal/fips140/sha3": case "crypto/internal/fips140/sha256": case "crypto/internal/fips140/sha512": + case "crypto/internal/fips140/entropy": default: if !importCheck[pkg] { t.Errorf("package %s does not import crypto/internal/fips140/check", pkg) diff --git a/src/crypto/internal/fips140deps/time/time.go b/src/crypto/internal/fips140deps/time/time.go new file mode 100644 index 00000000000000..eea37b772e4351 --- /dev/null +++ b/src/crypto/internal/fips140deps/time/time.go @@ -0,0 +1,21 @@ +// Copyright 2025 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. + +//go:build !windows + +package time + +import "time" + +var start = time.Now() + +// HighPrecisionNow returns a high-resolution timestamp suitable for measuring +// small time differences. It uses the time package's monotonic clock. +// +// Its unit, epoch, and resolution are unspecified, and may change, but can be +// assumed to be sufficiently precise to measure time differences on the order +// of tens to hundreds of nanoseconds. +func HighPrecisionNow() int64 { + return int64(time.Since(start)) +} diff --git a/src/crypto/internal/fips140deps/time/time_windows.go b/src/crypto/internal/fips140deps/time/time_windows.go new file mode 100644 index 00000000000000..410ede4ee91705 --- /dev/null +++ b/src/crypto/internal/fips140deps/time/time_windows.go @@ -0,0 +1,17 @@ +// Copyright 2025 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 time + +import "internal/syscall/windows" + +// HighPrecisionNow returns a high-resolution timestamp suitable for measuring +// small time differences. It uses Windows' QueryPerformanceCounter. +// +// Its unit, epoch, and resolution are unspecified, and may change, but can be +// assumed to be sufficiently precise to measure time differences on the order +// of tens to hundreds of nanoseconds. +func HighPrecisionNow() int64 { + return windows.QueryPerformanceCounter() +} diff --git a/src/crypto/internal/fips140test/cast_test.go b/src/crypto/internal/fips140test/cast_test.go index b043a71f04effa..5bbc964b617b2b 100644 --- a/src/crypto/internal/fips140test/cast_test.go +++ b/src/crypto/internal/fips140test/cast_test.go @@ -48,8 +48,8 @@ var allCASTs = []string{ "HKDF-SHA2-256", "HMAC-SHA2-256", "KAS-ECC-SSC P-256", - "ML-KEM PCT", - "ML-KEM PCT", + "ML-KEM PCT", // -768 + "ML-KEM PCT", // -1024 "ML-KEM-768", "PBKDF2", "RSA sign and verify PCT", @@ -104,29 +104,44 @@ func TestAllCASTs(t *testing.T) { // TestConditionals causes the conditional CASTs and PCTs to be invoked. func TestConditionals(t *testing.T) { - mlkem.GenerateKey768() + // ML-KEM PCT + kMLKEM, err := mlkem.GenerateKey768() + if err != nil { + t.Error(err) + } else { + // ML-KEM-768 + kMLKEM.EncapsulationKey().Encapsulate() + } + // ECDH PCT kDH, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader) if err != nil { t.Error(err) } else { + // KAS-ECC-SSC P-256 ecdh.ECDH(ecdh.P256(), kDH, kDH.PublicKey()) } + // ECDSA PCT kDSA, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader) if err != nil { t.Error(err) } else { + // ECDSA P-256 SHA2-512 sign and verify ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32)) } + // Ed25519 sign and verify PCT k25519, err := ed25519.GenerateKey() if err != nil { t.Error(err) } else { + // Ed25519 sign and verify ed25519.Sign(k25519, make([]byte, 32)) } + // RSA sign and verify PCT kRSA, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Error(err) } else { + // RSASSA-PKCS-v1.5 2048-bit sign and verify rsa.SignPKCS1v15(kRSA, crypto.SHA256.String(), make([]byte, 32)) } t.Log("completed successfully") diff --git a/src/crypto/internal/fips140test/entropy_test.go b/src/crypto/internal/fips140test/entropy_test.go new file mode 100644 index 00000000000000..76c24289520e17 --- /dev/null +++ b/src/crypto/internal/fips140test/entropy_test.go @@ -0,0 +1,264 @@ +// Copyright 2025 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. + +//go:build !fips140v1.0 + +package fipstest + +import ( + "bytes" + "crypto/internal/cryptotest" + "crypto/internal/fips140/drbg" + "crypto/internal/fips140/entropy" + "crypto/sha256" + "crypto/sha512" + "encoding/hex" + "flag" + "fmt" + "internal/testenv" + "io/fs" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" +) + +var flagEntropySamples = flag.String("entropy-samples", "", "store entropy samples with the provided `suffix`") +var flagNISTSP80090B = flag.Bool("nist-sp800-90b", false, "run NIST SP 800-90B tests (requires docker)") + +func TestEntropySamples(t *testing.T) { + cryptotest.MustSupportFIPS140(t) + + var seqSamples [1_000_000]uint8 + samplesOrTryAgain(t, seqSamples[:]) + seqSamplesName := fmt.Sprintf("entropy_samples_sequential_%s_%s_%s_%s_%s.bin", entropy.Version(), + runtime.GOOS, runtime.GOARCH, *flagEntropySamples, time.Now().Format("20060102T150405Z")) + if *flagEntropySamples != "" { + if err := os.WriteFile(seqSamplesName, seqSamples[:], 0644); err != nil { + t.Fatalf("failed to write samples to %q: %v", seqSamplesName, err) + } + t.Logf("wrote %s", seqSamplesName) + } + + var restartSamples [1000][1000]uint8 + for i := range restartSamples { + var samples [1024]uint8 + samplesOrTryAgain(t, samples[:]) + copy(restartSamples[i][:], samples[:]) + } + restartSamplesName := fmt.Sprintf("entropy_samples_restart_%s_%s_%s_%s_%s.bin", entropy.Version(), + runtime.GOOS, runtime.GOARCH, *flagEntropySamples, time.Now().Format("20060102T150405Z")) + if *flagEntropySamples != "" { + f, err := os.Create(restartSamplesName) + if err != nil { + t.Fatalf("failed to create %q: %v", restartSamplesName, err) + } + for i := range restartSamples { + if _, err := f.Write(restartSamples[i][:]); err != nil { + t.Fatalf("failed to write samples to %q: %v", restartSamplesName, err) + } + } + if err := f.Close(); err != nil { + t.Fatalf("failed to close %q: %v", restartSamplesName, err) + } + t.Logf("wrote %s", restartSamplesName) + } + + if *flagNISTSP80090B { + if *flagEntropySamples == "" { + t.Fatalf("-nist-sp800-90b requires -entropy-samples to be set too") + } + + // Check if the nist-sp800-90b docker image is already present, + // and build it otherwise. + if err := testenv.Command(t, + "docker", "image", "inspect", "nist-sp800-90b", + ).Run(); err != nil { + t.Logf("building nist-sp800-90b docker image") + dockerfile := filepath.Join(t.TempDir(), "Dockerfile.SP800-90B_EntropyAssessment") + if err := os.WriteFile(dockerfile, []byte(NISTSP80090BDockerfile), 0644); err != nil { + t.Fatalf("failed to write Dockerfile: %v", err) + } + out, err := testenv.Command(t, + "docker", "build", "-t", "nist-sp800-90b", "-f", dockerfile, "/var/empty", + ).CombinedOutput() + if err != nil { + t.Fatalf("failed to build nist-sp800-90b docker image: %v\n%s", err, out) + } + } + + pwd, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get current working directory: %v", err) + } + t.Logf("running ea_non_iid analysis") + out, err := testenv.Command(t, + "docker", "run", "--rm", "-v", fmt.Sprintf("%s:%s", pwd, pwd), "-w", pwd, + "nist-sp800-90b", "ea_non_iid", seqSamplesName, "8", + ).CombinedOutput() + if err != nil { + t.Fatalf("ea_non_iid failed: %v\n%s", err, out) + } + t.Logf("\n%s", out) + + H_I := string(out) + H_I = strings.TrimSpace(H_I[strings.LastIndexByte(H_I, ' ')+1:]) + t.Logf("running ea_restart analysis with H_I = %s", H_I) + out, err = testenv.Command(t, + "docker", "run", "--rm", "-v", fmt.Sprintf("%s:%s", pwd, pwd), "-w", pwd, + "nist-sp800-90b", "ea_restart", restartSamplesName, "8", H_I, + ).CombinedOutput() + if err != nil { + t.Fatalf("ea_restart failed: %v\n%s", err, out) + } + t.Logf("\n%s", out) + } +} + +var NISTSP80090BDockerfile = ` +FROM ubuntu:24.04 +RUN apt-get update && apt-get install -y build-essential git \ + libbz2-dev libdivsufsort-dev libjsoncpp-dev libgmp-dev libmpfr-dev libssl-dev \ + && rm -rf /var/lib/apt/lists/* +RUN git clone --depth 1 https://github.com/usnistgov/SP800-90B_EntropyAssessment.git +RUN cd SP800-90B_EntropyAssessment && git checkout 8924f158c97e7b805e0f95247403ad4c44b9cd6f +WORKDIR ./SP800-90B_EntropyAssessment/cpp/ +RUN make all +RUN cd selftest && ./selftest +RUN cp ea_non_iid ea_restart /usr/local/bin/ +` + +var memory entropy.ScratchBuffer + +// samplesOrTryAgain calls entropy.Samples up to 10 times until it succeeds. +// Samples has a non-negligible chance of failing the health tests, as required +// by SP 800-90B. +func samplesOrTryAgain(t *testing.T, samples []uint8) { + t.Helper() + for range 10 { + if err := entropy.Samples(samples, &memory); err != nil { + t.Logf("entropy.Samples() failed: %v", err) + continue + } + return + } + t.Fatal("entropy.Samples() failed 10 times in a row") +} + +func TestEntropySHA384(t *testing.T) { + var input [1024]uint8 + for i := range input { + input[i] = uint8(i) + } + want := sha512.Sum384(input[:]) + got := entropy.SHA384(&input) + if got != want { + t.Errorf("SHA384() = %x, want %x", got, want) + } +} + +func TestEntropyRepetitionCountTest(t *testing.T) { + good := bytes.Repeat(append(bytes.Repeat([]uint8{42}, 40), 1), 100) + if err := entropy.RepetitionCountTest(good); err != nil { + t.Errorf("RepetitionCountTest(good) = %v, want nil", err) + } + + bad := bytes.Repeat([]uint8{0}, 40) + bad = append(bad, bytes.Repeat([]uint8{1}, 40)...) + bad = append(bad, bytes.Repeat([]uint8{42}, 41)...) + bad = append(bad, bytes.Repeat([]uint8{2}, 40)...) + if err := entropy.RepetitionCountTest(bad); err == nil { + t.Error("RepetitionCountTest(bad) = nil, want error") + } + + bad = bytes.Repeat([]uint8{42}, 41) + if err := entropy.RepetitionCountTest(bad); err == nil { + t.Error("RepetitionCountTest(bad) = nil, want error") + } +} + +func TestEntropyAdaptiveProportionTest(t *testing.T) { + good := bytes.Repeat([]uint8{0}, 409) + good = append(good, bytes.Repeat([]uint8{1}, 512-409)...) + good = append(good, bytes.Repeat([]uint8{0}, 409)...) + if err := entropy.AdaptiveProportionTest(good); err != nil { + t.Errorf("AdaptiveProportionTest(good) = %v, want nil", err) + } + + // These fall out of the window. + bad := bytes.Repeat([]uint8{1}, 100) + bad = append(bad, bytes.Repeat([]uint8{1, 2, 3, 4, 5, 6}, 100)...) + // These are in the window. + bad = append(bad, bytes.Repeat([]uint8{42}, 410)...) + if err := entropy.AdaptiveProportionTest(bad[:len(bad)-1]); err != nil { + t.Errorf("AdaptiveProportionTest(bad[:len(bad)-1]) = %v, want nil", err) + } + if err := entropy.AdaptiveProportionTest(bad); err == nil { + t.Error("AdaptiveProportionTest(bad) = nil, want error") + } +} + +func TestEntropyUnchanged(t *testing.T) { + testenv.MustHaveSource(t) + + h := sha256.New() + root := os.DirFS("../fips140/entropy") + if err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + data, err := fs.ReadFile(root, path) + if err != nil { + return err + } + t.Logf("Hashing %s (%d bytes)", path, len(data)) + fmt.Fprintf(h, "%s %d\n", path, len(data)) + h.Write(data) + return nil + }); err != nil { + t.Fatalf("WalkDir: %v", err) + } + + // The crypto/internal/fips140/entropy package is certified as a FIPS 140-3 + // entropy source through the Entropy Source Validation program, + // independently of the FIPS 140-3 module. It must not change even across + // FIPS 140-3 module versions, in order to reuse the ESV certificate. + exp := "35976eb8a11678c79777da07aaab5511d4325701f837777df205f6e7b20c6821" + if got := hex.EncodeToString(h.Sum(nil)); got != exp { + t.Errorf("hash of crypto/internal/fips140/entropy = %s, want %s", got, exp) + } +} + +func TestEntropyRace(t *testing.T) { + // Check that concurrent calls to Seed don't trigger the race detector. + for range 2 { + go func() { + _, _ = entropy.Seed(&memory) + }() + } + // Same, with the higher-level DRBG. More concurrent calls to hit the Pool. + for range 16 { + go func() { + var b [64]byte + drbg.Read(b[:]) + }() + } +} + +var sink byte + +func BenchmarkEntropySeed(b *testing.B) { + for b.Loop() { + seed, err := entropy.Seed(&memory) + if err != nil { + b.Fatalf("entropy.Seed() failed: %v", err) + } + sink ^= seed[0] + } +} diff --git a/src/crypto/internal/sysrand/internal/seccomp/seccomp_linux.go b/src/crypto/internal/sysrand/internal/seccomp/seccomp_linux.go index 32ef52ad9e4e24..5447242d27ba3a 100644 --- a/src/crypto/internal/sysrand/internal/seccomp/seccomp_linux.go +++ b/src/crypto/internal/sysrand/internal/seccomp/seccomp_linux.go @@ -49,7 +49,18 @@ struct seccomp_data { #define SECCOMP_RET_ALLOW 0x7fff0000U #define SECCOMP_SET_MODE_FILTER 1 +#ifndef SYS_getrandom +#define SYS_getrandom -1 +#endif + +#ifndef SYS_seccomp +#define SYS_seccomp -1 +#endif + int disable_getrandom() { + if (SYS_getrandom == -1 || SYS_seccomp == -1) { + return 3; + } if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { return 1; } diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 1e0b5f06672d15..088c66fadb2a44 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -357,7 +357,7 @@ func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, erro if http11fallback { return "", nil } - return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos) + return "", fmt.Errorf("tls: client requested unsupported application protocols (%q)", clientProtos) } // supportsECDHE returns whether ECDHE key exchanges can be used with this diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index 50452b5bef45f4..1d56a06c3fb221 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -25,6 +25,7 @@ import ( "internal/saferio" "internal/zstd" "io" + "math" "os" "strings" "unsafe" @@ -830,17 +831,9 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { switch t { case R_X86_64_64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) case R_X86_64_32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -872,12 +865,7 @@ func (f *File) applyRelocations386(dst []byte, rels []byte) error { sym := &symbols[symNo-1] if t == R_386_32 { - if rel.Off+4 >= uint32(len(dst)) { - continue - } - val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) - val += uint32(sym.Value) - f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) + putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true) } } @@ -910,12 +898,7 @@ func (f *File) applyRelocationsARM(dst []byte, rels []byte) error { switch t { case R_ARM_ABS32: - if rel.Off+4 >= uint32(len(dst)) { - continue - } - val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) - val += uint32(sym.Value) - f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) + putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true) } } @@ -955,17 +938,9 @@ func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error { switch t { case R_AARCH64_ABS64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) case R_AARCH64_ABS32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -1001,11 +976,7 @@ func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error { switch t { case R_PPC_ADDR32: - if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, uint64(rela.Off), 4, sym.Value, 0, false) } } @@ -1041,17 +1012,9 @@ func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error { switch t { case R_PPC64_ADDR64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) case R_PPC64_ADDR32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -1084,12 +1047,7 @@ func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error { switch t { case R_MIPS_32: - if rel.Off+4 >= uint32(len(dst)) { - continue - } - val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) - val += uint32(sym.Value) - f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) + putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true) } } @@ -1132,17 +1090,9 @@ func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error { switch t { case R_MIPS_64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) case R_MIPS_32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -1180,17 +1130,9 @@ func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error { switch t { case R_LARCH_64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) case R_LARCH_32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -1226,17 +1168,9 @@ func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error { switch t { case R_RISCV_64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) case R_RISCV_32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -1272,17 +1206,9 @@ func (f *File) applyRelocationss390x(dst []byte, rels []byte) error { switch t { case R_390_64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) case R_390_32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -1318,17 +1244,10 @@ func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error { switch t { case R_SPARC_64, R_SPARC_UA64: - if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val64 := sym.Value + uint64(rela.Addend) - f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false) + case R_SPARC_32, R_SPARC_UA32: - if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { - continue - } - val32 := uint32(sym.Value) + uint32(rela.Addend) - f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false) } } @@ -1903,3 +1822,38 @@ type nobitsSectionReader struct{} func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) { return 0, errors.New("unexpected read from SHT_NOBITS section") } + +// putUint writes a relocation to slice +// at offset start of length length (4 or 8 bytes), +// adding sym+addend to the existing value if readUint is true, +// or just writing sym+addend if readUint is false. +// If the write would extend beyond the end of slice, putUint does nothing. +// If the addend is negative, putUint does nothing. +// If the addition would overflow, putUint does nothing. +func putUint(byteOrder binary.ByteOrder, slice []byte, start, length, sym uint64, addend int64, readUint bool) { + if start+length > uint64(len(slice)) || math.MaxUint64-start < length { + return + } + if addend < 0 { + return + } + + s := slice[start : start+length] + + switch length { + case 4: + ae := uint32(addend) + if readUint { + ae += byteOrder.Uint32(s) + } + byteOrder.PutUint32(s, uint32(sym)+ae) + case 8: + ae := uint64(addend) + if readUint { + ae += byteOrder.Uint64(s) + } + byteOrder.PutUint64(s, sym+ae) + default: + panic("can't happen") + } +} diff --git a/src/encoding/xml/marshal.go b/src/encoding/xml/marshal.go index 133503fa2de41c..13fbeeeedc75ce 100644 --- a/src/encoding/xml/marshal.go +++ b/src/encoding/xml/marshal.go @@ -416,12 +416,6 @@ func (p *printer) popPrefix() { } } -var ( - marshalerType = reflect.TypeFor[Marshaler]() - marshalerAttrType = reflect.TypeFor[MarshalerAttr]() - textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]() -) - // marshalValue writes one or more XML elements representing val. // If val was obtained from a struct field, finfo must have its details. func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error { @@ -450,24 +444,32 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat typ := val.Type() // Check for marshaler. - if val.CanInterface() && typ.Implements(marshalerType) { - return p.marshalInterface(val.Interface().(Marshaler), defaultStart(typ, finfo, startTemplate)) + if val.CanInterface() { + if marshaler, ok := reflect.TypeAssert[Marshaler](val); ok { + return p.marshalInterface(marshaler, defaultStart(typ, finfo, startTemplate)) + } } if val.CanAddr() { pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(marshalerType) { - return p.marshalInterface(pv.Interface().(Marshaler), defaultStart(pv.Type(), finfo, startTemplate)) + if pv.CanInterface() { + if marshaler, ok := reflect.TypeAssert[Marshaler](pv); ok { + return p.marshalInterface(marshaler, defaultStart(pv.Type(), finfo, startTemplate)) + } } } // Check for text marshaler. - if val.CanInterface() && typ.Implements(textMarshalerType) { - return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), defaultStart(typ, finfo, startTemplate)) + if val.CanInterface() { + if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](val); ok { + return p.marshalTextInterface(textMarshaler, defaultStart(typ, finfo, startTemplate)) + } } if val.CanAddr() { pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { - return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), defaultStart(pv.Type(), finfo, startTemplate)) + if pv.CanInterface() { + if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](pv); ok { + return p.marshalTextInterface(textMarshaler, defaultStart(pv.Type(), finfo, startTemplate)) + } } } @@ -503,7 +505,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name } else { fv := xmlname.value(val, dontInitNilPointers) - if v, ok := fv.Interface().(Name); ok && v.Local != "" { + if v, ok := reflect.TypeAssert[Name](fv); ok && v.Local != "" { start.Name = v } } @@ -580,21 +582,9 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat // marshalAttr marshals an attribute with the given name and value, adding to start.Attr. func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value) error { - if val.CanInterface() && val.Type().Implements(marshalerAttrType) { - attr, err := val.Interface().(MarshalerAttr).MarshalXMLAttr(name) - if err != nil { - return err - } - if attr.Name.Local != "" { - start.Attr = append(start.Attr, attr) - } - return nil - } - - if val.CanAddr() { - pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) { - attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name) + if val.CanInterface() { + if marshaler, ok := reflect.TypeAssert[MarshalerAttr](val); ok { + attr, err := marshaler.MarshalXMLAttr(name) if err != nil { return err } @@ -605,19 +595,25 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value) } } - if val.CanInterface() && val.Type().Implements(textMarshalerType) { - text, err := val.Interface().(encoding.TextMarshaler).MarshalText() - if err != nil { - return err + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() { + if marshaler, ok := reflect.TypeAssert[MarshalerAttr](pv); ok { + attr, err := marshaler.MarshalXMLAttr(name) + if err != nil { + return err + } + if attr.Name.Local != "" { + start.Attr = append(start.Attr, attr) + } + return nil + } } - start.Attr = append(start.Attr, Attr{name, string(text)}) - return nil } - if val.CanAddr() { - pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { - text, err := pv.Interface().(encoding.TextMarshaler).MarshalText() + if val.CanInterface() { + if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](val); ok { + text, err := textMarshaler.MarshalText() if err != nil { return err } @@ -626,6 +622,20 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value) } } + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() { + if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](pv); ok { + text, err := textMarshaler.MarshalText() + if err != nil { + return err + } + start.Attr = append(start.Attr, Attr{name, string(text)}) + return nil + } + } + } + // Dereference or skip nil pointer, interface values. switch val.Kind() { case reflect.Pointer, reflect.Interface: @@ -647,7 +657,8 @@ func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value) } if val.Type() == attrType { - start.Attr = append(start.Attr, val.Interface().(Attr)) + attr, _ := reflect.TypeAssert[Attr](val) + start.Attr = append(start.Attr, attr) return nil } @@ -854,20 +865,9 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { if err := s.trim(finfo.parents); err != nil { return err } - if vf.CanInterface() && vf.Type().Implements(textMarshalerType) { - data, err := vf.Interface().(encoding.TextMarshaler).MarshalText() - if err != nil { - return err - } - if err := emit(p, data); err != nil { - return err - } - continue - } - if vf.CanAddr() { - pv := vf.Addr() - if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { - data, err := pv.Interface().(encoding.TextMarshaler).MarshalText() + if vf.CanInterface() { + if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](vf); ok { + data, err := textMarshaler.MarshalText() if err != nil { return err } @@ -877,6 +877,21 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { continue } } + if vf.CanAddr() { + pv := vf.Addr() + if pv.CanInterface() { + if textMarshaler, ok := reflect.TypeAssert[encoding.TextMarshaler](pv); ok { + data, err := textMarshaler.MarshalText() + if err != nil { + return err + } + if err := emit(p, data); err != nil { + return err + } + continue + } + } + } var scratch [64]byte vf = indirect(vf) @@ -902,7 +917,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { return err } case reflect.Slice: - if elem, ok := vf.Interface().([]byte); ok { + if elem, ok := reflect.TypeAssert[[]byte](vf); ok { if err := emit(p, elem); err != nil { return err } diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go index af25c20f0618dc..d3cb74b2c4311a 100644 --- a/src/encoding/xml/read.go +++ b/src/encoding/xml/read.go @@ -255,28 +255,36 @@ func (d *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error { } val = val.Elem() } - if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) { + if val.CanInterface() { // This is an unmarshaler with a non-pointer receiver, // so it's likely to be incorrect, but we do what we're told. - return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr) + if unmarshaler, ok := reflect.TypeAssert[UnmarshalerAttr](val); ok { + return unmarshaler.UnmarshalXMLAttr(attr) + } } if val.CanAddr() { pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(unmarshalerAttrType) { - return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr) + if pv.CanInterface() { + if unmarshaler, ok := reflect.TypeAssert[UnmarshalerAttr](pv); ok { + return unmarshaler.UnmarshalXMLAttr(attr) + } } } // Not an UnmarshalerAttr; try encoding.TextUnmarshaler. - if val.CanInterface() && val.Type().Implements(textUnmarshalerType) { + if val.CanInterface() { // This is an unmarshaler with a non-pointer receiver, // so it's likely to be incorrect, but we do what we're told. - return val.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value)) + if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](val); ok { + return textUnmarshaler.UnmarshalText([]byte(attr.Value)) + } } if val.CanAddr() { pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) { - return pv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value)) + if pv.CanInterface() { + if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](pv); ok { + return textUnmarshaler.UnmarshalText([]byte(attr.Value)) + } } } @@ -303,12 +311,7 @@ func (d *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error { return copyValue(val, []byte(attr.Value)) } -var ( - attrType = reflect.TypeFor[Attr]() - unmarshalerType = reflect.TypeFor[Unmarshaler]() - unmarshalerAttrType = reflect.TypeFor[UnmarshalerAttr]() - textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]() -) +var attrType = reflect.TypeFor[Attr]() const ( maxUnmarshalDepth = 10000 @@ -352,27 +355,35 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement, depth int) e val = val.Elem() } - if val.CanInterface() && val.Type().Implements(unmarshalerType) { + if val.CanInterface() { // This is an unmarshaler with a non-pointer receiver, // so it's likely to be incorrect, but we do what we're told. - return d.unmarshalInterface(val.Interface().(Unmarshaler), start) + if unmarshaler, ok := reflect.TypeAssert[Unmarshaler](val); ok { + return d.unmarshalInterface(unmarshaler, start) + } } if val.CanAddr() { pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(unmarshalerType) { - return d.unmarshalInterface(pv.Interface().(Unmarshaler), start) + if pv.CanInterface() { + if unmarshaler, ok := reflect.TypeAssert[Unmarshaler](pv); ok { + return d.unmarshalInterface(unmarshaler, start) + } } } - if val.CanInterface() && val.Type().Implements(textUnmarshalerType) { - return d.unmarshalTextInterface(val.Interface().(encoding.TextUnmarshaler)) + if val.CanInterface() { + if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](val); ok { + return d.unmarshalTextInterface(textUnmarshaler) + } } if val.CanAddr() { pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) { - return d.unmarshalTextInterface(pv.Interface().(encoding.TextUnmarshaler)) + if pv.CanInterface() { + if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](pv); ok { + return d.unmarshalTextInterface(textUnmarshaler) + } } } @@ -453,7 +464,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement, depth int) e return UnmarshalError(e) } fv := finfo.value(sv, initNilPointers) - if _, ok := fv.Interface().(Name); ok { + if _, ok := reflect.TypeAssert[Name](fv); ok { fv.Set(reflect.ValueOf(start.Name)) } } @@ -578,20 +589,24 @@ Loop: } } - if saveData.IsValid() && saveData.CanInterface() && saveData.Type().Implements(textUnmarshalerType) { - if err := saveData.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { - return err + if saveData.IsValid() && saveData.CanInterface() { + if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](saveData); ok { + if err := textUnmarshaler.UnmarshalText(data); err != nil { + return err + } + saveData = reflect.Value{} } - saveData = reflect.Value{} } if saveData.IsValid() && saveData.CanAddr() { pv := saveData.Addr() - if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) { - if err := pv.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { - return err + if pv.CanInterface() { + if textUnmarshaler, ok := reflect.TypeAssert[encoding.TextUnmarshaler](pv); ok { + if err := textUnmarshaler.UnmarshalText(data); err != nil { + return err + } + saveData = reflect.Value{} } - saveData = reflect.Value{} } } diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index d50a98d34c9092..c76b254b23ffc8 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -485,11 +485,16 @@ var depsRules = ` internal/byteorder < crypto/internal/fips140deps/byteorder; internal/cpu, internal/goarch < crypto/internal/fips140deps/cpu; internal/godebug < crypto/internal/fips140deps/godebug; + time, internal/syscall/windows < crypto/internal/fips140deps/time; + + crypto/internal/fips140deps/time, errors, math/bits, sync/atomic, unsafe + < crypto/internal/fips140/entropy; STR, hash, crypto/internal/impl, crypto/internal/entropy, crypto/internal/randutil, + crypto/internal/fips140/entropy, crypto/internal/fips140deps/byteorder, crypto/internal/fips140deps/cpu, crypto/internal/fips140deps/godebug diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go index dd9845d1b223c3..9323e49eb745d7 100644 --- a/src/internal/poll/fd_windows.go +++ b/src/internal/poll/fd_windows.go @@ -9,6 +9,7 @@ import ( "internal/race" "internal/syscall/windows" "io" + "runtime" "sync" "sync/atomic" "syscall" @@ -75,9 +76,6 @@ type operation struct { // fields used by runtime.netpoll runtimeCtx uintptr mode int32 - - // fields used only by net package - buf syscall.WSABuf } func (o *operation) setEvent() { @@ -107,9 +105,8 @@ func (fd *FD) overlapped(o *operation) *syscall.Overlapped { return &o.o } -func (o *operation) InitBuf(buf []byte) { - o.buf.Len = uint32(len(buf)) - o.buf.Buf = unsafe.SliceData(buf) +func newWsaBuf(b []byte) *syscall.WSABuf { + return &syscall.WSABuf{Buf: unsafe.SliceData(b), Len: uint32(len(b))} } var wsaBufsPool = sync.Pool{ @@ -362,6 +359,9 @@ type FD struct { isBlocking bool disassociated atomic.Bool + + readPinner runtime.Pinner + writePinner runtime.Pinner } // setOffset sets the offset fields of the overlapped object @@ -537,6 +537,11 @@ func (fd *FD) Read(buf []byte) (int, error) { defer fd.readUnlock() } + if len(buf) > 0 && !fd.isBlocking { + fd.readPinner.Pin(&buf[0]) + defer fd.readPinner.Unpin() + } + if len(buf) > maxRW { buf = buf[:maxRW] } @@ -547,10 +552,8 @@ func (fd *FD) Read(buf []byte) (int, error) { case kindConsole: n, err = fd.readConsole(buf) case kindFile, kindPipe: - o := &fd.rop - o.InitBuf(buf) - n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = syscall.ReadFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, fd.overlapped(o)) + n, err = fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) { + err = syscall.ReadFile(fd.Sysfd, buf, &qty, fd.overlapped(o)) return qty, err }) fd.addOffset(n) @@ -564,11 +567,9 @@ func (fd *FD) Read(buf []byte) (int, error) { } } case kindNet: - o := &fd.rop - o.InitBuf(buf) - n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) { + n, err = fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) { var flags uint32 - err = syscall.WSARecv(fd.Sysfd, &o.buf, 1, &qty, &flags, &o.o, nil) + err = syscall.WSARecv(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, &o.o, nil) return qty, err }) if race.Enabled { @@ -656,7 +657,7 @@ func (fd *FD) readConsole(b []byte) (int, error) { } // Pread emulates the Unix pread system call. -func (fd *FD) Pread(b []byte, off int64) (int, error) { +func (fd *FD) Pread(buf []byte, off int64) (int, error) { if fd.kind == kindPipe { // Pread does not work with pipes return 0, syscall.ESPIPE @@ -667,8 +668,13 @@ func (fd *FD) Pread(b []byte, off int64) (int, error) { } defer fd.readWriteUnlock() - if len(b) > maxRW { - b = b[:maxRW] + if len(buf) > 0 && !fd.isBlocking { + fd.readPinner.Pin(&buf[0]) + defer fd.readPinner.Unpin() + } + + if len(buf) > maxRW { + buf = buf[:maxRW] } if fd.isBlocking { @@ -687,17 +693,15 @@ func (fd *FD) Pread(b []byte, off int64) (int, error) { curoffset := fd.offset defer fd.setOffset(curoffset) } - o := &fd.rop - o.InitBuf(b) fd.setOffset(off) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = syscall.ReadFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, &o.o) + n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) { + err = syscall.ReadFile(fd.Sysfd, buf, &qty, &o.o) return qty, err }) if err == syscall.ERROR_HANDLE_EOF { err = io.EOF } - if len(b) != 0 { + if len(buf) != 0 { err = fd.eofError(n, err) } return n, err @@ -715,14 +719,18 @@ func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) { return 0, nil, err } defer fd.readUnlock() - o := &fd.rop - o.InitBuf(buf) + + if !fd.isBlocking { + fd.readPinner.Pin(&buf[0]) + defer fd.readPinner.Unpin() + } + rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny) defer wsaRsaPool.Put(rsa) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { + n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) { rsan := int32(unsafe.Sizeof(*rsa)) var flags uint32 - err = syscall.WSARecvFrom(fd.Sysfd, &o.buf, 1, &qty, &flags, rsa, &rsan, &o.o, nil) + err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, rsa, &rsan, &o.o, nil) return qty, err }) err = fd.eofError(n, err) @@ -745,14 +753,18 @@ func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) return 0, err } defer fd.readUnlock() - o := &fd.rop - o.InitBuf(buf) + + if !fd.isBlocking { + fd.readPinner.Pin(&buf[0]) + defer fd.readPinner.Unpin() + } + rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny) defer wsaRsaPool.Put(rsa) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { + n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) { rsan := int32(unsafe.Sizeof(*rsa)) var flags uint32 - err = syscall.WSARecvFrom(fd.Sysfd, &o.buf, 1, &qty, &flags, rsa, &rsan, &o.o, nil) + err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, rsa, &rsan, &o.o, nil) return qty, err }) err = fd.eofError(n, err) @@ -775,14 +787,18 @@ func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) return 0, err } defer fd.readUnlock() - o := &fd.rop - o.InitBuf(buf) + + if !fd.isBlocking { + fd.readPinner.Pin(&buf[0]) + defer fd.readPinner.Unpin() + } + rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny) defer wsaRsaPool.Put(rsa) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { + n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) { rsan := int32(unsafe.Sizeof(*rsa)) var flags uint32 - err = syscall.WSARecvFrom(fd.Sysfd, &o.buf, 1, &qty, &flags, rsa, &rsan, &o.o, nil) + err = syscall.WSARecvFrom(fd.Sysfd, newWsaBuf(buf), 1, &qty, &flags, rsa, &rsan, &o.o, nil) return qty, err }) err = fd.eofError(n, err) @@ -807,6 +823,11 @@ func (fd *FD) Write(buf []byte) (int, error) { defer fd.writeUnlock() } + if len(buf) > 0 && !fd.isBlocking { + fd.writePinner.Pin(&buf[0]) + defer fd.writePinner.Unpin() + } + var ntotal int for { max := len(buf) @@ -820,10 +841,8 @@ func (fd *FD) Write(buf []byte) (int, error) { case kindConsole: n, err = fd.writeConsole(b) case kindPipe, kindFile: - o := &fd.wop - o.InitBuf(b) - n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = syscall.WriteFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, fd.overlapped(o)) + n, err = fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = syscall.WriteFile(fd.Sysfd, b, &qty, fd.overlapped(o)) return qty, err }) fd.addOffset(n) @@ -831,10 +850,8 @@ func (fd *FD) Write(buf []byte) (int, error) { if race.Enabled { race.ReleaseMerge(unsafe.Pointer(&ioSync)) } - o := &fd.wop - o.InitBuf(b) - n, err = fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = syscall.WSASend(fd.Sysfd, &o.buf, 1, &qty, 0, &o.o, nil) + n, err = fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = syscall.WSASend(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, &o.o, nil) return qty, err }) } @@ -903,6 +920,11 @@ func (fd *FD) Pwrite(buf []byte, off int64) (int, error) { } defer fd.readWriteUnlock() + if len(buf) > 0 && !fd.isBlocking { + fd.writePinner.Pin(&buf[0]) + defer fd.writePinner.Unpin() + } + if fd.isBlocking { curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) if err != nil { @@ -926,12 +948,9 @@ func (fd *FD) Pwrite(buf []byte, off int64) (int, error) { if max-ntotal > maxRW { max = ntotal + maxRW } - b := buf[ntotal:max] - o := &fd.wop - o.InitBuf(b) fd.setOffset(off + int64(ntotal)) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = syscall.WriteFile(fd.Sysfd, unsafe.Slice(o.buf.Buf, o.buf.Len), &qty, &o.o) + n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = syscall.WriteFile(fd.Sysfd, buf[ntotal:max], &qty, &o.o) return qty, err }) if n > 0 { @@ -978,25 +997,26 @@ func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { if len(buf) == 0 { // handle zero-byte payload - o := &fd.wop - o.InitBuf(buf) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = syscall.WSASendto(fd.Sysfd, &o.buf, 1, &qty, 0, sa, &o.o, nil) + n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = syscall.WSASendto(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa, &o.o, nil) return qty, err }) return n, err } + if !fd.isBlocking { + fd.writePinner.Pin(&buf[0]) + defer fd.writePinner.Unpin() + } + ntotal := 0 for len(buf) > 0 { b := buf if len(b) > maxRW { b = b[:maxRW] } - o := &fd.wop - o.InitBuf(b) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = syscall.WSASendto(fd.Sysfd, &o.buf, 1, &qty, 0, sa, &o.o, nil) + n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = syscall.WSASendto(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa, &o.o, nil) return qty, err }) ntotal += int(n) @@ -1017,25 +1037,26 @@ func (fd *FD) WriteToInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) if len(buf) == 0 { // handle zero-byte payload - o := &fd.wop - o.InitBuf(buf) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = windows.WSASendtoInet4(fd.Sysfd, &o.buf, 1, &qty, 0, sa4, &o.o, nil) + n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = windows.WSASendtoInet4(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa4, &o.o, nil) return qty, err }) return n, err } + if !fd.isBlocking { + fd.writePinner.Pin(&buf[0]) + defer fd.writePinner.Unpin() + } + ntotal := 0 for len(buf) > 0 { b := buf if len(b) > maxRW { b = b[:maxRW] } - o := &fd.wop - o.InitBuf(b) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = windows.WSASendtoInet4(fd.Sysfd, &o.buf, 1, &qty, 0, sa4, &o.o, nil) + n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = windows.WSASendtoInet4(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa4, &o.o, nil) return qty, err }) ntotal += int(n) @@ -1056,25 +1077,26 @@ func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) if len(buf) == 0 { // handle zero-byte payload - o := &fd.wop - o.InitBuf(buf) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = windows.WSASendtoInet6(fd.Sysfd, &o.buf, 1, &qty, 0, sa6, &o.o, nil) + n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = windows.WSASendtoInet6(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, 0, sa6, &o.o, nil) return qty, err }) return n, err } + if !fd.isBlocking { + fd.writePinner.Pin(&buf[0]) + defer fd.writePinner.Unpin() + } + ntotal := 0 for len(buf) > 0 { b := buf if len(b) > maxRW { b = b[:maxRW] } - o := &fd.wop - o.InitBuf(b) - n, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { - err = windows.WSASendtoInet6(fd.Sysfd, &o.buf, 1, &qty, 0, sa6, &o.o, nil) + n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) { + err = windows.WSASendtoInet6(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, sa6, &o.o, nil) return qty, err }) ntotal += int(n) @@ -1264,14 +1286,12 @@ func (fd *FD) RawRead(f func(uintptr) bool) error { // Use a zero-byte read as a way to get notified when this // socket is readable. h/t https://stackoverflow.com/a/42019668/332798 - o := &fd.rop - o.InitBuf(nil) - _, err := fd.execIO(o, func(o *operation) (qty uint32, err error) { + _, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) { var flags uint32 if !fd.IsStream { flags |= windows.MSG_PEEK } - err = syscall.WSARecv(fd.Sysfd, &o.buf, 1, &qty, &flags, &o.o, nil) + err = syscall.WSARecv(fd.Sysfd, &syscall.WSABuf{}, 1, &qty, &flags, &o.o, nil) return qty, err }) if err == windows.WSAEMSGSIZE { diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go index fb8bc80629a626..70c4d76dff0d0f 100644 --- a/src/internal/syscall/windows/zsyscall_windows.go +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -328,7 +328,7 @@ func GetFileInformationByHandleEx(handle syscall.Handle, class uint32, info *byt } func GetFileSizeEx(handle syscall.Handle, size *int64) (err error) { - r1, _, e1 := syscall.Syscall(procGetFileSizeEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(size)), 0) + r1, _, e1 := syscall.SyscallN(procGetFileSizeEx.Addr(), uintptr(handle), uintptr(unsafe.Pointer(size))) if r1 == 0 { err = errnoErr(e1) } diff --git a/src/net/tcpsock_test.go b/src/net/tcpsock_test.go index 9ed49a925b4b39..085989c7499b60 100644 --- a/src/net/tcpsock_test.go +++ b/src/net/tcpsock_test.go @@ -475,6 +475,9 @@ func TestTCPReadWriteAllocs(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } + // Optimizations are required to remove the allocs. + testenv.SkipIfOptimizationOff(t) + ln, err := Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) @@ -509,7 +512,7 @@ func TestTCPReadWriteAllocs(t *testing.T) { } }) if allocs > 0 { - t.Fatalf("got %v; want 0", allocs) + t.Errorf("got %v; want 0", allocs) } var bufwrt [128]byte @@ -531,7 +534,7 @@ func TestTCPReadWriteAllocs(t *testing.T) { } }) if allocs > 0 { - t.Fatalf("got %v; want 0", allocs) + t.Errorf("got %v; want 0", allocs) } } diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c index 926f9168434638..7e7ff3e667266f 100644 --- a/src/runtime/cgo/gcc_libinit_windows.c +++ b/src/runtime/cgo/gcc_libinit_windows.c @@ -15,15 +15,6 @@ #include "libcgo.h" #include "libcgo_windows.h" -// Ensure there's one symbol marked __declspec(dllexport). -// If there are no exported symbols, the unfortunate behavior of -// the binutils linker is to also strip the relocations table, -// resulting in non-PIE binary. The other option is the -// --export-all-symbols flag, but we don't need to export all symbols -// and this may overflow the export table (#40795). -// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 -__declspec(dllexport) int _cgo_dummy_export; - static volatile LONG runtime_init_once_gate = 0; static volatile LONG runtime_init_once_done = 0; diff --git a/src/runtime/cgo/windows.go b/src/runtime/cgo/windows.go new file mode 100644 index 00000000000000..7ba61753dffda2 --- /dev/null +++ b/src/runtime/cgo/windows.go @@ -0,0 +1,22 @@ +// Copyright 2025 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. + +//go:build windows + +package cgo + +import _ "unsafe" // for go:linkname + +// _cgo_stub_export is only used to ensure there's at least one symbol +// in the .def file passed to the external linker. +// If there are no exported symbols, the unfortunate behavior of +// the binutils linker is to also strip the relocations table, +// resulting in non-PIE binary. The other option is the +// --export-all-symbols flag, but we don't need to export all symbols +// and this may overflow the export table (#40795). +// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 +// +//go:cgo_export_static _cgo_stub_export +//go:linkname _cgo_stub_export _cgo_stub_export +var _cgo_stub_export uintptr diff --git a/src/runtime/rt0_netbsd_arm64.s b/src/runtime/rt0_netbsd_arm64.s index 07fb0a1240aec5..80802209b78f12 100644 --- a/src/runtime/rt0_netbsd_arm64.s +++ b/src/runtime/rt0_netbsd_arm64.s @@ -7,7 +7,7 @@ TEXT _rt0_arm64_netbsd(SB),NOSPLIT,$0 MOVD 0(RSP), R0 // argc ADD $8, RSP, R1 // argv - BL main(SB) + JMP main(SB) // When building with -buildmode=c-shared, this symbol is called when the shared // library is loaded. diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 949d48c79a6df5..53f41bca0b11ff 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -429,7 +429,7 @@ func (u *unwinder) resolveInternal(innermost, isSyscall bool) { // gp._defer for a defer corresponding to this function, but that // is hard to do with defer records on the stack during a stack copy.) // Note: the +1 is to offset the -1 that - // stack.go:getStackMap does to back up a return + // (*stkframe).getStackMap does to back up a return // address make sure the pc is in the CALL instruction. } else { frame.continpc = 0