diff --git a/VERSION b/VERSION index 65e93060..d17bb788 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -go1.24.3 -time 2025-04-30T18:13:34Z +go1.24.4 +time 2025-05-29T19:37:36Z diff --git a/lib/fips140/inprocess.txt b/lib/fips140/inprocess.txt new file mode 100644 index 00000000..0ec25f75 --- /dev/null +++ b/lib/fips140/inprocess.txt @@ -0,0 +1 @@ +v1.0.0 diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 182379f0..fa0d3457 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -452,33 +452,50 @@ func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind in if oldsym.Dupok() { return oldi } - // If one is a DATA symbol (i.e. has content, DataSize != 0) - // and the other is BSS, the one with content wins. + // If one is a DATA symbol (i.e. has content, DataSize != 0, + // including RODATA) and the other is BSS, the one with content wins. // If both are BSS, the one with larger size wins. - // Specifically, the "overwrite" variable and the final result are // - // new sym old sym overwrite + // For a special case, we allow a TEXT symbol overwrites a BSS symbol + // even if the BSS symbol has larger size. This is because there is + // code like below to take the address of a function + // + // //go:linkname fn + // var fn uintptr + // var fnAddr = uintptr(unsafe.Pointer(&fn)) + // + // TODO: maybe limit this case to just pointer sized variable? + // + // In summary, the "overwrite" variable and the final result are + // + // new sym old sym result // --------------------------------------------- - // DATA DATA true => ERROR - // DATA lg/eq BSS sm/eq true => new wins - // DATA small BSS large true => ERROR - // BSS large DATA small true => ERROR - // BSS large BSS small true => new wins - // BSS sm/eq D/B lg/eq false => old wins - overwrite := r.DataSize(li) != 0 || oldsz < sz - if overwrite { + // TEXT BSS new wins + // DATA DATA ERROR + // DATA lg/eq BSS sm/eq new wins + // DATA small BSS large ERROR + // BSS large DATA small ERROR + // BSS large BSS small new wins + // BSS sm/eq D/B lg/eq old wins + // BSS TEXT old wins + oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())] + newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())] + oldIsText := oldtyp.IsText() + newIsText := newtyp.IsText() + oldHasContent := oldr.DataSize(oldli) != 0 + newHasContent := r.DataSize(li) != 0 + oldIsBSS := oldtyp.IsData() && !oldHasContent + newIsBSS := newtyp.IsData() && !newHasContent + switch { + case newIsText && oldIsBSS, + newHasContent && oldIsBSS && sz >= oldsz, + newIsBSS && oldIsBSS && sz > oldsz: // new symbol overwrites old symbol. - oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())] - if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) || oldsz > sz { - log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg) - } l.objSyms[oldi] = objSym{r.objidx, li} - } else { - // old symbol overwrites new symbol. - typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())] - if !typ.IsData() { // only allow overwriting data symbol - log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg) - } + case newIsBSS && (oldsz >= sz || oldIsText): + // old win, just ignore the new symbol. + default: + log.Fatalf("duplicated definition of symbol %s, from %s (type %s size %d) and %s (type %s size %d)", name, r.unit.Lib.Pkg, newtyp, sz, oldr.unit.Lib.Pkg, oldtyp, oldsz) } return oldi } diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index cd2f9e39..7231a36d 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -1512,6 +1512,9 @@ func TestCheckLinkname(t *testing.T) { {"ok.go", true}, // push linkname is ok {"push.go", true}, + // using a linknamed variable to reference an assembly + // function in the same package is ok + {"textvar", true}, // pull linkname of blocked symbol is not ok {"coro.go", false}, {"coro_var.go", false}, @@ -1529,7 +1532,7 @@ func TestCheckLinkname(t *testing.T) { test := test t.Run(test.src, func(t *testing.T) { t.Parallel() - src := filepath.Join("testdata", "linkname", test.src) + src := "./testdata/linkname/" + test.src exe := filepath.Join(tmpdir, test.src+".exe") cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src) out, err := cmd.CombinedOutput() diff --git a/src/cmd/link/testdata/linkname/textvar/asm.s b/src/cmd/link/testdata/linkname/textvar/asm.s new file mode 100644 index 00000000..332dcdb4 --- /dev/null +++ b/src/cmd/link/testdata/linkname/textvar/asm.s @@ -0,0 +1,6 @@ +// 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. + +TEXT ·asmfunc(SB),0,$0-0 + RET diff --git a/src/cmd/link/testdata/linkname/textvar/main.go b/src/cmd/link/testdata/linkname/textvar/main.go new file mode 100644 index 00000000..b38995e7 --- /dev/null +++ b/src/cmd/link/testdata/linkname/textvar/main.go @@ -0,0 +1,17 @@ +// 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. + +// Using a linknamed variable to reference an assembly +// function in the same package is ok. + +package main + +import _ "unsafe" + +func main() { + println(&asmfunc) +} + +//go:linkname asmfunc +var asmfunc uintptr diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 5fe93c61..7cc0fb2e 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -841,31 +841,45 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e } } - if len(opts.KeyUsages) == 0 { - opts.KeyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} - } - - for _, eku := range opts.KeyUsages { - if eku == ExtKeyUsageAny { - // If any key usage is acceptable, no need to check the chain for - // key usages. - return candidateChains, nil - } - } - chains = make([][]*Certificate, 0, len(candidateChains)) - var incompatibleKeyUsageChains, invalidPoliciesChains int + + var invalidPoliciesChains int for _, candidate := range candidateChains { - if !checkChainForKeyUsage(candidate, opts.KeyUsages) { - incompatibleKeyUsageChains++ - continue - } if !policiesValid(candidate, opts) { invalidPoliciesChains++ continue } chains = append(chains, candidate) } + + if len(chains) == 0 { + return nil, CertificateInvalidError{c, NoValidChains, "all candidate chains have invalid policies"} + } + + for _, eku := range opts.KeyUsages { + if eku == ExtKeyUsageAny { + // If any key usage is acceptable, no need to check the chain for + // key usages. + return chains, nil + } + } + + if len(opts.KeyUsages) == 0 { + opts.KeyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} + } + + candidateChains = chains + chains = chains[:0] + + var incompatibleKeyUsageChains int + for _, candidate := range candidateChains { + if !checkChainForKeyUsage(candidate, opts.KeyUsages) { + incompatibleKeyUsageChains++ + continue + } + chains = append(chains, candidate) + } + if len(chains) == 0 { var details []string if incompatibleKeyUsageChains > 0 { diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go index 1175e7d8..7991f499 100644 --- a/src/crypto/x509/verify_test.go +++ b/src/crypto/x509/verify_test.go @@ -3012,3 +3012,39 @@ func TestPoliciesValid(t *testing.T) { }) } } + +func TestInvalidPolicyWithAnyKeyUsage(t *testing.T) { + loadTestCert := func(t *testing.T, path string) *Certificate { + b, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + p, _ := pem.Decode(b) + c, err := ParseCertificate(p.Bytes) + if err != nil { + t.Fatal(err) + } + return c + } + + testOID3 := mustNewOIDFromInts([]uint64{1, 2, 840, 113554, 4, 1, 72585, 2, 3}) + root, intermediate, leaf := loadTestCert(t, "testdata/policy_root.pem"), loadTestCert(t, "testdata/policy_intermediate_require.pem"), loadTestCert(t, "testdata/policy_leaf.pem") + + expectedErr := "x509: no valid chains built: all candidate chains have invalid policies" + + roots, intermediates := NewCertPool(), NewCertPool() + roots.AddCert(root) + intermediates.AddCert(intermediate) + + _, err := leaf.Verify(VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + KeyUsages: []ExtKeyUsage{ExtKeyUsageAny}, + CertificatePolicies: []OID{testOID3}, + }) + if err == nil { + t.Fatal("unexpected success, invalid policy shouldn't be bypassed by passing VerifyOptions.KeyUsages with ExtKeyUsageAny") + } else if err.Error() != expectedErr { + t.Fatalf("unexpected error, got %q, want %q", err, expectedErr) + } +} diff --git a/src/hash/maphash/maphash_purego.go b/src/hash/maphash/maphash_purego.go index 53636a48..07b5eaa4 100644 --- a/src/hash/maphash/maphash_purego.go +++ b/src/hash/maphash/maphash_purego.go @@ -161,7 +161,7 @@ func appendT(h *Hash, v reflect.Value) { case reflect.Bool: h.WriteByte(btoi(v.Bool())) return - case reflect.UnsafePointer, reflect.Pointer: + case reflect.UnsafePointer, reflect.Pointer, reflect.Chan: var buf [8]byte // because pointing to the abi.Escape call in comparableReady, // So this is ok to hash pointer, diff --git a/src/hash/maphash/maphash_test.go b/src/hash/maphash/maphash_test.go index 4a85c8a6..c39876c3 100644 --- a/src/hash/maphash/maphash_test.go +++ b/src/hash/maphash/maphash_test.go @@ -253,12 +253,17 @@ func TestComparable(t *testing.T) { } testComparable(t, s1, s2) testComparable(t, s1.s, s2.s) + c1 := make(chan struct{}) + c2 := make(chan struct{}) + testComparable(t, c1, c1) + testComparable(t, chan struct{}(nil)) testComparable(t, float32(0), negativeZero[float32]()) testComparable(t, float64(0), negativeZero[float64]()) testComparableNoEqual(t, math.NaN(), math.NaN()) testComparableNoEqual(t, [2]string{"a", ""}, [2]string{"", "a"}) testComparableNoEqual(t, struct{ a, b string }{"foo", ""}, struct{ a, b string }{"", "foo"}) testComparableNoEqual(t, struct{ a, b any }{int(0), struct{}{}}, struct{ a, b any }{struct{}{}, int(0)}) + testComparableNoEqual(t, c1, c2) } func testComparableNoEqual[T comparable](t *testing.T, v1, v2 T) { diff --git a/src/internal/syscall/windows/at_windows.go b/src/internal/syscall/windows/at_windows.go index 18429773..05170b21 100644 --- a/src/internal/syscall/windows/at_windows.go +++ b/src/internal/syscall/windows/at_windows.go @@ -88,6 +88,7 @@ func Openat(dirfd syscall.Handle, name string, flag int, perm uint32) (_ syscall switch { case flag&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): disposition = FILE_CREATE + options |= FILE_OPEN_REPARSE_POINT // don't follow symlinks case flag&syscall.O_CREAT == syscall.O_CREAT: disposition = FILE_OPEN_IF default: diff --git a/src/net/http/client.go b/src/net/http/client.go index 9231f63e..a814cf3b 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -805,7 +805,8 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensit for k, vv := range ireqhdr { sensitive := false switch CanonicalHeaderKey(k) { - case "Authorization", "Www-Authenticate", "Cookie", "Cookie2": + case "Authorization", "Www-Authenticate", "Cookie", "Cookie2", + "Proxy-Authorization", "Proxy-Authenticate": sensitive = true } if !(sensitive && stripSensitiveHeaders) { diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 1ce95395..8ab4f58d 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -1547,6 +1547,8 @@ func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) { if r.Host+r.URL.Path != "a.example.com/" { if h := r.Header.Get("Authorization"); h != "" { t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h) + } else if h := r.Header.Get("Proxy-Authorization"); h != "" { + t.Errorf("on request to %v%v, Proxy-Authorization=%q, want no header", r.Host, r.URL.Path, h) } } // Follow a chain of redirects from a to b and back to a. @@ -1575,6 +1577,7 @@ func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) { req, _ := NewRequest("GET", proto+"://a.example.com/", nil) req.Header.Add("Cookie", "foo=bar") req.Header.Add("Authorization", "secretpassword") + req.Header.Add("Proxy-Authorization", "secretpassword") res, err := c.Do(req) if err != nil { t.Fatal(err) diff --git a/src/os/os_test.go b/src/os/os_test.go index 4ddbe602..3b348f93 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -2223,6 +2223,31 @@ func TestFilePermissions(t *testing.T) { } +func TestOpenFileCreateExclDanglingSymlink(t *testing.T) { + testMaybeRooted(t, func(t *testing.T, r *Root) { + const link = "link" + if err := Symlink("does_not_exist", link); err != nil { + t.Fatal(err) + } + var f *File + var err error + if r == nil { + f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444) + } else { + f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444) + } + if err == nil { + f.Close() + } + if !errors.Is(err, ErrExist) { + t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL = %v, want ErrExist", err) + } + if _, err := Stat(link); err == nil { + t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL created a file") + } + }) +} + // TestFileRDWRFlags tests the O_RDONLY, O_WRONLY, and O_RDWR flags. func TestFileRDWRFlags(t *testing.T) { for _, test := range []struct { diff --git a/src/runtime/debug/mod.go b/src/runtime/debug/mod.go index 53bbf1d8..3eab0874 100644 --- a/src/runtime/debug/mod.go +++ b/src/runtime/debug/mod.go @@ -77,6 +77,7 @@ type Module struct { // - CGO_CPPFLAGS: the effective CGO_CPPFLAGS environment variable // - CGO_CXXFLAGS: the effective CGO_CXXFLAGS environment variable // - CGO_LDFLAGS: the effective CGO_LDFLAGS environment variable +// - DefaultGODEBUG: the effective GODEBUG settings // - GOARCH: the architecture target // - GOAMD64/GOARM/GO386/etc: the architecture feature level for GOARCH // - GOOS: the operating system target diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 344f6c32..1c5a6777 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -376,20 +376,6 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) { if flag&O_CLOEXEC == 0 { sa = makeInheritSa() } - // We don't use CREATE_ALWAYS, because when opening a file with - // FILE_ATTRIBUTE_READONLY these will replace an existing file - // with a new, read-only one. See https://go.dev/issue/38225. - // - // Instead, we ftruncate the file after opening when O_TRUNC is set. - var createmode uint32 - switch { - case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL): - createmode = CREATE_NEW - case flag&O_CREAT == O_CREAT: - createmode = OPEN_ALWAYS - default: - createmode = OPEN_EXISTING - } var attrs uint32 = FILE_ATTRIBUTE_NORMAL if perm&S_IWRITE == 0 { attrs = FILE_ATTRIBUTE_READONLY @@ -404,6 +390,21 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) { const _FILE_FLAG_WRITE_THROUGH = 0x80000000 attrs |= _FILE_FLAG_WRITE_THROUGH } + // We don't use CREATE_ALWAYS, because when opening a file with + // FILE_ATTRIBUTE_READONLY these will replace an existing file + // with a new, read-only one. See https://go.dev/issue/38225. + // + // Instead, we ftruncate the file after opening when O_TRUNC is set. + var createmode uint32 + switch { + case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL): + createmode = CREATE_NEW + attrs |= FILE_FLAG_OPEN_REPARSE_POINT // don't follow symlinks + case flag&O_CREAT == O_CREAT: + createmode = OPEN_ALWAYS + default: + createmode = OPEN_EXISTING + } h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0) if h == InvalidHandle { if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {