diff --git a/doc/go1.17.html b/doc/go1.17.html index ee498f7603..7438d894fe 100644 --- a/doc/go1.17.html +++ b/doc/go1.17.html @@ -224,12 +224,6 @@ Do not send CLs removing the interior tags from such phrases. TODO: complete the Vet section
-- TODO: complete the Runtime section -
-@@ -434,7 +428,7 @@ Do not send CLs removing the interior tags from such phrases.
- TODO: https://golang.org/cl/271788: panic if flag name begins with - or contains = + Flag declarations now panic if an invalid name is specified.
+ The constants SYS_WAIT6
and WEXITED
+ are now defined on NetBSD systems (SYS_WAIT6
was
+ already defined on DragonFly and FreeBSD systems;
+ WEXITED
was already defined on Darwin, DragonFly,
+ FreeBSD, Linux, and Solaris systems).
+
- TODO: https://golang.org/cl/310033: add -shuffle=off|on|N to alter the execution order of tests and benchmarks
+ Added a new testing flag -shuffle
which controls the execution order of tests and benchmarks.
The new
T.Setenv
@@ -689,15 +690,26 @@ Do not send CLs removing the interior tags from such phrases.
- TODO: https://golang.org/cl/264077: add Time.IsDST() to check if its Location is in Daylight Savings Time
+ The new Time.IsDST
method can be used to check whether the time
+ is in Daylight Savings Time in its configured location.
- TODO: https://golang.org/cl/293349: add Time.Unix{Milli,Micro} and to-Time helpers UnixMicro, UnixMilli
+ The new Time.UnixMilli
and
+ Time.UnixMicro
methods return the number of milliseconds and
+ microseconds elapsed since January 1, 1970 UTC respectively.
+ The new UnixMilli
and UnixMicro
functions return local Time corresponding to given
+ Unix time.
- TODO: https://golang.org/cl/300996: support "," as separator for fractional seconds + The package now accepts comma "," as a separator for fractional seconds when parsing and formatting time. + The following time formats are now accepted: +
diff --git a/doc/go_spec.html b/doc/go_spec.html index e59b3554f2..561d44271a 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -4909,7 +4909,7 @@ if x := f(); x < y {
"Switch" statements provide multi-way execution. -An expression or type specifier is compared to the "cases" +An expression or type is compared to the "cases" inside the "switch" to determine which branch to execute.
@@ -5020,7 +5020,7 @@ floating point, or string constants in case expressions. A type switch compares types rather than values. It is otherwise similar to an expression switch. It is marked by a special switch expression that has the form of a type assertion -using the reserved wordtype
rather than an actual type:
+using the keyword type
rather than an actual type:
diff --git a/misc/cgo/errors/errors_test.go b/misc/cgo/errors/errors_test.go index a077b59478..68a30a44fe 100644 --- a/misc/cgo/errors/errors_test.go +++ b/misc/cgo/errors/errors_test.go @@ -40,7 +40,8 @@ func check(t *testing.T, file string) { if len(frags) == 1 { continue } - re, err := regexp.Compile(string(frags[1])) + frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1]) + re, err := regexp.Compile(frag) if err != nil { t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1]) continue diff --git a/misc/cgo/errors/testdata/err2.go b/misc/cgo/errors/testdata/err2.go index 1d22401aee..a90598fe35 100644 --- a/misc/cgo/errors/testdata/err2.go +++ b/misc/cgo/errors/testdata/err2.go @@ -40,15 +40,15 @@ func main() { C.foop = x // ERROR HERE // issue 13129: used to output error about C.unsignedshort with CC=clang - var x C.ushort - x = int(0) // ERROR HERE: C\.ushort + var x1 C.ushort + x1 = int(0) // ERROR HERE: C\.ushort // issue 13423 _ = C.fopen() // ERROR HERE // issue 13467 - var x rune = '✈' - var _ rune = C.transform(x) // ERROR HERE: C\.int + var x2 rune = '✈' + var _ rune = C.transform(x2) // ERROR HERE: C\.int // issue 13635: used to output error about C.unsignedchar. // This test tests all such types. @@ -91,10 +91,10 @@ func main() { // issue 26745 _ = func(i int) int { - return C.i + 1 // ERROR HERE: :13 + return C.i + 1 // ERROR HERE: 14 } _ = func(i int) { - C.fi(i) // ERROR HERE: :6 + C.fi(i) // ERROR HERE: 7 } C.fi = C.fi // ERROR HERE diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index ae61725bc7..a73e998877 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -1638,6 +1638,8 @@ func (p *Package) gccCmd() []string { c = append(c, "-maix64") c = append(c, "-mcmodel=large") } + // disable LTO so we get an object whose symbols we can read + c = append(c, "-fno-lto") c = append(c, "-") //read input from standard input return c } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 8c31d5b794..94152f4278 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -168,8 +168,18 @@ func (p *Package) writeDefs() { if *gccgo { fmt.Fprintf(fc, "extern byte *%s;\n", n.C) } else { - fmt.Fprintf(fm, "extern char %s[];\n", n.C) - fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) + // Force a reference to all symbols so that + // the external linker will add DT_NEEDED + // entries as needed on ELF systems. + // Treat function variables differently + // to avoid type confict errors from LTO + // (Link Time Optimization). + if n.Kind == "fpvar" { + fmt.Fprintf(fm, "extern void %s();\n", n.C) + } else { + fmt.Fprintf(fm, "extern char %s[];\n", n.C) + fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) + } fmt.Fprintf(fgo2, "//go:linkname __cgo_%s %s\n", n.C, n.C) fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", n.C) fmt.Fprintf(fgo2, "var __cgo_%s byte\n", n.C) @@ -1042,7 +1052,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { // This unpacks the argument struct above and calls the Go function. fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype) - fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) + fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName) if gccResult != "void" { // Write results back to frame. diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go index cb8e9d7b0f..b8ea1955d1 100644 --- a/src/cmd/compile/internal/abi/abiutils.go +++ b/src/cmd/compile/internal/abi/abiutils.go @@ -449,7 +449,7 @@ func (config *ABIConfig) ABIAnalyze(t *types.Type, setNname bool) *ABIParamResul // parameterUpdateMu protects the Offset field of function/method parameters (a subset of structure Fields) var parameterUpdateMu sync.Mutex -// FieldOffsetOf returns a concurency-safe version of f.Offset +// FieldOffsetOf returns a concurrency-safe version of f.Offset func FieldOffsetOf(f *types.Field) int64 { parameterUpdateMu.Lock() defer parameterUpdateMu.Unlock() diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 2fb852b184..08c05a69be 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -886,9 +886,6 @@ func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype { if n == nil { return nil } - if _, ok := n.(ir.Ntype); !ok { - ir.Dump("NOT NTYPE", n) - } return n.(ir.Ntype) } diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go index f9ee686f9e..15756a47e4 100644 --- a/src/cmd/compile/internal/typecheck/func.go +++ b/src/cmd/compile/internal/typecheck/func.go @@ -999,6 +999,12 @@ func tcRecover(n *ir.CallExpr) ir.Node { // tcUnsafeAdd typechecks an OUNSAFEADD node. func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr { + if !types.AllowsGoVersion(curpkg(), 1, 17) { + base.ErrorfVers("go1.17", "unsafe.Add") + n.SetType(nil) + return n + } + n.X = AssignConv(Expr(n.X), types.Types[types.TUNSAFEPTR], "argument to unsafe.Add") n.Y = DefaultLit(Expr(n.Y), types.Types[types.TINT]) if n.X.Type() == nil || n.Y.Type() == nil { @@ -1015,6 +1021,12 @@ func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr { // tcUnsafeSlice typechecks an OUNSAFESLICE node. func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr { + if !types.AllowsGoVersion(curpkg(), 1, 17) { + base.ErrorfVers("go1.17", "unsafe.Slice") + n.SetType(nil) + return n + } + n.X = Expr(n.X) n.Y = Expr(n.Y) if n.X.Type() == nil || n.Y.Type() == nil { diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index 8e13b0ff18..8f2d849ef5 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -579,6 +579,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( case _Add: // unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer + if !check.allowVersion(check.pkg, 1, 17) { + check.error(call.Fun, "unsafe.Add requires go1.17 or later") + return + } + check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add") if x.mode == invalid { return @@ -675,6 +680,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( case _Slice: // unsafe.Slice(ptr *T, len IntegerType) []T + if !check.allowVersion(check.pkg, 1, 17) { + check.error(call.Fun, "unsafe.Slice requires go1.17 or later") + return + } + typ := asPointer(x.typ) if typ == nil { check.errorf(x, invalidArg+"%s is not a pointer", x) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 50bf80ba59..bc49c6d804 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -722,14 +722,29 @@ func (t *tester) registerTests() { }, }) if t.hasCxx() { - t.tests = append(t.tests, distTest{ - name: "swig_callback", - heading: "../misc/swig/callback", - fn: func(dt *distTest) error { - t.addCmd(dt, "misc/swig/callback", t.goTest()) - return nil + t.tests = append(t.tests, + distTest{ + name: "swig_callback", + heading: "../misc/swig/callback", + fn: func(dt *distTest) error { + t.addCmd(dt, "misc/swig/callback", t.goTest()) + return nil + }, }, - }) + distTest{ + name: "swig_callback_lto", + heading: "../misc/swig/callback", + fn: func(dt *distTest) error { + cmd := t.addCmd(dt, "misc/swig/callback", t.goTest()) + cmd.Env = append(os.Environ(), + "CGO_CFLAGS=-flto", + "CGO_CXXFLAGS=-flto", + "CGO_LDFLAGS=-flto", + ) + return nil + }, + }, + ) } } } diff --git a/src/cmd/link/cgo_test.go b/src/cmd/link/cgo_test.go new file mode 100644 index 0000000000..26ab802454 --- /dev/null +++ b/src/cmd/link/cgo_test.go @@ -0,0 +1,141 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "internal/testenv" + "os" + "os/exec" + "path/filepath" + "testing" +) + +// Issues 43830, 46295 +func TestCGOLTO(t *testing.T) { + testenv.MustHaveCGO(t) + testenv.MustHaveGoBuild(t) + + t.Parallel() + + for _, cc := range []string{"gcc", "clang"} { + for test := 0; test < 2; test++ { + t.Run(fmt.Sprintf("%s-%d", cc, test), func(t *testing.T) { + testCGOLTO(t, cc, test) + }) + } + } +} + +const test1_main = ` +package main + +/* +extern int myadd(int, int); +int c_add(int a, int b) { + return myadd(a, b); +} +*/ +import "C" + +func main() { + println(C.c_add(1, 2)) +} +` + +const test1_add = ` +package main + +import "C" + +/* test */ + +//export myadd +func myadd(a C.int, b C.int) C.int { + return a + b +} +` + +const test2_main = ` +package main + +import "fmt" + +/* +#include+ +void hello(void) { + printf("hello\n"); +} +*/ +import "C" + +func main() { + hello := C.hello + fmt.Printf("%v\n", hello) +} +` + +func testCGOLTO(t *testing.T, cc string, test int) { + t.Parallel() + + if _, err := exec.LookPath(cc); err != nil { + t.Skipf("no %s compiler", cc) + } + + dir := t.TempDir() + + writeTempFile := func(name, contents string) { + if err := os.WriteFile(filepath.Join(dir, name), []byte(contents), 0644); err != nil { + t.Fatal(err) + } + } + + writeTempFile("go.mod", "module cgolto\n") + + switch test { + case 0: + writeTempFile("main.go", test1_main) + writeTempFile("add.go", test1_add) + case 1: + writeTempFile("main.go", test2_main) + default: + t.Fatalf("bad case %d", test) + } + + cmd := exec.Command(testenv.GoToolPath(t), "build") + cmd.Dir = dir + cmd.Env = append(os.Environ(), + "CC="+cc, + "CGO_CFLAGS=-flto", + ) + + t.Log("go build") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + + if err != nil { + t.Logf("go build failed: %v", err) + + // Error messages we've seen indicating that LTO is not supported. + // These errors come from GCC or clang, not Go. + var noLTO = []string{ + `unrecognized command line option "-flto"`, + "unable to pass LLVM bit-code files to linker", + "file not recognized: File format not recognized", + "LTO support has not been enabled", + "linker command failed with exit code", + "gcc: can't load library", + } + for _, msg := range noLTO { + if bytes.Contains(out, []byte(msg)) { + t.Skipf("C compiler %v does not support LTO", cc) + } + } + + t.Error("failed") + } +} diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index 22f53a4df2..23915f9032 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -124,6 +124,10 @@ func hostArchive(ctxt *Link, name string) { libgcc := sym.Library{Pkg: "libgcc"} h := ldobj(ctxt, f, &libgcc, l, pname, name) + if h.ld == nil { + Errorf(nil, "%s unrecognized object file at offset %d", name, off) + continue + } f.MustSeek(h.off, 0) h.ld(ctxt, f, h.pkg, h.length, h.pn) } diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go index ae0d7520eb..20f1d0b8c1 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -241,6 +241,10 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { return true, "dynamically linking with a shared library" } + if unknownObjFormat { + return true, "some input objects have an unrecognized file format" + } + return false, "" } @@ -248,7 +252,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { // // It is called after flags are processed and inputs are processed, // so the ctxt.LinkMode variable has an initial value from the -linkmode -// flag and the iscgo externalobj variables are set. +// flag and the iscgo, externalobj, and unknownObjFormat variables are set. func determineLinkMode(ctxt *Link) { extNeeded, extReason := mustLinkExternal(ctxt) via := "" diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index e8f001ba8e..644faeb2fb 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -343,10 +343,16 @@ var ( const pkgdef = "__.PKGDEF" var ( - // Set if we see an object compiled by the host compiler that is not - // from a package that is known to support internal linking mode. + // externalobj is set to true if we see an object compiled by + // the host compiler that is not from a package that is known + // to support internal linking mode. externalobj = false - theline string + + // unknownObjFormat is set to true if we see an object whose + // format we don't recognize. + unknownObjFormat = false + + theline string ) func Lflag(ctxt *Link, arg string) { @@ -1065,6 +1071,10 @@ func hostobjs(ctxt *Link) { } f.MustSeek(h.off, 0) + if h.ld == nil { + Errorf(nil, "%s: unrecognized object file format", h.pn) + continue + } h.ld(ctxt, f, h.pkg, h.length, h.pn) f.Close() } @@ -1855,6 +1865,14 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) } + if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' { + // An unrecognized object is just passed to the external linker. + // If we try to read symbols from this object, we will + // report an error at that time. + unknownObjFormat = true + return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file) + } + /* check the header */ line, err := f.ReadString('\n') if err != nil { @@ -1874,7 +1892,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, return nil } - Errorf(nil, "%s: not an object file: @%d %02x%02x%02x%02x", pn, start, c1, c2, c3, c4) + Errorf(nil, "%s: not an object file: @%d %q", pn, start, line) return nil } diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index 3540c07da1..8eb4231c3a 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -475,7 +475,7 @@ func (f *peFile) addDWARFSection(name string, size int) *peSection { off := f.stringTable.add(name) h := f.addSection(name, size, size) h.shortName = fmt.Sprintf("/%d", off) - h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA + h.characteristics = IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA return h } diff --git a/src/crypto/elliptic/elliptic.go b/src/crypto/elliptic/elliptic.go index b8e5a3097d..f072960bfe 100644 --- a/src/crypto/elliptic/elliptic.go +++ b/src/crypto/elliptic/elliptic.go @@ -455,7 +455,7 @@ func initP384() { // Multiple invocations of this function will return the same value, so it can // be used for equality checks and switch statements. // -// The cryptographic operations are implemented using constant-time algorithms. +// ScalarMult and ScalarBaseMult are implemented using constant-time algorithms. func P256() Curve { initonce.Do(initAll) return p256 @@ -479,7 +479,7 @@ func P384() Curve { // Multiple invocations of this function will return the same value, so it can // be used for equality checks and switch statements. // -// The cryptographic operations do not use constant-time algorithms. +// The cryptographic operations are implemented using constant-time algorithms. func P521() Curve { initonce.Do(initAll) return p521 diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index 77957ef82b..d561e61707 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -619,7 +619,7 @@ type Config struct { // protocol will be one from this list, and the connection will fail // if there is no mutually supported protocol. If NextProtos is empty // or the peer doesn't support ALPN, the connection will succeed and - // ConnectionState.NegotiatedProtocol will be empty." + // ConnectionState.NegotiatedProtocol will be empty. NextProtos []string // ServerName is used to verify the hostname on the returned diff --git a/src/fmt/doc.go b/src/fmt/doc.go index d05ee519c3..c584cc9465 100644 --- a/src/fmt/doc.go +++ b/src/fmt/doc.go @@ -189,7 +189,7 @@ When printing a struct, fmt cannot and therefore does not invoke formatting methods such as Error or String on unexported fields. - Explicit argument indexes: + Explicit argument indexes In Printf, Sprintf, and Fprintf, the default behavior is for each formatting verb to format successive arguments passed in the call. @@ -211,7 +211,7 @@ fmt.Sprintf("%d %d %#[1]x %#x", 16, 17) will yield "16 17 0x10 0x11". - Format errors: + Format errors If an invalid argument is given for a verb, such as providing a string to %d, the generated string will contain a diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index b74daca246..73cf6334fd 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -145,17 +145,14 @@ func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDi err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path) case "$$B\n": - var data []byte - data, err = io.ReadAll(buf) - if err != nil { - break - } + var exportFormat byte + exportFormat, err = buf.ReadByte() // The indexed export format starts with an 'i'; the older // binary export format starts with a 'c', 'd', or 'v' // (from "version"). Select appropriate importer. - if len(data) > 0 && data[0] == 'i' { - _, pkg, err = iImportData(fset, packages, data[1:], id) + if err == nil && exportFormat == 'i' { + pkg, err = iImportData(fset, packages, buf, id) } else { err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path) } diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go index e003dc9767..b300860e94 100644 --- a/src/go/internal/gcimporter/iimport.go +++ b/src/go/internal/gcimporter/iimport.go @@ -8,6 +8,7 @@ package gcimporter import ( + "bufio" "bytes" "encoding/binary" "fmt" @@ -20,7 +21,7 @@ import ( ) type intReader struct { - *bytes.Reader + *bufio.Reader path string } @@ -73,7 +74,7 @@ const ( // and returns the number of bytes consumed and a reference to the package. // If the export data version is not recognized or the format is otherwise // compromised, an error is returned. -func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { +func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataReader *bufio.Reader, path string) (pkg *types.Package, err error) { const currentVersion = iexportVersionCurrent version := int64(-1) defer func() { @@ -86,7 +87,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data [] } }() - r := &intReader{bytes.NewReader(data), path} + r := &intReader{dataReader, path} version = int64(r.uint64()) switch version { @@ -102,10 +103,12 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data [] sLen := int64(r.uint64()) dLen := int64(r.uint64()) - whence, _ := r.Seek(0, io.SeekCurrent) - stringData := data[whence : whence+sLen] - declData := data[whence+sLen : whence+sLen+dLen] - r.Seek(sLen+dLen, io.SeekCurrent) + data := make([]byte, sLen+dLen) + if _, err := io.ReadFull(r, data); err != nil { + errorf("cannot read %d bytes of stringData and declData: %s", len(data), err) + } + stringData := data[:sLen] + declData := data[sLen:] p := iimporter{ exportVersion: version, @@ -182,9 +185,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data [] // package was imported completely and without errors localpkg.MarkComplete() - - consumed, _ := r.Seek(0, io.SeekCurrent) - return int(consumed), localpkg, nil + return localpkg, nil } type iimporter struct { diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 739051cc61..2a2d54da88 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -588,6 +588,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Add: // unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer + if !check.allowVersion(check.pkg, 1, 17) { + check.errorf(call.Fun, _InvalidUnsafeAdd, "unsafe.Add requires go1.17 or later") + return + } + check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add") if x.mode == invalid { return @@ -684,6 +689,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Slice: // unsafe.Slice(ptr *T, len IntegerType) []T + if !check.allowVersion(check.pkg, 1, 17) { + check.errorf(call.Fun, _InvalidUnsafeSlice, "unsafe.Slice requires go1.17 or later") + return + } + typ := asPointer(x.typ) if typ == nil { check.invalidArg(x, _InvalidUnsafeSlice, "%s is not a pointer", x) diff --git a/src/io/fs/sub.go b/src/io/fs/sub.go index 7822e555ea..ae20e030a9 100644 --- a/src/io/fs/sub.go +++ b/src/io/fs/sub.go @@ -19,10 +19,10 @@ type SubFS interface { // Sub returns an FS corresponding to the subtree rooted at fsys's dir. // -// If fs implements SubFS, Sub calls returns fsys.Sub(dir). -// Otherwise, if dir is ".", Sub returns fsys unchanged. +// If dir is ".", Sub returns fsys unchanged. +// Otherwise, if fs implements SubFS, Sub returns fsys.Sub(dir). // Otherwise, Sub returns a new FS implementation sub that, -// in effect, implements sub.Open(dir) as fsys.Open(path.Join(dir, name)). +// in effect, implements sub.Open(name) as fsys.Open(path.Join(dir, name)). // The implementation also translates calls to ReadDir, ReadFile, and Glob appropriately. // // Note that Sub(os.DirFS("/"), "prefix") is equivalent to os.DirFS("/prefix") diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index a718e75a72..a59be7fea0 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -1898,61 +1898,62 @@ func TestCVE202133195(t *testing.T) { // Change the default resolver to match our manipulated resolver originalDefault := DefaultResolver DefaultResolver = &r - defer func() { - DefaultResolver = originalDefault - }() + defer func() { DefaultResolver = originalDefault }() + // Redirect host file lookups. + defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath) + testHookHostsPath = "testdata/hosts" _, err := r.LookupCNAME(context.Background(), "golang.org") if expected := "lookup golang.org: CNAME target is invalid"; err == nil || err.Error() != expected { - t.Errorf("Resolver.LookupCNAME returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("Resolver.LookupCNAME returned unexpected error, got %q, want %q", err, expected) } _, err = LookupCNAME("golang.org") if expected := "lookup golang.org: CNAME target is invalid"; err == nil || err.Error() != expected { - t.Errorf("LookupCNAME returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("LookupCNAME returned unexpected error, got %q, want %q", err, expected) } _, _, err = r.LookupSRV(context.Background(), "target", "tcp", "golang.org") if expected := "lookup golang.org: SRV target is invalid"; err == nil || err.Error() != expected { - t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err, expected) } _, _, err = LookupSRV("target", "tcp", "golang.org") if expected := "lookup golang.org: SRV target is invalid"; err == nil || err.Error() != expected { - t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected) } _, _, err = r.LookupSRV(context.Background(), "hdr", "tcp", "golang.org") if expected := "lookup golang.org: SRV header name is invalid"; err == nil || err.Error() != expected { - t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err, expected) } _, _, err = LookupSRV("hdr", "tcp", "golang.org") if expected := "lookup golang.org: SRV header name is invalid"; err == nil || err.Error() != expected { - t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected) } _, err = r.LookupMX(context.Background(), "golang.org") if expected := "lookup golang.org: MX target is invalid"; err == nil || err.Error() != expected { - t.Errorf("Resolver.LookupMX returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("Resolver.LookupMX returned unexpected error, got %q, want %q", err, expected) } _, err = LookupMX("golang.org") if expected := "lookup golang.org: MX target is invalid"; err == nil || err.Error() != expected { - t.Errorf("LookupMX returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("LookupMX returned unexpected error, got %q, want %q", err, expected) } _, err = r.LookupNS(context.Background(), "golang.org") if expected := "lookup golang.org: NS target is invalid"; err == nil || err.Error() != expected { - t.Errorf("Resolver.LookupNS returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("Resolver.LookupNS returned unexpected error, got %q, want %q", err, expected) } _, err = LookupNS("golang.org") if expected := "lookup golang.org: NS target is invalid"; err == nil || err.Error() != expected { - t.Errorf("LookupNS returned unexpected error, got %q, want %q", err.Error(), expected) + t.Errorf("LookupNS returned unexpected error, got %q, want %q", err, expected) } - _, err = r.LookupAddr(context.Background(), "1.2.3.4") - if expected := "lookup 1.2.3.4: PTR target is invalid"; err == nil || err.Error() != expected { - t.Errorf("Resolver.LookupAddr returned unexpected error, got %q, want %q", err.Error(), expected) + _, err = r.LookupAddr(context.Background(), "192.0.2.42") + if expected := "lookup 192.0.2.42: PTR target is invalid"; err == nil || err.Error() != expected { + t.Errorf("Resolver.LookupAddr returned unexpected error, got %q, want %q", err, expected) } - _, err = LookupAddr("1.2.3.4") - if expected := "lookup 1.2.3.4: PTR target is invalid"; err == nil || err.Error() != expected { - t.Errorf("LookupAddr returned unexpected error, got %q, want %q", err.Error(), expected) + _, err = LookupAddr("192.0.2.42") + if expected := "lookup 192.0.2.42: PTR target is invalid"; err == nil || err.Error() != expected { + t.Errorf("LookupAddr returned unexpected error, got %q, want %q", err, expected) } } diff --git a/src/net/http/server.go b/src/net/http/server.go index 4e73508973..430019de50 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -577,37 +577,17 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) { return io.CopyBuffer(writerOnly{w}, src, buf) } - // sendfile path: - - // Do not start actually writing response until src is readable. - // If body length is <= sniffLen, sendfile/splice path will do - // little anyway. This small read also satisfies sniffing the - // body in case Content-Type is missing. - nr, er := src.Read(buf[:sniffLen]) - atEOF := errors.Is(er, io.EOF) - n += int64(nr) - - if nr > 0 { - // Write the small amount read normally. - nw, ew := w.Write(buf[:nr]) - if ew != nil { - err = ew - } else if nr != nw { - err = io.ErrShortWrite + // Copy the first sniffLen bytes before switching to ReadFrom. + // This ensures we don't start writing the response before the + // source is available (see golang.org/issue/5660) and provides + // enough bytes to perform Content-Type sniffing when required. + if !w.cw.wroteHeader { + n0, err := io.CopyBuffer(writerOnly{w}, io.LimitReader(src, sniffLen), buf) + n += n0 + if err != nil || n0 < sniffLen { + return n, err } } - if err == nil && er != nil && !atEOF { - err = er - } - - // Do not send StatusOK in the error case where nothing has been written. - if err == nil && !w.wroteHeader { - w.WriteHeader(StatusOK) // nr == 0, no error (or EOF) - } - - if err != nil || atEOF { - return n, err - } w.w.Flush() // get rid of any previous writes w.cw.flush() // make sure Header is written; flush data to rwc @@ -620,7 +600,7 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) { return n, err } - n0, err := io.Copy(writerOnly{w}, src) + n0, err := io.CopyBuffer(writerOnly{w}, src, buf) n += n0 return n, err } diff --git a/src/net/http/sniff_test.go b/src/net/http/sniff_test.go index 8d5350374d..e91335729a 100644 --- a/src/net/http/sniff_test.go +++ b/src/net/http/sniff_test.go @@ -157,9 +157,25 @@ func testServerIssue5953(t *testing.T, h2 bool) { resp.Body.Close() } -func TestContentTypeWithCopy_h1(t *testing.T) { testContentTypeWithCopy(t, h1Mode) } -func TestContentTypeWithCopy_h2(t *testing.T) { testContentTypeWithCopy(t, h2Mode) } -func testContentTypeWithCopy(t *testing.T, h2 bool) { +type byteAtATimeReader struct { + buf []byte +} + +func (b *byteAtATimeReader) Read(p []byte) (n int, err error) { + if len(p) < 1 { + return 0, nil + } + if len(b.buf) == 0 { + return 0, io.EOF + } + p[0] = b.buf[0] + b.buf = b.buf[1:] + return 1, nil +} + +func TestContentTypeWithVariousSources_h1(t *testing.T) { testContentTypeWithVariousSources(t, h1Mode) } +func TestContentTypeWithVariousSources_h2(t *testing.T) { testContentTypeWithVariousSources(t, h2Mode) } +func testContentTypeWithVariousSources(t *testing.T, h2 bool) { defer afterTest(t) const ( @@ -167,30 +183,86 @@ func testContentTypeWithCopy(t *testing.T, h2 bool) { expected = "text/html; charset=utf-8" ) - cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { - // Use io.Copy from a bytes.Buffer to trigger ReadFrom. - buf := bytes.NewBuffer([]byte(input)) - n, err := io.Copy(w, buf) - if int(n) != len(input) || err != nil { - t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) - } - })) - defer cst.close() + for _, test := range []struct { + name string + handler func(ResponseWriter, *Request) + }{{ + name: "write", + handler: func(w ResponseWriter, r *Request) { + // Write the whole input at once. + n, err := w.Write([]byte(input)) + if int(n) != len(input) || err != nil { + t.Errorf("w.Write(%q) = %v, %v want %d, nil", input, n, err, len(input)) + } + }, + }, { + name: "write one byte at a time", + handler: func(w ResponseWriter, r *Request) { + // Write the input one byte at a time. + buf := []byte(input) + for i := range buf { + n, err := w.Write(buf[i : i+1]) + if n != 1 || err != nil { + t.Errorf("w.Write(%q) = %v, %v want 1, nil", input, n, err) + } + } + }, + }, { + name: "copy from Reader", + handler: func(w ResponseWriter, r *Request) { + // Use io.Copy from a plain Reader. + type readerOnly struct{ io.Reader } + buf := bytes.NewBuffer([]byte(input)) + n, err := io.Copy(w, readerOnly{buf}) + if int(n) != len(input) || err != nil { + t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) + } + }, + }, { + name: "copy from bytes.Buffer", + handler: func(w ResponseWriter, r *Request) { + // Use io.Copy from a bytes.Buffer to trigger ReadFrom. + buf := bytes.NewBuffer([]byte(input)) + n, err := io.Copy(w, buf) + if int(n) != len(input) || err != nil { + t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) + } + }, + }, { + name: "copy one byte at a time", + handler: func(w ResponseWriter, r *Request) { + // Use io.Copy from a Reader that returns one byte at a time. + n, err := io.Copy(w, &byteAtATimeReader{[]byte(input)}) + if int(n) != len(input) || err != nil { + t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) + } + }, + }} { + t.Run(test.name, func(t *testing.T) { + cst := newClientServerTest(t, h2, HandlerFunc(test.handler)) + defer cst.close() + + resp, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + if ct := resp.Header.Get("Content-Type"); ct != expected { + t.Errorf("Content-Type = %q, want %q", ct, expected) + } + if want, got := resp.Header.Get("Content-Length"), fmt.Sprint(len(input)); want != got { + t.Errorf("Content-Length = %q, want %q", want, got) + } + data, err := io.ReadAll(resp.Body) + if err != nil { + t.Errorf("reading body: %v", err) + } else if !bytes.Equal(data, []byte(input)) { + t.Errorf("data is %q, want %q", data, input) + } + resp.Body.Close() + + }) - resp, err := cst.c.Get(cst.ts.URL) - if err != nil { - t.Fatalf("Get: %v", err) } - if ct := resp.Header.Get("Content-Type"); ct != expected { - t.Errorf("Content-Type = %q, want %q", ct, expected) - } - data, err := io.ReadAll(resp.Body) - if err != nil { - t.Errorf("reading body: %v", err) - } else if !bytes.Equal(data, []byte(input)) { - t.Errorf("data is %q, want %q", data, input) - } - resp.Body.Close() } func TestSniffWriteSize_h1(t *testing.T) { testSniffWriteSize(t, h1Mode) } diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go index 5710401acd..239bed198f 100644 --- a/src/os/exec_windows.go +++ b/src/os/exec_windows.go @@ -45,16 +45,6 @@ func (p *Process) wait() (ps *ProcessState, err error) { return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil } -func terminateProcess(pid, exitcode int) error { - h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid)) - if e != nil { - return NewSyscallError("OpenProcess", e) - } - defer syscall.CloseHandle(h) - e = syscall.TerminateProcess(h, uint32(exitcode)) - return NewSyscallError("TerminateProcess", e) -} - func (p *Process) signal(sig Signal) error { handle := atomic.LoadUintptr(&p.handle) if handle == uintptr(syscall.InvalidHandle) { @@ -64,16 +54,22 @@ func (p *Process) signal(sig Signal) error { return ErrProcessDone } if sig == Kill { - err := terminateProcess(p.Pid, 1) + var terminationHandle syscall.Handle + e := syscall.DuplicateHandle(^syscall.Handle(0), syscall.Handle(handle), ^syscall.Handle(0), &terminationHandle, syscall.PROCESS_TERMINATE, false, 0) + if e != nil { + return NewSyscallError("DuplicateHandle", e) + } runtime.KeepAlive(p) - return err + defer syscall.CloseHandle(terminationHandle) + e = syscall.TerminateProcess(syscall.Handle(terminationHandle), 1) + return NewSyscallError("TerminateProcess", e) } // TODO(rsc): Handle Interrupt too? return syscall.Errno(syscall.EWINDOWS) } func (p *Process) release() error { - handle := atomic.LoadUintptr(&p.handle) + handle := atomic.SwapUintptr(&p.handle, uintptr(syscall.InvalidHandle)) if handle == uintptr(syscall.InvalidHandle) { return syscall.EINVAL } @@ -81,7 +77,6 @@ func (p *Process) release() error { if e != nil { return NewSyscallError("CloseHandle", e) } - atomic.StoreUintptr(&p.handle, uintptr(syscall.InvalidHandle)) // no need for a finalizer anymore runtime.SetFinalizer(p, nil) return nil diff --git a/src/runtime/internal/sys/gengoos.go b/src/runtime/internal/sys/gengoos.go index 51f64a6e5c..ffe962f71d 100644 --- a/src/runtime/internal/sys/gengoos.go +++ b/src/runtime/internal/sys/gengoos.go @@ -48,18 +48,21 @@ func main() { if target == "nacl" { continue } - var buf bytes.Buffer - fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n") + var tags []string if target == "linux" { - fmt.Fprintf(&buf, "// +build !android\n") // must explicitly exclude android for linux + tags = append(tags, "!android") // must explicitly exclude android for linux } if target == "solaris" { - fmt.Fprintf(&buf, "// +build !illumos\n") // must explicitly exclude illumos for solaris + tags = append(tags, "!illumos") // must explicitly exclude illumos for solaris } if target == "darwin" { - fmt.Fprintf(&buf, "// +build !ios\n") // must explicitly exclude ios for darwin + tags = append(tags, "!ios") // must explicitly exclude ios for darwin } - fmt.Fprintf(&buf, "// +build %s\n\n", target) // must explicitly include target for bootstrapping purposes + tags = append(tags, target) // must explicitly include target for bootstrapping purposes + var buf bytes.Buffer + fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n") + fmt.Fprintf(&buf, "//go:build %s\n", strings.Join(tags, " && ")) + fmt.Fprintf(&buf, "// +build %s\n\n", strings.Join(tags, ",")) fmt.Fprintf(&buf, "package sys\n\n") fmt.Fprintf(&buf, "const GOOS = `%s`\n\n", target) for _, goos := range gooses { @@ -81,6 +84,7 @@ func main() { } var buf bytes.Buffer fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n") + fmt.Fprintf(&buf, "//go:build %s\n", target) fmt.Fprintf(&buf, "// +build %s\n\n", target) // must explicitly include target for bootstrapping purposes fmt.Fprintf(&buf, "package sys\n\n") fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target) diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 63eeb6c9c7..24566e5efa 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -4071,8 +4071,16 @@ func exitsyscall0(gp *g) { if schedEnabled(gp) { _p_ = pidleget() } + var locked bool if _p_ == nil { globrunqput(gp) + + // Below, we stoplockedm if gp is locked. globrunqput releases + // ownership of gp, so we must check if gp is locked prior to + // committing the release by unlocking sched.lock, otherwise we + // could race with another M transitioning gp from unlocked to + // locked. + locked = gp.lockedm != 0 } else if atomic.Load(&sched.sysmonwait) != 0 { atomic.Store(&sched.sysmonwait, 0) notewakeup(&sched.sysmonnote) @@ -4082,7 +4090,7 @@ func exitsyscall0(gp *g) { acquirep(_p_) execute(gp, false) // Never returns. } - if gp.lockedm != 0 { + if locked { // Wait until another thread schedules gp and so m again. // // N.B. lockedm must be this M, as this g was running on this M diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go index 253e9e8c1f..18d15028c3 100644 --- a/src/syscall/exec_windows.go +++ b/src/syscall/exec_windows.go @@ -313,6 +313,17 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle } } + var maj, min, build uint32 + rtlGetNtVersionNumbers(&maj, &min, &build) + isWin7 := maj < 6 || (maj == 6 && min <= 1) + // NT kernel handles are divisible by 4, with the bottom 3 bits left as + // a tag. The fully set tag correlates with the types of handles we're + // concerned about here. Except, the kernel will interpret some + // special handle values, like -1, -2, and so forth, so kernelbase.dll + // checks to see that those bottom three bits are checked, but that top + // bit is not checked. + isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 } + p, _ := GetCurrentProcess() parentProcess := p if sys.ParentProcess != 0 { @@ -321,7 +332,15 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle fd := make([]Handle, len(attr.Files)) for i := range attr.Files { if attr.Files[i] > 0 { - err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) + destinationProcessHandle := parentProcess + + // On Windows 7, console handles aren't real handles, and can only be duplicated + // into the current process, not a parent one, which amounts to the same thing. + if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) { + destinationProcessHandle = p + } + + err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) if err != nil { return 0, 0, err } @@ -351,19 +370,40 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle si.StdErr = fd[2] fd = append(fd, sys.AdditionalInheritedHandles...) + + // On Windows 7, console handles aren't real handles, so don't pass them + // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST. + for i := range fd { + if isLegacyWin7ConsoleHandle(fd[i]) { + fd[i] = 0 + } + } + + // The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST + // to treat the entire list as empty, so remove NULL handles. + j := 0 + for i := range fd { + if fd[i] != 0 { + fd[j] = fd[i] + j++ + } + } + fd = fd[:j] + // Do not accidentally inherit more than these handles. - err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil) - if err != nil { - return 0, 0, err + if len(fd) > 0 { + err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil) + if err != nil { + return 0, 0, err + } } pi := new(ProcessInformation) - flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT if sys.Token != 0 { - err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) + err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) } else { - err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) + err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) } if err != nil { return 0, 0, err diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index fc734effbb..660179ae9e 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -198,6 +198,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys FreeLibrary(handle Handle) (err error) //sys GetProcAddress(module Handle, procname string) (proc uintptr, err error) //sys GetVersion() (ver uint32, err error) +//sys rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) = ntdll.RtlGetNtVersionNumbers //sys formatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW //sys ExitProcess(exitcode uint32) //sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go index 10d0f54e8c..7bfff16be6 100644 --- a/src/syscall/zsyscall_windows.go +++ b/src/syscall/zsyscall_windows.go @@ -43,6 +43,7 @@ var ( modkernel32 = NewLazyDLL(sysdll.Add("kernel32.dll")) modmswsock = NewLazyDLL(sysdll.Add("mswsock.dll")) modnetapi32 = NewLazyDLL(sysdll.Add("netapi32.dll")) + modntdll = NewLazyDLL(sysdll.Add("ntdll.dll")) modsecur32 = NewLazyDLL(sysdll.Add("secur32.dll")) modshell32 = NewLazyDLL(sysdll.Add("shell32.dll")) moduserenv = NewLazyDLL(sysdll.Add("userenv.dll")) @@ -167,6 +168,7 @@ var ( procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree") procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation") procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo") + procRtlGetNtVersionNumbers = modntdll.NewProc("RtlGetNtVersionNumbers") procGetUserNameExW = modsecur32.NewProc("GetUserNameExW") procTranslateNameW = modsecur32.NewProc("TranslateNameW") procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW") @@ -1213,6 +1215,11 @@ func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **by return } +func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { + Syscall(procRtlGetNtVersionNumbers.Addr(), 3, uintptr(unsafe.Pointer(majorVersion)), uintptr(unsafe.Pointer(minorVersion)), uintptr(unsafe.Pointer(buildNumber))) + return +} + func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) { r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize))) if r1&0xff == 0 { diff --git a/test/fixedbugs/issue46525.go b/test/fixedbugs/issue46525.go new file mode 100644 index 0000000000..164e1473ce --- /dev/null +++ b/test/fixedbugs/issue46525.go @@ -0,0 +1,14 @@ +// errorcheck -lang=go1.16 + +// Copyright 2021 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 p + +import "unsafe" + +func main() { + _ = unsafe.Add(unsafe.Pointer(nil), 0) // ERROR "unsafe.Add requires go1.17 or later" + _ = unsafe.Slice(new(byte), 1) // ERROR "unsafe.Slice requires go1.17 or later" +}