mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.simd] all: merge master (adce7f1) into dev.simd
Conflicts: - src/internal/goexperiment/flags.go - src/runtime/export_test.go Merge List: + 2025-10-03adce7f196ecmd/link: support .def file with MSVC clang toolchain + 2025-10-03d5b950399dcmd/cgo: fix unaligned arguments typedmemmove crash on iOS + 2025-10-0253845004d6net/http/httputil: deprecate ReverseProxy.Director + 2025-10-02bbdff9e8e1net/http: update bundled x/net/http2 and delete obsolete http2inTests + 2025-10-024008e07080io/fs: move path name documentation up to the package doc comment + 2025-10-020e4e2e6832runtime: skip TestGoroutineLeakProfile under mayMoreStackPreempt + 2025-10-02f03c392295runtime: fix aix/ppc64 library initialization + 2025-10-02707454b41fcmd/go: update `go help mod edit` with the tool and ignore sections + 2025-10-028c68a1c1abruntime,net/http/pprof: goroutine leak detection by using the garbage collector + 2025-10-0284db201ae1cmd/compile: propagate len([]T{}) to make builtin to allow stack allocation + 2025-10-025799c139a7crypto/tls: rm marshalEncryptedClientHelloConfigList dead code + 2025-10-01633dd1d475encoding/json: fix Decoder.InputOffset regression in goexperiment.jsonv2 + 2025-10-018ad27fb656doc/go_spec.html: update date + 2025-10-013f451f2c54testing/synctest: fix inverted test failure message in TestContextAfterFunc + 2025-10-01be0fed8a5fcmd/go/testdata/script/test_fuzz_fuzztime.txt: disable + 2025-09-30eb1c7f6e69runtime: move loong64 library entry point to os-agnostic file + 2025-09-30c9257151e5runtime: unify ppc64/ppc64le library entry point + 2025-09-304ff8a457dbtest/codegen: codify handling of floating point constants on arm64 + 2025-09-30fcb893fc4bcmd/compile/internal/ssa: remove redundant "type:" prefix check + 2025-09-3019cc1022bamime: reduce allocs incurred by ParseMediaType + 2025-09-3008afc50beamime: extend "builtinTypes" to include a more complete list of common types + 2025-09-3097da068774cmd/compile: eliminate nil checks on .dict arg + 2025-09-30300d9d2714runtime: initialise debug settings much earlier in startup process + 2025-09-30a846bb0aa5errors: add AsType + 2025-09-307c8166d02dcmd/link/internal/arm64: support Mach-O ARM64_RELOC_SUBTRACTOR in internal linking + 2025-09-306e95748335cmd/link/internal/arm64: support Mach-O ARM64_RELOC_POINTER_TO_GOT in internal linking + 2025-09-30742f92063ecmd/compile, runtime: always enable Wasm signext and satconv features + 2025-09-30db10db6be3internal/poll: remove operation fields from FD + 2025-09-2975c87df58einternal/poll: pass the I/O mode instead of an overlapped object in execIO + 2025-09-29fc88e18b4acrypto/internal/fips140/entropy: add CPU jitter-based entropy source + 2025-09-29db4fade759crypto/internal/fips140/mlkem: make CAST conditional + 2025-09-29db3cb3fd9aruntime: correct reference to getStackMap in comment + 2025-09-29690fc2fb05internal/poll: remove buf field from operation + 2025-09-29eaf2345256cmd/link: use a .def file to mark exported symbols on Windows + 2025-09-294b77733565internal/syscall/windows: regenerate GetFileSizeEx + 2025-09-294e9006a716crypto/tls: quote protocols in ALPN error message + 2025-09-29047c2ab841cmd/link: don't pass -Wl,-S on Solaris + 2025-09-29ae8eba071bcmd/link: use correct length for pcln.cutab + 2025-09-29fe3ba74b9ecmd/link: skip TestFlagW on platforms without DWARF symbol table + 2025-09-29d42d56b764encoding/xml: make use of reflect.TypeAssert + 2025-09-296d51f93257runtime: jump instead of branch in netbsd/arm64 entry point + 2025-09-285500cbf0e4debug/elf: prevent offset overflow + 2025-09-2734e67623a8all: fix typos + 2025-09-27af6999e60dcmd/compile: implement jump table on loong64 + 2025-09-2663cd912083os/user: simplify go:build + 2025-09-2653009b26ddruntime: use a smaller arena size on Wasm + 2025-09-263a5df9d2b2net/http: add HTTP2Config.StrictMaxConcurrentRequests + 2025-09-2616be34df02net/http: add more tests of transport connection pool + 2025-09-263e4540b49dos/user: use getgrouplist on illumos && cgo + 2025-09-2615fbe3480binternal/poll: simplify WriteMsg and ReadMsg on Windows + 2025-09-2616ae11a9e1runtime: move TestReadMetricsSched to testprog + 2025-09-26459f3a3adccmd/link: don't pass -Wl,-S on AIX + 2025-09-264631a2d3c6cmd/link: skip TestFlagW on AIX + 2025-09-260f31d742cdcmd/compile: fix ICE with new(<untyped expr>) + 2025-09-267d7cd6e07binternal/poll: don't call SetFilePointerEx in Seek for overlapped handles + 2025-09-2641cba31e66mime/multipart: percent-encode CR and LF in header values to avoid CRLF injection + 2025-09-26dd1d597c3aRevert "cmd/internal/obj/loong64: use the MOVVP instruction to optimize prologue" + 2025-09-2645d6bc76afruntime: unify arm64 entry point code + 2025-09-25fdea7da3e6runtime: use common library entry point on windows amd64/386 + 2025-09-25e8a4f508d1lib/fips140: re-seal v1.0.0 + 2025-09-259b7a328089crypto/internal/fips140: remove key import PCTs, make keygen PCTs fatal + 2025-09-257f9ab7203fcrypto/internal/fips140: update frozen module version to "v1.0.0" + 2025-09-25fb5719cbdacrypto/internal/fips140/ecdsa: make TestingOnlyNewDRBG generic + 2025-09-2556067e31f2std: remove unused declarations Change-Id: Iecb28fd62c69fbed59da557f46d31bae55889e2c
This commit is contained in:
commit
fb1749a3fe
260 changed files with 15251 additions and 2505 deletions
1
api/next/51945.txt
Normal file
1
api/next/51945.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
pkg errors, func AsType[$0 error](error) ($0, bool) #51945
|
||||
1
api/next/67813.txt
Normal file
1
api/next/67813.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
pkg net/http, type HTTP2Config struct, StrictMaxConcurrentRequests bool #67813
|
||||
1
api/next/73161.txt
Normal file
1
api/next/73161.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
pkg net/http/httputil, type ReverseProxy struct, Director //deprecated #73161
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Language version go1.25 (Feb 25, 2025)",
|
||||
"Subtitle": "Language version go1.26 (Oct 1, 2025)",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
|
|
|
|||
2
doc/next/6-stdlib/99-minor/errors/51945.md
Normal file
2
doc/next/6-stdlib/99-minor/errors/51945.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
The new [AsType] function is a generic version of [As]. It is type-safe, faster,
|
||||
and, in most cases, easier to use.
|
||||
4
doc/next/6-stdlib/99-minor/net/http/67813.md
Normal file
4
doc/next/6-stdlib/99-minor/net/http/67813.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
The new
|
||||
[HTTP2Config.StrictMaxConcurrentRequests](/pkg/net/http#HTTP2Config.StrictMaxConcurrentRequests)
|
||||
field controls whether a new connection should be opened
|
||||
if an existing HTTP/2 connection has exceeded its stream limit.
|
||||
11
doc/next/6-stdlib/99-minor/net/http/httputil/73161.md
Normal file
11
doc/next/6-stdlib/99-minor/net/http/httputil/73161.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
The [ReverseProxy.Director] configuration field is deprecated
|
||||
in favor of [ReverseProxy.Rewrite].
|
||||
|
||||
A malicious client can remove headers added by a `Director` function
|
||||
by designating those headers as hop-by-hop. Since there is no way to address
|
||||
this problem within the scope of the `Director` API, we added a new
|
||||
`Rewrite` hook in Go 1.20. `Rewrite` hooks are provided with both the
|
||||
unmodified inbound request received by the proxy and the outbound request
|
||||
which will be sent by the proxy.
|
||||
|
||||
Since the `Director` hook is fundamentally unsafe, we are now deprecating it.
|
||||
|
|
@ -9,4 +9,4 @@
|
|||
#
|
||||
# go test cmd/go/internal/fips140 -update
|
||||
#
|
||||
v1.0.0.zip b50508feaeff05d22516b21e1fd210bbf5d6a1e422eaf2cfa23fe379342713b8
|
||||
v1.0.0-c2097c7c.zip daf3614e0406f67ae6323c902db3f953a1effb199142362a039e7526dfb9368b
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
v1.0.0
|
||||
v1.0.0-c2097c7c
|
||||
|
|
|
|||
Binary file not shown.
1
lib/fips140/v1.0.0.txt
Normal file
1
lib/fips140/v1.0.0.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
v1.0.0-c2097c7c
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
144
src/cmd/cgo/internal/testout/out_test.go
Normal file
144
src/cmd/cgo/internal/testout/out_test.go
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// 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 out_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"internal/goarch"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type methodAlign struct {
|
||||
Method string
|
||||
Align int
|
||||
}
|
||||
|
||||
var wantAligns = map[string]int{
|
||||
"ReturnEmpty": 1,
|
||||
"ReturnOnlyUint8": 1,
|
||||
"ReturnOnlyUint16": 2,
|
||||
"ReturnOnlyUint32": 4,
|
||||
"ReturnOnlyUint64": goarch.PtrSize,
|
||||
"ReturnOnlyInt": goarch.PtrSize,
|
||||
"ReturnOnlyPtr": goarch.PtrSize,
|
||||
"ReturnByteSlice": goarch.PtrSize,
|
||||
"ReturnString": goarch.PtrSize,
|
||||
"InputAndReturnUint8": 1,
|
||||
"MixedTypes": goarch.PtrSize,
|
||||
}
|
||||
|
||||
// TestAligned tests that the generated _cgo_export.c file has the wanted
|
||||
// align attributes for struct types used as arguments or results of
|
||||
// //exported functions.
|
||||
func TestAligned(t *testing.T) {
|
||||
testenv.MustHaveGoRun(t)
|
||||
testenv.MustHaveCGO(t)
|
||||
|
||||
testdata, err := filepath.Abs("testdata")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
objDir := t.TempDir()
|
||||
|
||||
cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "cgo",
|
||||
"-objdir", objDir,
|
||||
filepath.Join(testdata, "aligned.go"))
|
||||
cmd.Stderr = new(bytes.Buffer)
|
||||
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr)
|
||||
}
|
||||
|
||||
haveAligns, err := parseAlign(filepath.Join(objDir, "_cgo_export.c"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check that we have all the wanted methods
|
||||
if len(haveAligns) != len(wantAligns) {
|
||||
t.Fatalf("have %d methods with aligned, want %d", len(haveAligns), len(wantAligns))
|
||||
}
|
||||
|
||||
for i := range haveAligns {
|
||||
method := haveAligns[i].Method
|
||||
haveAlign := haveAligns[i].Align
|
||||
|
||||
wantAlign, ok := wantAligns[method]
|
||||
if !ok {
|
||||
t.Errorf("method %s: have aligned %d, want missing entry", method, haveAlign)
|
||||
} else if haveAlign != wantAlign {
|
||||
t.Errorf("method %s: have aligned %d, want %d", method, haveAlign, wantAlign)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseAlign(filename string) ([]methodAlign, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var results []methodAlign
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// Regex to match function declarations like "struct MethodName_return MethodName("
|
||||
funcRegex := regexp.MustCompile(`^struct\s+(\w+)_return\s+(\w+)\(`)
|
||||
// Regex to match simple function declarations like "GoSlice MethodName("
|
||||
simpleFuncRegex := regexp.MustCompile(`^Go\w+\s+(\w+)\(`)
|
||||
// Regex to match void-returning exported functions like "void ReturnEmpty("
|
||||
voidFuncRegex := regexp.MustCompile(`^void\s+(\w+)\(`)
|
||||
// Regex to match align attributes like "__attribute__((aligned(8)))"
|
||||
alignRegex := regexp.MustCompile(`__attribute__\(\(aligned\((\d+)\)\)\)`)
|
||||
|
||||
var currentMethod string
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
// Check if this line declares a function with struct return type
|
||||
if matches := funcRegex.FindStringSubmatch(line); matches != nil {
|
||||
currentMethod = matches[2] // Extract the method name
|
||||
} else if matches := simpleFuncRegex.FindStringSubmatch(line); matches != nil {
|
||||
// Check if this line declares a function with simple return type (like GoSlice)
|
||||
currentMethod = matches[1] // Extract the method name
|
||||
} else if matches := voidFuncRegex.FindStringSubmatch(line); matches != nil {
|
||||
// Check if this line declares a void-returning function
|
||||
currentMethod = matches[1] // Extract the method name
|
||||
}
|
||||
|
||||
// Check if this line contains align information
|
||||
if alignMatches := alignRegex.FindStringSubmatch(line); alignMatches != nil && currentMethod != "" {
|
||||
alignStr := alignMatches[1]
|
||||
align, err := strconv.Atoi(alignStr)
|
||||
if err != nil {
|
||||
// Skip this entry if we can't parse the align as integer
|
||||
currentMethod = ""
|
||||
continue
|
||||
}
|
||||
results = append(results, methodAlign{
|
||||
Method: currentMethod,
|
||||
Align: align,
|
||||
})
|
||||
currentMethod = "" // Reset for next method
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error reading file: %w", err)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
63
src/cmd/cgo/internal/testout/testdata/aligned.go
vendored
Normal file
63
src/cmd/cgo/internal/testout/testdata/aligned.go
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// 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 main
|
||||
|
||||
import "C"
|
||||
|
||||
//export ReturnEmpty
|
||||
func ReturnEmpty() {
|
||||
return
|
||||
}
|
||||
|
||||
//export ReturnOnlyUint8
|
||||
func ReturnOnlyUint8() (uint8, uint8, uint8) {
|
||||
return 1, 2, 3
|
||||
}
|
||||
|
||||
//export ReturnOnlyUint16
|
||||
func ReturnOnlyUint16() (uint16, uint16, uint16) {
|
||||
return 1, 2, 3
|
||||
}
|
||||
|
||||
//export ReturnOnlyUint32
|
||||
func ReturnOnlyUint32() (uint32, uint32, uint32) {
|
||||
return 1, 2, 3
|
||||
}
|
||||
|
||||
//export ReturnOnlyUint64
|
||||
func ReturnOnlyUint64() (uint64, uint64, uint64) {
|
||||
return 1, 2, 3
|
||||
}
|
||||
|
||||
//export ReturnOnlyInt
|
||||
func ReturnOnlyInt() (int, int, int) {
|
||||
return 1, 2, 3
|
||||
}
|
||||
|
||||
//export ReturnOnlyPtr
|
||||
func ReturnOnlyPtr() (*int, *int, *int) {
|
||||
a, b, c := 1, 2, 3
|
||||
return &a, &b, &c
|
||||
}
|
||||
|
||||
//export ReturnString
|
||||
func ReturnString() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
//export ReturnByteSlice
|
||||
func ReturnByteSlice() []byte {
|
||||
return []byte{1, 2, 3}
|
||||
}
|
||||
|
||||
//export InputAndReturnUint8
|
||||
func InputAndReturnUint8(a, b, c uint8) (uint8, uint8, uint8) {
|
||||
return a, b, c
|
||||
}
|
||||
|
||||
//export MixedTypes
|
||||
func MixedTypes(a uint8, b uint16, c uint32, d uint64, e int, f *int) (uint8, uint16, uint32, uint64, int, *int) {
|
||||
return a, b, c, d, e, f
|
||||
}
|
||||
|
|
@ -949,6 +949,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
|||
fmt.Fprintf(gotype, "struct {\n")
|
||||
off := int64(0)
|
||||
npad := 0
|
||||
// the align is at least 1 (for char)
|
||||
maxAlign := int64(1)
|
||||
argField := func(typ ast.Expr, namePat string, args ...interface{}) {
|
||||
name := fmt.Sprintf(namePat, args...)
|
||||
t := p.cgoType(typ)
|
||||
|
|
@ -963,6 +965,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
|||
noSourceConf.Fprint(gotype, fset, typ)
|
||||
fmt.Fprintf(gotype, "\n")
|
||||
off += t.Size
|
||||
// keep track of the maximum alignment among all fields
|
||||
// so that we can align the struct correctly
|
||||
if t.Align > maxAlign {
|
||||
maxAlign = t.Align
|
||||
}
|
||||
}
|
||||
if fn.Recv != nil {
|
||||
argField(fn.Recv.List[0].Type, "recv")
|
||||
|
|
@ -1005,12 +1012,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")
|
||||
|
|
@ -1051,7 +1054,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
|||
// string.h for memset, and is also robust to C++
|
||||
// types with constructors. Both GCC and LLVM optimize
|
||||
// this into just zeroing _cgo_a.
|
||||
fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype.String(), p.packedAttribute())
|
||||
//
|
||||
// The struct should be aligned to the maximum alignment
|
||||
// of any of its fields. This to avoid alignment
|
||||
// issues.
|
||||
fmt.Fprintf(fgcc, "\ttypedef %s %v __attribute__((aligned(%d))) _cgo_argtype;\n", ctype.String(), p.packedAttribute(), maxAlign)
|
||||
fmt.Fprintf(fgcc, "\tstatic _cgo_argtype _cgo_zero;\n")
|
||||
fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _cgo_zero;\n")
|
||||
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
|
||||
|
|
|
|||
|
|
@ -563,7 +563,10 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
|
|||
if ro == nil {
|
||||
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
|
||||
}
|
||||
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
|
||||
|
||||
s := ro.StaticValue(*r)
|
||||
switch s.Op() {
|
||||
case ir.OLITERAL:
|
||||
lit, ok := s.(*ir.BasicLit)
|
||||
if !ok || lit.Val().Kind() != constant.Int {
|
||||
base.Fatalf("unexpected BasicLit Kind")
|
||||
|
|
@ -577,6 +580,14 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
|
|||
assignTemp(n.Pos(), *r, n.PtrInit())
|
||||
*r = ir.NewBasicLit(n.Pos(), (*r).Type(), lit.Val())
|
||||
}
|
||||
case ir.OLEN:
|
||||
x := ro.StaticValue(s.(*ir.UnaryExpr).X)
|
||||
if x.Op() == ir.OSLICELIT {
|
||||
x := x.(*ir.CompLitExpr)
|
||||
// Preserve any side effects of the original expression, then update the value.
|
||||
assignTemp(n.Pos(), *r, n.PtrInit())
|
||||
*r = ir.NewBasicLit(n.Pos(), types.Types[types.TINT], constant.MakeInt64(x.Len))
|
||||
}
|
||||
}
|
||||
}
|
||||
case ir.OCONVIFACE:
|
||||
|
|
|
|||
|
|
@ -1266,6 +1266,29 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
|
|||
p.From.Reg = b.Controls[0].Reg()
|
||||
}
|
||||
}
|
||||
case ssa.BlockLOONG64JUMPTABLE:
|
||||
// ALSLV $3, Rarg0, Rarg1, REGTMP
|
||||
// MOVV (REGTMP), REGTMP
|
||||
// JMP (REGTMP)
|
||||
p := s.Prog(loong64.AALSLV)
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 3 // idx*8
|
||||
p.Reg = b.Controls[0].Reg()
|
||||
p.AddRestSourceReg(b.Controls[1].Reg())
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = loong64.REGTMP
|
||||
p1 := s.Prog(loong64.AMOVV)
|
||||
p1.From.Type = obj.TYPE_MEM
|
||||
p1.From.Reg = loong64.REGTMP
|
||||
p1.From.Offset = 0
|
||||
p1.To.Type = obj.TYPE_REG
|
||||
p1.To.Reg = loong64.REGTMP
|
||||
p2 := s.Prog(obj.AJMP)
|
||||
p2.To.Type = obj.TYPE_MEM
|
||||
p2.To.Reg = loong64.REGTMP
|
||||
// Save jump tables for later resolution of the target blocks.
|
||||
s.JumpTables = append(s.JumpTables, b)
|
||||
|
||||
default:
|
||||
b.Fatalf("branch not implemented: %s", b.LongString())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2434,6 +2434,7 @@ func (r *reader) expr() (res ir.Node) {
|
|||
if r.Bool() {
|
||||
// new(expr) -> tmp := expr; &tmp
|
||||
x := r.expr()
|
||||
x = typecheck.DefaultLit(x, nil) // See TODO in exprConvert case.
|
||||
var init ir.Nodes
|
||||
addr := ir.NewAddrExpr(pos, r.tempCopy(pos, x, &init))
|
||||
addr.SetInit(init)
|
||||
|
|
|
|||
|
|
@ -504,6 +504,8 @@
|
|||
(MOVBUreg x:((SGT|SGTU) _ _)) => x
|
||||
(MOVBUreg x:(XOR (MOVVconst [1]) ((SGT|SGTU) _ _))) => x
|
||||
|
||||
(JumpTable idx) => (JUMPTABLE {makeJumpTableSym(b)} idx (MOVVaddr <typ.Uintptr> {makeJumpTableSym(b)} (SB)))
|
||||
|
||||
// Write barrier.
|
||||
(WB ...) => (LoweredWB ...)
|
||||
|
||||
|
|
|
|||
|
|
@ -577,6 +577,12 @@ func init() {
|
|||
{name: "BLT", controls: 2}, // controls[0] < controls[1]
|
||||
{name: "BGEU", controls: 2}, // controls[0] >= controls[1], unsigned
|
||||
{name: "BLTU", controls: 2}, // controls[0] < controls[1], unsigned
|
||||
|
||||
// JUMPTABLE implements jump tables.
|
||||
// Aux is the symbol (an *obj.LSym) for the jump table.
|
||||
// control[0] is the index into the jump table.
|
||||
// control[1] is the address of the jump table (the address of the symbol stored in Aux).
|
||||
{name: "JUMPTABLE", controls: 2, aux: "Sym"},
|
||||
}
|
||||
|
||||
archs = append(archs, arch{
|
||||
|
|
|
|||
|
|
@ -55,12 +55,9 @@
|
|||
(ZeroExt32to64 x:(I64Load32U _ _)) => x
|
||||
(ZeroExt16to(64|32) x:(I64Load16U _ _)) => x
|
||||
(ZeroExt8to(64|32|16) x:(I64Load8U _ _)) => x
|
||||
(SignExt32to64 x) && buildcfg.GOWASM.SignExt => (I64Extend32S x)
|
||||
(SignExt8to(64|32|16) x) && buildcfg.GOWASM.SignExt => (I64Extend8S x)
|
||||
(SignExt16to(64|32) x) && buildcfg.GOWASM.SignExt => (I64Extend16S x)
|
||||
(SignExt32to64 x) => (I64ShrS (I64Shl x (I64Const [32])) (I64Const [32]))
|
||||
(SignExt16to(64|32) x) => (I64ShrS (I64Shl x (I64Const [48])) (I64Const [48]))
|
||||
(SignExt8to(64|32|16) x) => (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
|
||||
(SignExt32to64 x) => (I64Extend32S x)
|
||||
(SignExt8to(64|32|16) x) => (I64Extend8S x)
|
||||
(SignExt16to(64|32) x) => (I64Extend16S x)
|
||||
(ZeroExt32to64 x) => (I64And x (I64Const [0xffffffff]))
|
||||
(ZeroExt16to(64|32) x) => (I64And x (I64Const [0xffff]))
|
||||
(ZeroExt8to(64|32|16) x) => (I64And x (I64Const [0xff]))
|
||||
|
|
|
|||
|
|
@ -2083,6 +2083,9 @@
|
|||
&& warnRule(fe.Debug_checknil(), v, "removed nil check")
|
||||
=> ptr
|
||||
|
||||
// .dict args are always non-nil.
|
||||
(NilCheck ptr:(Arg {sym}) _) && isDictArgSym(sym) => ptr
|
||||
|
||||
// Nil checks of nil checks are redundant.
|
||||
// See comment at the end of https://go-review.googlesource.com/c/go/+/537775.
|
||||
(NilCheck ptr:(NilCheck _ _) _ ) => ptr
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ const (
|
|||
BlockLOONG64BLT
|
||||
BlockLOONG64BGEU
|
||||
BlockLOONG64BLTU
|
||||
BlockLOONG64JUMPTABLE
|
||||
|
||||
BlockMIPSEQ
|
||||
BlockMIPSNE
|
||||
|
|
@ -250,20 +251,21 @@ var blockString = [...]string{
|
|||
BlockARM64GEnoov: "GEnoov",
|
||||
BlockARM64JUMPTABLE: "JUMPTABLE",
|
||||
|
||||
BlockLOONG64EQZ: "EQZ",
|
||||
BlockLOONG64NEZ: "NEZ",
|
||||
BlockLOONG64LTZ: "LTZ",
|
||||
BlockLOONG64LEZ: "LEZ",
|
||||
BlockLOONG64GTZ: "GTZ",
|
||||
BlockLOONG64GEZ: "GEZ",
|
||||
BlockLOONG64FPT: "FPT",
|
||||
BlockLOONG64FPF: "FPF",
|
||||
BlockLOONG64BEQ: "BEQ",
|
||||
BlockLOONG64BNE: "BNE",
|
||||
BlockLOONG64BGE: "BGE",
|
||||
BlockLOONG64BLT: "BLT",
|
||||
BlockLOONG64BGEU: "BGEU",
|
||||
BlockLOONG64BLTU: "BLTU",
|
||||
BlockLOONG64EQZ: "EQZ",
|
||||
BlockLOONG64NEZ: "NEZ",
|
||||
BlockLOONG64LTZ: "LTZ",
|
||||
BlockLOONG64LEZ: "LEZ",
|
||||
BlockLOONG64GTZ: "GTZ",
|
||||
BlockLOONG64GEZ: "GEZ",
|
||||
BlockLOONG64FPT: "FPT",
|
||||
BlockLOONG64FPF: "FPF",
|
||||
BlockLOONG64BEQ: "BEQ",
|
||||
BlockLOONG64BNE: "BNE",
|
||||
BlockLOONG64BGE: "BGE",
|
||||
BlockLOONG64BLT: "BLT",
|
||||
BlockLOONG64BGEU: "BGEU",
|
||||
BlockLOONG64BLTU: "BLTU",
|
||||
BlockLOONG64JUMPTABLE: "JUMPTABLE",
|
||||
|
||||
BlockMIPSEQ: "EQ",
|
||||
BlockMIPSNE: "NE",
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ package ssa
|
|||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/logopt"
|
||||
"cmd/compile/internal/reflectdata"
|
||||
"cmd/compile/internal/rttype"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/obj/s390x"
|
||||
|
|
@ -2057,12 +2059,12 @@ func isFixedLoad(v *Value, sym Sym, off int64) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if strings.HasPrefix(lsym.Name, "type:") {
|
||||
if ti := lsym.TypeInfo(); ti != nil {
|
||||
// Type symbols do not contain information about their fields, unlike the cases above.
|
||||
// Hand-implement field accesses.
|
||||
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
|
||||
|
||||
t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
|
||||
t := ti.Type.(*types.Type)
|
||||
|
||||
for _, f := range rttype.Type.Fields() {
|
||||
if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
|
||||
|
|
@ -2116,12 +2118,12 @@ func rewriteFixedLoad(v *Value, sym Sym, sb *Value, off int64) *Value {
|
|||
base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(lsym.Name, "type:") {
|
||||
if ti := lsym.TypeInfo(); ti != nil {
|
||||
// Type symbols do not contain information about their fields, unlike the cases above.
|
||||
// Hand-implement field accesses.
|
||||
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
|
||||
|
||||
t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
|
||||
t := ti.Type.(*types.Type)
|
||||
|
||||
ptrSizedOpConst := OpConst64
|
||||
if f.Config.PtrSize == 4 {
|
||||
|
|
@ -2611,10 +2613,7 @@ func isDirectType1(v *Value) bool {
|
|||
return isDirectType2(v.Args[0])
|
||||
case OpAddr:
|
||||
lsym := v.Aux.(*obj.LSym)
|
||||
if lsym.Extra == nil {
|
||||
return false
|
||||
}
|
||||
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
|
||||
if ti := lsym.TypeInfo(); ti != nil {
|
||||
return types.IsDirectIface(ti.Type.(*types.Type))
|
||||
}
|
||||
}
|
||||
|
|
@ -2647,10 +2646,7 @@ func isDirectIface1(v *Value, depth int) bool {
|
|||
return isDirectIface2(v.Args[0], depth-1)
|
||||
case OpAddr:
|
||||
lsym := v.Aux.(*obj.LSym)
|
||||
if lsym.Extra == nil {
|
||||
return false
|
||||
}
|
||||
if ii, ok := (*lsym.Extra).(*obj.ItabInfo); ok {
|
||||
if ii := lsym.ItabInfo(); ii != nil {
|
||||
return types.IsDirectIface(ii.Type.(*types.Type))
|
||||
}
|
||||
case OpConstNil:
|
||||
|
|
@ -2744,3 +2740,7 @@ func panicBoundsCToAux(p PanicBoundsC) Aux {
|
|||
func panicBoundsCCToAux(p PanicBoundsCC) Aux {
|
||||
return p
|
||||
}
|
||||
|
||||
func isDictArgSym(sym Sym) bool {
|
||||
return sym.(*ir.Name).Sym().Name == typecheck.LocalDictName
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12148,6 +12148,19 @@ func rewriteBlockLOONG64(b *Block) bool {
|
|||
b.resetWithControl(BlockLOONG64NEZ, v0)
|
||||
return true
|
||||
}
|
||||
case BlockJumpTable:
|
||||
// match: (JumpTable idx)
|
||||
// result: (JUMPTABLE {makeJumpTableSym(b)} idx (MOVVaddr <typ.Uintptr> {makeJumpTableSym(b)} (SB)))
|
||||
for {
|
||||
idx := b.Controls[0]
|
||||
v0 := b.NewValue0(b.Pos, OpLOONG64MOVVaddr, typ.Uintptr)
|
||||
v0.Aux = symToAux(makeJumpTableSym(b))
|
||||
v1 := b.NewValue0(b.Pos, OpSB, typ.Uintptr)
|
||||
v0.AddArg(v1)
|
||||
b.resetWithControl2(BlockLOONG64JUMPTABLE, idx, v0)
|
||||
b.Aux = symToAux(makeJumpTableSym(b))
|
||||
return true
|
||||
}
|
||||
case BlockLOONG64LEZ:
|
||||
// match: (LEZ (MOVVconst [c]) yes no)
|
||||
// cond: c <= 0
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
package ssa
|
||||
|
||||
import "internal/buildcfg"
|
||||
import "math"
|
||||
import "cmd/compile/internal/types"
|
||||
|
||||
|
|
@ -3202,8 +3201,6 @@ func rewriteValueWasm_OpRsh8x8(v *Value) bool {
|
|||
}
|
||||
func rewriteValueWasm_OpSignExt16to32(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (SignExt16to32 x:(I64Load16S _ _))
|
||||
// result: x
|
||||
for {
|
||||
|
|
@ -3215,34 +3212,16 @@ func rewriteValueWasm_OpSignExt16to32(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (SignExt16to32 x)
|
||||
// cond: buildcfg.GOWASM.SignExt
|
||||
// result: (I64Extend16S x)
|
||||
for {
|
||||
x := v_0
|
||||
if !(buildcfg.GOWASM.SignExt) {
|
||||
break
|
||||
}
|
||||
v.reset(OpWasmI64Extend16S)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (SignExt16to32 x)
|
||||
// result: (I64ShrS (I64Shl x (I64Const [48])) (I64Const [48]))
|
||||
for {
|
||||
x := v_0
|
||||
v.reset(OpWasmI64ShrS)
|
||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
||||
v1.AuxInt = int64ToAuxInt(48)
|
||||
v0.AddArg2(x, v1)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueWasm_OpSignExt16to64(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (SignExt16to64 x:(I64Load16S _ _))
|
||||
// result: x
|
||||
for {
|
||||
|
|
@ -3254,34 +3233,16 @@ func rewriteValueWasm_OpSignExt16to64(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (SignExt16to64 x)
|
||||
// cond: buildcfg.GOWASM.SignExt
|
||||
// result: (I64Extend16S x)
|
||||
for {
|
||||
x := v_0
|
||||
if !(buildcfg.GOWASM.SignExt) {
|
||||
break
|
||||
}
|
||||
v.reset(OpWasmI64Extend16S)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (SignExt16to64 x)
|
||||
// result: (I64ShrS (I64Shl x (I64Const [48])) (I64Const [48]))
|
||||
for {
|
||||
x := v_0
|
||||
v.reset(OpWasmI64ShrS)
|
||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
||||
v1.AuxInt = int64ToAuxInt(48)
|
||||
v0.AddArg2(x, v1)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueWasm_OpSignExt32to64(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (SignExt32to64 x:(I64Load32S _ _))
|
||||
// result: x
|
||||
for {
|
||||
|
|
@ -3293,34 +3254,16 @@ func rewriteValueWasm_OpSignExt32to64(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (SignExt32to64 x)
|
||||
// cond: buildcfg.GOWASM.SignExt
|
||||
// result: (I64Extend32S x)
|
||||
for {
|
||||
x := v_0
|
||||
if !(buildcfg.GOWASM.SignExt) {
|
||||
break
|
||||
}
|
||||
v.reset(OpWasmI64Extend32S)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (SignExt32to64 x)
|
||||
// result: (I64ShrS (I64Shl x (I64Const [32])) (I64Const [32]))
|
||||
for {
|
||||
x := v_0
|
||||
v.reset(OpWasmI64ShrS)
|
||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
||||
v1.AuxInt = int64ToAuxInt(32)
|
||||
v0.AddArg2(x, v1)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueWasm_OpSignExt8to16(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (SignExt8to16 x:(I64Load8S _ _))
|
||||
// result: x
|
||||
for {
|
||||
|
|
@ -3332,34 +3275,16 @@ func rewriteValueWasm_OpSignExt8to16(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (SignExt8to16 x)
|
||||
// cond: buildcfg.GOWASM.SignExt
|
||||
// result: (I64Extend8S x)
|
||||
for {
|
||||
x := v_0
|
||||
if !(buildcfg.GOWASM.SignExt) {
|
||||
break
|
||||
}
|
||||
v.reset(OpWasmI64Extend8S)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (SignExt8to16 x)
|
||||
// result: (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
|
||||
for {
|
||||
x := v_0
|
||||
v.reset(OpWasmI64ShrS)
|
||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
||||
v1.AuxInt = int64ToAuxInt(56)
|
||||
v0.AddArg2(x, v1)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueWasm_OpSignExt8to32(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (SignExt8to32 x:(I64Load8S _ _))
|
||||
// result: x
|
||||
for {
|
||||
|
|
@ -3371,34 +3296,16 @@ func rewriteValueWasm_OpSignExt8to32(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (SignExt8to32 x)
|
||||
// cond: buildcfg.GOWASM.SignExt
|
||||
// result: (I64Extend8S x)
|
||||
for {
|
||||
x := v_0
|
||||
if !(buildcfg.GOWASM.SignExt) {
|
||||
break
|
||||
}
|
||||
v.reset(OpWasmI64Extend8S)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (SignExt8to32 x)
|
||||
// result: (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
|
||||
for {
|
||||
x := v_0
|
||||
v.reset(OpWasmI64ShrS)
|
||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
||||
v1.AuxInt = int64ToAuxInt(56)
|
||||
v0.AddArg2(x, v1)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueWasm_OpSignExt8to64(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (SignExt8to64 x:(I64Load8S _ _))
|
||||
// result: x
|
||||
for {
|
||||
|
|
@ -3410,29 +3317,13 @@ func rewriteValueWasm_OpSignExt8to64(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (SignExt8to64 x)
|
||||
// cond: buildcfg.GOWASM.SignExt
|
||||
// result: (I64Extend8S x)
|
||||
for {
|
||||
x := v_0
|
||||
if !(buildcfg.GOWASM.SignExt) {
|
||||
break
|
||||
}
|
||||
v.reset(OpWasmI64Extend8S)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (SignExt8to64 x)
|
||||
// result: (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
|
||||
for {
|
||||
x := v_0
|
||||
v.reset(OpWasmI64ShrS)
|
||||
v0 := b.NewValue0(v.Pos, OpWasmI64Shl, typ.Int64)
|
||||
v1 := b.NewValue0(v.Pos, OpWasmI64Const, typ.Int64)
|
||||
v1.AuxInt = int64ToAuxInt(56)
|
||||
v0.AddArg2(x, v1)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueWasm_OpSlicemask(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
|
|
|
|||
|
|
@ -21395,6 +21395,21 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool {
|
|||
v.copyOf(ptr)
|
||||
return true
|
||||
}
|
||||
// match: (NilCheck ptr:(Arg {sym}) _)
|
||||
// cond: isDictArgSym(sym)
|
||||
// result: ptr
|
||||
for {
|
||||
ptr := v_0
|
||||
if ptr.Op != OpArg {
|
||||
break
|
||||
}
|
||||
sym := auxToSym(ptr.Aux)
|
||||
if !(isDictArgSym(sym)) {
|
||||
break
|
||||
}
|
||||
v.copyOf(ptr)
|
||||
return true
|
||||
}
|
||||
// match: (NilCheck ptr:(NilCheck _ _) _ )
|
||||
// result: ptr
|
||||
for {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import (
|
|||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/obj/wasm"
|
||||
"internal/buildcfg"
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
@ -425,27 +424,11 @@ func ssaGenValueOnStack(s *ssagen.State, v *ssa.Value, extend bool) {
|
|||
|
||||
case ssa.OpWasmI64TruncSatF32S, ssa.OpWasmI64TruncSatF64S:
|
||||
getValue64(s, v.Args[0])
|
||||
if buildcfg.GOWASM.SatConv {
|
||||
s.Prog(v.Op.Asm())
|
||||
} else {
|
||||
if v.Op == ssa.OpWasmI64TruncSatF32S {
|
||||
s.Prog(wasm.AF64PromoteF32)
|
||||
}
|
||||
p := s.Prog(wasm.ACall)
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncS}
|
||||
}
|
||||
s.Prog(v.Op.Asm())
|
||||
|
||||
case ssa.OpWasmI64TruncSatF32U, ssa.OpWasmI64TruncSatF64U:
|
||||
getValue64(s, v.Args[0])
|
||||
if buildcfg.GOWASM.SatConv {
|
||||
s.Prog(v.Op.Asm())
|
||||
} else {
|
||||
if v.Op == ssa.OpWasmI64TruncSatF32U {
|
||||
s.Prog(wasm.AF64PromoteF32)
|
||||
}
|
||||
p := s.Prog(wasm.ACall)
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncU}
|
||||
}
|
||||
s.Prog(v.Op.Asm())
|
||||
|
||||
case ssa.OpWasmF32DemoteF64:
|
||||
getValue64(s, v.Args[0])
|
||||
|
|
|
|||
|
|
@ -1280,11 +1280,6 @@
|
|||
// The -json flag prints the final go.mod file in JSON format instead of
|
||||
// writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||
//
|
||||
// type Module struct {
|
||||
// Path string
|
||||
// Version string
|
||||
// }
|
||||
//
|
||||
// type GoMod struct {
|
||||
// Module ModPath
|
||||
// Go string
|
||||
|
|
@ -1294,6 +1289,13 @@
|
|||
// Exclude []Module
|
||||
// Replace []Replace
|
||||
// Retract []Retract
|
||||
// Tool []Tool
|
||||
// Ignore []Ignore
|
||||
// }
|
||||
//
|
||||
// type Module struct {
|
||||
// Path string
|
||||
// Version string
|
||||
// }
|
||||
//
|
||||
// type ModPath struct {
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
modzip "golang.org/x/mod/zip"
|
||||
)
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ func main() {
|
|||
|
||||
// Must have valid version, and must not overwrite existing file.
|
||||
version := flag.Arg(0)
|
||||
if !regexp.MustCompile(`^v\d+\.\d+\.\d+$`).MatchString(version) {
|
||||
if semver.Canonical(version) != version {
|
||||
log.Fatalf("invalid version %q; must be vX.Y.Z", version)
|
||||
}
|
||||
if _, err := os.Stat(version + ".zip"); err == nil {
|
||||
|
|
@ -117,7 +117,9 @@ func main() {
|
|||
if !bytes.Contains(contents, []byte(returnLine)) {
|
||||
log.Fatalf("did not find %q in fips140.go", returnLine)
|
||||
}
|
||||
newLine := `return "` + version + `"`
|
||||
// Use only the vX.Y.Z part of a possible vX.Y.Z-hash version.
|
||||
v, _, _ := strings.Cut(version, "-")
|
||||
newLine := `return "` + v + `"`
|
||||
contents = bytes.ReplaceAll(contents, []byte(returnLine), []byte(newLine))
|
||||
wf, err := zw.Create(f.Name)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -104,11 +104,6 @@ writing it back to go.mod.
|
|||
The -json flag prints the final go.mod file in JSON format instead of
|
||||
writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||
|
||||
type Module struct {
|
||||
Path string
|
||||
Version string
|
||||
}
|
||||
|
||||
type GoMod struct {
|
||||
Module ModPath
|
||||
Go string
|
||||
|
|
@ -118,6 +113,13 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
|||
Exclude []Module
|
||||
Replace []Replace
|
||||
Retract []Retract
|
||||
Tool []Tool
|
||||
Ignore []Ignore
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
Path string
|
||||
Version string
|
||||
}
|
||||
|
||||
type ModPath struct {
|
||||
|
|
|
|||
5
src/cmd/go/testdata/script/fipssnap.txt
vendored
5
src/cmd/go/testdata/script/fipssnap.txt
vendored
|
|
@ -1,4 +1,4 @@
|
|||
env snap=v1.0.0
|
||||
env snap=v1.0.0-c2097c7c
|
||||
env alias=inprocess
|
||||
|
||||
env GOFIPS140=$snap
|
||||
|
|
@ -23,8 +23,7 @@ stdout crypto/internal/fips140/$snap/sha256
|
|||
! stdout crypto/internal/fips140/check
|
||||
|
||||
# again with GOFIPS140=$alias
|
||||
# TODO: enable when we add inprocess.txt
|
||||
# env GOFIPS140=$alias
|
||||
env GOFIPS140=$alias
|
||||
|
||||
# default GODEBUG includes fips140=on
|
||||
go list -f '{{.DefaultGODEBUG}}'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
skip # a 5s timeout is never going to be reliable (go.dev/issue/72140)
|
||||
|
||||
[!fuzz] skip
|
||||
[short] skip
|
||||
env GOCACHE=$WORK/cache
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ type LSym struct {
|
|||
P []byte
|
||||
R []Reloc
|
||||
|
||||
Extra *interface{} // *FuncInfo, *VarInfo, *FileInfo, or *TypeInfo, if present
|
||||
Extra *interface{} // *FuncInfo, *VarInfo, *FileInfo, *TypeInfo, or *ItabInfo, if present
|
||||
|
||||
Pkg string
|
||||
PkgIdx int32
|
||||
|
|
@ -604,6 +604,15 @@ func (s *LSym) NewTypeInfo() *TypeInfo {
|
|||
return t
|
||||
}
|
||||
|
||||
// TypeInfo returns the *TypeInfo associated with s, or else nil.
|
||||
func (s *LSym) TypeInfo() *TypeInfo {
|
||||
if s.Extra == nil {
|
||||
return nil
|
||||
}
|
||||
t, _ := (*s.Extra).(*TypeInfo)
|
||||
return t
|
||||
}
|
||||
|
||||
// An ItabInfo contains information for a symbol
|
||||
// that contains a runtime.itab.
|
||||
type ItabInfo struct {
|
||||
|
|
@ -620,6 +629,15 @@ func (s *LSym) NewItabInfo() *ItabInfo {
|
|||
return t
|
||||
}
|
||||
|
||||
// ItabInfo returns the *ItabInfo associated with s, or else nil.
|
||||
func (s *LSym) ItabInfo() *ItabInfo {
|
||||
if s.Extra == nil {
|
||||
return nil
|
||||
}
|
||||
i, _ := (*s.Extra).(*ItabInfo)
|
||||
return i
|
||||
}
|
||||
|
||||
// WasmImport represents a WebAssembly (WASM) imported function with
|
||||
// parameters and results translated into WASM types based on the Go function
|
||||
// declaration.
|
||||
|
|
|
|||
|
|
@ -707,6 +707,15 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
// so instruction sequences that use REGTMP are unsafe to
|
||||
// preempt asynchronously.
|
||||
obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable)
|
||||
|
||||
// Now that we know byte offsets, we can generate jump table entries.
|
||||
for _, jt := range cursym.Func().JumpTables {
|
||||
for i, p := range jt.Targets {
|
||||
// The ith jumptable entry points to the p.Pc'th
|
||||
// byte in the function symbol s.
|
||||
jt.Sym.WriteAddr(ctxt, int64(i)*8, 8, cursym, p.Pc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isUnsafePoint returns whether p is an unsafe point.
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
q = c.ctxt.StartUnsafePoint(q, c.newprog)
|
||||
|
||||
q = obj.Appendp(q, newprog)
|
||||
q.As = AMOVVP
|
||||
q.As = mov
|
||||
q.Pos = p.Pos
|
||||
q.From.Type = obj.TYPE_REG
|
||||
q.From.Reg = REGLINK
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ var ArchLoong64 = &Arch{
|
|||
MinLC: 4,
|
||||
Alignment: 8, // Unaligned accesses are not guaranteed to be fast
|
||||
CanMergeLoads: true,
|
||||
CanJumpTable: true,
|
||||
HasLR: true,
|
||||
FixedFrameSize: 8, // LR
|
||||
}
|
||||
|
|
|
|||
|
|
@ -361,6 +361,13 @@ func TestDWARFLocationList(t *testing.T) {
|
|||
|
||||
func TestFlagW(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(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()
|
||||
|
|
@ -379,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},
|
||||
|
|
|
|||
|
|
@ -224,6 +224,28 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
|||
}
|
||||
return true
|
||||
|
||||
case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_SUBTRACTOR*2:
|
||||
// ARM64_RELOC_SUBTRACTOR must be followed by ARM64_RELOC_UNSIGNED.
|
||||
// The pair of relocations resolves to the difference between two
|
||||
// symbol addresses (each relocation specifies a symbol).
|
||||
outer, off := ld.FoldSubSymbolOffset(ldr, targ)
|
||||
if outer != s {
|
||||
// TODO: support subtracted symbol in different section.
|
||||
ldr.Errorf(s, "unsupported ARM64_RELOC_SUBTRACTOR reloc: target %s, outer %s", ldr.SymName(targ), ldr.SymName(outer))
|
||||
break
|
||||
}
|
||||
su := ldr.MakeSymbolUpdater(s)
|
||||
relocs := su.Relocs()
|
||||
if rIdx+1 >= relocs.Count() || relocs.At(rIdx+1).Type() != objabi.MachoRelocOffset+ld.MACHO_ARM64_RELOC_UNSIGNED*2 || relocs.At(rIdx+1).Off() != r.Off() {
|
||||
ldr.Errorf(s, "unexpected ARM64_RELOC_SUBTRACTOR reloc, must be followed by ARM64_RELOC_UNSIGNED at same offset")
|
||||
break
|
||||
}
|
||||
su.SetRelocType(rIdx+1, objabi.R_PCREL)
|
||||
su.SetRelocAdd(rIdx+1, r.Add()+int64(r.Off())+int64(r.Siz())-off)
|
||||
// Remove the other relocation
|
||||
su.SetRelocSiz(rIdx, 0)
|
||||
return true
|
||||
|
||||
case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
|
||||
su := ldr.MakeSymbolUpdater(s)
|
||||
su.SetRelocType(rIdx, objabi.R_CALLARM64)
|
||||
|
|
@ -277,6 +299,17 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
|
|||
su.SetRelocSym(rIdx, syms.GOT)
|
||||
su.SetRelocAdd(rIdx, int64(ldr.SymGot(targ)))
|
||||
return true
|
||||
|
||||
case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_POINTER_TO_GOT*2 + pcrel:
|
||||
if targType != sym.SDYNIMPORT {
|
||||
ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
|
||||
}
|
||||
ld.AddGotSym(target, ldr, syms, targ, 0)
|
||||
su := ldr.MakeSymbolUpdater(s)
|
||||
su.SetRelocType(rIdx, objabi.R_PCREL)
|
||||
su.SetRelocSym(rIdx, syms.GOT)
|
||||
su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())+int64(ldr.SymGot(targ)))
|
||||
return true
|
||||
}
|
||||
|
||||
// Reread the reloc to incorporate any changes in type above.
|
||||
|
|
|
|||
|
|
@ -1452,7 +1452,9 @@ func (ctxt *Link) hostlink() {
|
|||
argv = append(argv, "-s")
|
||||
}
|
||||
} else if *FlagW {
|
||||
argv = append(argv, "-Wl,-S") // suppress debugging symbols
|
||||
if !ctxt.IsAIX() && !ctxt.IsSolaris() { // The AIX and Solaris linkers' -S has different meaning
|
||||
argv = append(argv, "-Wl,-S") // suppress debugging symbols
|
||||
}
|
||||
}
|
||||
|
||||
// On darwin, whether to combine DWARF into executable.
|
||||
|
|
@ -1770,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 {
|
||||
|
|
@ -1781,10 +1784,16 @@ 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, wlPrefix+"export-all-symbols"):
|
||||
fileName := peCreateExportFile(ctxt, filepath.Base(outopt))
|
||||
prefix := ""
|
||||
if isMSVC {
|
||||
prefix = "-Wl,-def:"
|
||||
}
|
||||
argv = append(argv, prefix+fileName)
|
||||
}
|
||||
|
||||
const unusedArguments = "-Qunused-arguments"
|
||||
|
|
|
|||
|
|
@ -106,11 +106,13 @@ const (
|
|||
MACHO_ARM_RELOC_SECTDIFF = 2
|
||||
MACHO_ARM_RELOC_BR24 = 5
|
||||
MACHO_ARM64_RELOC_UNSIGNED = 0
|
||||
MACHO_ARM64_RELOC_SUBTRACTOR = 1
|
||||
MACHO_ARM64_RELOC_BRANCH26 = 2
|
||||
MACHO_ARM64_RELOC_PAGE21 = 3
|
||||
MACHO_ARM64_RELOC_PAGEOFF12 = 4
|
||||
MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
|
||||
MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
|
||||
MACHO_ARM64_RELOC_POINTER_TO_GOT = 7
|
||||
MACHO_ARM64_RELOC_ADDEND = 10
|
||||
MACHO_GENERIC_RELOC_VANILLA = 0
|
||||
MACHO_FAKE_GOTPCREL = 100
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -2437,6 +2450,9 @@ var blockedLinknames = map[string][]string{
|
|||
"sync_test.runtime_blockUntilEmptyCleanupQueue": {"sync_test"},
|
||||
"time.runtimeIsBubbled": {"time"},
|
||||
"unique.runtime_blockUntilEmptyCleanupQueue": {"unique"},
|
||||
// Experimental features
|
||||
"runtime.goroutineLeakGC": {"runtime/pprof"},
|
||||
"runtime.goroutineleakcount": {"runtime/pprof"},
|
||||
// Others
|
||||
"net.newWindowsFile": {"net"}, // pushed from os
|
||||
"testing/synctest.testingSynctestTest": {"testing/synctest"}, // pushed from testing
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type MakeHash func() hash.Hash
|
|||
// TestHash performs a set of tests on hash.Hash implementations, checking the
|
||||
// documented requirements of Write, Sum, Reset, Size, and BlockSize.
|
||||
func TestHash(t *testing.T, mh MakeHash) {
|
||||
if boring.Enabled || fips140.Version() == "v1.0" {
|
||||
if boring.Enabled || fips140.Version() == "v1.0.0" {
|
||||
testhash.TestHashWithoutClone(t, testhash.MakeHash(mh))
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -56,9 +56,10 @@ func CAST(name string, f func() error) {
|
|||
}
|
||||
|
||||
// PCT runs the named Pairwise Consistency Test (if operated in FIPS mode) and
|
||||
// returns any errors. If an error is returned, the key must not be used.
|
||||
// aborts the program (stopping the module input/output and entering the "error
|
||||
// state") if the test fails.
|
||||
//
|
||||
// PCTs are mandatory for every key pair that is generated/imported, including
|
||||
// PCTs are mandatory for every generated (but not imported) key pair, including
|
||||
// ephemeral keys (which effectively doubles the cost of key establishment). See
|
||||
// Implementation Guidance 10.3.A Additional Comment 1.
|
||||
//
|
||||
|
|
@ -66,17 +67,23 @@ func CAST(name string, f func() error) {
|
|||
//
|
||||
// If a package p calls PCT during key generation, an invocation of that
|
||||
// function should be added to fipstest.TestConditionals.
|
||||
func PCT(name string, f func() error) error {
|
||||
func PCT(name string, f func() error) {
|
||||
if strings.ContainsAny(name, ",#=:") {
|
||||
panic("fips: invalid self-test name: " + name)
|
||||
}
|
||||
if !Enabled {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
err := f()
|
||||
if name == failfipscast {
|
||||
err = errors.New("simulated PCT failure")
|
||||
}
|
||||
return err
|
||||
if err != nil {
|
||||
fatal("FIPS 140-3 self-test failed: " + name + ": " + err.Error())
|
||||
panic("unreachable")
|
||||
}
|
||||
if debug {
|
||||
println("FIPS 140-3 PCT passed:", name)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,6 +161,27 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
|||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// A "Pairwise Consistency Test" makes no sense if we just generated the
|
||||
// public key from an ephemeral private key. Moreover, there is no way to
|
||||
// check it aside from redoing the exact same computation again. SP 800-56A
|
||||
// Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it.
|
||||
// However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a
|
||||
// PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional
|
||||
// Comment 1 goes out of its way to say that "the PCT shall be performed
|
||||
// consistent [...], even if the underlying standard does not require a
|
||||
// PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode.
|
||||
fips140.PCT("ECDH PCT", func() error {
|
||||
p1, err := c.newPoint().ScalarBaseMult(privateKey.d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(p1.Bytes(), privateKey.pub.q) {
|
||||
return errors.New("crypto/ecdh: public key does not match private key")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -188,28 +209,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) {
|
|||
panic("crypto/ecdh: internal error: public key is the identity element")
|
||||
}
|
||||
|
||||
// A "Pairwise Consistency Test" makes no sense if we just generated the
|
||||
// public key from an ephemeral private key. Moreover, there is no way to
|
||||
// check it aside from redoing the exact same computation again. SP 800-56A
|
||||
// Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it.
|
||||
// However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a
|
||||
// PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional
|
||||
// Comment 1 goes out of its way to say that "the PCT shall be performed
|
||||
// consistent [...], even if the underlying standard does not require a
|
||||
// PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode.
|
||||
if err := fips140.PCT("ECDH PCT", func() error {
|
||||
p1, err := c.newPoint().ScalarBaseMult(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(p1.Bytes(), publicKey) {
|
||||
return errors.New("crypto/ecdh: public key does not match private key")
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
|
||||
return k, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ func testHash() []byte {
|
|||
}
|
||||
}
|
||||
|
||||
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error {
|
||||
return fips140.PCT("ECDSA PCT", func() error {
|
||||
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) {
|
||||
fips140.PCT("ECDSA PCT", func() error {
|
||||
hash := testHash()
|
||||
drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil)
|
||||
sig, err := sign(c, k, drbg, hash)
|
||||
|
|
|
|||
|
|
@ -167,11 +167,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], D, Q []byte) (*PrivateKey, error) {
|
|||
return nil, err
|
||||
}
|
||||
priv := &PrivateKey{pub: *pub, d: d.Bytes(c.N)}
|
||||
if err := fipsPCT(c, priv); err != nil {
|
||||
// This can happen if the application went out of its way to make an
|
||||
// ecdsa.PrivateKey with a mismatching PublicKey.
|
||||
return nil, err
|
||||
}
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
|
|
@ -204,10 +199,7 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
|||
},
|
||||
d: k.Bytes(c.N),
|
||||
}
|
||||
if err := fipsPCT(c, priv); err != nil {
|
||||
// This clearly can't happen, but FIPS 140-3 mandates that we check it.
|
||||
panic(err)
|
||||
}
|
||||
fipsPCT(c, priv)
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ func newDRBG[H hash.Hash](hash func() H, entropy, nonce []byte, s personalizatio
|
|||
//
|
||||
// This should only be used for ACVP testing. hmacDRBG is not intended to be
|
||||
// used directly.
|
||||
func TestingOnlyNewDRBG(hash func() hash.Hash, entropy, nonce []byte, s []byte) *hmacDRBG {
|
||||
func TestingOnlyNewDRBG[H hash.Hash](hash func() H, entropy, nonce []byte, s []byte) *hmacDRBG {
|
||||
return newDRBG(hash, entropy, nonce, plainPersonalizationString(s))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
func fipsPCT(k *PrivateKey) error {
|
||||
return fips140.PCT("Ed25519 sign and verify PCT", func() error {
|
||||
func fipsPCT(k *PrivateKey) {
|
||||
fips140.PCT("Ed25519 sign and verify PCT", func() error {
|
||||
return pairwiseTest(k)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,10 +69,7 @@ func generateKey(priv *PrivateKey) (*PrivateKey, error) {
|
|||
fips140.RecordApproved()
|
||||
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.
|
||||
panic(err)
|
||||
}
|
||||
fipsPCT(priv)
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
|
|
@ -88,10 +85,6 @@ func newPrivateKeyFromSeed(priv *PrivateKey, seed []byte) (*PrivateKey, error) {
|
|||
}
|
||||
copy(priv.seed[:], seed)
|
||||
precomputePrivateKey(priv)
|
||||
if err := fipsPCT(priv); err != nil {
|
||||
// This clearly can't happen, but FIPS 140-3 requires that we check.
|
||||
panic(err)
|
||||
}
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
|
|
@ -137,12 +130,6 @@ func newPrivateKey(priv *PrivateKey, privBytes []byte) (*PrivateKey, error) {
|
|||
|
||||
copy(priv.prefix[:], h[32:])
|
||||
|
||||
if err := fipsPCT(priv); err != nil {
|
||||
// This can happen if the application messed with the private key
|
||||
// encoding, and the public key doesn't match the seed anymore.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
202
src/crypto/internal/fips140/entropy/entropy.go
Normal file
202
src/crypto/internal/fips140/entropy/entropy.go
Normal file
|
|
@ -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
|
||||
}
|
||||
191
src/crypto/internal/fips140/entropy/sha384.go
Normal file
191
src/crypto/internal/fips140/entropy/sha384.go
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ package fips140
|
|||
import (
|
||||
"crypto/internal/fips140deps/godebug"
|
||||
"errors"
|
||||
"hash"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
|
|
@ -49,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",
|
||||
|
|
@ -68,16 +69,10 @@ func Name() string {
|
|||
return "Go Cryptographic Module"
|
||||
}
|
||||
|
||||
// Version returns the formal version (such as "v1.0") if building against a
|
||||
// Version returns the formal version (such as "v1.0.0") if building against a
|
||||
// frozen module with GOFIPS140. Otherwise, it returns "latest".
|
||||
func Version() string {
|
||||
// This return value is replaced by mkzip.go, it must not be changed or
|
||||
// moved to a different file.
|
||||
return "latest" //mkzip:version
|
||||
}
|
||||
|
||||
// Hash is a legacy compatibility alias for hash.Hash.
|
||||
//
|
||||
// It's only here because [crypto/internal/fips140/ecdsa.TestingOnlyNewDRBG]
|
||||
// takes a "func() fips140.Hash" in v1.0.0, instead of being generic.
|
||||
type Hash = hash.Hash
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -113,15 +113,13 @@ func GenerateKey1024() (*DecapsulationKey1024, error) {
|
|||
}
|
||||
|
||||
func generateKey1024(dk *DecapsulationKey1024) (*DecapsulationKey1024, error) {
|
||||
fipsSelfTest()
|
||||
var d [32]byte
|
||||
drbg.Read(d[:])
|
||||
var z [32]byte
|
||||
drbg.Read(z[:])
|
||||
kemKeyGen1024(dk, &d, &z)
|
||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) }); err != nil {
|
||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
||||
panic(err)
|
||||
}
|
||||
fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) })
|
||||
fips140.RecordApproved()
|
||||
return dk, nil
|
||||
}
|
||||
|
|
@ -129,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
|
||||
|
|
@ -149,10 +148,6 @@ func newKeyFromSeed1024(dk *DecapsulationKey1024, seed []byte) (*DecapsulationKe
|
|||
d := (*[32]byte)(seed[:32])
|
||||
z := (*[32]byte)(seed[32:])
|
||||
kemKeyGen1024(dk, d, z)
|
||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) }); err != nil {
|
||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
||||
panic(err)
|
||||
}
|
||||
fips140.RecordApproved()
|
||||
return dk, nil
|
||||
}
|
||||
|
|
@ -285,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
|
||||
|
|
@ -296,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)
|
||||
}
|
||||
|
|
@ -401,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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,15 +172,13 @@ func GenerateKey768() (*DecapsulationKey768, error) {
|
|||
}
|
||||
|
||||
func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) {
|
||||
fipsSelfTest()
|
||||
var d [32]byte
|
||||
drbg.Read(d[:])
|
||||
var z [32]byte
|
||||
drbg.Read(z[:])
|
||||
kemKeyGen(dk, &d, &z)
|
||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil {
|
||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
||||
panic(err)
|
||||
}
|
||||
fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) })
|
||||
fips140.RecordApproved()
|
||||
return dk, nil
|
||||
}
|
||||
|
|
@ -188,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
|
||||
|
|
@ -208,10 +207,6 @@ func newKeyFromSeed(dk *DecapsulationKey768, seed []byte) (*DecapsulationKey768,
|
|||
d := (*[32]byte)(seed[:32])
|
||||
z := (*[32]byte)(seed[32:])
|
||||
kemKeyGen(dk, d, z)
|
||||
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil {
|
||||
// This clearly can't happen, but FIPS 140-3 requires us to check.
|
||||
panic(err)
|
||||
}
|
||||
fips140.RecordApproved()
|
||||
return dk, nil
|
||||
}
|
||||
|
|
@ -344,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
|
||||
|
|
@ -355,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)
|
||||
}
|
||||
|
|
@ -460,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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,28 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
|
|||
// negligible chance of failure we can defer the check to the end of key
|
||||
// generation and return an error if it fails. See [checkPrivateKey].
|
||||
|
||||
return newPrivateKey(N, 65537, d, P, Q)
|
||||
k, err := newPrivateKey(N, 65537, d, P, Q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if k.fipsApproved {
|
||||
fips140.PCT("RSA sign and verify PCT", func() error {
|
||||
hash := []byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
}
|
||||
sig, err := signPKCS1v15(k, "SHA-256", hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return verifyPKCS1v15(k.PublicKey(), "SHA-256", hash, sig)
|
||||
})
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -320,26 +320,6 @@ func checkPrivateKey(priv *PrivateKey) error {
|
|||
return errors.New("crypto/rsa: d too small")
|
||||
}
|
||||
|
||||
// If the key is still in scope for FIPS mode, perform a Pairwise
|
||||
// Consistency Test.
|
||||
if priv.fipsApproved {
|
||||
if err := fips140.PCT("RSA sign and verify PCT", func() error {
|
||||
hash := []byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
}
|
||||
sig, err := signPKCS1v15(priv, "SHA-256", hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return verifyPKCS1v15(priv.PublicKey(), "SHA-256", hash, sig)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
21
src/crypto/internal/fips140deps/time/time.go
Normal file
21
src/crypto/internal/fips140deps/time/time.go
Normal file
|
|
@ -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))
|
||||
}
|
||||
17
src/crypto/internal/fips140deps/time/time_windows.go
Normal file
17
src/crypto/internal/fips140deps/time/time_windows.go
Normal file
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -1624,7 +1624,7 @@ func cmdHmacDrbgAft(h func() hash.Hash) command {
|
|||
// * Uninstantiate
|
||||
// See Table 7 in draft-vassilev-acvp-drbg
|
||||
out := make([]byte, outLen)
|
||||
drbg := ecdsa.TestingOnlyNewDRBG(func() fips140.Hash { return h() }, entropy, nonce, personalization)
|
||||
drbg := ecdsa.TestingOnlyNewDRBG(h, entropy, nonce, personalization)
|
||||
drbg.Generate(out)
|
||||
drbg.Generate(out)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
package fipstest
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io/fs"
|
||||
|
|
@ -18,6 +17,7 @@ import (
|
|||
"testing"
|
||||
|
||||
// Import packages that define CASTs to test them.
|
||||
"crypto/internal/cryptotest"
|
||||
_ "crypto/internal/fips140/aes"
|
||||
_ "crypto/internal/fips140/aes/gcm"
|
||||
_ "crypto/internal/fips140/drbg"
|
||||
|
|
@ -48,10 +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",
|
||||
"ML-KEM PCT",
|
||||
"ML-KEM PCT", // -768
|
||||
"ML-KEM PCT", // -1024
|
||||
"ML-KEM-768",
|
||||
"PBKDF2",
|
||||
"RSA sign and verify PCT",
|
||||
|
|
@ -106,61 +104,77 @@ func TestAllCASTs(t *testing.T) {
|
|||
|
||||
// TestConditionals causes the conditional CASTs and PCTs to be invoked.
|
||||
func TestConditionals(t *testing.T) {
|
||||
mlkem.GenerateKey768()
|
||||
k, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader)
|
||||
// ML-KEM PCT
|
||||
kMLKEM, err := mlkem.GenerateKey768()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Error(err)
|
||||
} else {
|
||||
// ML-KEM-768
|
||||
kMLKEM.EncapsulationKey().Encapsulate()
|
||||
}
|
||||
ecdh.ECDH(ecdh.P256(), k, k.PublicKey())
|
||||
// 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.Fatal(err)
|
||||
t.Error(err)
|
||||
} else {
|
||||
// ECDSA P-256 SHA2-512 sign and verify
|
||||
ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
|
||||
}
|
||||
ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
|
||||
// Ed25519 sign and verify PCT
|
||||
k25519, err := ed25519.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Error(err)
|
||||
} else {
|
||||
// Ed25519 sign and verify
|
||||
ed25519.Sign(k25519, make([]byte, 32))
|
||||
}
|
||||
ed25519.Sign(k25519, make([]byte, 32))
|
||||
rsa.VerifyPKCS1v15(&rsa.PublicKey{}, "", nil, nil)
|
||||
// Parse an RSA key to hit the PCT rather than generating one (which is slow).
|
||||
block, _ := pem.Decode([]byte(strings.ReplaceAll(
|
||||
`-----BEGIN RSA TESTING KEY-----
|
||||
MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso
|
||||
tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE
|
||||
89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU
|
||||
l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s
|
||||
B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59
|
||||
3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+
|
||||
dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI
|
||||
FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J
|
||||
aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2
|
||||
BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx
|
||||
IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/
|
||||
fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u
|
||||
pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT
|
||||
Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl
|
||||
u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD
|
||||
fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X
|
||||
Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE
|
||||
k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo
|
||||
qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS
|
||||
CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ
|
||||
XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw
|
||||
AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r
|
||||
UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0
|
||||
2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5
|
||||
7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3
|
||||
-----END RSA TESTING KEY-----`, "TESTING KEY", "PRIVATE KEY")))
|
||||
if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
|
||||
t.Fatal(err)
|
||||
// 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")
|
||||
}
|
||||
|
||||
func TestCASTPasses(t *testing.T) {
|
||||
moduleStatus(t)
|
||||
testenv.MustHaveExec(t)
|
||||
cryptotest.MustSupportFIPS140(t)
|
||||
|
||||
cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
|
||||
cmd.Env = append(cmd.Env, "GODEBUG=fips140=debug")
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("%s", out)
|
||||
if err != nil || !strings.Contains(string(out), "completed successfully") {
|
||||
t.Errorf("TestConditionals did not complete successfully")
|
||||
}
|
||||
|
||||
for _, name := range allCASTs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if !strings.Contains(string(out), fmt.Sprintf("passed: %s\n", name)) {
|
||||
t.Errorf("CAST/PCT %s success was not logged", name)
|
||||
} else {
|
||||
t.Logf("CAST/PCT succeeded: %s", name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCASTFailures(t *testing.T) {
|
||||
moduleStatus(t)
|
||||
testenv.MustHaveExec(t)
|
||||
cryptotest.MustSupportFIPS140(t)
|
||||
|
||||
for _, name := range allCASTs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
|
@ -169,7 +183,6 @@ func TestCASTFailures(t *testing.T) {
|
|||
if !testing.Verbose() {
|
||||
t.Parallel()
|
||||
}
|
||||
t.Logf("CAST/PCT succeeded: %s", name)
|
||||
t.Logf("Testing CAST/PCT failure...")
|
||||
cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v")
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", name))
|
||||
|
|
@ -180,6 +193,8 @@ func TestCASTFailures(t *testing.T) {
|
|||
}
|
||||
if strings.Contains(string(out), "completed successfully") {
|
||||
t.Errorf("CAST/PCT %s failure did not stop the program", name)
|
||||
} else if !strings.Contains(string(out), "self-test failed: "+name) {
|
||||
t.Errorf("CAST/PCT %s failure did not log the expected message", name)
|
||||
} else {
|
||||
t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name)
|
||||
}
|
||||
|
|
|
|||
264
src/crypto/internal/fips140test/entropy_test.go
Normal file
264
src/crypto/internal/fips140test/entropy_test.go
Normal file
|
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
|
@ -74,11 +74,9 @@ func TestVersion(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
exp := setting.Value
|
||||
if exp == "v1.0.0" {
|
||||
// Unfortunately we enshrined the version of the first module as
|
||||
// v1.0 before deciding to go for full versions.
|
||||
exp = "v1.0"
|
||||
}
|
||||
// Remove the -hash suffix, if any.
|
||||
// The version from fips140.Version omits it.
|
||||
exp, _, _ = strings.Cut(exp, "-")
|
||||
if v := fips140.Version(); v != exp {
|
||||
t.Errorf("Version is %q, expected %q", v, exp)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1246,7 +1246,7 @@ func TestModifiedPrivateKey(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("D+2", func(t *testing.T) {
|
||||
if fips140.Version() == "v1.0" {
|
||||
if fips140.Version() == "v1.0.0" {
|
||||
t.Skip("This was fixed after v1.0.0")
|
||||
}
|
||||
testModifiedPrivateKey(t, func(k *PrivateKey) {
|
||||
|
|
|
|||
|
|
@ -568,16 +568,6 @@ func parseECHExt(ext []byte) (echType echExtType, cs echCipher, configID uint8,
|
|||
return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil
|
||||
}
|
||||
|
||||
func marshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloKey) ([]byte, error) {
|
||||
builder := cryptobyte.NewBuilder(nil)
|
||||
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
for _, c := range configs {
|
||||
builder.AddBytes(c.Config)
|
||||
}
|
||||
})
|
||||
return builder.Bytes()
|
||||
}
|
||||
|
||||
func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedClientHelloKey) (*clientHelloMsg, *echServerContext, error) {
|
||||
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ type echServerContext struct {
|
|||
configID uint8
|
||||
ciphersuite echCipher
|
||||
transcript hash.Hash
|
||||
// inner indicates that the initial client_hello we recieved contained an
|
||||
// inner indicates that the initial client_hello we received contained an
|
||||
// encrypted_client_hello extension that indicated it was an "inner" hello.
|
||||
// We don't do any additional processing of the hello in this case, so all
|
||||
// fields above are unset.
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -557,3 +557,67 @@ func TestTokenTruncation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderInputOffset(t *testing.T) {
|
||||
const input = ` [
|
||||
[ ] , [ "one" ] , [ "one" , "two" ] ,
|
||||
{ } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }
|
||||
] `
|
||||
wantOffsets := []int64{
|
||||
0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,
|
||||
38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,
|
||||
76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,
|
||||
117, 117,
|
||||
}
|
||||
wantMores := []bool{
|
||||
true, true, false, true, true, false, true, true, true, false,
|
||||
true, false, true, true, true, false, true, true, true, true,
|
||||
true, false, false, false, false,
|
||||
}
|
||||
|
||||
d := NewDecoder(strings.NewReader(input))
|
||||
checkOffset := func() {
|
||||
t.Helper()
|
||||
got := d.InputOffset()
|
||||
if len(wantOffsets) == 0 {
|
||||
t.Fatalf("InputOffset = %d, want nil", got)
|
||||
}
|
||||
want := wantOffsets[0]
|
||||
if got != want {
|
||||
t.Fatalf("InputOffset = %d, want %d", got, want)
|
||||
}
|
||||
wantOffsets = wantOffsets[1:]
|
||||
}
|
||||
checkMore := func() {
|
||||
t.Helper()
|
||||
got := d.More()
|
||||
if len(wantMores) == 0 {
|
||||
t.Fatalf("More = %v, want nil", got)
|
||||
}
|
||||
want := wantMores[0]
|
||||
if got != want {
|
||||
t.Fatalf("More = %v, want %v", got, want)
|
||||
}
|
||||
wantMores = wantMores[1:]
|
||||
}
|
||||
checkOffset()
|
||||
checkMore()
|
||||
checkOffset()
|
||||
for {
|
||||
if _, err := d.Token(); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
t.Fatalf("Token error: %v", err)
|
||||
}
|
||||
checkOffset()
|
||||
checkMore()
|
||||
checkOffset()
|
||||
}
|
||||
checkOffset()
|
||||
checkMore()
|
||||
checkOffset()
|
||||
|
||||
if len(wantOffsets)+len(wantMores) > 0 {
|
||||
t.Fatal("unconsumed testdata")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ type Decoder struct {
|
|||
dec *jsontext.Decoder
|
||||
opts jsonv2.Options
|
||||
err error
|
||||
|
||||
// hadPeeked reports whether [Decoder.More] was called.
|
||||
// It is reset by [Decoder.Decode] and [Decoder.Token].
|
||||
hadPeeked bool
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
|
|
@ -76,6 +80,7 @@ func (dec *Decoder) Decode(v any) error {
|
|||
}
|
||||
return dec.err
|
||||
}
|
||||
dec.hadPeeked = false
|
||||
return jsonv2.Unmarshal(b, v, dec.opts)
|
||||
}
|
||||
|
||||
|
|
@ -206,6 +211,7 @@ func (dec *Decoder) Token() (Token, error) {
|
|||
}
|
||||
return nil, transformSyntacticError(err)
|
||||
}
|
||||
dec.hadPeeked = false
|
||||
switch k := tok.Kind(); k {
|
||||
case 'n':
|
||||
return nil, nil
|
||||
|
|
@ -230,6 +236,7 @@ func (dec *Decoder) Token() (Token, error) {
|
|||
// More reports whether there is another element in the
|
||||
// current array or object being parsed.
|
||||
func (dec *Decoder) More() bool {
|
||||
dec.hadPeeked = true
|
||||
k := dec.dec.PeekKind()
|
||||
return k > 0 && k != ']' && k != '}'
|
||||
}
|
||||
|
|
@ -238,5 +245,18 @@ func (dec *Decoder) More() bool {
|
|||
// The offset gives the location of the end of the most recently returned token
|
||||
// and the beginning of the next token.
|
||||
func (dec *Decoder) InputOffset() int64 {
|
||||
return dec.dec.InputOffset()
|
||||
offset := dec.dec.InputOffset()
|
||||
if dec.hadPeeked {
|
||||
// Historically, InputOffset reported the location of
|
||||
// the end of the most recently returned token
|
||||
// unless [Decoder.More] is called, in which case, it reported
|
||||
// the beginning of the next token.
|
||||
unreadBuffer := dec.dec.UnreadBuffer()
|
||||
trailingTokens := bytes.TrimLeft(unreadBuffer, " \n\r\t")
|
||||
if len(trailingTokens) > 0 {
|
||||
leadingWhitespace := len(unreadBuffer) - len(trailingTokens)
|
||||
offset += int64(leadingWhitespace)
|
||||
}
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
|
|
|||
|
|
@ -537,3 +537,67 @@ func TestTokenTruncation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderInputOffset(t *testing.T) {
|
||||
const input = ` [
|
||||
[ ] , [ "one" ] , [ "one" , "two" ] ,
|
||||
{ } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }
|
||||
] `
|
||||
wantOffsets := []int64{
|
||||
0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,
|
||||
38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,
|
||||
76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,
|
||||
117, 117,
|
||||
}
|
||||
wantMores := []bool{
|
||||
true, true, false, true, true, false, true, true, true, false,
|
||||
true, false, true, true, true, false, true, true, true, true,
|
||||
true, false, false, false, false,
|
||||
}
|
||||
|
||||
d := NewDecoder(strings.NewReader(input))
|
||||
checkOffset := func() {
|
||||
t.Helper()
|
||||
got := d.InputOffset()
|
||||
if len(wantOffsets) == 0 {
|
||||
t.Fatalf("InputOffset = %d, want nil", got)
|
||||
}
|
||||
want := wantOffsets[0]
|
||||
if got != want {
|
||||
t.Fatalf("InputOffset = %d, want %d", got, want)
|
||||
}
|
||||
wantOffsets = wantOffsets[1:]
|
||||
}
|
||||
checkMore := func() {
|
||||
t.Helper()
|
||||
got := d.More()
|
||||
if len(wantMores) == 0 {
|
||||
t.Fatalf("More = %v, want nil", got)
|
||||
}
|
||||
want := wantMores[0]
|
||||
if got != want {
|
||||
t.Fatalf("More = %v, want %v", got, want)
|
||||
}
|
||||
wantMores = wantMores[1:]
|
||||
}
|
||||
checkOffset()
|
||||
checkMore()
|
||||
checkOffset()
|
||||
for {
|
||||
if _, err := d.Token(); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
t.Fatalf("Token error: %v", err)
|
||||
}
|
||||
checkOffset()
|
||||
checkMore()
|
||||
checkOffset()
|
||||
}
|
||||
checkOffset()
|
||||
checkMore()
|
||||
checkOffset()
|
||||
|
||||
if len(wantOffsets)+len(wantMores) > 0 {
|
||||
t.Fatal("unconsumed testdata")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 val.CanAddr() {
|
||||
pv := val.Addr()
|
||||
if pv.CanInterface() && pv.Type().Implements(unmarshalerType) {
|
||||
return d.unmarshalInterface(pv.Interface().(Unmarshaler), start)
|
||||
if unmarshaler, ok := reflect.TypeAssert[Unmarshaler](val); ok {
|
||||
return d.unmarshalInterface(unmarshaler, start)
|
||||
}
|
||||
}
|
||||
|
||||
if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
|
||||
return d.unmarshalTextInterface(val.Interface().(encoding.TextUnmarshaler))
|
||||
if val.CanAddr() {
|
||||
pv := val.Addr()
|
||||
if pv.CanInterface() {
|
||||
if unmarshaler, ok := reflect.TypeAssert[Unmarshaler](pv); ok {
|
||||
return d.unmarshalInterface(unmarshaler, start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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{}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@
|
|||
//
|
||||
// because the former will succeed if err wraps [io/fs.ErrExist].
|
||||
//
|
||||
// [As] examines the tree of its first argument looking for an error that can be
|
||||
// assigned to its second argument, which must be a pointer. If it succeeds, it
|
||||
// performs the assignment and returns true. Otherwise, it returns false. The form
|
||||
// [AsType] examines the tree of its argument looking for an error whose
|
||||
// type matches its type argument. If it succeeds, it returns the
|
||||
// corresponding value of that type and true. Otherwise, it returns the
|
||||
// zero value of that type and false. The form
|
||||
//
|
||||
// var perr *fs.PathError
|
||||
// if errors.As(err, &perr) {
|
||||
// if perr, ok := errors.AsType[*fs.PathError](err); ok {
|
||||
// fmt.Println(perr.Path)
|
||||
// }
|
||||
//
|
||||
|
|
|
|||
|
|
@ -102,6 +102,18 @@ func ExampleAs() {
|
|||
// Failed at path: non-existing
|
||||
}
|
||||
|
||||
func ExampleAsType() {
|
||||
if _, err := os.Open("non-existing"); err != nil {
|
||||
if pathError, ok := errors.AsType[*fs.PathError](err); ok {
|
||||
fmt.Println("Failed at path:", pathError.Path)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
// Output:
|
||||
// Failed at path: non-existing
|
||||
}
|
||||
|
||||
func ExampleUnwrap() {
|
||||
err1 := errors.New("error1")
|
||||
err2 := fmt.Errorf("error2: [%w]", err1)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,10 @@ func is(err, target error, targetComparable bool) bool {
|
|||
// As finds the first error in err's tree that matches target, and if one is found, sets
|
||||
// target to that error value and returns true. Otherwise, it returns false.
|
||||
//
|
||||
// For most uses, prefer [AsType]. As is equivalent to [AsType] but sets its target
|
||||
// argument rather than returning the matching error and doesn't require its target
|
||||
// argument to implement error.
|
||||
//
|
||||
// The tree consists of err itself, followed by the errors obtained by repeatedly
|
||||
// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
|
||||
// errors, As examines err followed by a depth-first traversal of its children.
|
||||
|
|
@ -145,3 +149,60 @@ func as(err error, target any, targetVal reflectlite.Value, targetType reflectli
|
|||
}
|
||||
|
||||
var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
// AsType finds the first error in err's tree that matches the type E, and
|
||||
// if one is found, returns that error value and true. Otherwise, it
|
||||
// returns the zero value of E and false.
|
||||
//
|
||||
// The tree consists of err itself, followed by the errors obtained by
|
||||
// repeatedly calling its Unwrap() error or Unwrap() []error method. When
|
||||
// err wraps multiple errors, AsType examines err followed by a
|
||||
// depth-first traversal of its children.
|
||||
//
|
||||
// An error err matches the type E if the type assertion err.(E) holds,
|
||||
// or if the error has a method As(any) bool such that err.As(target)
|
||||
// returns true when target is a non-nil *E. In the latter case, the As
|
||||
// method is responsible for setting target.
|
||||
func AsType[E error](err error) (E, bool) {
|
||||
if err == nil {
|
||||
var zero E
|
||||
return zero, false
|
||||
}
|
||||
var pe *E // lazily initialized
|
||||
return asType(err, &pe)
|
||||
}
|
||||
|
||||
func asType[E error](err error, ppe **E) (_ E, _ bool) {
|
||||
for {
|
||||
if e, ok := err.(E); ok {
|
||||
return e, true
|
||||
}
|
||||
if x, ok := err.(interface{ As(any) bool }); ok {
|
||||
if *ppe == nil {
|
||||
*ppe = new(E)
|
||||
}
|
||||
if x.As(*ppe) {
|
||||
return **ppe, true
|
||||
}
|
||||
}
|
||||
switch x := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
err = x.Unwrap()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
case interface{ Unwrap() []error }:
|
||||
for _, err := range x.Unwrap() {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if x, ok := asType(err, ppe); ok {
|
||||
return x, true
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,123 @@ func TestAsValidation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAsType(t *testing.T) {
|
||||
var errT errorT
|
||||
var errP *fs.PathError
|
||||
type timeout interface {
|
||||
Timeout() bool
|
||||
error
|
||||
}
|
||||
_, errF := os.Open("non-existing")
|
||||
poserErr := &poser{"oh no", nil}
|
||||
|
||||
testAsType(t,
|
||||
nil,
|
||||
errP,
|
||||
false,
|
||||
)
|
||||
testAsType(t,
|
||||
wrapped{"pitied the fool", errorT{"T"}},
|
||||
errorT{"T"},
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
errF,
|
||||
errF,
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
errT,
|
||||
errP,
|
||||
false,
|
||||
)
|
||||
testAsType(t,
|
||||
wrapped{"wrapped", nil},
|
||||
errT,
|
||||
false,
|
||||
)
|
||||
testAsType(t,
|
||||
&poser{"error", nil},
|
||||
errorT{"poser"},
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
&poser{"path", nil},
|
||||
poserPathErr,
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
poserErr,
|
||||
poserErr,
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
errors.New("err"),
|
||||
timeout(nil),
|
||||
false,
|
||||
)
|
||||
testAsType(t,
|
||||
errF,
|
||||
errF.(timeout),
|
||||
true)
|
||||
testAsType(t,
|
||||
wrapped{"path error", errF},
|
||||
errF.(timeout),
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
multiErr{},
|
||||
errT,
|
||||
false,
|
||||
)
|
||||
testAsType(t,
|
||||
multiErr{errors.New("a"), errorT{"T"}},
|
||||
errorT{"T"},
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
multiErr{errorT{"T"}, errors.New("a")},
|
||||
errorT{"T"},
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
multiErr{errorT{"a"}, errorT{"b"}},
|
||||
errorT{"a"},
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
multiErr{multiErr{errors.New("a"), errorT{"a"}}, errorT{"b"}},
|
||||
errorT{"a"},
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
multiErr{wrapped{"path error", errF}},
|
||||
errF.(timeout),
|
||||
true,
|
||||
)
|
||||
testAsType(t,
|
||||
multiErr{nil},
|
||||
errT,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
type compError interface {
|
||||
comparable
|
||||
error
|
||||
}
|
||||
|
||||
func testAsType[E compError](t *testing.T, err error, want E, wantOK bool) {
|
||||
t.Helper()
|
||||
name := fmt.Sprintf("AsType[%T](Errorf(..., %v))", want, err)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, gotOK := errors.AsType[E](err)
|
||||
if gotOK != wantOK || got != want {
|
||||
t.Fatalf("got %v, %t; want %v, %t", got, gotOK, want, wantOK)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkIs(b *testing.B) {
|
||||
err1 := errors.New("1")
|
||||
err2 := multiErr{multiErr{multiErr{err1, errorT{"a"}}, errorT{"b"}}}
|
||||
|
|
@ -260,6 +377,15 @@ func BenchmarkAs(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkAsType(b *testing.B) {
|
||||
err := multiErr{multiErr{multiErr{errors.New("a"), errorT{"a"}}, errorT{"b"}}}
|
||||
for range b.N {
|
||||
if _, ok := errors.AsType[errorT](err); !ok {
|
||||
b.Fatal("AsType failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
err1 := errors.New("1")
|
||||
erra := wrapped{"wrap 2", err1}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ go 1.26
|
|||
|
||||
require (
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/net v0.44.0
|
||||
golang.org/x/net v0.44.1-0.20251002015445-edb764c2296f
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/net v0.44.1-0.20251002015445-edb764c2296f h1:vNklv+oJQSYNGsWXHoCPi2MHMcpj9/Q7aBhvvfnJvGg=
|
||||
golang.org/x/net v0.44.1-0.20251002015445-edb764c2296f/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
|
|
|
|||
|
|
@ -487,11 +487,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
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ func gofips140() string {
|
|||
}
|
||||
|
||||
// isFIPSVersion reports whether v is a valid FIPS version,
|
||||
// of the form vX.Y.Z.
|
||||
// of the form vX.Y.Z or vX.Y.Z-hash.
|
||||
func isFIPSVersion(v string) bool {
|
||||
if !strings.HasPrefix(v, "v") {
|
||||
return false
|
||||
|
|
@ -99,7 +99,8 @@ func isFIPSVersion(v string) bool {
|
|||
return false
|
||||
}
|
||||
v, ok = skipNum(v[len("."):])
|
||||
return ok && v == ""
|
||||
hasHash := strings.HasPrefix(v, "-") && len(v) == len("-")+8
|
||||
return ok && (v == "" || hasHash)
|
||||
}
|
||||
|
||||
// skipNum skips the leading text matching [0-9]+
|
||||
|
|
@ -320,18 +321,13 @@ func goriscv64() int {
|
|||
}
|
||||
|
||||
type gowasmFeatures struct {
|
||||
SatConv bool
|
||||
SignExt bool
|
||||
// Legacy features, now always enabled
|
||||
//SatConv bool
|
||||
//SignExt bool
|
||||
}
|
||||
|
||||
func (f gowasmFeatures) String() string {
|
||||
var flags []string
|
||||
if f.SatConv {
|
||||
flags = append(flags, "satconv")
|
||||
}
|
||||
if f.SignExt {
|
||||
flags = append(flags, "signext")
|
||||
}
|
||||
return strings.Join(flags, ",")
|
||||
}
|
||||
|
||||
|
|
@ -339,9 +335,9 @@ func gowasm() (f gowasmFeatures) {
|
|||
for opt := range strings.SplitSeq(envOr("GOWASM", ""), ",") {
|
||||
switch opt {
|
||||
case "satconv":
|
||||
f.SatConv = true
|
||||
// ignore, always enabled
|
||||
case "signext":
|
||||
f.SignExt = true
|
||||
// ignore, always enabled
|
||||
case "":
|
||||
// ignore
|
||||
default:
|
||||
|
|
@ -451,12 +447,10 @@ func gogoarchTags() []string {
|
|||
return list
|
||||
case "wasm":
|
||||
var list []string
|
||||
if GOWASM.SatConv {
|
||||
list = append(list, GOARCH+".satconv")
|
||||
}
|
||||
if GOWASM.SignExt {
|
||||
list = append(list, GOARCH+".signext")
|
||||
}
|
||||
// SatConv is always enabled
|
||||
list = append(list, GOARCH+".satconv")
|
||||
// SignExt is always enabled
|
||||
list = append(list, GOARCH+".signext")
|
||||
return list
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
// Code generated by mkconsts.go. DO NOT EDIT.
|
||||
|
||||
//go:build !goexperiment.goroutineleakprofile
|
||||
|
||||
package goexperiment
|
||||
|
||||
const GoroutineLeakProfile = false
|
||||
const GoroutineLeakProfileInt = 0
|
||||
8
src/internal/goexperiment/exp_goroutineleakprofile_on.go
Normal file
8
src/internal/goexperiment/exp_goroutineleakprofile_on.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Code generated by mkconsts.go. DO NOT EDIT.
|
||||
|
||||
//go:build goexperiment.goroutineleakprofile
|
||||
|
||||
package goexperiment
|
||||
|
||||
const GoroutineLeakProfile = true
|
||||
const GoroutineLeakProfileInt = 1
|
||||
|
|
@ -119,6 +119,9 @@ type Flags struct {
|
|||
// SizeSpecializedMalloc enables malloc implementations that are specialized per size class.
|
||||
SizeSpecializedMalloc bool
|
||||
|
||||
// GoroutineLeakProfile enables the collection of goroutine leak profiles.
|
||||
GoroutineLeakProfile bool
|
||||
|
||||
// SIMD enables the simd package and the compiler's handling
|
||||
// of SIMD intrinsics.
|
||||
SIMD bool
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"internal/race"
|
||||
"internal/syscall/windows"
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
|
@ -75,25 +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() {
|
||||
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||
if err != nil {
|
||||
// This shouldn't happen when all CreateEvent arguments are zero.
|
||||
panic(err)
|
||||
}
|
||||
// Set the low bit so that the external IOCP doesn't receive the completion packet.
|
||||
o.o.HEvent = h | 1
|
||||
}
|
||||
|
||||
func (o *operation) close() {
|
||||
if o.o.HEvent != 0 {
|
||||
syscall.CloseHandle(o.o.HEvent)
|
||||
}
|
||||
}
|
||||
|
||||
func (fd *FD) overlapped(o *operation) *syscall.Overlapped {
|
||||
|
|
@ -107,9 +89,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{
|
||||
|
|
@ -168,7 +149,7 @@ var wsaMsgPool = sync.Pool{
|
|||
|
||||
// newWSAMsg creates a new WSAMsg with the provided parameters.
|
||||
// Use [freeWSAMsg] to free it.
|
||||
func newWSAMsg(p []byte, oob []byte, flags int, rsa *syscall.RawSockaddrAny) *windows.WSAMsg {
|
||||
func newWSAMsg(p []byte, oob []byte, flags int, unconnected bool) *windows.WSAMsg {
|
||||
// The returned object can't be allocated in the stack because it is accessed asynchronously
|
||||
// by Windows in between several system calls. If the stack frame is moved while that happens,
|
||||
// then Windows may access invalid memory.
|
||||
|
|
@ -183,11 +164,9 @@ func newWSAMsg(p []byte, oob []byte, flags int, rsa *syscall.RawSockaddrAny) *wi
|
|||
Buf: unsafe.SliceData(oob),
|
||||
}
|
||||
msg.Flags = uint32(flags)
|
||||
msg.Name = syscall.Pointer(unsafe.Pointer(rsa))
|
||||
if rsa != nil {
|
||||
msg.Namelen = int32(unsafe.Sizeof(*rsa))
|
||||
} else {
|
||||
msg.Namelen = 0
|
||||
if unconnected {
|
||||
msg.Name = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||
msg.Namelen = int32(unsafe.Sizeof(syscall.RawSockaddrAny{}))
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
|
@ -198,6 +177,12 @@ func freeWSAMsg(msg *windows.WSAMsg) {
|
|||
msg.Buffers.Buf = nil
|
||||
msg.Control.Len = 0
|
||||
msg.Control.Buf = nil
|
||||
if msg.Name != nil {
|
||||
*msg.Name = syscall.RawSockaddrAny{}
|
||||
wsaRsaPool.Put(msg.Name)
|
||||
msg.Name = nil
|
||||
msg.Namelen = 0
|
||||
}
|
||||
wsaMsgPool.Put(msg)
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +192,12 @@ var wsaRsaPool = sync.Pool{
|
|||
},
|
||||
}
|
||||
|
||||
var operationPool = sync.Pool{
|
||||
New: func() any {
|
||||
return new(operation)
|
||||
},
|
||||
}
|
||||
|
||||
// waitIO waits for the IO operation o to complete.
|
||||
func (fd *FD) waitIO(o *operation) error {
|
||||
if fd.isBlocking {
|
||||
|
|
@ -245,23 +236,57 @@ func (fd *FD) cancelIO(o *operation) {
|
|||
fd.pd.waitCanceled(int(o.mode))
|
||||
}
|
||||
|
||||
// pin pins ptr for the duration of the IO operation.
|
||||
// If fd is in blocking mode, pin does nothing.
|
||||
func (fd *FD) pin(mode int, ptr any) {
|
||||
if fd.isBlocking {
|
||||
return
|
||||
}
|
||||
if mode == 'r' {
|
||||
fd.readPinner.Pin(ptr)
|
||||
} else {
|
||||
fd.writePinner.Pin(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
// execIO executes a single IO operation o.
|
||||
// It supports both synchronous and asynchronous IO.
|
||||
// o.qty and o.flags are set to zero before calling submit
|
||||
// to avoid reusing the values from a previous call.
|
||||
func (fd *FD) execIO(o *operation, submit func(o *operation) (uint32, error)) (int, error) {
|
||||
func (fd *FD) execIO(mode int, submit func(o *operation) (uint32, error)) (int, error) {
|
||||
if mode == 'r' {
|
||||
defer fd.readPinner.Unpin()
|
||||
} else {
|
||||
defer fd.writePinner.Unpin()
|
||||
}
|
||||
// Notify runtime netpoll about starting IO.
|
||||
err := fd.pd.prepare(int(o.mode), fd.isFile)
|
||||
err := fd.pd.prepare(mode, fd.isFile)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
o := operationPool.Get().(*operation)
|
||||
defer operationPool.Put(o)
|
||||
*o = operation{
|
||||
o: syscall.Overlapped{
|
||||
OffsetHigh: uint32(fd.offset >> 32),
|
||||
Offset: uint32(fd.offset),
|
||||
},
|
||||
runtimeCtx: fd.pd.runtimeCtx,
|
||||
mode: int32(mode),
|
||||
}
|
||||
// Start IO.
|
||||
if !fd.isBlocking && o.o.HEvent == 0 && !fd.pollable() {
|
||||
if !fd.isBlocking && !fd.pollable() {
|
||||
// If the handle is opened for overlapped IO but we can't
|
||||
// use the runtime poller, then we need to use an
|
||||
// event to wait for the IO to complete.
|
||||
o.setEvent()
|
||||
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||
if err != nil {
|
||||
// This shouldn't happen when all CreateEvent arguments are zero.
|
||||
panic(err)
|
||||
}
|
||||
// Set the low bit so that the external IOCP doesn't receive the completion packet.
|
||||
o.o.HEvent = h | 1
|
||||
defer syscall.CloseHandle(h)
|
||||
}
|
||||
fd.pin(mode, o)
|
||||
qty, err := submit(o)
|
||||
var waitErr error
|
||||
// Blocking operations shouldn't return ERROR_IO_PENDING.
|
||||
|
|
@ -316,11 +341,6 @@ type FD struct {
|
|||
// System file descriptor. Immutable until Close.
|
||||
Sysfd syscall.Handle
|
||||
|
||||
// Read operation.
|
||||
rop operation
|
||||
// Write operation.
|
||||
wop operation
|
||||
|
||||
// I/O poller.
|
||||
pd pollDesc
|
||||
|
||||
|
|
@ -358,6 +378,11 @@ type FD struct {
|
|||
isBlocking bool
|
||||
|
||||
disassociated atomic.Bool
|
||||
|
||||
// readPinner and writePinner are automatically unpinned
|
||||
// before execIO returns.
|
||||
readPinner runtime.Pinner
|
||||
writePinner runtime.Pinner
|
||||
}
|
||||
|
||||
// setOffset sets the offset fields of the overlapped object
|
||||
|
|
@ -375,8 +400,6 @@ type FD struct {
|
|||
// using an external mechanism.
|
||||
func (fd *FD) setOffset(off int64) {
|
||||
fd.offset = off
|
||||
fd.rop.o.OffsetHigh, fd.rop.o.Offset = uint32(off>>32), uint32(off)
|
||||
fd.wop.o.OffsetHigh, fd.wop.o.Offset = uint32(off>>32), uint32(off)
|
||||
}
|
||||
|
||||
// addOffset adds the given offset to the current offset.
|
||||
|
|
@ -427,8 +450,6 @@ func (fd *FD) Init(net string, pollable bool) error {
|
|||
}
|
||||
fd.isFile = fd.kind != kindNet
|
||||
fd.isBlocking = !pollable
|
||||
fd.rop.mode = 'r'
|
||||
fd.wop.mode = 'w'
|
||||
|
||||
// It is safe to add overlapped handles that also perform I/O
|
||||
// outside of the runtime poller. The runtime poller will ignore
|
||||
|
|
@ -437,8 +458,6 @@ func (fd *FD) Init(net string, pollable bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd.rop.runtimeCtx = fd.pd.runtimeCtx
|
||||
fd.wop.runtimeCtx = fd.pd.runtimeCtx
|
||||
if fd.kind != kindNet || socketCanUseSetFileCompletionNotificationModes {
|
||||
// Non-socket handles can use SetFileCompletionNotificationModes without problems.
|
||||
err := syscall.SetFileCompletionNotificationModes(fd.Sysfd,
|
||||
|
|
@ -477,8 +496,6 @@ func (fd *FD) destroy() error {
|
|||
if fd.Sysfd == syscall.InvalidHandle {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
fd.rop.close()
|
||||
fd.wop.close()
|
||||
// Poller may want to unregister fd in readiness notification mechanism,
|
||||
// so this must be executed before fd.CloseFunc.
|
||||
fd.pd.close()
|
||||
|
|
@ -533,6 +550,10 @@ func (fd *FD) Read(buf []byte) (int, error) {
|
|||
defer fd.readUnlock()
|
||||
}
|
||||
|
||||
if len(buf) > 0 {
|
||||
fd.pin('r', &buf[0])
|
||||
}
|
||||
|
||||
if len(buf) > maxRW {
|
||||
buf = buf[:maxRW]
|
||||
}
|
||||
|
|
@ -543,10 +564,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('r', func(o *operation) (qty uint32, err error) {
|
||||
err = syscall.ReadFile(fd.Sysfd, buf, &qty, fd.overlapped(o))
|
||||
return qty, err
|
||||
})
|
||||
fd.addOffset(n)
|
||||
|
|
@ -560,11 +579,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('r', 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 {
|
||||
|
|
@ -652,7 +669,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
|
||||
|
|
@ -663,8 +680,12 @@ 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.pin('r', &buf[0])
|
||||
}
|
||||
|
||||
if len(buf) > maxRW {
|
||||
buf = buf[:maxRW]
|
||||
}
|
||||
|
||||
if fd.isBlocking {
|
||||
|
|
@ -683,17 +704,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('r', 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
|
||||
|
|
@ -711,14 +730,15 @@ func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) {
|
|||
return 0, nil, err
|
||||
}
|
||||
defer fd.readUnlock()
|
||||
o := &fd.rop
|
||||
o.InitBuf(buf)
|
||||
|
||||
fd.pin('r', &buf[0])
|
||||
|
||||
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('r', 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)
|
||||
|
|
@ -741,14 +761,15 @@ func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error)
|
|||
return 0, err
|
||||
}
|
||||
defer fd.readUnlock()
|
||||
o := &fd.rop
|
||||
o.InitBuf(buf)
|
||||
|
||||
fd.pin('r', &buf[0])
|
||||
|
||||
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('r', 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)
|
||||
|
|
@ -771,14 +792,15 @@ func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error)
|
|||
return 0, err
|
||||
}
|
||||
defer fd.readUnlock()
|
||||
o := &fd.rop
|
||||
o.InitBuf(buf)
|
||||
|
||||
fd.pin('r', &buf[0])
|
||||
|
||||
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('r', 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)
|
||||
|
|
@ -803,6 +825,9 @@ func (fd *FD) Write(buf []byte) (int, error) {
|
|||
defer fd.writeUnlock()
|
||||
}
|
||||
|
||||
if len(buf) > 0 {
|
||||
fd.pin('w', &buf[0])
|
||||
}
|
||||
var ntotal int
|
||||
for {
|
||||
max := len(buf)
|
||||
|
|
@ -816,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('w', func(o *operation) (qty uint32, err error) {
|
||||
err = syscall.WriteFile(fd.Sysfd, b, &qty, fd.overlapped(o))
|
||||
return qty, err
|
||||
})
|
||||
fd.addOffset(n)
|
||||
|
|
@ -827,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('w', func(o *operation) (qty uint32, err error) {
|
||||
err = syscall.WSASend(fd.Sysfd, newWsaBuf(b), 1, &qty, 0, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
}
|
||||
|
|
@ -899,6 +920,10 @@ func (fd *FD) Pwrite(buf []byte, off int64) (int, error) {
|
|||
}
|
||||
defer fd.readWriteUnlock()
|
||||
|
||||
if len(buf) > 0 {
|
||||
fd.pin('w', &buf[0])
|
||||
}
|
||||
|
||||
if fd.isBlocking {
|
||||
curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
|
|
@ -922,12 +947,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('w', func(o *operation) (qty uint32, err error) {
|
||||
err = syscall.WriteFile(fd.Sysfd, buf[ntotal:max], &qty, &o.o)
|
||||
return qty, err
|
||||
})
|
||||
if n > 0 {
|
||||
|
|
@ -956,7 +978,7 @@ func (fd *FD) Writev(buf *[][]byte) (int64, error) {
|
|||
}
|
||||
bufs := newWSABufs(buf)
|
||||
defer freeWSABufs(bufs)
|
||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
||||
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||
err = syscall.WSASend(fd.Sysfd, &(*bufs)[0], uint32(len(*bufs)), &qty, 0, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
|
|
@ -974,25 +996,23 @@ 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('w', 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
|
||||
}
|
||||
|
||||
fd.pin('w', &buf[0])
|
||||
|
||||
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('w', 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)
|
||||
|
|
@ -1013,25 +1033,23 @@ 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('w', 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
|
||||
}
|
||||
|
||||
fd.pin('w', &buf[0])
|
||||
|
||||
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('w', 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)
|
||||
|
|
@ -1052,25 +1070,23 @@ 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('w', 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
|
||||
}
|
||||
|
||||
fd.pin('w', &buf[0])
|
||||
|
||||
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('w', 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)
|
||||
|
|
@ -1086,17 +1102,16 @@ func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error)
|
|||
// called when the descriptor is first created. This is here rather
|
||||
// than in the net package so that it can use fd.wop.
|
||||
func (fd *FD) ConnectEx(ra syscall.Sockaddr) error {
|
||||
o := &fd.wop
|
||||
_, err := fd.execIO(o, func(o *operation) (uint32, error) {
|
||||
_, err := fd.execIO('w', func(o *operation) (uint32, error) {
|
||||
return 0, ConnectExFunc(fd.Sysfd, ra, nil, 0, nil, &o.o)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny, o *operation) (string, error) {
|
||||
func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny) (string, error) {
|
||||
// Submit accept request.
|
||||
rsan := uint32(unsafe.Sizeof(rawsa[0]))
|
||||
_, err := fd.execIO(o, func(o *operation) (qty uint32, err error) {
|
||||
_, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||
err = AcceptFunc(fd.Sysfd, s, (*byte)(unsafe.Pointer(&rawsa[0])), 0, rsan, rsan, &qty, &o.o)
|
||||
return qty, err
|
||||
|
||||
|
|
@ -1124,7 +1139,6 @@ func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle,
|
|||
}
|
||||
defer fd.readUnlock()
|
||||
|
||||
o := &fd.rop
|
||||
var rawsa [2]syscall.RawSockaddrAny
|
||||
for {
|
||||
s, err := sysSocket()
|
||||
|
|
@ -1132,7 +1146,7 @@ func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle,
|
|||
return syscall.InvalidHandle, nil, 0, "", err
|
||||
}
|
||||
|
||||
errcall, err := fd.acceptOne(s, rawsa[:], o)
|
||||
errcall, err := fd.acceptOne(s, rawsa[:])
|
||||
if err == nil {
|
||||
return s, rawsa[:], uint32(unsafe.Sizeof(rawsa[0])), "", nil
|
||||
}
|
||||
|
|
@ -1165,11 +1179,29 @@ func (fd *FD) Seek(offset int64, whence int) (int64, error) {
|
|||
}
|
||||
defer fd.readWriteUnlock()
|
||||
|
||||
if !fd.isBlocking && whence == io.SeekCurrent {
|
||||
// Windows doesn't keep the file pointer for overlapped file handles.
|
||||
// We do it ourselves in case to account for any read or write
|
||||
// operations that may have occurred.
|
||||
offset += fd.offset
|
||||
if !fd.isBlocking {
|
||||
// Windows doesn't use the file pointer for overlapped file handles,
|
||||
// there is no point on calling syscall.Seek.
|
||||
var newOffset int64
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
newOffset = offset
|
||||
case io.SeekCurrent:
|
||||
newOffset = fd.offset + offset
|
||||
case io.SeekEnd:
|
||||
var size int64
|
||||
if err := windows.GetFileSizeEx(fd.Sysfd, &size); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
newOffset = size + offset
|
||||
default:
|
||||
return 0, windows.ERROR_INVALID_PARAMETER
|
||||
}
|
||||
if newOffset < 0 {
|
||||
return 0, windows.ERROR_NEGATIVE_SEEK
|
||||
}
|
||||
fd.setOffset(newOffset)
|
||||
return newOffset, nil
|
||||
}
|
||||
n, err := syscall.Seek(fd.Sysfd, offset, whence)
|
||||
fd.setOffset(n)
|
||||
|
|
@ -1242,14 +1274,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('r', 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 {
|
||||
|
|
@ -1337,18 +1367,16 @@ func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.S
|
|||
p = p[:maxRW]
|
||||
}
|
||||
|
||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||
defer wsaRsaPool.Put(rsa)
|
||||
msg := newWSAMsg(p, oob, flags, rsa)
|
||||
msg := newWSAMsg(p, oob, flags, true)
|
||||
defer freeWSAMsg(msg)
|
||||
n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) {
|
||||
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
err = fd.eofError(n, err)
|
||||
var sa syscall.Sockaddr
|
||||
if err == nil {
|
||||
sa, err = rsa.Sockaddr()
|
||||
sa, err = msg.Name.Sockaddr()
|
||||
}
|
||||
return n, int(msg.Control.Len), int(msg.Flags), sa, err
|
||||
}
|
||||
|
|
@ -1364,17 +1392,15 @@ func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.Sockadd
|
|||
p = p[:maxRW]
|
||||
}
|
||||
|
||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||
defer wsaRsaPool.Put(rsa)
|
||||
msg := newWSAMsg(p, oob, flags, rsa)
|
||||
msg := newWSAMsg(p, oob, flags, true)
|
||||
defer freeWSAMsg(msg)
|
||||
n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) {
|
||||
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
err = fd.eofError(n, err)
|
||||
if err == nil {
|
||||
rawToSockaddrInet4(rsa, sa4)
|
||||
rawToSockaddrInet4(msg.Name, sa4)
|
||||
}
|
||||
return n, int(msg.Control.Len), int(msg.Flags), err
|
||||
}
|
||||
|
|
@ -1390,17 +1416,15 @@ func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.Sockadd
|
|||
p = p[:maxRW]
|
||||
}
|
||||
|
||||
rsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||
defer wsaRsaPool.Put(rsa)
|
||||
msg := newWSAMsg(p, oob, flags, rsa)
|
||||
msg := newWSAMsg(p, oob, flags, true)
|
||||
defer freeWSAMsg(msg)
|
||||
n, err := fd.execIO(&fd.rop, func(o *operation) (qty uint32, err error) {
|
||||
n, err := fd.execIO('r', func(o *operation) (qty uint32, err error) {
|
||||
err = windows.WSARecvMsg(fd.Sysfd, msg, &qty, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
err = fd.eofError(n, err)
|
||||
if err == nil {
|
||||
rawToSockaddrInet6(rsa, sa6)
|
||||
rawToSockaddrInet6(msg.Name, sa6)
|
||||
}
|
||||
return n, int(msg.Control.Len), int(msg.Flags), err
|
||||
}
|
||||
|
|
@ -1416,21 +1440,16 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err
|
|||
}
|
||||
defer fd.writeUnlock()
|
||||
|
||||
var rsa *syscall.RawSockaddrAny
|
||||
if sa != nil {
|
||||
rsa = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||
defer wsaRsaPool.Put(rsa)
|
||||
}
|
||||
msg := newWSAMsg(p, oob, 0, rsa)
|
||||
msg := newWSAMsg(p, oob, 0, sa != nil)
|
||||
defer freeWSAMsg(msg)
|
||||
if sa != nil {
|
||||
var err error
|
||||
msg.Namelen, err = sockaddrToRaw(rsa, sa)
|
||||
msg.Namelen, err = sockaddrToRaw(msg.Name, sa)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
||||
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
|
|
@ -1448,17 +1467,12 @@ func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (in
|
|||
}
|
||||
defer fd.writeUnlock()
|
||||
|
||||
var rsa *syscall.RawSockaddrAny
|
||||
if sa != nil {
|
||||
rsa = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||
defer wsaRsaPool.Put(rsa)
|
||||
}
|
||||
msg := newWSAMsg(p, oob, 0, rsa)
|
||||
msg := newWSAMsg(p, oob, 0, sa != nil)
|
||||
defer freeWSAMsg(msg)
|
||||
if sa != nil {
|
||||
msg.Namelen = sockaddrInet4ToRaw(rsa, sa)
|
||||
msg.Namelen = sockaddrInet4ToRaw(msg.Name, sa)
|
||||
}
|
||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
||||
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
|
|
@ -1476,17 +1490,12 @@ func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (in
|
|||
}
|
||||
defer fd.writeUnlock()
|
||||
|
||||
var rsa *syscall.RawSockaddrAny
|
||||
if sa != nil {
|
||||
rsa = wsaRsaPool.Get().(*syscall.RawSockaddrAny)
|
||||
defer wsaRsaPool.Put(rsa)
|
||||
}
|
||||
msg := newWSAMsg(p, oob, 0, rsa)
|
||||
msg := newWSAMsg(p, oob, 0, sa != nil)
|
||||
defer freeWSAMsg(msg)
|
||||
if sa != nil {
|
||||
msg.Namelen = sockaddrInet6ToRaw(rsa, sa)
|
||||
msg.Namelen = sockaddrInet6ToRaw(msg.Name, sa)
|
||||
}
|
||||
n, err := fd.execIO(&fd.wop, func(o *operation) (qty uint32, err error) {
|
||||
n, err := fd.execIO('w', func(o *operation) (qty uint32, err error) {
|
||||
err = windows.WSASendMsg(fd.Sysfd, msg, 0, nil, &o.o, nil)
|
||||
return qty, err
|
||||
})
|
||||
|
|
|
|||
|
|
@ -62,18 +62,14 @@ func SendFile(fd *FD, src uintptr, size int64) (written int64, err error, handle
|
|||
// See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
|
||||
const maxChunkSizePerCall = int64(0x7fffffff - 1)
|
||||
|
||||
o := &fd.wop
|
||||
for size > 0 {
|
||||
chunkSize := maxChunkSizePerCall
|
||||
if chunkSize > size {
|
||||
chunkSize = size
|
||||
}
|
||||
|
||||
off := startpos + written
|
||||
o.o.Offset = uint32(off)
|
||||
o.o.OffsetHigh = uint32(off >> 32)
|
||||
|
||||
n, err := fd.execIO(o, func(o *operation) (uint32, error) {
|
||||
fd.setOffset(startpos + written)
|
||||
n, err := fd.execIO('w', func(o *operation) (uint32, error) {
|
||||
err := syscall.TransmitFile(fd.Sysfd, hsrc, uint32(chunkSize), 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import "internal/goarch"
|
|||
|
||||
// ObjMask is a bitmap where each bit corresponds to an object in a span.
|
||||
//
|
||||
// It is sized to accomodate all size classes.
|
||||
// It is sized to accommodate all size classes.
|
||||
type ObjMask [MaxObjsPerSpan / (goarch.PtrSize * 8)]uintptr
|
||||
|
||||
// PtrMask is a bitmap where each bit represents a pointer-word in a single runtime page.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func ExpandReference(sizeClass int, packed *gc.ObjMask, unpacked *gc.PtrMask) {
|
|||
// Look up the size and derive the number of objects in a span.
|
||||
// We're only concerned with small objects in single-page spans,
|
||||
// and gc.PtrMask enforces this by being statically sized to
|
||||
// accomodate only such spans.
|
||||
// accommodate only such spans.
|
||||
size := uintptr(gc.SizeClassToSize[sizeClass])
|
||||
nObj := uintptr(gc.SizeClassToNPages[sizeClass]) * gc.PageSize / size
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ const (
|
|||
ERROR_NOT_SUPPORTED syscall.Errno = 50
|
||||
ERROR_CALL_NOT_IMPLEMENTED syscall.Errno = 120
|
||||
ERROR_INVALID_NAME syscall.Errno = 123
|
||||
ERROR_NEGATIVE_SEEK syscall.Errno = 131
|
||||
ERROR_LOCK_FAILED syscall.Errno = 167
|
||||
ERROR_IO_INCOMPLETE syscall.Errno = 996
|
||||
ERROR_NO_TOKEN syscall.Errno = 1008
|
||||
|
|
@ -195,6 +196,7 @@ const (
|
|||
//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf unsafe.Pointer, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle
|
||||
//sys VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) = kernel32.VirtualQuery
|
||||
//sys GetTempPath2(buflen uint32, buf *uint16) (n uint32, err error) = GetTempPath2W
|
||||
//sys GetFileSizeEx(handle syscall.Handle, size *int64) (err error) = kernel32.GetFileSizeEx
|
||||
|
||||
const (
|
||||
// flags for CreateToolhelp32Snapshot
|
||||
|
|
@ -259,7 +261,7 @@ var sendRecvMsgFunc struct {
|
|||
}
|
||||
|
||||
type WSAMsg struct {
|
||||
Name syscall.Pointer
|
||||
Name *syscall.RawSockaddrAny
|
||||
Namelen int32
|
||||
Buffers *syscall.WSABuf
|
||||
BufferCount uint32
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ var (
|
|||
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procGetFileSizeEx = modkernel32.NewProc("GetFileSizeEx")
|
||||
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
||||
procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
|
||||
procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW")
|
||||
|
|
@ -326,6 +327,14 @@ func GetFileInformationByHandleEx(handle syscall.Handle, class uint32, info *byt
|
|||
return
|
||||
}
|
||||
|
||||
func GetFileSizeEx(handle syscall.Handle, size *int64) (err error) {
|
||||
r1, _, e1 := syscall.SyscallN(procGetFileSizeEx.Addr(), uintptr(handle), uintptr(unsafe.Pointer(size)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) {
|
||||
r0, _, e1 := syscall.SyscallN(procGetFinalPathNameByHandleW.Addr(), uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags))
|
||||
n = uint32(r0)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,19 @@
|
|||
// A file system can be provided by the host operating system
|
||||
// but also by other packages.
|
||||
//
|
||||
// # Path Names
|
||||
//
|
||||
// The interfaces in this package all operate on the same
|
||||
// path name syntax, regardless of the host operating system.
|
||||
//
|
||||
// Path names are UTF-8-encoded,
|
||||
// unrooted, slash-separated sequences of path elements, like “x/y/z”.
|
||||
// Path names must not contain an element that is “.” or “..” or the empty string,
|
||||
// except for the special case that the name "." may be used for the root directory.
|
||||
// Paths must not start or end with a slash: “/x” and “x/” are invalid.
|
||||
//
|
||||
// # Testing
|
||||
//
|
||||
// See the [testing/fstest] package for support with testing
|
||||
// implementations of file systems.
|
||||
package fs
|
||||
|
|
@ -41,16 +54,13 @@ type FS interface {
|
|||
// ValidPath reports whether the given path name
|
||||
// is valid for use in a call to Open.
|
||||
//
|
||||
// Path names passed to open are UTF-8-encoded,
|
||||
// unrooted, slash-separated sequences of path elements, like “x/y/z”.
|
||||
// Path names must not contain an element that is “.” or “..” or the empty string,
|
||||
// except for the special case that the name "." may be used for the root directory.
|
||||
// Paths must not start or end with a slash: “/x” and “x/” are invalid.
|
||||
//
|
||||
// Note that paths are slash-separated on all systems, even Windows.
|
||||
// Paths containing other characters such as backslash and colon
|
||||
// are accepted as valid, but those characters must never be
|
||||
// interpreted by an [FS] implementation as path element separators.
|
||||
// See the [Path Names] section for more details.
|
||||
//
|
||||
// [Path Names]: https://pkg.go.dev/io/fs#hdr-Path_Names
|
||||
func ValidPath(name string) bool {
|
||||
if !utf8.ValidString(name) {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -98,24 +98,32 @@ func FormatMediaType(t string, param map[string]string) string {
|
|||
func checkMediaTypeDisposition(s string) error {
|
||||
typ, rest := consumeToken(s)
|
||||
if typ == "" {
|
||||
return errors.New("mime: no media type")
|
||||
return errNoMediaType
|
||||
}
|
||||
if rest == "" {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasPrefix(rest, "/") {
|
||||
return errors.New("mime: expected slash after first token")
|
||||
var ok bool
|
||||
if rest, ok = strings.CutPrefix(rest, "/"); !ok {
|
||||
return errNoSlashAfterFirstToken
|
||||
}
|
||||
subtype, rest := consumeToken(rest[1:])
|
||||
subtype, rest := consumeToken(rest)
|
||||
if subtype == "" {
|
||||
return errors.New("mime: expected token after slash")
|
||||
return errNoTokenAfterSlash
|
||||
}
|
||||
if rest != "" {
|
||||
return errors.New("mime: unexpected content after media subtype")
|
||||
return errUnexpectedContentAfterMediaSubtype
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
errNoMediaType = errors.New("mime: no media type")
|
||||
errNoSlashAfterFirstToken = errors.New("mime: expected slash after first token")
|
||||
errNoTokenAfterSlash = errors.New("mime: expected token after slash")
|
||||
errUnexpectedContentAfterMediaSubtype = errors.New("mime: unexpected content after media subtype")
|
||||
)
|
||||
|
||||
// ErrInvalidMediaParameter is returned by [ParseMediaType] if
|
||||
// the media type value was found but there was an error parsing
|
||||
// the optional parameters
|
||||
|
|
@ -169,7 +177,6 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
|
|||
if continuation == nil {
|
||||
continuation = make(map[string]map[string]string)
|
||||
}
|
||||
var ok bool
|
||||
if pmap, ok = continuation[baseName]; !ok {
|
||||
continuation[baseName] = make(map[string]string)
|
||||
pmap = continuation[baseName]
|
||||
|
|
@ -177,7 +184,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
|
|||
}
|
||||
if v, exists := pmap[key]; exists && v != value {
|
||||
// Duplicate parameter names are incorrect, but we allow them if they are equal.
|
||||
return "", nil, errors.New("mime: duplicate parameter name")
|
||||
return "", nil, errDuplicateParamName
|
||||
}
|
||||
pmap[key] = value
|
||||
v = rest
|
||||
|
|
@ -227,27 +234,28 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
|
|||
return
|
||||
}
|
||||
|
||||
var errDuplicateParamName = errors.New("mime: duplicate parameter name")
|
||||
|
||||
func decode2231Enc(v string) (string, bool) {
|
||||
sv := strings.SplitN(v, "'", 3)
|
||||
if len(sv) != 3 {
|
||||
charset, v, ok := strings.Cut(v, "'")
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
// TODO: ignoring lang in sv[1] for now. If anybody needs it we'll
|
||||
// TODO: ignoring the language part for now. If anybody needs it, we'll
|
||||
// need to decide how to expose it in the API. But I'm not sure
|
||||
// anybody uses it in practice.
|
||||
charset := strings.ToLower(sv[0])
|
||||
if len(charset) == 0 {
|
||||
_, extOtherVals, ok := strings.Cut(v, "'")
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
if charset != "us-ascii" && charset != "utf-8" {
|
||||
// TODO: unsupported encoding
|
||||
charset = strings.ToLower(charset)
|
||||
switch charset {
|
||||
case "us-ascii", "utf-8":
|
||||
default:
|
||||
// Empty or unsupported encoding.
|
||||
return "", false
|
||||
}
|
||||
encv, err := percentHexUnescape(sv[2])
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
return encv, true
|
||||
return percentHexUnescape(extOtherVals)
|
||||
}
|
||||
|
||||
// consumeToken consumes a token from the beginning of provided
|
||||
|
|
@ -309,11 +317,11 @@ func consumeValue(v string) (value, rest string) {
|
|||
|
||||
func consumeMediaParam(v string) (param, value, rest string) {
|
||||
rest = strings.TrimLeftFunc(v, unicode.IsSpace)
|
||||
if !strings.HasPrefix(rest, ";") {
|
||||
var ok bool
|
||||
if rest, ok = strings.CutPrefix(rest, ";"); !ok {
|
||||
return "", "", v
|
||||
}
|
||||
|
||||
rest = rest[1:] // consume semicolon
|
||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||
param, rest = consumeToken(rest)
|
||||
param = strings.ToLower(param)
|
||||
|
|
@ -322,10 +330,9 @@ func consumeMediaParam(v string) (param, value, rest string) {
|
|||
}
|
||||
|
||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||
if !strings.HasPrefix(rest, "=") {
|
||||
if rest, ok = strings.CutPrefix(rest, "="); !ok {
|
||||
return "", "", v
|
||||
}
|
||||
rest = rest[1:] // consume equals sign
|
||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||
value, rest2 := consumeValue(rest)
|
||||
if value == "" && rest2 == rest {
|
||||
|
|
@ -335,7 +342,7 @@ func consumeMediaParam(v string) (param, value, rest string) {
|
|||
return param, value, rest
|
||||
}
|
||||
|
||||
func percentHexUnescape(s string) (string, error) {
|
||||
func percentHexUnescape(s string) (string, bool) {
|
||||
// Count %, check that they're well-formed.
|
||||
percents := 0
|
||||
for i := 0; i < len(s); {
|
||||
|
|
@ -345,16 +352,12 @@ func percentHexUnescape(s string) (string, error) {
|
|||
}
|
||||
percents++
|
||||
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
|
||||
s = s[i:]
|
||||
if len(s) > 3 {
|
||||
s = s[0:3]
|
||||
}
|
||||
return "", fmt.Errorf("mime: bogus characters after %%: %q", s)
|
||||
return "", false
|
||||
}
|
||||
i += 3
|
||||
}
|
||||
if percents == 0 {
|
||||
return s, nil
|
||||
return s, true
|
||||
}
|
||||
|
||||
t := make([]byte, len(s)-2*percents)
|
||||
|
|
@ -371,7 +374,7 @@ func percentHexUnescape(s string) (string, error) {
|
|||
i++
|
||||
}
|
||||
}
|
||||
return string(t), nil
|
||||
return string(t), true
|
||||
}
|
||||
|
||||
func ishex(c byte) bool {
|
||||
|
|
|
|||
|
|
@ -125,8 +125,20 @@ func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"", "\r", "%0D", "\n", "%0A")
|
||||
|
||||
// escapeQuotes escapes special characters in field parameter values.
|
||||
//
|
||||
// For historical reasons, this uses \ escaping for " and \ characters,
|
||||
// and percent encoding for CR and LF.
|
||||
//
|
||||
// The WhatWG specification for form data encoding suggests that we should
|
||||
// use percent encoding for " (%22), and should not escape \.
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart/form-data-encoding-algorithm
|
||||
//
|
||||
// Empirically, as of the time this comment was written, it is necessary
|
||||
// to escape \ characters or else Chrome (and possibly other browsers) will
|
||||
// interpet the unescaped \ as an escape.
|
||||
func escapeQuotes(s string) string {
|
||||
return quoteEscaper.Replace(s)
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue