Merge branch 'golang:master' into master

This commit is contained in:
Lokesh 2025-05-27 13:59:43 +02:00 committed by GitHub
commit 090372e831
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
370 changed files with 13951 additions and 15831 deletions

4
api/next/43936.txt Normal file
View File

@ -0,0 +1,4 @@
pkg testing, method (*B) Attr(string, string) #43936
pkg testing, method (*F) Attr(string, string) #43936
pkg testing, method (*T) Attr(string, string) #43936
pkg testing, type TB interface, Attr(string, string) #43936

9
api/next/63185.txt Normal file
View File

@ -0,0 +1,9 @@
pkg runtime/trace, func NewFlightRecorder(FlightRecorderConfig) *FlightRecorder #63185
pkg runtime/trace, method (*FlightRecorder) Enabled() bool #63185
pkg runtime/trace, method (*FlightRecorder) Start() error #63185
pkg runtime/trace, method (*FlightRecorder) Stop() #63185
pkg runtime/trace, method (*FlightRecorder) WriteTo(io.Writer) (int64, error) #63185
pkg runtime/trace, type FlightRecorder struct #63185
pkg runtime/trace, type FlightRecorderConfig struct #63185
pkg runtime/trace, type FlightRecorderConfig struct, MaxBytes uint64 #63185
pkg runtime/trace, type FlightRecorderConfig struct, MinAge time.Duration #63185

View File

@ -1 +1 @@
pkg sync, method (*WaitGroup) Go(func()) #63769
pkg sync, method (*WaitGroup) Go(func()) #63796

4
api/next/63963.txt Normal file
View File

@ -0,0 +1,4 @@
pkg crypto/ecdsa, func ParseRawPrivateKey(elliptic.Curve, []uint8) (*PrivateKey, error) #63963
pkg crypto/ecdsa, func ParseUncompressedPublicKey(elliptic.Curve, []uint8) (*PublicKey, error) #63963
pkg crypto/ecdsa, method (*PrivateKey) Bytes() ([]uint8, error) #63963
pkg crypto/ecdsa, method (*PublicKey) Bytes() ([]uint8, error) #63963

1
api/next/66365.txt Normal file
View File

@ -0,0 +1 @@
pkg log/slog, func GroupAttrs(string, ...Attr) Attr #66365

View File

@ -3,6 +3,8 @@ pkg os, method (*Root) Chown(string, int, int) error #67002
pkg os, method (*Root) Chtimes(string, time.Time, time.Time) error #67002
pkg os, method (*Root) Lchown(string, int, int) error #67002
pkg os, method (*Root) Link(string, string) error #67002
pkg os, method (*Root) MkdirAll(string, fs.FileMode) error #67002
pkg os, method (*Root) Readlink(string) (string, error) #67002
pkg os, method (*Root) RemoveAll(string) error #67002
pkg os, method (*Root) Rename(string, string) error #67002
pkg os, method (*Root) Symlink(string, string) error #67002

5
api/next/69518.txt Normal file
View File

@ -0,0 +1,5 @@
pkg hash, type XOF interface { BlockSize, Read, Reset, Write } #69518
pkg hash, type XOF interface, BlockSize() int #69518
pkg hash, type XOF interface, Read([]uint8) (int, error) #69518
pkg hash, type XOF interface, Reset() #69518
pkg hash, type XOF interface, Write([]uint8) (int, error) #69518

9
api/next/69521.txt Normal file
View File

@ -0,0 +1,9 @@
pkg crypto/sha3, method (*SHA3) Clone() (hash.Cloner, error) #69521
pkg hash, type Cloner interface { BlockSize, Clone, Reset, Size, Sum, Write } #69521
pkg hash, type Cloner interface, BlockSize() int #69521
pkg hash, type Cloner interface, Clone() (Cloner, error) #69521
pkg hash, type Cloner interface, Reset() #69521
pkg hash, type Cloner interface, Size() int #69521
pkg hash, type Cloner interface, Sum([]uint8) []uint8 #69521
pkg hash, type Cloner interface, Write([]uint8) (int, error) #69521
pkg hash/maphash, method (*Hash) Clone() (hash.Cloner, error) #69521

1
api/next/71920.txt Normal file
View File

@ -0,0 +1 @@
pkg crypto/tls, type Config struct, GetEncryptedClientHelloKeys func(*ClientHelloInfo) ([]EncryptedClientHelloKey, error) #71920

2
api/next/73126.txt Normal file
View File

@ -0,0 +1,2 @@
pkg os, method (*Root) ReadFile(string) ([]uint8, error) #73126
pkg os, method (*Root) WriteFile(string, []uint8, fs.FileMode) error #73126

1
api/next/73193.txt Normal file
View File

@ -0,0 +1 @@
pkg runtime, func SetDefaultGOMAXPROCS() #73193

7
api/next/73626.txt Normal file
View File

@ -0,0 +1,7 @@
pkg net/http, func NewCrossOriginProtection() *CrossOriginProtection #73626
pkg net/http, method (*CrossOriginProtection) AddInsecureBypassPattern(string) #73626
pkg net/http, method (*CrossOriginProtection) AddTrustedOrigin(string) error #73626
pkg net/http, method (*CrossOriginProtection) Check(*Request) error #73626
pkg net/http, method (*CrossOriginProtection) Handler(Handler) Handler #73626
pkg net/http, method (*CrossOriginProtection) SetDenyHandler(Handler) #73626
pkg net/http, type CrossOriginProtection struct #73626

View File

@ -169,6 +169,23 @@ Go command will follow symlinks to regular files embedding files.
The default value `embedfollowsymlinks=0` does not allow following
symlinks. `embedfollowsymlinks=1` will allow following symlinks.
Go 1.25 added a new `containermaxprocs` setting that controls whether the Go
runtime will consider cgroup CPU limits when setting the default GOMAXPROCS.
The default value `containermaxprocs=1` will use cgroup limits in addition to
the total logical CPU count and CPU affinity. `containermaxprocs=0` will
disable consideration of cgroup limits. This setting only affects Linux.
Go 1.25 added a new `updatemaxprocs` setting that controls whether the Go
runtime will periodically update GOMAXPROCS for new CPU affinity or cgroup
limits. The default value `updatemaxprocs=1` will enable periodic updates.
`updatemaxprocs=0` will disable periodic updates.
Go 1.25 disabled SHA-1 signature algorithms in TLS 1.2 according to RFC 9155.
The default can be reverted using the `tlssha1=1` setting.
Go 1.25 switched to SHA-256 to fill in missing SubjectKeyId in
crypto/x509.CreateCertificate. The setting `x509sha256skid=0` reverts to SHA-1.
Go 1.25 corrected the semantics of contention reports for runtime-internal locks,
and so removed the [`runtimecontentionstacks` setting](/pkg/runtime#hdr-Environment_Variable).

View File

@ -17,6 +17,31 @@ This program will now print:
panic: PANIC [recovered, repanicked]
<!-- go.dev/issue/73193 -->
The default behavior of the `GOMAXPROCS` has changed. In prior versions of Go,
`GOMAXPROCS` defaults to the number of logical CPUs available at startup
([runtime.NumCPU]). Go 1.25 introduces two changes:
1. On Linux, the runtime considers the CPU bandwidth limit of the cgroup
containing the process, if any. If the CPU bandwidth limit is lower than the
number of logical CPUs available, `GOMAXPROCS` will default to the lower
limit. In container runtime systems like Kubernetes, cgroup CPU bandwidth
limits generally correspond to the "CPU limit" option. The Go runtime does
not consider the "CPU requests" option.
2. On all OSes, the runtime periodically updates `GOMAXPROCS` if the number
of logical CPUs available or the cgroup CPU bandwidth limit change.
Both of these behaviors are automatically disabled if `GOMAXPROCS` is set
manually via the `GOMAXPROCS` environment variable or a call to
[runtime.GOMAXPROCS]. They can also be disabled explicitly with the [GODEBUG
settings](/doc/godebug) `containermaxprocs=0` and `updatemaxprocs=0`,
respectively.
In order to support reading updated cgroup limits, the runtime will keep cached
file descriptors for the cgroup files for the duration of the process lifetime.
<!-- go.dev/issue/71546 -->
On Linux systems with kernel support for anonymous VMA names

View File

@ -38,6 +38,18 @@ successfully in Go 1.25. If this change is affecting your code, the solution is
the non-nil error check earlier in your code, preferably immediately after
the error-generating statement.
<!-- CLs 653856, 657937, 663795, 664299 -->
The compiler can now allocate the backing store for slices on the
stack in more situations, which improves performance. This change has
the potential to amplify the effects of incorrect
[unsafe.Pointer](/pkg/unsafe#Pointer) usage, see for example [issue
73199](/issue/73199). In order to track down these problems, the
[bisect tool](https://pkg.go.dev/golang.org/x/tools/cmd/bisect) can be
used to find the allocation causing trouble using the
`-compile=variablemake` flag. All such new stack allocations can also
be turned off using `-gcflags=all=-d=variablemakehash=n`.
## Assembler {#assembler}
## Linker {#linker}

View File

@ -1,5 +1,6 @@
### New testing/synctest package
<!-- go.dev/issue/67434, go.dev/issue/73567 -->
The new [testing/synctest](/pkg/testing/synctest) package
provides support for testing concurrent code.

View File

@ -0,0 +1,3 @@
The new [ParseRawPrivateKey], [ParseUncompressedPublicKey], [PrivateKey.Bytes],
and [PublicKey.Bytes] functions and methods implement low-level encodings,
replacing the need to use crypto/elliptic or math/big functions and methods.

View File

@ -0,0 +1 @@
The new [SHA3.Clone] method implements [hash.Cloner](/pkg/hash#Cloner).

View File

@ -0,0 +1,3 @@
The new [Config.GetEncryptedClientHelloKeys] callback can be used to set the
[EncryptedClientHelloKey]s for a server to use when a client sends an Encrypted
Client Hello extension.

View File

@ -0,0 +1,3 @@
SHA-1 signature algorithms are now disallowed in TLS 1.2 handshakes, per
[RFC 9155](https://www.rfc-editor.org/rfc/rfc9155.html).
They can be re-enabled with the `tlssha1=1` GODEBUG option.

View File

@ -0,0 +1 @@
TLS servers now prefer the highest supported protocol version, even if it isn't the client's most preferred protocol version.

View File

@ -0,0 +1,2 @@
[CreateCertificate] now uses truncated SHA-256 to populate the `SubjectKeyId` if
it is missing. The GODEBUG setting `x509sha256skid=0` reverts to SHA-1.

View File

@ -0,0 +1,3 @@
The new [XOF](/pkg/hash#XOF) interface can be implemented by "extendable output
functions", which are hash functions with arbitrary or unlimited output length
such as [SHAKE](https://pkg.go.dev/crypto/sha3#SHAKE).

View File

@ -0,0 +1,2 @@
Hashes implementing the new [Cloner] interface can return a copy of their state.
All standard library [Hash] implementations now implement [Cloner].

View File

@ -0,0 +1 @@
The new [Hash.Clone] method implements [hash.Cloner](/pkg/hash#Cloner).

View File

@ -0,0 +1 @@
[GroupAttrs] creates a group [Attr] from a slice of [Attr] values.

View File

@ -0,0 +1,7 @@
The new [CrossOriginProtection] implements protections against [Cross-Site
Request Forgery (CSRF)][] by rejecting non-safe cross-origin browser requests.
It uses [modern browser Fetch metadata][Sec-Fetch-Site], doesn't require tokens
or cookies, and supports origin-based and pattern-based bypasses.
[Sec-Fetch-Site]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site
[Cross-Site Request Forgery (CSRF)]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF

View File

@ -5,6 +5,10 @@ The [os.Root] type supports the following additional methods:
* [os.Root.Chtimes]
* [os.Root.Lchown]
* [os.Root.Link]
* [os.Root.MkdirAll]
* [os.Root.ReadFile]
* [os.Root.Readlink]
* [os.Root.RemoveAll]
* [os.Root.Rename]
* [os.Root.Symlink]
* [os.Root.WriteFile]

View File

@ -0,0 +1 @@
<!-- go.dev/issue/73126 is documented as part of 67002 -->

View File

@ -0,0 +1,5 @@
The new [SetDefaultGOMAXPROCS] function sets `GOMAXPROCS` to the runtime
default value, as if the `GOMAXPROCS` environment variable is not set. This is
useful for enabling the [new `GOMAXPROCS` default](#runtime) if it has been
disabled by the `GOMAXPROCS` environment variable or a prior call to
[GOMAXPROCS].

View File

@ -0,0 +1,2 @@
<!-- go.dev/issue/63185 -->
TODO The flight recorder has been added to the runtime/trace package.

View File

@ -0,0 +1,10 @@
The new methods [T.Attr], [B.Attr], and [F.Attr] emit an
attribute to the test log. An attribute is an arbitrary
key and value associated with a test.
For example, in a test named `TestAttr`,
`t.Attr("key", "value")` emits:
```
=== ATTR TestAttr key value
```

202
doc/next/9-todo.md Normal file
View File

@ -0,0 +1,202 @@
<!--
Output from relnote todo that was generated and reviewed on 2025-05-23, plus summary info from bug/CL: -->
<!-- TODO: CL 420114 has a RELNOTE comment without a suggested text (from RELNOTE comment in https://go.dev/cl/420114) -->
all: implement plugin build mode for riscv64
<!-- TODO: CL 660996 has a RELNOTE comment without a suggested text (from RELNOTE comment in https://go.dev/cl/660996) -->
cmd/link/internal/ld: introduce -funcalign=N option
This patch adds linker option -funcalign=N that allows to set alignment
for function entries.
For \#72130.
<!-- TODO: accepted proposal https://go.dev/issue/32816 (from https://go.dev/cl/645155, https://go.dev/cl/645455, https://go.dev/cl/645955, https://go.dev/cl/646255, https://go.dev/cl/646455, https://go.dev/cl/646495, https://go.dev/cl/646655, https://go.dev/cl/646875, https://go.dev/cl/647298, https://go.dev/cl/647299, https://go.dev/cl/647736, https://go.dev/cl/648581, https://go.dev/cl/648715, https://go.dev/cl/648976, https://go.dev/cl/648995, https://go.dev/cl/649055, https://go.dev/cl/649056, https://go.dev/cl/649057, https://go.dev/cl/649456, https://go.dev/cl/649476, https://go.dev/cl/650755, https://go.dev/cl/651615, https://go.dev/cl/651617, https://go.dev/cl/651655, https://go.dev/cl/653436) -->
cmd/fix: automate migrations for simple deprecations
<!-- TODO: accepted proposal https://go.dev/issue/34055 (from https://go.dev/cl/625577) -->
cmd/go: allow serving module under the subdirectory of git repository
cmd/go: add subdirectory support to go-import meta tag
This CL adds ability to specify a subdirectory in the go-import meta tag.
A go-import meta tag now will support:
\<meta name="go-import" content="root-path vcs repo-url subdir">
Fixes: \#34055
<!-- TODO: accepted proposal https://go.dev/issue/42965 (from https://go.dev/cl/643355, https://go.dev/cl/670656, https://go.dev/cl/670975, https://go.dev/cl/674076) -->
cmd/go: add global ignore mechanism for Go tooling ecosystem
<!-- TODO: accepted proposal https://go.dev/issue/51430 (from https://go.dev/cl/644997, https://go.dev/cl/646355) -->
cmd/cover: extend coverage testing to include applications
<!-- TODO: accepted proposal https://go.dev/issue/60905 (from https://go.dev/cl/645795) -->
all: add GOARM64=v8.1 and so on
runtime: check LSE support on ARM64 at runtime init
Check presence of LSE support on ARM64 chip if we targeted it at compile
time.
Related to \#69124
Updates \#60905
Fixes \#71411
<!-- TODO: accepted proposal https://go.dev/issue/61476 (from https://go.dev/cl/633417) -->
all: add GORISCV64 environment variable
cmd/go: add rva23u64 as a valid value for GORISCV64
The RVA23 profile was ratified on the 21st of October 2024.
https://riscv.org/announcements/2024/10/risc-v-announces-ratification-of-the-rva23-profile-standard/
Now that it's ratified we can add rva23u64 as a valid value for the
GORISCV64 environment variable. This will allow the compiler and
assembler to generate instructions made mandatory by the new profile
without a runtime check. Examples of such instructions include those
introduced by the Vector and Zicond extensions.
Setting GORISCV64=rva23u64 defines the riscv64.rva20u64,
riscv64.rva22u64 and riscv64.rva23u64 build tags, sets the internal
variable buildcfg.GORISCV64 to 23 and defines the macros
GORISCV64_rva23u64, hasV, hasZba, hasZbb, hasZbs, hasZfa, and
hasZicond for use in assembly language code.
Updates \#61476
<!-- TODO: accepted proposal https://go.dev/issue/61716 (from https://go.dev/cl/644475) -->
math/rand/v2: revised API for math/rand
rand: deprecate in favor of math/rand/v2
For golang/go#61716
Fixes golang/go#71373
<!-- TODO: accepted proposal https://go.dev/issue/64876 (from https://go.dev/cl/649435) -->
cmd/go: enable GOCACHEPROG by default
cmd/go/internal/cacheprog: drop Request.ObjectID
ObjectID was a misnaming of OutputID from cacheprog's initial
implementation. It was maintained for compatibility with existing
cacheprog users in 1.24 but can be removed in 1.25.
<!-- TODO: accepted proposal https://go.dev/issue/68106 (from https://go.dev/cl/628175, https://go.dev/cl/674158, https://go.dev/cl/674436, https://go.dev/cl/674437, https://go.dev/cl/674555, https://go.dev/cl/674556, https://go.dev/cl/674575, https://go.dev/cl/675075, https://go.dev/cl/675076, https://go.dev/cl/675155, https://go.dev/cl/675235) -->
cmd/go: doc -http should start a pkgsite instance and open a browser
<!-- TODO: accepted proposal https://go.dev/issue/69712 (from https://go.dev/cl/619955) -->
cmd/go: -json flag for go version -m
cmd/go: support -json flag in go version
It supports features described in the issue:
- add -json flag for 'go version -m' to print json encoding of
runtime/debug.BuildSetting to standard output.
- report an error when specifying -json flag without -m.
- print build settings on seperated line for each binary
Fixes \#69712
<!-- TODO: accepted proposal https://go.dev/issue/70123 (from https://go.dev/cl/657116) -->
crypto: mechanism to enable FIPS mode
<!-- TODO: accepted proposal https://go.dev/issue/70128 (from https://go.dev/cl/645716, https://go.dev/cl/647455, https://go.dev/cl/651215, https://go.dev/cl/651256, https://go.dev/cl/652136, https://go.dev/cl/652215, https://go.dev/cl/653095, https://go.dev/cl/653139, https://go.dev/cl/653156, https://go.dev/cl/654395) -->
spec: remove notion of core types
<!-- TODO: accepted proposal https://go.dev/issue/70200 (from https://go.dev/cl/674916) -->
cmd/go: add fips140 module selection mechanism
lib/fips140: set inprocess.txt to v1.0.0
<!-- TODO: accepted proposal https://go.dev/issue/70464 (from https://go.dev/cl/630137) -->
testing: panic in AllocsPerRun during parallel test
testing: panic in AllocsPerRun if parallel tests are running
If other tests are running, AllocsPerRun's result will be inherently flaky.
Saw this with CL 630136 and \#70327.
Proposed in \#70464.
Fixes \#70464.
<!-- TODO: accepted proposal https://go.dev/issue/71845 (from https://go.dev/cl/665796, https://go.dev/cl/666935) -->
encoding/json/v2: add new JSON API behind a GOEXPERIMENT=jsonv2 guard
<!-- TODO: accepted proposal https://go.dev/issue/71867 (from https://go.dev/cl/666476, https://go.dev/cl/666755, https://go.dev/cl/673119, https://go.dev/cl/673696) -->
cmd/go, cmd/distpack: build and run tools that are not necessary for builds as needed and don't include in binary distribution
<!-- Items that don't need to be mentioned in Go 1.25 release notes but are picked up by relnote todo
TODO: accepted proposal https://go.dev/issue/30999 (from https://go.dev/cl/671795)
net: reject leading zeros in IP address parsers
net: don't test with leading 0 in ipv4 addresses
Updates \#30999
Fixes \#73378
TODO: accepted proposal https://go.dev/issue/36532 (from https://go.dev/cl/647555)
testing: reconsider adding Context method to testing.T
database/sql: use t.Context in tests
Replace "context.WithCancel(context.Background())" with "t.Context()".
Updates \#36532
TODO: accepted proposal https://go.dev/issue/48429 (from https://go.dev/cl/648577)
cmd/go: track tool dependencies in go.mod
cmd/go: document -modfile and other flags for 'go tool'
Mention -modfile, -C, -overlay, and -modcacherw in the 'go tool'
documentation. We let a reference to 'go help build' give a pointer to
more detailed information.
The -modfile flag in particular is newly useful with the Go 1.24 support
for user-defined tools with 'go tool'.
Updates \#48429
Updates \#33926
Updates \#71663
Fixes \#71502
TODO: accepted proposal https://go.dev/issue/51572 (from https://go.dev/cl/651996)
cmd/go: add 'unix' build tag but not \*\_unix.go file support
os, syscall: use unix build tag where appropriate
These newly added files may use the unix build tag instead of explitly
listing all unix-like GOOS values.
For \#51572
TODO: accepted proposal https://go.dev/issue/53757 (from https://go.dev/cl/644575)
x/sync/errgroup: propagate panics and Goexits through Wait
errgroup: propagate panic and Goexit through Wait
Recovered panic values are wrapped and saved in Group.
Goexits are detected by a sentinel value set after the given function
returns normally. Wait propagates the first instance of a panic or
Goexit.
According to the runtime.Goexit after the code will not be executed,
with a bool, if f not call runtime.Goexit, is true,
determine whether to propagate runtime.Goexit.
Fixes golang/go#53757
TODO: accepted proposal https://go.dev/issue/54743 (from https://go.dev/cl/532415)
ssh: add server side support for Diffie Hellman Group Exchange
TODO: accepted proposal https://go.dev/issue/57792 (from https://go.dev/cl/649716, https://go.dev/cl/651737)
x/crypto/x509roots: new module
TODO: accepted proposal https://go.dev/issue/58523 (from https://go.dev/cl/538235)
ssh: expose negotiated algorithms
Fixes golang/go#58523
Fixes golang/go#46638
TODO: accepted proposal https://go.dev/issue/61537 (from https://go.dev/cl/531935)
ssh: export supported algorithms
Fixes golang/go#61537
TODO: accepted proposal https://go.dev/issue/61901 (from https://go.dev/cl/647875)
bytes, strings: add iterator forms of existing functions
TODO: accepted proposal https://go.dev/issue/61940 (from https://go.dev/cl/650235)
all: fix links to Go wiki
The Go wiki on GitHub has moved to go.dev in golang/go#61940.
TODO: accepted proposal https://go.dev/issue/64207 (from https://go.dev/cl/647015, https://go.dev/cl/652235)
all: end support for macOS 10.15 in Go 1.23
TODO: accepted proposal https://go.dev/issue/67839 (from https://go.dev/cl/646535)
x/sys/unix: access to ELF auxiliary vector
runtime: adjust comments for auxv getAuxv
github.com/cilium/ebpf no longer accesses getAuxv using linkname but now
uses the golang.org/x/sys/unix.Auxv wrapper introduced in
go.dev/cl/644295.
Also adjust the list of users to include x/sys/unix.
Updates \#67839
Updates \#67401
TODO: accepted proposal https://go.dev/issue/68780 (from https://go.dev/cl/659835)
x/term: support pluggable history
term: support pluggable history
Expose a new History interface that allows replacement of the default
ring buffer to customize what gets added or not; as well as to allow
saving/restoring history on either the default ringbuffer or a custom
replacement.
Fixes golang/go#68780
TODO: accepted proposal https://go.dev/issue/69095 (from https://go.dev/cl/649320, https://go.dev/cl/649321, https://go.dev/cl/649337, https://go.dev/cl/649376, https://go.dev/cl/649377, https://go.dev/cl/649378, https://go.dev/cl/649379, https://go.dev/cl/649380, https://go.dev/cl/649397, https://go.dev/cl/649398, https://go.dev/cl/649419, https://go.dev/cl/649497, https://go.dev/cl/649498, https://go.dev/cl/649618, https://go.dev/cl/649675, https://go.dev/cl/649676, https://go.dev/cl/649677, https://go.dev/cl/649695, https://go.dev/cl/649696, https://go.dev/cl/649697, https://go.dev/cl/649698, https://go.dev/cl/649715, https://go.dev/cl/649717, https://go.dev/cl/649718, https://go.dev/cl/649755, https://go.dev/cl/649775, https://go.dev/cl/649795, https://go.dev/cl/649815, https://go.dev/cl/649835, https://go.dev/cl/651336, https://go.dev/cl/651736, https://go.dev/cl/651737, https://go.dev/cl/658018)
all, x/build/cmd/relui: automate go directive maintenance in golang.org/x repositories
TODO: accepted proposal https://go.dev/issue/70859 (from https://go.dev/cl/666056, https://go.dev/cl/670835, https://go.dev/cl/672015, https://go.dev/cl/672016, https://go.dev/cl/672017)
x/tools/go/ast/inspector: add Cursor, to enable partial and multi-level traversals
-->

View File

@ -0,0 +1 @@
v1.0.0

View File

@ -260,8 +260,11 @@ func (s *Scanner) setErr(err error) {
}
}
// Buffer sets the initial buffer to use when scanning
// Buffer controls memory allocation by the Scanner.
// It sets the initial buffer to use when scanning
// and the maximum size of buffer that may be allocated during scanning.
// The contents of the buffer are ignored.
//
// The maximum token size must be less than the larger of max and cap(buf).
// If max <= cap(buf), [Scanner.Scan] will use this buffer only and do no allocation.
//

View File

@ -40,6 +40,7 @@ func (e *escape) call(ks []hole, call ir.Node) {
var fn *ir.Name
switch call.Op() {
case ir.OCALLFUNC:
// TODO(thepudds): use an ir.ReassignOracle here.
v := ir.StaticValue(call.Fun)
fn = ir.StaticCalleeName(v)
}
@ -83,15 +84,19 @@ func (e *escape) call(ks []hole, call ir.Node) {
argument(e.tagHole(ks, fn, param), arg)
}
// hash/maphash.escapeForHash forces its argument to be on
// the heap, if it contains a non-string pointer. We cannot
// internal/abi.EscapeNonString forces its argument to be on
// the heap, if it contains a non-string pointer.
// This is used in hash/maphash.Comparable, where we cannot
// hash pointers to local variables, as the address of the
// local variable might change on stack growth.
// Strings are okay as the hash depends on only the content,
// not the pointer.
// This is also used in unique.clone, to model the data flow
// edge on the value with strings excluded, because strings
// are cloned (by content).
// The actual call we match is
// hash/maphash.escapeForHash[go.shape.T](dict, go.shape.T)
if fn != nil && fn.Sym().Pkg.Path == "hash/maphash" && strings.HasPrefix(fn.Sym().Name, "escapeForHash[") {
// internal/abi.EscapeNonString[go.shape.T](dict, go.shape.T)
if fn != nil && fn.Sym().Pkg.Path == "internal/abi" && strings.HasPrefix(fn.Sym().Name, "EscapeNonString[") {
ps := fntype.Params()
if len(ps) == 2 && ps[1].Type.IsShape() {
if !hasNonStringPointers(ps[1].Type) {

View File

@ -6,6 +6,8 @@ package escape
import (
"fmt"
"go/constant"
"go/token"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@ -86,8 +88,9 @@ import (
// A batch holds escape analysis state that's shared across an entire
// batch of functions being analyzed at once.
type batch struct {
allLocs []*location
closures []closure
allLocs []*location
closures []closure
reassignOracles map[*ir.Func]*ir.ReassignOracle
heapLoc location
mutatorLoc location
@ -129,6 +132,7 @@ func Batch(fns []*ir.Func, recursive bool) {
b.heapLoc.attrs = attrEscapes | attrPersists | attrMutates | attrCalls
b.mutatorLoc.attrs = attrMutates
b.calleeLoc.attrs = attrCalls
b.reassignOracles = make(map[*ir.Func]*ir.ReassignOracle)
// Construct data-flow graph from syntax trees.
for _, fn := range fns {
@ -154,6 +158,11 @@ func Batch(fns []*ir.Func, recursive bool) {
b.closures = nil
for _, loc := range b.allLocs {
// Try to replace some non-constant expressions with literals.
b.rewriteWithLiterals(loc.n, loc.curfn)
// Check if the node must be heap allocated for certain reasons
// such as OMAKESLICE for a large slice.
if why := HeapAllocReason(loc.n); why != "" {
b.flow(b.heapHole().addr(loc.n, why), loc)
}
@ -515,3 +524,101 @@ func (b *batch) reportLeaks(pos src.XPos, name string, esc leaks, sig *types.Typ
base.WarnfAt(pos, "%v does not escape, mutate, or call", name)
}
}
// rewriteWithLiterals attempts to replace certain non-constant expressions
// within n with a literal if possible.
func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
if n == nil || fn == nil {
return
}
if n.Op() != ir.OMAKESLICE && n.Op() != ir.OCONVIFACE {
return
}
if base.Flag.Cfg.CoverageInfo != nil {
// Avoid altering coverage results.
return
}
// Look up a cached ReassignOracle for the function, lazily computing one if needed.
ro := b.reassignOracle(fn)
if ro == nil {
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
}
switch n.Op() {
case ir.OMAKESLICE:
// Check if we can replace a non-constant argument to make with
// a literal to allow for this slice to be stack allocated if otherwise allowed.
n := n.(*ir.MakeExpr)
r := &n.Cap
if n.Cap == nil {
r = &n.Len
}
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
lit, ok := s.(*ir.BasicLit)
if !ok || lit.Val().Kind() != constant.Int {
base.Fatalf("unexpected BasicLit Kind")
}
if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
*r = lit
}
}
case ir.OCONVIFACE:
// Check if we can replace a non-constant expression in an interface conversion with
// a literal to avoid heap allocating the underlying interface value.
conv := n.(*ir.ConvExpr)
if conv.X.Op() != ir.OLITERAL && !conv.X.Type().IsInterface() {
v := ro.StaticValue(conv.X)
if v != nil && v.Op() == ir.OLITERAL && ir.ValidTypeForConst(conv.X.Type(), v.Val()) {
if base.Debug.EscapeDebug >= 3 {
base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
}
v := v.(*ir.BasicLit)
conv.X = ir.NewBasicLit(conv.X.Pos(), conv.X.Type(), v.Val())
typecheck.Expr(conv)
}
}
}
}
// reassignOracle returns an initialized *ir.ReassignOracle for fn.
// If fn is a closure, it returns the ReassignOracle for the ultimate parent.
//
// A new ReassignOracle is initialized lazily if needed, and the result
// is cached to reduce duplicative work of preparing a ReassignOracle.
func (b *batch) reassignOracle(fn *ir.Func) *ir.ReassignOracle {
if ro, ok := b.reassignOracles[fn]; ok {
return ro // Hit.
}
// For closures, we want the ultimate parent's ReassignOracle,
// so walk up the parent chain, if any.
f := fn
for f.ClosureParent != nil && !f.ClosureParent.IsPackageInit() {
f = f.ClosureParent
}
if f != fn {
// We found a parent.
ro := b.reassignOracles[f]
if ro != nil {
// Hit, via a parent. Before returning, store this ro for the original fn as well.
b.reassignOracles[fn] = ro
return ro
}
}
// Miss. We did not find a ReassignOracle for fn or a parent, so lazily create one.
ro := &ir.ReassignOracle{}
ro.Init(f)
// Cache the answer for the original fn.
b.reassignOracles[fn] = ro
if f != fn {
// Cache for the parent as well.
b.reassignOracles[f] = ro
}
return ro
}

View File

@ -139,35 +139,6 @@ func (l *location) leakTo(sink *location, derefs int) {
l.paramEsc.AddHeap(derefs)
}
// leakTo records that parameter l leaks to sink.
func (b *batch) leakTo(l, sink *location, derefs int) {
if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.hasAttr(attrEscapes) {
if base.Flag.LowerM >= 2 {
fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(sink), derefs)
}
explanation := b.explainPath(sink, l)
if logopt.Enabled() {
var e_curfn *ir.Func // TODO(mdempsky): Fix.
logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(sink), derefs), explanation)
}
}
// If sink is a result parameter that doesn't escape (#44614)
// and we can fit return bits into the escape analysis tag,
// then record as a result leak.
if !sink.hasAttr(attrEscapes) && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
if ri := sink.resultIndex - 1; ri < numEscResults {
// Leak to result parameter.
l.paramEsc.AddResult(ri, derefs)
return
}
}
// Otherwise, record as heap leak.
l.paramEsc.AddHeap(derefs)
}
func (l *location) isName(c ir.Class) bool {
return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
}
@ -241,7 +212,7 @@ func (b *batch) flow(k hole, src *location) {
if base.Flag.LowerM >= 2 || logopt.Enabled() {
pos := base.FmtPos(src.n.Pos())
if base.Flag.LowerM >= 2 {
fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
fmt.Printf("%s: %v escapes to heap in %v:\n", pos, src.n, ir.FuncName(src.curfn))
}
explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
if logopt.Enabled() {

View File

@ -116,7 +116,7 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location))
if b.outlives(root, l) {
if !l.hasAttr(attrEscapes) && (logopt.Enabled() || base.Flag.LowerM >= 2) {
if base.Flag.LowerM >= 2 {
fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
fmt.Printf("%s: %v escapes to heap in %v:\n", base.FmtPos(l.n.Pos()), l.n, ir.FuncName(l.curfn))
}
explanation := b.explainPath(root, l)
if logopt.Enabled() {
@ -146,7 +146,7 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location))
if b.outlives(root, l) {
if !l.hasAttr(attrEscapes) && (logopt.Enabled() || base.Flag.LowerM >= 2) {
if base.Flag.LowerM >= 2 {
fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
fmt.Printf("%s: parameter %v leaks to %s for %v with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), ir.FuncName(l.curfn), derefs)
}
explanation := b.explainPath(root, l)
if logopt.Enabled() {
@ -234,7 +234,7 @@ func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes
}
print := base.Flag.LowerM >= 2
flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
flow := fmt.Sprintf(" flow: %s %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
if print {
fmt.Printf("%s:%s\n", pos, flow)
}

View File

@ -5,12 +5,9 @@
package escape
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"go/constant"
"go/token"
)
func isSliceSelfAssign(dst, src ir.Node) bool {
@ -210,21 +207,9 @@ func HeapAllocReason(n ir.Node) string {
if n.Op() == ir.OMAKESLICE {
n := n.(*ir.MakeExpr)
r := &n.Cap
r := n.Cap
if n.Cap == nil {
r = &n.Len
}
// Try to determine static values of make() calls, to avoid allocating them on the heap.
// We are doing this in escape analysis, so that it happens after inlining and devirtualization.
if s := ir.StaticValue(*r); s.Op() == ir.OLITERAL {
lit, ok := s.(*ir.BasicLit)
if !ok || lit.Val().Kind() != constant.Int {
base.Fatalf("unexpected BasicLit Kind")
}
if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
*r = lit
}
r = n.Len
}
elem := n.Type().Elem()
@ -232,7 +217,7 @@ func HeapAllocReason(n ir.Node) string {
// TODO: stack allocate these? See #65685.
return "zero-sized element"
}
if !ir.IsSmallIntConst(*r) {
if !ir.IsSmallIntConst(r) {
// For non-constant sizes, we do a hybrid approach:
//
// if cap <= K {
@ -249,7 +234,7 @@ func HeapAllocReason(n ir.Node) string {
// Implementation is in ../walk/builtin.go:walkMakeSlice.
return ""
}
if ir.Int64Val(*r) > ir.MaxImplicitStackVarSize/elem.Size() {
if ir.Int64Val(r) > ir.MaxImplicitStackVarSize/elem.Size() {
return "too large for stack"
}
}

View File

@ -454,6 +454,11 @@ opSwitch:
// generate code.
cheap = true
}
if strings.HasPrefix(fn, "EscapeNonString[") {
// internal/abi.EscapeNonString[T] is a compiler intrinsic
// implemented in the escape analysis phase.
cheap = true
}
case "internal/runtime/sys":
switch fn {
case "GetCallerPC", "GetCallerSP":
@ -472,12 +477,6 @@ opSwitch:
case "panicrangestate":
cheap = true
}
case "hash/maphash":
if strings.HasPrefix(fn, "escapeForHash[") {
// hash/maphash.escapeForHash[T] is a compiler intrinsic
// implemented in the escape analysis phase.
cheap = true
}
}
}
// Special case for coverage counter updates; although
@ -801,10 +800,10 @@ func inlineCallCheck(callerfn *ir.Func, call *ir.CallExpr) (bool, bool) {
}
}
// hash/maphash.escapeForHash[T] is a compiler intrinsic implemented
// internal/abi.EscapeNonString[T] is a compiler intrinsic implemented
// in the escape analysis phase.
if fn := ir.StaticCalleeName(call.Fun); fn != nil && fn.Sym().Pkg.Path == "hash/maphash" &&
strings.HasPrefix(fn.Sym().Name, "escapeForHash[") {
if fn := ir.StaticCalleeName(call.Fun); fn != nil && fn.Sym().Pkg.Path == "internal/abi" &&
strings.HasPrefix(fn.Sym().Name, "EscapeNonString[") {
return false, true
}

View File

@ -59,6 +59,7 @@ type symsStruct struct {
Udiv *obj.LSym
WriteBarrier *obj.LSym
Zerobase *obj.LSym
ZeroVal *obj.LSym
ARM64HasATOMICS *obj.LSym
ARMHasVFPv4 *obj.LSym
Loong64HasLAMCAS *obj.LSym

View File

@ -203,16 +203,16 @@ func s15a8(x *[15]int64) [15]int64 {
// escape analysis explanation
want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r0 with derefs=0",`+
`"relatedInformation":[`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y z:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r0 = y:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r0 y:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y.b (dot of pointer)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~r0 = \u0026y.b (assign-pair)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~r0:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 ~r0:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~r0 (return)"}]}`)
})
}

View File

@ -165,6 +165,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpLOONG64OR,
ssa.OpLOONG64XOR,
ssa.OpLOONG64NOR,
ssa.OpLOONG64ANDN,
ssa.OpLOONG64ORN,
ssa.OpLOONG64SLL,
ssa.OpLOONG64SLLV,
ssa.OpLOONG64SRL,

View File

@ -305,6 +305,7 @@ func ForCapture(fn *ir.Func) []VarAndLoop {
as := ir.NewAssignStmt(x.Pos(), z, tz)
as.Def = true
as.SetTypecheck(1)
z.Defn = as
preBody.Append(as)
dclFixups[z] = as

View File

@ -804,6 +804,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpMIPSLoweredPubBarrier:
// SYNC
s.Prog(v.Op.Asm())
case ssa.OpClobber, ssa.OpClobberReg:
// TODO: implement for clobberdead experiment. Nop is ok for now.
default:

View File

@ -813,6 +813,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpMIPS64LoweredPubBarrier:
// SYNC
s.Prog(v.Op.Asm())
case ssa.OpClobber, ssa.OpClobberReg:
// TODO: implement for clobberdead experiment. Nop is ok for now.
default:

View File

@ -49,6 +49,9 @@ type pkgReader struct {
// but bitwise inverted so we can detect if we're missing the entry
// or not.
newindex []index
// indicates whether the data is reading during reshaping.
reshaping bool
}
func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
@ -116,6 +119,10 @@ type reader struct {
// find parameters/results.
funarghack bool
// reshaping is used during reading exprReshape code, preventing
// the reader from shapifying the re-shaped type.
reshaping bool
// methodSym is the name of method's name, if reading a method.
// It's nil if reading a normal function or closure body.
methodSym *types.Sym
@ -762,7 +769,7 @@ func (pr *pkgReader) objIdxMayFail(idx index, implicits, explicits []*types.Type
if hack {
if sym.Def != nil {
name = sym.Def.(*ir.Name)
assert(name.Type() == typ)
assert(types.IdenticalStrict(name.Type(), typ))
return name, nil
}
sym.Def = name
@ -1007,7 +1014,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits
// arguments.
for i, targ := range dict.targs {
basic := r.Bool()
if dict.shaped {
if dict.shaped && !pr.reshaping {
dict.targs[i] = shapify(targ, basic)
}
}
@ -2445,7 +2452,10 @@ func (r *reader) expr() (res ir.Node) {
case exprReshape:
typ := r.typ()
old := r.reshaping
r.reshaping = true
x := r.expr()
r.reshaping = old
if types.IdenticalStrict(x.Type(), typ) {
return x
@ -2568,7 +2578,10 @@ func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) {
info := r.dict.subdicts[idx]
explicits := r.p.typListIdx(info.explicits, r.dict)
old := r.p.reshaping
r.p.reshaping = r.reshaping
baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
r.p.reshaping = old
// TODO(mdempsky): Is there a more robust way to get the
// dictionary pointer type here?

View File

@ -782,6 +782,9 @@
(AND x x) => x
(OR x x) => x
(XOR x x) => (MOVVconst [0])
(ORN x (MOVVconst [-1])) => x
(AND x (NORconst [0] y)) => (ANDN x y)
(OR x (NORconst [0] y)) => (ORN x y)
// Fold negation into subtraction.
(NEGV (SUBV x y)) => (SUBV y x)

View File

@ -221,6 +221,8 @@ func init() {
{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0 | arg1)
{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"}, // ^(arg0 | auxInt)
{name: "ANDN", argLength: 2, reg: gp21, asm: "ANDN"}, // arg0 & ^arg1
{name: "ORN", argLength: 2, reg: gp21, asm: "ORN"}, // arg0 | ^arg1
{name: "FMADDF", argLength: 3, reg: fp31, asm: "FMADDF", commutative: true, typ: "Float32"}, // (arg0 * arg1) + arg2
{name: "FMADDD", argLength: 3, reg: fp31, asm: "FMADDD", commutative: true, typ: "Float64"}, // (arg0 * arg1) + arg2

View File

@ -420,6 +420,9 @@
// Write barrier.
(WB ...) => (LoweredWB ...)
// Publication barrier as intrinsic
(PubBarrier ...) => (LoweredPubBarrier ...)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem)

View File

@ -476,6 +476,9 @@
// Write barrier.
(WB ...) => (LoweredWB ...)
// Publication barrier as intrinsic
(PubBarrier ...) => (LoweredPubBarrier ...)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem)

View File

@ -466,6 +466,9 @@ func init() {
// Returns a pointer to a write barrier buffer in R25.
{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R31"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
// Do data barrier. arg0=memorys
{name: "LoweredPubBarrier", argLength: 1, asm: "SYNC", hasSideEffects: true},
// There are three of these functions so that they can have three different register inputs.
// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
// default registers to match so we don't need to copy registers around unnecessarily.

View File

@ -408,6 +408,9 @@ func init() {
// Returns a pointer to a write barrier buffer in R25.
{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R31"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
// Do data barrier. arg0=memorys
{name: "LoweredPubBarrier", argLength: 1, asm: "SYNC", hasSideEffects: true},
// There are three of these functions so that they can have three different register inputs.
// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
// default registers to match so we don't need to copy registers around unnecessarily.

View File

@ -1188,6 +1188,8 @@
// TODO: more of this
(ADD x (NEG y)) => (SUB x y)
(ADDW x (NEGW y)) => (SUBW x y)
(SUB x (NEG y)) => (ADD x y)
(SUBW x (NEGW y)) => (ADDW x y)
(SUB x x) => (MOVDconst [0])
(SUBW x x) => (MOVDconst [0])
(AND x x) => x
@ -1196,6 +1198,7 @@
(ORW x x) => x
(XOR x x) => (MOVDconst [0])
(XORW x x) => (MOVDconst [0])
(NEG (NEG x)) => x
(NEG (ADDconst [c] (NEG x))) && c != -(1<<31) => (ADDconst [-c] x)
(MOVBZreg (ANDWconst [m] x)) => (MOVWZreg (ANDWconst <typ.UInt32> [int32( uint8(m))] x))
(MOVHZreg (ANDWconst [m] x)) => (MOVWZreg (ANDWconst <typ.UInt32> [int32(uint16(m))] x))

View File

@ -2832,3 +2832,14 @@
&& clobber(sbts)
&& clobber(key)
=> (StaticLECall {f} [argsize] typ_ map_ (StringMake <typ.String> ptr len) mem)
// Similarly to map lookups, also handle unique.Make for strings, which unique.Make will clone.
(StaticLECall {f} [argsize] dict_ key:(SelectN [0] sbts:(StaticLECall {g} _ ptr len mem)) m:(SelectN [1] sbts))
&& isSameCall(f, "unique.Make[go.shape.string]")
&& isSameCall(g, "runtime.slicebytetostring")
&& key.Uses == 1
&& sbts.Uses == 2
&& resetCopy(m, mem)
&& clobber(sbts)
&& clobber(key)
=> (StaticLECall {f} [argsize] dict_ (StringMake <typ.String> ptr len) mem)

View File

@ -581,7 +581,7 @@ func combineStores(root *Value) {
mask := int64(1)<<(8*a[i].size) - 1
s := 8 * (a[i].offset - a[0].offset)
if root.Block.Func.Config.BigEndian {
s = aTotalSize*8 - a[i].size - s
s = (aTotalSize-a[i].size)*8 - s
}
c |= (a[i].store.Args[1].AuxInt & mask) << s
}

View File

@ -1810,6 +1810,8 @@ const (
OpLOONG64XORconst
OpLOONG64NOR
OpLOONG64NORconst
OpLOONG64ANDN
OpLOONG64ORN
OpLOONG64FMADDF
OpLOONG64FMADDD
OpLOONG64FMSUBF
@ -2074,6 +2076,7 @@ const (
OpMIPSLoweredGetCallerSP
OpMIPSLoweredGetCallerPC
OpMIPSLoweredWB
OpMIPSLoweredPubBarrier
OpMIPSLoweredPanicBoundsA
OpMIPSLoweredPanicBoundsB
OpMIPSLoweredPanicBoundsC
@ -2205,6 +2208,7 @@ const (
OpMIPS64LoweredGetCallerSP
OpMIPS64LoweredGetCallerPC
OpMIPS64LoweredWB
OpMIPS64LoweredPubBarrier
OpMIPS64LoweredPanicBoundsA
OpMIPS64LoweredPanicBoundsB
OpMIPS64LoweredPanicBoundsC
@ -24379,6 +24383,34 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "ANDN",
argLen: 2,
asm: loong64.AANDN,
reg: regInfo{
inputs: []inputInfo{
{0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31
{1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31
},
outputs: []outputInfo{
{0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31
},
},
},
{
name: "ORN",
argLen: 2,
asm: loong64.AORN,
reg: regInfo{
inputs: []inputInfo{
{0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31
{1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31
},
outputs: []outputInfo{
{0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31
},
},
},
{
name: "FMADDF",
argLen: 3,
@ -27959,6 +27991,13 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "LoweredPubBarrier",
argLen: 1,
hasSideEffects: true,
asm: mips.ASYNC,
reg: regInfo{},
},
{
name: "LoweredPanicBoundsA",
auxType: auxInt64,
@ -29725,6 +29764,13 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "LoweredPubBarrier",
argLen: 1,
hasSideEffects: true,
asm: mips.ASYNC,
reg: regInfo{},
},
{
name: "LoweredPanicBoundsA",
auxType: auxInt64,

View File

@ -436,6 +436,8 @@ func rewriteValueLOONG64(v *Value) bool {
return rewriteValueLOONG64_OpLOONG64NORconst(v)
case OpLOONG64OR:
return rewriteValueLOONG64_OpLOONG64OR(v)
case OpLOONG64ORN:
return rewriteValueLOONG64_OpLOONG64ORN(v)
case OpLOONG64ORconst:
return rewriteValueLOONG64_OpLOONG64ORconst(v)
case OpLOONG64REMV:
@ -1926,6 +1928,21 @@ func rewriteValueLOONG64_OpLOONG64AND(v *Value) bool {
v.copyOf(x)
return true
}
// match: (AND x (NORconst [0] y))
// result: (ANDN x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
x := v_0
if v_1.Op != OpLOONG64NORconst || auxIntToInt64(v_1.AuxInt) != 0 {
continue
}
y := v_1.Args[0]
v.reset(OpLOONG64ANDN)
v.AddArg2(x, y)
return true
}
break
}
return false
}
func rewriteValueLOONG64_OpLOONG64ANDconst(v *Value) bool {
@ -5583,6 +5600,36 @@ func rewriteValueLOONG64_OpLOONG64OR(v *Value) bool {
v.copyOf(x)
return true
}
// match: (OR x (NORconst [0] y))
// result: (ORN x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
x := v_0
if v_1.Op != OpLOONG64NORconst || auxIntToInt64(v_1.AuxInt) != 0 {
continue
}
y := v_1.Args[0]
v.reset(OpLOONG64ORN)
v.AddArg2(x, y)
return true
}
break
}
return false
}
func rewriteValueLOONG64_OpLOONG64ORN(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (ORN x (MOVVconst [-1]))
// result: x
for {
x := v_0
if v_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_1.AuxInt) != -1 {
break
}
v.copyOf(x)
return true
}
return false
}
func rewriteValueLOONG64_OpLOONG64ORconst(v *Value) bool {

View File

@ -450,6 +450,9 @@ func rewriteValueMIPS(v *Value) bool {
return rewriteValueMIPS_OpPanicBounds(v)
case OpPanicExtend:
return rewriteValueMIPS_OpPanicExtend(v)
case OpPubBarrier:
v.Op = OpMIPSLoweredPubBarrier
return true
case OpRotateLeft16:
return rewriteValueMIPS_OpRotateLeft16(v)
case OpRotateLeft32:

View File

@ -502,6 +502,9 @@ func rewriteValueMIPS64(v *Value) bool {
return true
case OpPanicBounds:
return rewriteValueMIPS64_OpPanicBounds(v)
case OpPubBarrier:
v.Op = OpMIPS64LoweredPubBarrier
return true
case OpRotateLeft16:
return rewriteValueMIPS64_OpRotateLeft16(v)
case OpRotateLeft32:

View File

@ -11292,6 +11292,16 @@ func rewriteValueS390X_OpS390XNEG(v *Value) bool {
v.AuxInt = int64ToAuxInt(-c)
return true
}
// match: (NEG (NEG x))
// result: x
for {
if v_0.Op != OpS390XNEG {
break
}
x := v_0.Args[0]
v.copyOf(x)
return true
}
// match: (NEG (ADDconst [c] (NEG x)))
// cond: c != -(1<<31)
// result: (ADDconst [-c] x)
@ -13326,6 +13336,18 @@ func rewriteValueS390X_OpS390XSUB(v *Value) bool {
v.AddArg(v0)
return true
}
// match: (SUB x (NEG y))
// result: (ADD x y)
for {
x := v_0
if v_1.Op != OpS390XNEG {
break
}
y := v_1.Args[0]
v.reset(OpS390XADD)
v.AddArg2(x, y)
return true
}
// match: (SUB x x)
// result: (MOVDconst [0])
for {
@ -13467,6 +13489,18 @@ func rewriteValueS390X_OpS390XSUBW(v *Value) bool {
v.AddArg(v0)
return true
}
// match: (SUBW x (NEGW y))
// result: (ADDW x y)
for {
x := v_0
if v_1.Op != OpS390XNEGW {
break
}
y := v_1.Args[0]
v.reset(OpS390XADDW)
v.AddArg2(x, y)
return true
}
// match: (SUBW x x)
// result: (MOVDconst [0])
for {

View File

@ -30743,6 +30743,41 @@ func rewriteValuegeneric_OpStaticLECall(v *Value) bool {
v.AddArg4(typ_, map_, v0, mem)
return true
}
// match: (StaticLECall {f} [argsize] dict_ key:(SelectN [0] sbts:(StaticLECall {g} _ ptr len mem)) m:(SelectN [1] sbts))
// cond: isSameCall(f, "unique.Make[go.shape.string]") && isSameCall(g, "runtime.slicebytetostring") && key.Uses == 1 && sbts.Uses == 2 && resetCopy(m, mem) && clobber(sbts) && clobber(key)
// result: (StaticLECall {f} [argsize] dict_ (StringMake <typ.String> ptr len) mem)
for {
if len(v.Args) != 3 {
break
}
argsize := auxIntToInt32(v.AuxInt)
f := auxToCall(v.Aux)
_ = v.Args[2]
dict_ := v.Args[0]
key := v.Args[1]
if key.Op != OpSelectN || auxIntToInt64(key.AuxInt) != 0 {
break
}
sbts := key.Args[0]
if sbts.Op != OpStaticLECall || len(sbts.Args) != 4 {
break
}
g := auxToCall(sbts.Aux)
mem := sbts.Args[3]
ptr := sbts.Args[1]
len := sbts.Args[2]
m := v.Args[2]
if m.Op != OpSelectN || auxIntToInt64(m.AuxInt) != 1 || sbts != m.Args[0] || !(isSameCall(f, "unique.Make[go.shape.string]") && isSameCall(g, "runtime.slicebytetostring") && key.Uses == 1 && sbts.Uses == 2 && resetCopy(m, mem) && clobber(sbts) && clobber(key)) {
break
}
v.reset(OpStaticLECall)
v.AuxInt = int32ToAuxInt(argsize)
v.Aux = callToAux(f)
v0 := b.NewValue0(v.Pos, OpStringMake, typ.String)
v0.AddArg2(ptr, len)
v.AddArg3(dict_, v0, mem)
return true
}
return false
}
func rewriteValuegeneric_OpStore(v *Value) bool {

View File

@ -163,7 +163,7 @@ func initIntrinsics(cfg *intrinsicBuildConfig) {
s.vars[memVar] = s.newValue1(ssa.OpPubBarrier, types.TypeMem, s.mem())
return nil
},
sys.ARM64, sys.Loong64, sys.PPC64, sys.RISCV64)
sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64)
/******** internal/runtime/sys ********/
add("internal/runtime/sys", "GetCallerPC",

View File

@ -554,6 +554,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mips", "math/bits", "TrailingZeros64"}: struct{}{},
{"mips", "math/bits", "TrailingZeros8"}: struct{}{},
{"mips", "runtime", "KeepAlive"}: struct{}{},
{"mips", "runtime", "publicationBarrier"}: struct{}{},
{"mips", "runtime", "slicebytetostringtmp"}: struct{}{},
{"mips", "sync", "runtime_LoadAcquintptr"}: struct{}{},
{"mips", "sync", "runtime_StoreReluintptr"}: struct{}{},
@ -631,6 +632,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mips64", "math/bits", "Sub"}: struct{}{},
{"mips64", "math/bits", "Sub64"}: struct{}{},
{"mips64", "runtime", "KeepAlive"}: struct{}{},
{"mips64", "runtime", "publicationBarrier"}: struct{}{},
{"mips64", "runtime", "slicebytetostringtmp"}: struct{}{},
{"mips64", "sync", "runtime_LoadAcquintptr"}: struct{}{},
{"mips64", "sync", "runtime_StoreReluintptr"}: struct{}{},
@ -718,6 +720,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mips64le", "math/bits", "Sub"}: struct{}{},
{"mips64le", "math/bits", "Sub64"}: struct{}{},
{"mips64le", "runtime", "KeepAlive"}: struct{}{},
{"mips64le", "runtime", "publicationBarrier"}: struct{}{},
{"mips64le", "runtime", "slicebytetostringtmp"}: struct{}{},
{"mips64le", "sync", "runtime_LoadAcquintptr"}: struct{}{},
{"mips64le", "sync", "runtime_StoreReluintptr"}: struct{}{},
@ -797,6 +800,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mipsle", "math/bits", "TrailingZeros64"}: struct{}{},
{"mipsle", "math/bits", "TrailingZeros8"}: struct{}{},
{"mipsle", "runtime", "KeepAlive"}: struct{}{},
{"mipsle", "runtime", "publicationBarrier"}: struct{}{},
{"mipsle", "runtime", "slicebytetostringtmp"}: struct{}{},
{"mipsle", "sync", "runtime_LoadAcquintptr"}: struct{}{},
{"mipsle", "sync", "runtime_StoreReluintptr"}: struct{}{},

View File

@ -166,6 +166,7 @@ func InitConfig() {
ir.Syms.Udiv = typecheck.LookupRuntimeVar("udiv") // asm func with special ABI
ir.Syms.WriteBarrier = typecheck.LookupRuntimeVar("writeBarrier") // struct { bool; ... }
ir.Syms.Zerobase = typecheck.LookupRuntimeVar("zerobase")
ir.Syms.ZeroVal = typecheck.LookupRuntimeVar("zeroVal")
if Arch.LinkArch.Family == sys.Wasm {
BoundsCheckFunc[ssa.BoundsIndex] = typecheck.LookupRuntimeFunc("goPanicIndex")

View File

@ -332,6 +332,7 @@ func TestStdFixed(t *testing.T) {
"issue49814.go", // go/types does not have constraints on array size
"issue56103.go", // anonymous interface cycles; will be a type checker error in 1.22
"issue52697.go", // types2 does not have constraints on stack size
"issue73309.go", // this test requires GODEBUG=gotypesalias=1
// These tests requires runtime/cgo.Incomplete, which is only available on some platforms.
// However, types2 does not know about build constraints.

View File

@ -175,6 +175,11 @@ func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node {
xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
xe.SetBounded(true)
value = xe
case n.Op() == ir.OLINKSYMOFFSET && n.(*ir.LinksymOffsetExpr).Linksym == ir.Syms.ZeroVal && n.(*ir.LinksymOffsetExpr).Offset_ == 0:
// n is using zeroVal, so we can use n directly.
// (Note that n does not have a proper pos in this case, so using conv for the diagnostic instead.)
diagnose("using global for zero value interface value", conv)
value = n
case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
// n is a readonly global; use it directly.
diagnose("using global for interface value", n)

View File

@ -594,8 +594,8 @@ func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
if n.Op() == ir.OCALLFUNC {
fn := ir.StaticCalleeName(n.Fun)
if fn != nil && fn.Sym().Pkg.Path == "hash/maphash" && strings.HasPrefix(fn.Sym().Name, "escapeForHash[") {
// hash/maphash.escapeForHash[T] is a compiler intrinsic
if fn != nil && fn.Sym().Pkg.Path == "internal/abi" && strings.HasPrefix(fn.Sym().Name, "EscapeNonString[") {
// internal/abi.EscapeNonString[T] is a compiler intrinsic
// for the escape analysis to escape its argument based on
// the type. The call itself is no-op. Just walk the
// argument.

View File

@ -7,6 +7,7 @@ package walk
import (
"fmt"
"go/constant"
"internal/abi"
"internal/buildcfg"
"cmd/compile/internal/base"
@ -226,7 +227,8 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node {
// for the implicit conversion of "foo" to any, and we can't handle
// the relocations in that temp.
if n.Op() == ir.ONIL || (n.Op() == ir.OLITERAL && !base.Ctxt.IsFIPS()) {
// TODO: expand this to all static composite literal nodes?
// This is a basic literal or nil that we can store
// directly in the read-only data section.
n = typecheck.DefaultLit(n, nil)
types.CalcSize(n.Type())
vstat := readonlystaticname(n.Type())
@ -239,6 +241,28 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node {
return vstat
}
// Check now for a composite literal to possibly store in the read-only data section.
v := staticValue(n)
if v == nil {
v = n
}
if (v.Op() == ir.OSTRUCTLIT || v.Op() == ir.OARRAYLIT) && !base.Ctxt.IsFIPS() {
if ir.IsZero(v) && 0 < v.Type().Size() && v.Type().Size() <= abi.ZeroValSize {
// This zero value can be represented by the read-only zeroVal.
zeroVal := ir.NewLinksymExpr(v.Pos(), ir.Syms.ZeroVal, v.Type())
vstat := typecheck.Expr(zeroVal).(*ir.LinksymOffsetExpr)
return vstat
}
if isStaticCompositeLiteral(v) {
// v can be directly represented in the read-only data section.
lit := v.(*ir.CompLitExpr)
vstat := readonlystaticname(lit.Type())
fixedlit(inInitFunction, initKindStatic, lit, vstat, nil) // nil init
vstat = typecheck.Expr(vstat).(*ir.Name)
return vstat
}
}
// Prevent taking the address of an SSA-able local variable (#63332).
//
// TODO(mdempsky): Note that OuterValue unwraps OCONVNOPs, but
@ -337,8 +361,8 @@ func (o *orderState) mapKeyTemp(outerPos src.XPos, t *types.Type, n ir.Node) ir.
//
// Note that this code does not handle the case:
//
// s := string(k)
// x = m[s]
// s := string(k)
// x = m[s]
//
// Cases like this are handled during SSA, search for slicebytetostring
// in ../ssa/_gen/generic.rules.

View File

@ -24,6 +24,13 @@ const tmpstringbufsize = 32
func Walk(fn *ir.Func) {
ir.CurFunc = fn
// Set and then clear a package-level cache of static values for this fn.
// (At some point, it might be worthwhile to have a walkState structure
// that gets passed everywhere where things like this can go.)
staticValues = findStaticValues(fn)
defer func() { staticValues = nil }()
errorsBefore := base.Errors()
order(fn)
if base.Errors() > errorsBefore {
@ -422,3 +429,43 @@ func ifaceData(pos src.XPos, n ir.Node, t *types.Type) ir.Node {
ind.SetBounded(true)
return ind
}
// staticValue returns the earliest expression it can find that always
// evaluates to n, with similar semantics to [ir.StaticValue].
//
// It only returns results for the ir.CurFunc being processed in [Walk],
// including its closures, and uses a cache to reduce duplicative work.
// It can return n or nil if it does not find an earlier expression.
//
// The current use case is reducing OCONVIFACE allocations, and hence
// staticValue is currently only useful when given an *ir.ConvExpr.X as n.
func staticValue(n ir.Node) ir.Node {
if staticValues == nil {
base.Fatalf("staticValues is nil. staticValue called outside of walk.Walk?")
}
return staticValues[n]
}
// staticValues is a cache of static values for use by staticValue.
var staticValues map[ir.Node]ir.Node
// findStaticValues returns a map of static values for fn.
func findStaticValues(fn *ir.Func) map[ir.Node]ir.Node {
// We can't use an ir.ReassignOracle or ir.StaticValue in the
// middle of walk because they don't currently handle
// transformed assignments (e.g., will complain about 'RHS == nil').
// So we instead build this map to use in walk.
ro := &ir.ReassignOracle{}
ro.Init(fn)
m := make(map[ir.Node]ir.Node)
ir.Visit(fn, func(n ir.Node) {
if n.Op() == ir.OCONVIFACE {
x := n.(*ir.ConvExpr).X
v := ro.StaticValue(x)
if v != nil && v != x {
m[x] = v
}
}
})
return m
}

View File

@ -122,8 +122,28 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
}
}
if serveHTTP {
// We want to run the logic below to determine a match for a symbol, method,
// or field, but not actually print the documentation to the output.
// Special case: if there are no arguments, try to go to an appropriate page
// depending on whether we're in a module or workspace. The pkgsite homepage
// is often not the most useful page.
if len(flagSet.Args()) == 0 {
mod, err := runCmd(append(os.Environ(), "GOWORK=off"), "go", "list", "-m")
if err == nil && mod != "" && mod != "command-line-arguments" {
// If there's a module, go to the module's doc page.
return doPkgsite(mod)
}
gowork, err := runCmd(nil, "go", "env", "GOWORK")
if err == nil && gowork != "" {
// Outside a module, but in a workspace, go to the home page
// with links to each of the modules' pages.
return doPkgsite("")
}
// Outside a module or workspace, go to the documentation for the standard library.
return doPkgsite("std")
}
// If args are provided, we need to figure out which page to open on the pkgsite
// instance. Run the logic below to determine a match for a symbol, method,
// or field, but don't actually print the documentation to the output.
writer = io.Discard
}
var paths []string
@ -179,44 +199,42 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
}
if found {
if serveHTTP {
return doPkgsite(userPath, pkg, symbol, method)
path, err := objectPath(userPath, pkg, symbol, method)
if err != nil {
return err
}
return doPkgsite(path)
}
return nil
}
}
}
func listUserPath(userPath string) (string, error) {
func runCmd(env []string, cmdline ...string) (string, error) {
var stdout, stderr strings.Builder
cmd := exec.Command("go", "list", userPath)
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Env = env
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("go doc: go list %s: %v\n%s\n", userPath, err, stderr.String())
return "", fmt.Errorf("go doc: %s: %v\n%s\n", strings.Join(cmdline, " "), err, stderr.String())
}
return strings.TrimSpace(stdout.String()), nil
}
func doPkgsite(userPath string, pkg *Package, symbol, method string) error {
port, err := pickUnusedPort()
if err != nil {
return fmt.Errorf("failed to find port for documentation server: %v", err)
}
addr := fmt.Sprintf("localhost:%d", port)
// Assemble url to open on the browser, to point to documentation of
// the requested object.
importPath := pkg.build.ImportPath
if importPath == "." {
func objectPath(userPath string, pkg *Package, symbol, method string) (string, error) {
var err error
path := pkg.build.ImportPath
if path == "." {
// go/build couldn't determine the import path, probably
// because this was a relative path into a module. Use
// go list to get the import path.
importPath, err = listUserPath(userPath)
path, err = runCmd(nil, "go", "list", userPath)
if err != nil {
return err
return "", err
}
}
path := path.Join("http://"+addr, importPath)
object := symbol
if symbol != "" && method != "" {
object = symbol + "." + method
@ -224,17 +242,38 @@ func doPkgsite(userPath string, pkg *Package, symbol, method string) error {
if object != "" {
path = path + "#" + object
}
return path, nil
}
func doPkgsite(urlPath string) error {
port, err := pickUnusedPort()
if err != nil {
return fmt.Errorf("failed to find port for documentation server: %v", err)
}
addr := fmt.Sprintf("localhost:%d", port)
path := path.Join("http://"+addr, urlPath)
// Turn off the default signal handler for SIGINT (and SIGQUIT on Unix)
// and instead wait for the child process to handle the signal and
// exit before exiting ourselves.
signal.Ignore(signalsToIgnore...)
// Prepend the local download cache to GOPROXY to get around deprecation checks.
env := os.Environ()
vars, err := runCmd(nil, "go", "env", "GOPROXY", "GOMODCACHE")
fields := strings.Fields(vars)
if err == nil && len(fields) == 2 {
goproxy, gomodcache := fields[0], fields[1]
goproxy = "file://" + filepath.Join(gomodcache, "cache", "download") + "," + goproxy
env = append(env, "GOPROXY="+goproxy)
}
const version = "v0.0.0-20250520201116-40659211760d"
cmd := exec.Command("go", "run", "golang.org/x/pkgsite/cmd/internal/doc@"+version,
"-gorepo", buildCtx.GOROOT,
"-http", addr,
"-open", path)
cmd.Env = env
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr

View File

@ -191,20 +191,28 @@ func GetExitStatus() int {
// connected to the go command's own stdout and stderr.
// If the command fails, Run reports the error using Errorf.
func Run(cmdargs ...any) {
if err := RunErr(cmdargs...); err != nil {
Errorf("%v", err)
}
}
// Run runs the command, with stdout and stderr
// connected to the go command's own stdout and stderr.
// If the command fails, RunErr returns the error, which
// may be an *exec.ExitError.
func RunErr(cmdargs ...any) error {
cmdline := str.StringList(cmdargs...)
if cfg.BuildN || cfg.BuildX {
fmt.Printf("%s\n", strings.Join(cmdline, " "))
if cfg.BuildN {
return
return nil
}
}
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
Errorf("%v", err)
}
return cmd.Run()
}
// RunStdin is like run but connects Stdin. It retries if it encounters an ETXTBSY.

View File

@ -9,6 +9,9 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"context"
"errors"
"os"
"os/exec"
"path/filepath"
)
@ -131,5 +134,13 @@ Flags:
}
func runDoc(ctx context.Context, cmd *base.Command, args []string) {
base.Run(cfg.BuildToolexec, filepath.Join(cfg.GOROOTbin, "go"), "tool", "doc", args)
base.StartSigHandlers()
err := base.RunErr(cfg.BuildToolexec, filepath.Join(cfg.GOROOTbin, "go"), "tool", "doc", args)
if err != nil {
var ee *exec.ExitError
if errors.As(err, &ee) {
os.Exit(ee.ExitCode())
}
base.Error(err)
}
}

View File

@ -225,8 +225,6 @@ const (
REGZERO = REG_R0 // set to zero
REGLINK = REG_R1
REGSP = REG_R3
REGRET = REG_R20 // not use
REGARG = -1 // -1 disables passing the first argument in register
REGRT1 = REG_R20 // reserved for runtime, duffzero and duffcopy
REGRT2 = REG_R21 // reserved for runtime, duffcopy
REGCTXT = REG_R29 // context for closures

View File

@ -49,6 +49,7 @@ var runtimePkgs = []string{
"runtime",
"internal/runtime/atomic",
"internal/runtime/cgroup",
"internal/runtime/exithook",
"internal/runtime/gc",
"internal/runtime/maps",

View File

@ -36,6 +36,8 @@ type event struct {
Elapsed *float64 `json:",omitempty"`
Output *textBytes `json:",omitempty"`
FailedBuild string `json:",omitempty"`
Key string `json:",omitempty"`
Value string `json:",omitempty"`
}
// textBytes is a hack to get JSON to emit a []byte as a string
@ -177,6 +179,7 @@ var (
[]byte("=== PASS "),
[]byte("=== FAIL "),
[]byte("=== SKIP "),
[]byte("=== ATTR "),
}
reports = [][]byte{
@ -333,6 +336,11 @@ func (c *Converter) handleInputLine(line []byte) {
c.output.write(origLine)
return
}
if action == "attr" {
var rest string
name, rest, _ = strings.Cut(name, " ")
e.Key, e.Value, _ = strings.Cut(rest, " ")
}
// === update.
// Finish any pending PASS/FAIL reports.
c.needMarker = sawMarker

View File

@ -0,0 +1,15 @@
{"Action":"start"}
{"Action":"run","Test":"TestAttr"}
{"Action":"output","Test":"TestAttr","Output":"=== RUN TestAttr\n"}
{"Action":"attr","Test":"TestAttr","Key":"key","Value":"value"}
{"Action":"output","Test":"TestAttr","Output":"=== ATTR TestAttr key value\n"}
{"Action":"run","Test":"TestAttr/sub"}
{"Action":"output","Test":"TestAttr/sub","Output":"=== RUN TestAttr/sub\n"}
{"Action":"attr","Test":"TestAttr/sub","Key":"key","Value":"value"}
{"Action":"output","Test":"TestAttr/sub","Output":"=== ATTR TestAttr/sub key value\n"}
{"Action":"output","Test":"TestAttr","Output":"--- PASS: TestAttr (0.00s)\n"}
{"Action":"output","Test":"TestAttr/sub","Output":" --- PASS: TestAttr/sub (0.00s)\n"}
{"Action":"pass","Test":"TestAttr/sub"}
{"Action":"pass","Test":"TestAttr"}
{"Action":"output","Output":"PASS\n"}
{"Action":"pass"}

View File

@ -0,0 +1,7 @@
=== RUN TestAttr
=== ATTR TestAttr key value
=== RUN TestAttr/sub
=== ATTR TestAttr/sub key value
--- PASS: TestAttr (0.00s)
--- PASS: TestAttr/sub (0.00s)
PASS

View File

@ -430,8 +430,10 @@ func (ctxt *Link) domacho() {
// This must be fairly recent for Apple signing (go.dev/issue/30488).
// Having too old a version here was also implicated in some problems
// calling into macOS libraries (go.dev/issue/56784).
// In general this can be the most recent supported macOS version.
version = 11<<16 | 0<<8 | 0<<0 // 11.0.0
// CL 460476 noted that in general this can be the most recent supported
// macOS version, but we haven't tested if going higher than Go's oldest
// supported macOS version could cause new problems.
version = 12<<16 | 0<<8 | 0<<0 // 12.0.0
}
ml := newMachoLoad(ctxt.Arch, imacho.LC_BUILD_VERSION, 4)
ml.data[0] = uint32(machoPlatform)

View File

@ -195,7 +195,7 @@ func TestIssue33979(t *testing.T) {
testenv.MustHaveCGO(t)
// N.B. go build below explictly doesn't pass through
// -asan/-msan/-race, so we don't care about those.
testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
testenv.MustInternalLink(t, testenv.SpecialBuildTypes{Cgo: true})
t.Parallel()
@ -397,8 +397,8 @@ func TestMachOBuildVersion(t *testing.T) {
found := false
checkMin := func(ver uint32) {
major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
if major < 11 {
t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 11.0.0", major, minor, patch)
if major < 12 {
t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 12.0.0", major, minor, patch)
}
}
for _, cmd := range exem.Loads {

View File

@ -5,13 +5,19 @@
package crypto_test
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"internal/testenv"
"io"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)
@ -88,3 +94,50 @@ UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0
t.Errorf("VerifyPSS failed for MessageSigner signature: %s", err)
}
}
func TestDisallowedAssemblyInstructions(t *testing.T) {
// This test enforces the cryptography assembly policy rule that we do not
// use BYTE or WORD instructions, since these instructions can obscure what
// the assembly is actually doing. If we do not support specific
// instructions in the assembler, we should not be using them until we do.
//
// Instead of using the output of the 'go tool asm' tool, we take the simple
// approach and just search the text of .s files for usage of BYTE and WORD.
// We do this because the assembler itself will sometimes insert WORD
// instructions for things like function preambles etc.
boringSigPath := filepath.Join("internal", "boring", "sig")
matcher, err := regexp.Compile(`(^|;)\s(BYTE|WORD)\s`)
if err != nil {
t.Fatal(err)
}
if err := filepath.WalkDir(filepath.Join(testenv.GOROOT(t), "src/crypto"), func(path string, d fs.DirEntry, err error) error {
if err != nil {
t.Fatal(err)
}
if d.IsDir() || !strings.HasSuffix(path, ".s") {
return nil
}
if strings.Contains(path, boringSigPath) {
return nil
}
f, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
i := 1
for line := range bytes.Lines(f) {
if matcher.Match(line) {
t.Errorf("%s:%d assembly contains BYTE or WORD instruction (%q)", path, i, string(line))
}
i++
}
return nil
}); err != nil {
t.Fatal(err)
}
}

View File

@ -23,6 +23,7 @@ import (
"crypto/internal/boring"
"crypto/internal/boring/bbig"
"crypto/internal/fips140/ecdsa"
"crypto/internal/fips140/nistec"
"crypto/internal/fips140cache"
"crypto/internal/fips140hash"
"crypto/internal/fips140only"
@ -40,6 +41,18 @@ import (
// PublicKey represents an ECDSA public key.
type PublicKey struct {
elliptic.Curve
// X, Y are the coordinates of the public key point.
//
// Modifying the raw coordinates can produce invalid keys, and may
// invalidate internal optimizations; moreover, [big.Int] methods are not
// suitable for operating on cryptographic values. To encode and decode
// PublicKey values, use [PublicKey.Bytes] and [ParseUncompressedPublicKey]
// or [x509.MarshalPKIXPublicKey] and [x509.ParsePKIXPublicKey]. For ECDH,
// use [crypto/ecdh]. For lower-level elliptic curve operations, use a
// third-party module like filippo.io/nistec.
//
// These fields will be deprecated in Go 1.26.
X, Y *big.Int
}
@ -78,9 +91,94 @@ func (pub *PublicKey) Equal(x crypto.PublicKey) bool {
pub.Curve == xx.Curve
}
// ParseUncompressedPublicKey parses a public key encoded as an uncompressed
// point according to SEC 1, Version 2.0, Section 2.3.3 (also known as the X9.62
// uncompressed format). It returns an error if the point is not in uncompressed
// form, is not on the curve, or is the point at infinity.
//
// curve must be one of [elliptic.P224], [elliptic.P256], [elliptic.P384], or
// [elliptic.P521], or ParseUncompressedPublicKey returns an error.
//
// ParseUncompressedPublicKey accepts the same format as
// [ecdh.Curve.NewPublicKey] does for NIST curves, but returns a [PublicKey]
// instead of an [ecdh.PublicKey].
//
// Note that public keys are more commonly encoded in DER (or PEM) format, which
// can be parsed with [x509.ParsePKIXPublicKey] (and [encoding/pem]).
func ParseUncompressedPublicKey(curve elliptic.Curve, data []byte) (*PublicKey, error) {
if len(data) < 1 || data[0] != 4 {
return nil, errors.New("ecdsa: invalid uncompressed public key")
}
switch curve {
case elliptic.P224():
return parseUncompressedPublicKey(ecdsa.P224(), curve, data)
case elliptic.P256():
return parseUncompressedPublicKey(ecdsa.P256(), curve, data)
case elliptic.P384():
return parseUncompressedPublicKey(ecdsa.P384(), curve, data)
case elliptic.P521():
return parseUncompressedPublicKey(ecdsa.P521(), curve, data)
default:
return nil, errors.New("ecdsa: curve not supported by ParseUncompressedPublicKey")
}
}
func parseUncompressedPublicKey[P ecdsa.Point[P]](c *ecdsa.Curve[P], curve elliptic.Curve, data []byte) (*PublicKey, error) {
k, err := ecdsa.NewPublicKey(c, data)
if err != nil {
return nil, err
}
return publicKeyFromFIPS(curve, k)
}
// Bytes encodes the public key as an uncompressed point according to SEC 1,
// Version 2.0, Section 2.3.3 (also known as the X9.62 uncompressed format).
// It returns an error if the public key is invalid.
//
// PublicKey.Curve must be one of [elliptic.P224], [elliptic.P256],
// [elliptic.P384], or [elliptic.P521], or Bytes returns an error.
//
// Bytes returns the same format as [ecdh.PublicKey.Bytes] does for NIST curves.
//
// Note that public keys are more commonly encoded in DER (or PEM) format, which
// can be generated with [x509.MarshalPKIXPublicKey] (and [encoding/pem]).
func (pub *PublicKey) Bytes() ([]byte, error) {
switch pub.Curve {
case elliptic.P224():
return publicKeyBytes(ecdsa.P224(), pub)
case elliptic.P256():
return publicKeyBytes(ecdsa.P256(), pub)
case elliptic.P384():
return publicKeyBytes(ecdsa.P384(), pub)
case elliptic.P521():
return publicKeyBytes(ecdsa.P521(), pub)
default:
return nil, errors.New("ecdsa: curve not supported by PublicKey.Bytes")
}
}
func publicKeyBytes[P ecdsa.Point[P]](c *ecdsa.Curve[P], pub *PublicKey) ([]byte, error) {
k, err := publicKeyToFIPS(c, pub)
if err != nil {
return nil, err
}
return k.Bytes(), nil
}
// PrivateKey represents an ECDSA private key.
type PrivateKey struct {
PublicKey
// D is the private scalar value.
//
// Modifying the raw value can produce invalid keys, and may
// invalidate internal optimizations; moreover, [big.Int] methods are not
// suitable for operating on cryptographic values. To encode and decode
// PrivateKey values, use [PrivateKey.Bytes] and [ParseRawPrivateKey]
// or [x509.MarshalPKCS8PrivateKey] and [x509.ParsePKCS8PrivateKey].
// For ECDH, use [crypto/ecdh].
//
// This field will be deprecated in Go 1.26.
D *big.Int
}
@ -134,6 +232,82 @@ func bigIntEqual(a, b *big.Int) bool {
return subtle.ConstantTimeCompare(a.Bytes(), b.Bytes()) == 1
}
// ParseRawPrivateKey parses a private key encoded as a fixed-length big-endian
// integer, according to SEC 1, Version 2.0, Section 2.3.6 (sometimes referred
// to as the raw format). It returns an error if the value is not reduced modulo
// the curve's order, or if it's zero.
//
// curve must be one of [elliptic.P224], [elliptic.P256], [elliptic.P384], or
// [elliptic.P521], or ParseRawPrivateKey returns an error.
//
// ParseRawPrivateKey accepts the same format as [ecdh.Curve.NewPrivateKey] does
// for NIST curves, but returns a [PrivateKey] instead of an [ecdh.PrivateKey].
//
// Note that private keys are more commonly encoded in ASN.1 or PKCS#8 format,
// which can be parsed with [x509.ParseECPrivateKey] or
// [x509.ParsePKCS8PrivateKey] (and [encoding/pem]).
func ParseRawPrivateKey(curve elliptic.Curve, data []byte) (*PrivateKey, error) {
switch curve {
case elliptic.P224():
return parseRawPrivateKey(ecdsa.P224(), nistec.NewP224Point, curve, data)
case elliptic.P256():
return parseRawPrivateKey(ecdsa.P256(), nistec.NewP256Point, curve, data)
case elliptic.P384():
return parseRawPrivateKey(ecdsa.P384(), nistec.NewP384Point, curve, data)
case elliptic.P521():
return parseRawPrivateKey(ecdsa.P521(), nistec.NewP521Point, curve, data)
default:
return nil, errors.New("ecdsa: curve not supported by ParseRawPrivateKey")
}
}
func parseRawPrivateKey[P ecdsa.Point[P]](c *ecdsa.Curve[P], newPoint func() P, curve elliptic.Curve, data []byte) (*PrivateKey, error) {
q, err := newPoint().ScalarBaseMult(data)
if err != nil {
return nil, err
}
k, err := ecdsa.NewPrivateKey(c, data, q.Bytes())
if err != nil {
return nil, err
}
return privateKeyFromFIPS(curve, k)
}
// Bytes encodes the private key as a fixed-length big-endian integer according
// to SEC 1, Version 2.0, Section 2.3.6 (sometimes referred to as the raw
// format). It returns an error if the private key is invalid.
//
// PrivateKey.Curve must be one of [elliptic.P224], [elliptic.P256],
// [elliptic.P384], or [elliptic.P521], or Bytes returns an error.
//
// Bytes returns the same format as [ecdh.PrivateKey.Bytes] does for NIST curves.
//
// Note that private keys are more commonly encoded in ASN.1 or PKCS#8 format,
// which can be generated with [x509.MarshalECPrivateKey] or
// [x509.MarshalPKCS8PrivateKey] (and [encoding/pem]).
func (priv *PrivateKey) Bytes() ([]byte, error) {
switch priv.Curve {
case elliptic.P224():
return privateKeyBytes(ecdsa.P224(), priv)
case elliptic.P256():
return privateKeyBytes(ecdsa.P256(), priv)
case elliptic.P384():
return privateKeyBytes(ecdsa.P384(), priv)
case elliptic.P521():
return privateKeyBytes(ecdsa.P521(), priv)
default:
return nil, errors.New("ecdsa: curve not supported by PrivateKey.Bytes")
}
}
func privateKeyBytes[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey) ([]byte, error) {
k, err := privateKeyToFIPS(c, priv)
if err != nil {
return nil, err
}
return k.Bytes(), nil
}
// Sign signs a hash (which should be the result of hashing a larger message
// with opts.HashFunc()) using the private key, priv. If the hash is longer than
// the bit-length of the private key's curve order, the hash will be truncated

View File

@ -546,6 +546,161 @@ func testRFC6979(t *testing.T, curve elliptic.Curve, D, X, Y, msg, r, s string)
}
}
func TestParseAndBytesRoundTrip(t *testing.T) {
testAllCurves(t, testParseAndBytesRoundTrip)
}
func testParseAndBytesRoundTrip(t *testing.T, curve elliptic.Curve) {
if strings.HasSuffix(t.Name(), "/Generic") {
t.Skip("these methods don't support generic curves")
}
priv, _ := GenerateKey(curve, rand.Reader)
b, err := priv.PublicKey.Bytes()
if err != nil {
t.Fatalf("failed to serialize private key's public key: %v", err)
}
if b[0] != 4 {
t.Fatalf("public key bytes doesn't start with 0x04 (uncompressed format)")
}
p, err := ParseUncompressedPublicKey(curve, b)
if err != nil {
t.Fatalf("failed to parse private key's public key: %v", err)
}
if !priv.PublicKey.Equal(p) {
t.Errorf("parsed private key's public key doesn't match original")
}
bk, err := priv.Bytes()
if err != nil {
t.Fatalf("failed to serialize private key: %v", err)
}
k, err := ParseRawPrivateKey(curve, bk)
if err != nil {
t.Fatalf("failed to parse private key: %v", err)
}
if !priv.Equal(k) {
t.Errorf("parsed private key doesn't match original")
}
if curve != elliptic.P224() {
privECDH, err := priv.ECDH()
if err != nil {
t.Fatalf("failed to convert private key to ECDH: %v", err)
}
pp, err := privECDH.Curve().NewPublicKey(b)
if err != nil {
t.Fatalf("failed to parse with ECDH: %v", err)
}
if !privECDH.PublicKey().Equal(pp) {
t.Errorf("parsed ECDH public key doesn't match original")
}
if !bytes.Equal(b, pp.Bytes()) {
t.Errorf("encoded ECDH public key doesn't match Bytes")
}
kk, err := privECDH.Curve().NewPrivateKey(bk)
if err != nil {
t.Fatalf("failed to parse with ECDH: %v", err)
}
if !privECDH.Equal(kk) {
t.Errorf("parsed ECDH private key doesn't match original")
}
if !bytes.Equal(bk, kk.Bytes()) {
t.Errorf("encoded ECDH private key doesn't match Bytes")
}
}
}
func TestInvalidPublicKeys(t *testing.T) {
testAllCurves(t, testInvalidPublicKeys)
}
func testInvalidPublicKeys(t *testing.T, curve elliptic.Curve) {
t.Run("Infinity", func(t *testing.T) {
k := &PublicKey{Curve: curve, X: big.NewInt(0), Y: big.NewInt(0)}
if _, err := k.Bytes(); err == nil {
t.Errorf("PublicKey.Bytes accepted infinity")
}
b := []byte{0}
if _, err := ParseUncompressedPublicKey(curve, b); err == nil {
t.Errorf("ParseUncompressedPublicKey accepted infinity")
}
b = make([]byte, 1+2*(curve.Params().BitSize+7)/8)
b[0] = 4
if _, err := ParseUncompressedPublicKey(curve, b); err == nil {
t.Errorf("ParseUncompressedPublicKey accepted infinity")
}
})
t.Run("NotOnCurve", func(t *testing.T) {
k, _ := GenerateKey(curve, rand.Reader)
k.X = k.X.Add(k.X, big.NewInt(1))
if _, err := k.Bytes(); err == nil {
t.Errorf("PublicKey.Bytes accepted not on curve")
}
b := make([]byte, 1+2*(curve.Params().BitSize+7)/8)
b[0] = 4
k.X.FillBytes(b[1 : 1+len(b)/2])
k.Y.FillBytes(b[1+len(b)/2:])
if _, err := ParseUncompressedPublicKey(curve, b); err == nil {
t.Errorf("ParseUncompressedPublicKey accepted not on curve")
}
})
t.Run("Compressed", func(t *testing.T) {
k, _ := GenerateKey(curve, rand.Reader)
b := elliptic.MarshalCompressed(curve, k.X, k.Y)
if _, err := ParseUncompressedPublicKey(curve, b); err == nil {
t.Errorf("ParseUncompressedPublicKey accepted compressed key")
}
})
}
func TestInvalidPrivateKeys(t *testing.T) {
testAllCurves(t, testInvalidPrivateKeys)
}
func testInvalidPrivateKeys(t *testing.T, curve elliptic.Curve) {
t.Run("Zero", func(t *testing.T) {
k := &PrivateKey{PublicKey{curve, big.NewInt(0), big.NewInt(0)}, big.NewInt(0)}
if _, err := k.Bytes(); err == nil {
t.Errorf("PrivateKey.Bytes accepted zero key")
}
b := make([]byte, (curve.Params().BitSize+7)/8)
if _, err := ParseRawPrivateKey(curve, b); err == nil {
t.Errorf("ParseRawPrivateKey accepted zero key")
}
})
t.Run("Overflow", func(t *testing.T) {
d := new(big.Int).Add(curve.Params().N, big.NewInt(5))
x, y := curve.ScalarBaseMult(d.Bytes())
k := &PrivateKey{PublicKey{curve, x, y}, d}
if _, err := k.Bytes(); err == nil {
t.Errorf("PrivateKey.Bytes accepted overflow key")
}
b := make([]byte, (curve.Params().BitSize+7)/8)
k.D.FillBytes(b)
if _, err := ParseRawPrivateKey(curve, b); err == nil {
t.Errorf("ParseRawPrivateKey accepted overflow key")
}
})
t.Run("Length", func(t *testing.T) {
b := []byte{1, 2, 3}
if _, err := ParseRawPrivateKey(curve, b); err == nil {
t.Errorf("ParseRawPrivateKey accepted short key")
}
b = append(b, make([]byte, (curve.Params().BitSize+7)/8)...)
if _, err := ParseRawPrivateKey(curve, b); err == nil {
t.Errorf("ParseRawPrivateKey accepted long key")
}
})
}
func benchmarkAllCurves(b *testing.B, f func(*testing.B, elliptic.Curve)) {
tests := []struct {
name string

View File

@ -7,11 +7,8 @@ package fips140
import (
"crypto/internal/fips140"
"crypto/internal/fips140/check"
"internal/godebug"
)
var fips140GODEBUG = godebug.New("fips140")
// Enabled reports whether the cryptography libraries are operating in FIPS
// 140-3 mode.
//
@ -21,11 +18,6 @@ var fips140GODEBUG = godebug.New("fips140")
//
// This can't be changed after the program has started.
func Enabled() bool {
godebug := fips140GODEBUG.Value()
currentlyEnabled := godebug == "on" || godebug == "only" || godebug == "debug"
if currentlyEnabled != fips140.Enabled {
panic("crypto/fips140: GODEBUG setting changed after program start")
}
if fips140.Enabled && !check.Verified {
panic("crypto/fips140: FIPS 140-3 mode enabled, but integrity check didn't pass")
}

View File

@ -0,0 +1,51 @@
// 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 fips140
import (
"internal/godebug"
"os"
"testing"
)
func TestImmutableGODEBUG(t *testing.T) {
defer func(v string) { os.Setenv("GODEBUG", v) }(os.Getenv("GODEBUG"))
fips140Enabled := Enabled()
fips140Setting := godebug.New("fips140")
fips140SettingValue := fips140Setting.Value()
os.Setenv("GODEBUG", "fips140=off")
if Enabled() != fips140Enabled {
t.Errorf("Enabled() changed after setting GODEBUG=fips140=off")
}
if fips140Setting.Value() != fips140SettingValue {
t.Errorf("fips140Setting.Value() changed after setting GODEBUG=fips140=off")
}
os.Setenv("GODEBUG", "fips140=on")
if Enabled() != fips140Enabled {
t.Errorf("Enabled() changed after setting GODEBUG=fips140=on")
}
if fips140Setting.Value() != fips140SettingValue {
t.Errorf("fips140Setting.Value() changed after setting GODEBUG=fips140=on")
}
os.Setenv("GODEBUG", "fips140=")
if Enabled() != fips140Enabled {
t.Errorf("Enabled() changed after setting GODEBUG=fips140=")
}
if fips140Setting.Value() != fips140SettingValue {
t.Errorf("fips140Setting.Value() changed after setting GODEBUG=fips140=")
}
os.Setenv("GODEBUG", "")
if Enabled() != fips140Enabled {
t.Errorf("Enabled() changed after setting GODEBUG=")
}
if fips140Setting.Value() != fips140SettingValue {
t.Errorf("fips140Setting.Value() changed after setting GODEBUG=")
}
}

View File

@ -632,6 +632,18 @@ func TestHMACHash(t *testing.T) {
}
}
func TestExtraMethods(t *testing.T) {
h := New(sha256.New, []byte("key"))
cryptotest.NoExtraMethods(t, maybeCloner(h))
}
func maybeCloner(h hash.Hash) any {
if c, ok := h.(hash.Cloner); ok {
return &c
}
return &h
}
func BenchmarkHMACSHA256_1K(b *testing.B) {
key := make([]byte, 32)
buf := make([]byte, 1024)

View File

@ -5,6 +5,8 @@
package cryptotest
import (
"crypto/internal/boring"
"crypto/internal/fips140"
"hash"
"internal/testhash"
"io"
@ -18,6 +20,10 @@ 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" {
testhash.TestHashWithoutClone(t, testhash.MakeHash(mh))
return
}
testhash.TestHash(t, testhash.MakeHash(mh))
}

View File

@ -7,6 +7,7 @@ package cryptotest
import (
"crypto/internal/boring"
"crypto/internal/impl"
"internal/goarch"
"internal/goos"
"internal/testenv"
"testing"
@ -35,15 +36,14 @@ func TestAllImplementations(t *testing.T, pkg string, f func(t *testing.T)) {
t.Run(name, f)
} else {
t.Run(name, func(t *testing.T) {
// Report an error if we're on Linux CI (assumed to be the most
// consistent) and the builder can't test this implementation.
if testenv.Builder() != "" && goos.GOOS == "linux" {
// Report an error if we're on the most capable builder for the
// architecture and the builder can't test this implementation.
flagship := goos.GOOS == "linux" && goarch.GOARCH != "arm64" ||
goos.GOOS == "darwin" && goarch.GOARCH == "arm64"
if testenv.Builder() != "" && flagship {
if name == "SHA-NI" {
t.Skip("known issue, see golang.org/issue/69592")
}
if name == "Armv8.2" {
t.Skip("known issue, see golang.org/issue/69593")
}
t.Error("builder doesn't support CPU features needed to test this implementation")
} else {
t.Skip("implementation not supported")

View File

@ -11,6 +11,7 @@ import (
"crypto/internal/fips140/drbg"
"crypto/internal/fips140/nistec"
"errors"
"hash"
"io"
"sync"
)
@ -271,7 +272,7 @@ type Signature struct {
// the hash function H) using the private key, priv. If the hash is longer than
// the bit-length of the private key's curve order, the hash will be truncated
// to that length.
func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) {
func Sign[P Point[P], H hash.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) {
if priv.pub.curve != c.curve {
return nil, errors.New("ecdsa: private key does not match curve")
}
@ -304,7 +305,7 @@ func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey,
// hash is longer than the bit-length of the private key's curve order, the hash
// will be truncated to that length. This applies Deterministic ECDSA as
// specified in FIPS 186-5 and RFC 6979.
func SignDeterministic[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey, hash []byte) (*Signature, error) {
func SignDeterministic[P Point[P], H hash.Hash](c *Curve[P], h func() H, priv *PrivateKey, hash []byte) (*Signature, error) {
if priv.pub.curve != c.curve {
return nil, errors.New("ecdsa: private key does not match curve")
}

View File

@ -8,6 +8,7 @@ import (
"bytes"
"crypto/internal/fips140"
"crypto/internal/fips140/hmac"
"hash"
)
// hmacDRBG is an SP 800-90A Rev. 1 HMAC_DRBG.
@ -48,7 +49,7 @@ type personalizationString interface {
isPersonalizationString()
}
func newDRBG[H fips140.Hash](hash func() H, entropy, nonce []byte, s personalizationString) *hmacDRBG {
func newDRBG[H hash.Hash](hash func() H, entropy, nonce []byte, s personalizationString) *hmacDRBG {
// HMAC_DRBG_Instantiate_algorithm, per Section 10.1.2.3.
fips140.RecordApproved()
@ -121,7 +122,7 @@ func newDRBG[H fips140.Hash](hash func() H, entropy, nonce []byte, s personaliza
//
// This should only be used for ACVP testing. hmacDRBG is not intended to be
// used directly.
func TestingOnlyNewDRBG(hash func() fips140.Hash, entropy, nonce []byte, s []byte) *hmacDRBG {
func TestingOnlyNewDRBG(hash func() hash.Hash, entropy, nonce []byte, s []byte) *hmacDRBG {
return newDRBG(hash, entropy, nonce, plainPersonalizationString(s))
}

View File

@ -7,6 +7,7 @@ package fips140
import (
"crypto/internal/fips140deps/godebug"
"errors"
"hash"
"runtime"
)
@ -69,3 +70,9 @@ func Version() string {
// 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

View File

@ -1,32 +0,0 @@
// Copyright 2024 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 fips140
import "io"
// Hash is the common interface implemented by all hash functions. It is a copy
// of [hash.Hash] from the standard library, to avoid depending on security
// definitions from outside of the module.
type Hash interface {
// Write (via the embedded io.Writer interface) adds more data to the
// running hash. It never returns an error.
io.Writer
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
Sum(b []byte) []byte
// Reset resets the Hash to its initial state.
Reset()
// Size returns the number of bytes Sum will return.
Size() int
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently if all writes
// are a multiple of the block size.
BlockSize() int
}

View File

@ -7,9 +7,10 @@ package hkdf
import (
"crypto/internal/fips140"
"crypto/internal/fips140/hmac"
"hash"
)
func Extract[H fips140.Hash](h func() H, secret, salt []byte) []byte {
func Extract[H hash.Hash](h func() H, secret, salt []byte) []byte {
if len(secret) < 112/8 {
fips140.RecordNonApproved()
}
@ -23,7 +24,7 @@ func Extract[H fips140.Hash](h func() H, secret, salt []byte) []byte {
return extractor.Sum(nil)
}
func Expand[H fips140.Hash](h func() H, pseudorandomKey []byte, info string, keyLen int) []byte {
func Expand[H hash.Hash](h func() H, pseudorandomKey []byte, info string, keyLen int) []byte {
out := make([]byte, 0, keyLen)
expander := hmac.New(h, pseudorandomKey)
hmac.MarkAsUsedInKDF(expander)
@ -50,7 +51,7 @@ func Expand[H fips140.Hash](h func() H, pseudorandomKey []byte, info string, key
return out
}
func Key[H fips140.Hash](h func() H, secret, salt []byte, info string, keyLen int) []byte {
func Key[H hash.Hash](h func() H, secret, salt []byte, info string, keyLen int) []byte {
prk := Extract(h, secret, salt)
return Expand(h, prk, info, keyLen)
}

View File

@ -12,6 +12,8 @@ import (
"crypto/internal/fips140/sha256"
"crypto/internal/fips140/sha3"
"crypto/internal/fips140/sha512"
"errors"
"hash"
)
// key is zero padded to the block size of the hash function
@ -28,8 +30,9 @@ type marshalable interface {
}
type HMAC struct {
// opad and ipad may share underlying storage with HMAC clones.
opad, ipad []byte
outer, inner fips140.Hash
outer, inner hash.Hash
// If marshaled is true, then opad and ipad do not contain a padded
// copy of the key, but rather the marshaled state of outer/inner after
@ -127,8 +130,32 @@ func (h *HMAC) Reset() {
h.marshaled = true
}
// New returns a new HMAC hash using the given [fips140.Hash] type and key.
func New[H fips140.Hash](h func() H, key []byte) *HMAC {
// Clone implements [hash.Cloner] if the underlying hash does.
// Otherwise, it returns [errors.ErrUnsupported].
func (h *HMAC) Clone() (hash.Cloner, error) {
r := *h
ic, ok := h.inner.(hash.Cloner)
if !ok {
return nil, errors.ErrUnsupported
}
oc, ok := h.outer.(hash.Cloner)
if !ok {
return nil, errors.ErrUnsupported
}
var err error
r.inner, err = ic.Clone()
if err != nil {
return nil, errors.ErrUnsupported
}
r.outer, err = oc.Clone()
if err != nil {
return nil, errors.ErrUnsupported
}
return &r, nil
}
// New returns a new HMAC hash using the given [hash.Hash] type and key.
func New[H hash.Hash](h func() H, key []byte) *HMAC {
hm := &HMAC{keyLen: len(key)}
hm.outer = h()
hm.inner = h()

View File

@ -7,11 +7,6 @@
#include "textflag.h"
#include "go_asm.h"
DATA p256ordK0<>+0x00(SB)/4, $0xee00bc4f
DATA p256ord<>+0x00(SB)/8, $0xffffffff00000000
DATA p256ord<>+0x08(SB)/8, $0xffffffffffffffff
DATA p256ord<>+0x10(SB)/8, $0xbce6faada7179e84
DATA p256ord<>+0x18(SB)/8, $0xf3b9cac2fc632551
DATA p256<>+0x00(SB)/8, $0xffffffff00000001 // P256
DATA p256<>+0x08(SB)/8, $0x0000000000000000 // P256
DATA p256<>+0x10(SB)/8, $0x00000000ffffffff // P256
@ -44,8 +39,6 @@ DATA p256mul<>+0x80(SB)/8, $0x00000000fffffffe // (1*2^256)%P256
DATA p256mul<>+0x88(SB)/8, $0xffffffffffffffff // (1*2^256)%P256
DATA p256mul<>+0x90(SB)/8, $0xffffffff00000000 // (1*2^256)%P256
DATA p256mul<>+0x98(SB)/8, $0x0000000000000001 // (1*2^256)%P256
GLOBL p256ordK0<>(SB), 8, $4
GLOBL p256ord<>(SB), 8, $32
GLOBL p256<>(SB), 8, $96
GLOBL p256mul<>(SB), 8, $160
@ -500,392 +493,6 @@ loop_select:
#undef SEL2
#undef CPOOL
// ---------------------------------------
// func p256OrdMul(res, in1, in2 *p256OrdElement)
#define res_ptr R1
#define x_ptr R2
#define y_ptr R3
#define X0 V0
#define X1 V1
#define Y0 V2
#define Y1 V3
#define M0 V4
#define M1 V5
#define T0 V6
#define T1 V7
#define T2 V8
#define YDIG V9
#define ADD1 V16
#define ADD1H V17
#define ADD2 V18
#define ADD2H V19
#define RED1 V20
#define RED1H V21
#define RED2 V22
#define RED2H V23
#define CAR1 V24
#define CAR1M V25
#define MK0 V30
#define K0 V31
TEXT ·p256OrdMul<>(SB), NOSPLIT, $0
MOVD res+0(FP), res_ptr
MOVD in1+8(FP), x_ptr
MOVD in2+16(FP), y_ptr
VZERO T2
MOVD $p256ordK0<>+0x00(SB), R4
// VLEF $3, 0(R4), K0
WORD $0xE7F40000
BYTE $0x38
BYTE $0x03
MOVD $p256ord<>+0x00(SB), R4
VL 16(R4), M0
VL 0(R4), M1
VL (0*16)(x_ptr), X0
VPDI $0x4, X0, X0, X0
VL (1*16)(x_ptr), X1
VPDI $0x4, X1, X1, X1
VL (0*16)(y_ptr), Y0
VPDI $0x4, Y0, Y0, Y0
VL (1*16)(y_ptr), Y1
VPDI $0x4, Y1, Y1, Y1
// ---------------------------------------------------------------------------/
VREPF $3, Y0, YDIG
VMLF X0, YDIG, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMLF X1, YDIG, ADD2
VMLHF X0, YDIG, ADD1H
VMLHF X1, YDIG, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
/* *
* ---+--------+--------+
* T2| T1 | T0 |
* ---+--------+--------+
* *(add)*
* +--------+--------+
* | X1 | X0 |
* +--------+--------+
* *(mul)*
* +--------+--------+
* | YDIG | YDIG |
* +--------+--------+
* *(add)*
* +--------+--------+
* | M1 | M0 |
* +--------+--------+
* *(mul)*
* +--------+--------+
* | MK0 | MK0 |
* +--------+--------+
*
* ---------------------
*
* +--------+--------+
* | ADD2 | ADD1 |
* +--------+--------+
* +--------+--------+
* | ADD2H | ADD1H |
* +--------+--------+
* +--------+--------+
* | RED2 | RED1 |
* +--------+--------+
* +--------+--------+
* | RED2H | RED1H |
* +--------+--------+
*/
VREPF $2, Y0, YDIG
VMALF X0, YDIG, T0, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMALF X1, YDIG, T1, ADD2
VMALHF X0, YDIG, T0, ADD1H
VMALHF X1, YDIG, T1, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
VREPF $1, Y0, YDIG
VMALF X0, YDIG, T0, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMALF X1, YDIG, T1, ADD2
VMALHF X0, YDIG, T0, ADD1H
VMALHF X1, YDIG, T1, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
VREPF $0, Y0, YDIG
VMALF X0, YDIG, T0, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMALF X1, YDIG, T1, ADD2
VMALHF X0, YDIG, T0, ADD1H
VMALHF X1, YDIG, T1, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
VREPF $3, Y1, YDIG
VMALF X0, YDIG, T0, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMALF X1, YDIG, T1, ADD2
VMALHF X0, YDIG, T0, ADD1H
VMALHF X1, YDIG, T1, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
VREPF $2, Y1, YDIG
VMALF X0, YDIG, T0, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMALF X1, YDIG, T1, ADD2
VMALHF X0, YDIG, T0, ADD1H
VMALHF X1, YDIG, T1, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
VREPF $1, Y1, YDIG
VMALF X0, YDIG, T0, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMALF X1, YDIG, T1, ADD2
VMALHF X0, YDIG, T0, ADD1H
VMALHF X1, YDIG, T1, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
VREPF $0, Y1, YDIG
VMALF X0, YDIG, T0, ADD1
VMLF ADD1, K0, MK0
VREPF $3, MK0, MK0
VMALF X1, YDIG, T1, ADD2
VMALHF X0, YDIG, T0, ADD1H
VMALHF X1, YDIG, T1, ADD2H
VMALF M0, MK0, ADD1, RED1
VMALHF M0, MK0, ADD1, RED1H
VMALF M1, MK0, ADD2, RED2
VMALHF M1, MK0, ADD2, RED2H
VSLDB $12, RED2, RED1, RED1
VSLDB $12, T2, RED2, RED2
VACCQ RED1, ADD1H, CAR1
VAQ RED1, ADD1H, T0
VACCQ RED1H, T0, CAR1M
VAQ RED1H, T0, T0
// << ready for next MK0
VACQ RED2, ADD2H, CAR1, T1
VACCCQ RED2, ADD2H, CAR1, CAR1
VACCCQ RED2H, T1, CAR1M, T2
VACQ RED2H, T1, CAR1M, T1
VAQ CAR1, T2, T2
// ---------------------------------------------------
VZERO RED1
VSCBIQ M0, T0, CAR1
VSQ M0, T0, ADD1
VSBCBIQ T1, M1, CAR1, CAR1M
VSBIQ T1, M1, CAR1, ADD2
VSBIQ T2, RED1, CAR1M, T2
// what output to use, ADD2||ADD1 or T1||T0?
VSEL T0, ADD1, T2, T0
VSEL T1, ADD2, T2, T1
VPDI $0x4, T0, T0, T0
VST T0, (0*16)(res_ptr)
VPDI $0x4, T1, T1, T1
VST T1, (1*16)(res_ptr)
RET
#undef res_ptr
#undef x_ptr
#undef y_ptr
#undef X0
#undef X1
#undef Y0
#undef Y1
#undef M0
#undef M1
#undef T0
#undef T1
#undef T2
#undef YDIG
#undef ADD1
#undef ADD1H
#undef ADD2
#undef ADD2H
#undef RED1
#undef RED1H
#undef RED2
#undef RED2H
#undef CAR1
#undef CAR1M
#undef MK0
#undef K0
// ---------------------------------------
// p256MulInternal
// V0-V3,V30,V31 - Not Modified

View File

@ -8,6 +8,7 @@ import (
"crypto/internal/fips140"
"crypto/internal/fips140/hmac"
"errors"
"hash"
)
// divRoundUp divides x+y-1 by y, rounding up if the result is not whole.
@ -19,7 +20,7 @@ func divRoundUp(x, y int) int {
return int((int64(x) + int64(y) - 1) / int64(y))
}
func Key[Hash fips140.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) {
func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) {
setServiceIndicator(salt, keyLength)
if keyLength <= 0 {

View File

@ -16,6 +16,7 @@ import (
"crypto/internal/fips140/sha512"
"crypto/internal/fips140/subtle"
"errors"
"hash"
"io"
)
@ -48,7 +49,7 @@ func incCounter(c *[4]byte) {
// mgf1XOR XORs the bytes in out with a mask generated using the MGF1 function
// specified in PKCS #1 v2.1.
func mgf1XOR(out []byte, hash fips140.Hash, seed []byte) {
func mgf1XOR(out []byte, hash hash.Hash, seed []byte) {
var counter [4]byte
var digest []byte
@ -67,7 +68,7 @@ func mgf1XOR(out []byte, hash fips140.Hash, seed []byte) {
}
}
func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash fips140.Hash) ([]byte, error) {
func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) {
// See RFC 8017, Section 9.1.1.
hLen := hash.Size()
@ -144,7 +145,7 @@ func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash fips140.Hash) ([]
const pssSaltLengthAutodetect = -1
func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash fips140.Hash) error {
func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
// See RFC 8017, Section 9.1.2.
hLen := hash.Size()
@ -250,7 +251,7 @@ func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash fips140.Hash) error
// PSSMaxSaltLength returns the maximum salt length for a given public key and
// hash function.
func PSSMaxSaltLength(pub *PublicKey, hash fips140.Hash) (int, error) {
func PSSMaxSaltLength(pub *PublicKey, hash hash.Hash) (int, error) {
saltLength := (pub.N.BitLen()-1+7)/8 - 2 - hash.Size()
if saltLength < 0 {
return 0, ErrMessageTooLong
@ -264,7 +265,7 @@ func PSSMaxSaltLength(pub *PublicKey, hash fips140.Hash) (int, error) {
}
// SignPSS calculates the signature of hashed using RSASSA-PSS.
func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte, saltLength int) ([]byte, error) {
func SignPSS(rand io.Reader, priv *PrivateKey, hash hash.Hash, hashed []byte, saltLength int) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()
checkApprovedHash(hash)
@ -311,19 +312,19 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte,
}
// VerifyPSS verifies sig with RSASSA-PSS automatically detecting the salt length.
func VerifyPSS(pub *PublicKey, hash fips140.Hash, digest []byte, sig []byte) error {
func VerifyPSS(pub *PublicKey, hash hash.Hash, digest []byte, sig []byte) error {
return verifyPSS(pub, hash, digest, sig, pssSaltLengthAutodetect)
}
// VerifyPSS verifies sig with RSASSA-PSS and an expected salt length.
func VerifyPSSWithSaltLength(pub *PublicKey, hash fips140.Hash, digest []byte, sig []byte, saltLength int) error {
func VerifyPSSWithSaltLength(pub *PublicKey, hash hash.Hash, digest []byte, sig []byte, saltLength int) error {
if saltLength < 0 {
return errors.New("crypto/rsa: salt length cannot be negative")
}
return verifyPSS(pub, hash, digest, sig, saltLength)
}
func verifyPSS(pub *PublicKey, hash fips140.Hash, digest []byte, sig []byte, saltLength int) error {
func verifyPSS(pub *PublicKey, hash hash.Hash, digest []byte, sig []byte, saltLength int) error {
fipsSelfTest()
fips140.RecordApproved()
checkApprovedHash(hash)
@ -359,7 +360,7 @@ func verifyPSS(pub *PublicKey, hash fips140.Hash, digest []byte, sig []byte, sal
return emsaPSSVerify(digest, em, emBits, saltLength, hash)
}
func checkApprovedHash(hash fips140.Hash) {
func checkApprovedHash(hash hash.Hash) {
switch hash.(type) {
case *sha256.Digest, *sha512.Digest, *sha3.Digest:
default:
@ -368,7 +369,7 @@ func checkApprovedHash(hash fips140.Hash) {
}
// EncryptOAEP encrypts the given message with RSAES-OAEP.
func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
func EncryptOAEP(hash, mgfHash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
// Note that while we don't commit to deterministic execution with respect
// to the random stream, we also don't apply MaybeReadByte, so per Hyrum's
// Law it's probably relied upon by some. It's a tolerable promise because a
@ -411,7 +412,7 @@ func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, m
}
// DecryptOAEP decrypts ciphertext using RSAES-OAEP.
func DecryptOAEP(hash, mgfHash fips140.Hash, priv *PrivateKey, ciphertext []byte, label []byte) ([]byte, error) {
func DecryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, label []byte) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()
checkApprovedHash(hash)

View File

@ -8,8 +8,6 @@ import (
"os"
. "github.com/mmcloughlin/avo/build"
. "github.com/mmcloughlin/avo/operand"
. "github.com/mmcloughlin/avo/reg"
)
//go:generate go run . -out ../sha256block_amd64.s
@ -61,203 +59,11 @@ func main() {
Package("crypto/internal/fips140/sha256")
ConstraintExpr("!purego")
blockAMD64()
blockAVX2()
blockSHANI()
Generate()
}
// Wt = Mt; for 0 <= t <= 15
func msgSchedule0(index int) {
MOVL(Mem{Base: SI}.Offset(index*4), EAX)
BSWAPL(EAX)
MOVL(EAX, Mem{Base: BP}.Offset(index*4))
}
// Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63
//
// SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x)
// SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x)
func msgSchedule1(index int) {
MOVL(Mem{Base: BP}.Offset((index-2)*4), EAX)
MOVL(EAX, ECX)
RORL(Imm(17), EAX)
MOVL(ECX, EDX)
RORL(Imm(19), ECX)
SHRL(Imm(10), EDX)
MOVL(Mem{Base: BP}.Offset((index-15)*4), EBX)
XORL(ECX, EAX)
MOVL(EBX, ECX)
XORL(EDX, EAX)
RORL(Imm(7), EBX)
MOVL(ECX, EDX)
SHRL(Imm(3), EDX)
RORL(Imm(18), ECX)
ADDL(Mem{Base: BP}.Offset((index-7)*4), EAX)
XORL(ECX, EBX)
XORL(EDX, EBX)
ADDL(Mem{Base: BP}.Offset((index-16)*4), EBX)
ADDL(EBX, EAX)
MOVL(EAX, Mem{Base: BP}.Offset((index)*4))
}
// Calculate T1 in AX - uses AX, CX and DX registers.
// h is also used as an accumulator. Wt is passed in AX.
//
// T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt
// BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x)
// Ch(x, y, z) = (x AND y) XOR (NOT x AND z)
func sha256T1(konst uint32, e, f, g, h GPPhysical) {
ADDL(EAX, h)
MOVL(e, EAX)
ADDL(U32(konst), h)
MOVL(e, ECX)
RORL(U8(6), EAX)
MOVL(e, EDX)
RORL(U8(11), ECX)
XORL(ECX, EAX)
MOVL(e, ECX)
RORL(U8(25), EDX)
ANDL(f, ECX)
XORL(EAX, EDX)
MOVL(e, EAX)
NOTL(EAX)
ADDL(EDX, h)
ANDL(g, EAX)
XORL(ECX, EAX)
ADDL(h, EAX)
}
// Calculate T2 in BX - uses BX, CX, DX and DI registers.
//
// T2 = BIGSIGMA0(a) + Maj(a, b, c)
// BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x)
// Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z)
func sha256T2(a, b, c GPPhysical) {
MOVL(a, EDI)
MOVL(c, EBX)
RORL(U8(2), EDI)
MOVL(a, EDX)
ANDL(b, EBX)
RORL(U8(13), EDX)
MOVL(a, ECX)
ANDL(c, ECX)
XORL(EDX, EDI)
XORL(ECX, EBX)
MOVL(a, EDX)
MOVL(b, ECX)
RORL(U8(22), EDX)
ANDL(a, ECX)
XORL(ECX, EBX)
XORL(EDX, EDI)
ADDL(EDI, EBX)
}
// Calculate T1 and T2, then e = d + T1 and a = T1 + T2.
// The values for e and a are stored in d and h, ready for rotation.
func sha256Round(index int, konst uint32, a, b, c, d, e, f, g, h GPPhysical) {
sha256T1(konst, e, f, g, h)
sha256T2(a, b, c)
MOVL(EBX, h)
ADDL(EAX, d)
ADDL(EAX, h)
}
func sha256Round0(index int, konst uint32, a, b, c, d, e, f, g, h GPPhysical) {
msgSchedule0(index)
sha256Round(index, konst, a, b, c, d, e, f, g, h)
}
func sha256Round1(index int, konst uint32, a, b, c, d, e, f, g, h GPPhysical) {
msgSchedule1(index)
sha256Round(index, konst, a, b, c, d, e, f, g, h)
}
func blockAMD64() {
Implement("blockAMD64")
AllocLocal(256 + 8)
Load(Param("p").Base(), RSI)
Load(Param("p").Len(), RDX)
SHRQ(Imm(6), RDX)
SHLQ(Imm(6), RDX)
// Return if p is empty
LEAQ(Mem{Base: RSI, Index: RDX, Scale: 1}, RDI)
MOVQ(RDI, Mem{Base: SP}.Offset(256))
CMPQ(RSI, RDI)
JEQ(LabelRef("end"))
BP := Mem{Base: BP}
Load(Param("dig"), RBP)
MOVL(BP.Offset(0*4), R8L) // a = H0
MOVL(BP.Offset(1*4), R9L) // b = H1
MOVL(BP.Offset(2*4), R10L) // c = H2
MOVL(BP.Offset(3*4), R11L) // d = H3
MOVL(BP.Offset(4*4), R12L) // e = H4
MOVL(BP.Offset(5*4), R13L) // f = H5
MOVL(BP.Offset(6*4), R14L) // g = H6
MOVL(BP.Offset(7*4), R15L) // h = H7
loop()
end()
}
func rotateRight(slice *[]GPPhysical) []GPPhysical {
n := len(*slice)
new := make([]GPPhysical, n)
for i, reg := range *slice {
new[(i+1)%n] = reg
}
return new
}
func loop() {
Label("loop")
MOVQ(RSP, RBP)
regs := []GPPhysical{R8L, R9L, R10L, R11L, R12L, R13L, R14L, R15L}
n := len(_K)
for i := 0; i < 16; i++ {
sha256Round0(i, _K[i], regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7])
regs = rotateRight(&regs)
}
for i := 16; i < n; i++ {
sha256Round1(i, _K[i], regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7])
regs = rotateRight(&regs)
}
Load(Param("dig"), RBP)
BP := Mem{Base: BP}
ADDL(BP.Offset(0*4), R8L) // H0 = a + H0
MOVL(R8L, BP.Offset(0*4))
ADDL(BP.Offset(1*4), R9L) // H1 = b + H1
MOVL(R9L, BP.Offset(1*4))
ADDL(BP.Offset(2*4), R10L) // H2 = c + H2
MOVL(R10L, BP.Offset(2*4))
ADDL(BP.Offset(3*4), R11L) // H3 = d + H3
MOVL(R11L, BP.Offset(3*4))
ADDL(BP.Offset(4*4), R12L) // H4 = e + H4
MOVL(R12L, BP.Offset(4*4))
ADDL(BP.Offset(5*4), R13L) // H5 = f + H5
MOVL(R13L, BP.Offset(5*4))
ADDL(BP.Offset(6*4), R14L) // H6 = g + H6
MOVL(R14L, BP.Offset(6*4))
ADDL(BP.Offset(7*4), R15L) // H7 = h + H7
MOVL(R15L, BP.Offset(7*4))
ADDQ(Imm(64), RSI)
CMPQ(RSI, Mem{Base: SP}.Offset(256))
JB(LabelRef("loop"))
}
func end() {
Label("end")
RET()
}
var _K = []uint32{
0x428a2f98,
0x71374491,

Some files were not shown because too many files have changed in this diff Show More