diff options
Diffstat (limited to 'libgo/misc/cgo')
50 files changed, 938 insertions, 59 deletions
diff --git a/libgo/misc/cgo/errors/errors_test.go b/libgo/misc/cgo/errors/errors_test.go index 68a30a44fe4..e90ed1e058d 100644 --- a/libgo/misc/cgo/errors/errors_test.go +++ b/libgo/misc/cgo/errors/errors_test.go @@ -36,14 +36,13 @@ func check(t *testing.T, file string) { continue } - frags := bytes.SplitAfterN(line, []byte("ERROR HERE: "), 2) - if len(frags) == 1 { + _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: ")) + if !ok { continue } - frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1]) - re, err := regexp.Compile(frag) + re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag)) if err != nil { - t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1]) + t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag) continue } errors = append(errors, re) diff --git a/libgo/misc/cgo/errors/testdata/err2.go b/libgo/misc/cgo/errors/testdata/err2.go index a90598fe35b..aa941584c3c 100644 --- a/libgo/misc/cgo/errors/testdata/err2.go +++ b/libgo/misc/cgo/errors/testdata/err2.go @@ -91,10 +91,18 @@ func main() { // issue 26745 _ = func(i int) int { - return C.i + 1 // ERROR HERE: 14 + // typecheck reports at column 14 ('+'), but types2 reports at + // column 10 ('C'). + // TODO(mdempsky): Investigate why, and see if types2 can be + // updated to match typecheck behavior. + return C.i + 1 // ERROR HERE: \b(10|14)\b } _ = func(i int) { - C.fi(i) // ERROR HERE: 7 + // typecheck reports at column 7 ('('), but types2 reports at + // column 8 ('i'). The types2 position is more correct, but + // updating typecheck here is fundamentally challenging because of + // IR limitations. + C.fi(i) // ERROR HERE: \b(7|8)\b } C.fi = C.fi // ERROR HERE diff --git a/libgo/misc/cgo/gmp/fib.go b/libgo/misc/cgo/gmp/fib.go index f1091b1c54f..f453fcf1843 100644 --- a/libgo/misc/cgo/gmp/fib.go +++ b/libgo/misc/cgo/gmp/fib.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Compute Fibonacci numbers with two goroutines diff --git a/libgo/misc/cgo/gmp/pi.go b/libgo/misc/cgo/gmp/pi.go index d5851e8e6bd..5ea034900a9 100644 --- a/libgo/misc/cgo/gmp/pi.go +++ b/libgo/misc/cgo/gmp/pi.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main diff --git a/libgo/misc/cgo/test/cgo_test.go b/libgo/misc/cgo/test/cgo_test.go index 143f23f0e0c..774277e10da 100644 --- a/libgo/misc/cgo/test/cgo_test.go +++ b/libgo/misc/cgo/test/cgo_test.go @@ -59,7 +59,9 @@ func Test28896(t *testing.T) { test28896(t) } func Test30065(t *testing.T) { test30065(t) } func Test32579(t *testing.T) { test32579(t) } func Test31891(t *testing.T) { test31891(t) } +func Test42018(t *testing.T) { test42018(t) } func Test45451(t *testing.T) { test45451(t) } +func Test49633(t *testing.T) { test49633(t) } func TestAlign(t *testing.T) { testAlign(t) } func TestAtol(t *testing.T) { testAtol(t) } func TestBlocking(t *testing.T) { testBlocking(t) } diff --git a/libgo/misc/cgo/test/cgo_thread_lock.go b/libgo/misc/cgo/test/cgo_thread_lock.go index b1050685182..3b9ac845493 100644 --- a/libgo/misc/cgo/test/cgo_thread_lock.go +++ b/libgo/misc/cgo/test/cgo_thread_lock.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build linux && freebsd && openbsd // +build linux,freebsd,openbsd package cgotest diff --git a/libgo/misc/cgo/test/cgo_unix_test.go b/libgo/misc/cgo/test/cgo_unix_test.go index e3d59166498..a324503a22f 100644 --- a/libgo/misc/cgo/test/cgo_unix_test.go +++ b/libgo/misc/cgo/test/cgo_unix_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/libgo/misc/cgo/test/issue18146.go b/libgo/misc/cgo/test/issue18146.go index f92d6c7f939..e50f9ae5301 100644 --- a/libgo/misc/cgo/test/issue18146.go +++ b/libgo/misc/cgo/test/issue18146.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows // Issue 18146: pthread_create failure during syscall.Exec. diff --git a/libgo/misc/cgo/test/issue21897.go b/libgo/misc/cgo/test/issue21897.go index d13246bd84a..8f39252e688 100644 --- a/libgo/misc/cgo/test/issue21897.go +++ b/libgo/misc/cgo/test/issue21897.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build darwin && cgo && !internal // +build darwin,cgo,!internal package cgotest diff --git a/libgo/misc/cgo/test/issue21897b.go b/libgo/misc/cgo/test/issue21897b.go index 08b5f4d808e..50aece35289 100644 --- a/libgo/misc/cgo/test/issue21897b.go +++ b/libgo/misc/cgo/test/issue21897b.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !darwin || !cgo || internal // +build !darwin !cgo internal package cgotest diff --git a/libgo/misc/cgo/test/issue4029.go b/libgo/misc/cgo/test/issue4029.go index b2d131833a9..90ca08cbfb7 100644 --- a/libgo/misc/cgo/test/issue4029.go +++ b/libgo/misc/cgo/test/issue4029.go @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !windows,!static +//go:build !windows && !static && (!darwin || (!internal_pie && !arm64)) +// +build !windows +// +build !static // +build !darwin !internal_pie,!arm64 // Excluded in darwin internal linking PIE mode, as dynamic export is not diff --git a/libgo/misc/cgo/test/issue4029w.go b/libgo/misc/cgo/test/issue4029w.go index b969bdd0fe8..c2f59485e49 100644 --- a/libgo/misc/cgo/test/issue4029w.go +++ b/libgo/misc/cgo/test/issue4029w.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build windows || static || (darwin && internal_pie) || (darwin && arm64) // +build windows static darwin,internal_pie darwin,arm64 package cgotest diff --git a/libgo/misc/cgo/test/issue42018.go b/libgo/misc/cgo/test/issue42018.go new file mode 100644 index 00000000000..fab686a6783 --- /dev/null +++ b/libgo/misc/cgo/test/issue42018.go @@ -0,0 +1,14 @@ +// 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. + +//go:build !windows +// +build !windows + +package cgotest + +import "testing" + +func test42018(t *testing.T) { + t.Skip("skipping Windows-only test") +} diff --git a/libgo/misc/cgo/test/issue42018_windows.go b/libgo/misc/cgo/test/issue42018_windows.go new file mode 100644 index 00000000000..8f4570ab2a5 --- /dev/null +++ b/libgo/misc/cgo/test/issue42018_windows.go @@ -0,0 +1,46 @@ +// 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 cgotest + +/* +typedef void *HANDLE; + +struct HWND__{int unused;}; typedef struct HWND__ *HWND; +*/ +import "C" + +import ( + "testing" + "unsafe" +) + +func test42018(t *testing.T) { + // Test that Windows handles are marked go:notinheap, by growing the + // stack and checking for pointer adjustments. Trick from + // test/fixedbugs/issue40954.go. + var i int + handle := C.HANDLE(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) + recurseHANDLE(100, handle, uintptr(unsafe.Pointer(&i))) + hwnd := C.HWND(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) + recurseHWND(400, hwnd, uintptr(unsafe.Pointer(&i))) +} + +func recurseHANDLE(n int, p C.HANDLE, v uintptr) { + if n > 0 { + recurseHANDLE(n-1, p, v) + } + if uintptr(unsafe.Pointer(p)) != v { + panic("adjusted notinheap pointer") + } +} + +func recurseHWND(n int, p C.HWND, v uintptr) { + if n > 0 { + recurseHWND(n-1, p, v) + } + if uintptr(unsafe.Pointer(p)) != v { + panic("adjusted notinheap pointer") + } +} diff --git a/libgo/misc/cgo/test/issue8517.go b/libgo/misc/cgo/test/issue8517.go index 4e431df921d..7316ab0335d 100644 --- a/libgo/misc/cgo/test/issue8517.go +++ b/libgo/misc/cgo/test/issue8517.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/libgo/misc/cgo/test/issue8694.go b/libgo/misc/cgo/test/issue8694.go index 89be7ea0907..19071ce1595 100644 --- a/libgo/misc/cgo/test/issue8694.go +++ b/libgo/misc/cgo/test/issue8694.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !android // +build !android package cgotest diff --git a/libgo/misc/cgo/test/sigaltstack.go b/libgo/misc/cgo/test/sigaltstack.go index 034cc4b3719..6b371897a73 100644 --- a/libgo/misc/cgo/test/sigaltstack.go +++ b/libgo/misc/cgo/test/sigaltstack.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !android // +build !windows,!android // Test that the Go runtime still works if C code changes the signal stack. diff --git a/libgo/misc/cgo/test/sigprocmask.go b/libgo/misc/cgo/test/sigprocmask.go index e2b939f05e2..983734cc7b6 100644 --- a/libgo/misc/cgo/test/sigprocmask.go +++ b/libgo/misc/cgo/test/sigprocmask.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/libgo/misc/cgo/test/test.go b/libgo/misc/cgo/test/test.go index 3b8f548b13d..dd81f770a20 100644 --- a/libgo/misc/cgo/test/test.go +++ b/libgo/misc/cgo/test/test.go @@ -915,6 +915,11 @@ void issue40494(enum Enum40494 e, union Union40494* up) {} // Issue 45451, bad handling of go:notinheap types. typedef struct issue45451Undefined issue45451; + +// Issue 49633, example of cgo.Handle with void*. +extern void GoFunc49633(void*); +void cfunc49633(void *context) { GoFunc49633(context); } + */ import "C" diff --git a/libgo/misc/cgo/test/test_unix.go b/libgo/misc/cgo/test/test_unix.go index 4a234469dbc..831b9ca625a 100644 --- a/libgo/misc/cgo/test/test_unix.go +++ b/libgo/misc/cgo/test/test_unix.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/libgo/misc/cgo/test/testdata/issue43639.go b/libgo/misc/cgo/test/testdata/issue43639.go new file mode 100644 index 00000000000..e755fbd4bc0 --- /dev/null +++ b/libgo/misc/cgo/test/testdata/issue43639.go @@ -0,0 +1,9 @@ +// 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 cgotest + +// Issue 43639: No runtime test needed, make sure package cgotest/issue43639 compiles well. + +import _ "cgotest/issue43639" diff --git a/libgo/misc/cgo/test/testdata/issue43639/a.go b/libgo/misc/cgo/test/testdata/issue43639/a.go new file mode 100644 index 00000000000..fe37d5e4b0f --- /dev/null +++ b/libgo/misc/cgo/test/testdata/issue43639/a.go @@ -0,0 +1,8 @@ +// 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 issue43639 + +// #cgo CFLAGS: -W -Wall -Werror +import "C" diff --git a/libgo/misc/cgo/test/testdata/issue9400_linux.go b/libgo/misc/cgo/test/testdata/issue9400_linux.go index e94a9bb45f5..051b9ab0bbe 100644 --- a/libgo/misc/cgo/test/testdata/issue9400_linux.go +++ b/libgo/misc/cgo/test/testdata/issue9400_linux.go @@ -15,6 +15,7 @@ import "C" import ( "runtime" + "runtime/debug" "sync/atomic" "testing" @@ -46,6 +47,14 @@ func test9400(t *testing.T) { big[i] = pattern } + // Disable GC for the duration of the test. + // This avoids a potential GC deadlock when spinning in uninterruptable ASM below #49695. + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // SetGCPercent waits until the mark phase is over, but the runtime + // also preempts at the start of the sweep phase, so make sure that's + // done too. See #49695. + runtime.GC() + // Temporarily rewind the stack and trigger SIGSETXID issue9400.RewindAndSetgid() diff --git a/libgo/misc/cgo/test/testx.go b/libgo/misc/cgo/test/testx.go index 823c3e13d29..8ec84a8b22e 100644 --- a/libgo/misc/cgo/test/testx.go +++ b/libgo/misc/cgo/test/testx.go @@ -113,6 +113,7 @@ typedef struct { int i; } Issue38408, *PIssue38408; +extern void cfunc49633(void*); // definition is in test.go */ import "C" @@ -554,3 +555,26 @@ func GoFunc37033(handle C.uintptr_t) { // A typedef pointer can be used as the element type. // No runtime test; just make sure it compiles. var _ C.PIssue38408 = &C.Issue38408{i: 1} + +// issue 49633, example use of cgo.Handle with void* + +type data49633 struct { + msg string +} + +//export GoFunc49633 +func GoFunc49633(context unsafe.Pointer) { + h := *(*cgo.Handle)(context) + v := h.Value().(*data49633) + v.msg = "hello" +} + +func test49633(t *testing.T) { + v := &data49633{} + h := cgo.NewHandle(v) + defer h.Delete() + C.cfunc49633(unsafe.Pointer(&h)) + if v.msg != "hello" { + t.Errorf("msg = %q, want 'hello'", v.msg) + } +} diff --git a/libgo/misc/cgo/test/typeparam.go b/libgo/misc/cgo/test/typeparam.go new file mode 100644 index 00000000000..9291005db67 --- /dev/null +++ b/libgo/misc/cgo/test/typeparam.go @@ -0,0 +1,21 @@ +// 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 cgotest + +// #include <stddef.h> +import "C" + +/* FIXME: Commented out for gofrontend. + +func generic[T, U any](t T, u U) {} + +func useGeneric() { + const zero C.size_t = 0 + + generic(zero, zero) + generic[C.size_t, C.size_t](0, 0) +} + +*/ diff --git a/libgo/misc/cgo/testcarchive/carchive_test.go b/libgo/misc/cgo/testcarchive/carchive_test.go index e5db0d628a4..8ed72fb0e59 100644 --- a/libgo/misc/cgo/testcarchive/carchive_test.go +++ b/libgo/misc/cgo/testcarchive/carchive_test.go @@ -10,12 +10,15 @@ import ( "debug/elf" "flag" "fmt" + "io" + "io/fs" "log" "os" "os/exec" "path/filepath" "regexp" "runtime" + "strconv" "strings" "syscall" "testing" @@ -245,6 +248,29 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`) +// checkIsExecutable verifies that exe exists and has execute permission. +// +// (https://golang.org/issue/49693 notes failures with "no such file or +// directory", so we want to double-check that the executable actually exists +// immediately after we build it in order to better understand that failure +// mode.) +func checkIsExecutable(t *testing.T, exe string) { + t.Helper() + fi, err := os.Stat(exe) + if err != nil { + t.Fatal(err) + } + if runtime.GOOS == "windows" { + // os.File doesn't check the "execute" permission on Windows files + // and as a result doesn't set that bit in a file's permissions. + // Assume that if the file exists it is “executable enough”. + return + } + if fi.Mode()&0111 == 0 { + t.Fatalf("%s is not executable: %0o", exe, fi.Mode()&fs.ModePerm) + } +} + // checkLineComments checks that the export header generated by // -buildmode=c-archive doesn't have any absolute paths in the #line // comments. We don't want those paths because they are unhelpful for @@ -263,6 +289,173 @@ func checkLineComments(t *testing.T, hdrname string) { } } +// checkArchive verifies that the created library looks OK. +// We just check a couple of things now, we can add more checks as needed. +func checkArchive(t *testing.T, arname string) { + t.Helper() + + switch GOOS { + case "aix", "darwin", "ios", "windows": + // We don't have any checks for non-ELF libraries yet. + if _, err := os.Stat(arname); err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + } + default: + checkELFArchive(t, arname) + } +} + +// checkELFArchive checks an ELF archive. +func checkELFArchive(t *testing.T, arname string) { + t.Helper() + + f, err := os.Open(arname) + if err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + return + } + defer f.Close() + + // TODO(iant): put these in a shared package? But where? + const ( + magic = "!<arch>\n" + fmag = "`\n" + + namelen = 16 + datelen = 12 + uidlen = 6 + gidlen = 6 + modelen = 8 + sizelen = 10 + fmaglen = 2 + hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen + ) + + type arhdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string + } + + var magbuf [len(magic)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + t.Errorf("%s: archive too short", arname) + return + } + if string(magbuf[:]) != magic { + t.Errorf("%s: incorrect archive magic string %q", arname, magbuf) + } + + off := int64(len(magic)) + for { + if off&1 != 0 { + var b [1]byte + if _, err := f.Read(b[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err) + } + off++ + } + + var hdrbuf [hdrlen]byte + if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error reading archive header at %d: %v", arname, off, err) + return + } + + var hdr arhdr + hdrslice := hdrbuf[:] + set := func(len int, ps *string) { + *ps = string(bytes.TrimSpace(hdrslice[:len])) + hdrslice = hdrslice[len:] + } + set(namelen, &hdr.name) + set(datelen, &hdr.date) + set(uidlen, &hdr.uid) + set(gidlen, &hdr.gid) + set(modelen, &hdr.mode) + set(sizelen, &hdr.size) + hdr.fmag = string(hdrslice[:fmaglen]) + hdrslice = hdrslice[fmaglen:] + if len(hdrslice) != 0 { + t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice)) + } + + if hdr.fmag != fmag { + t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off) + return + } + + size, err := strconv.ParseInt(hdr.size, 10, 64) + if err != nil { + t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err) + return + } + + off += hdrlen + + switch hdr.name { + case "__.SYMDEF", "/", "/SYM64/": + // The archive symbol map. + case "//", "ARFILENAMES/": + // The extended name table. + default: + // This should be an ELF object. + checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size)) + } + + off += size + if _, err := f.Seek(off, os.SEEK_SET); err != nil { + t.Errorf("%s: failed to seek to %d: %v", arname, off, err) + } + } +} + +// checkELFArchiveObject checks an object in an ELF archive. +func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) { + t.Helper() + + ef, err := elf.NewFile(obj) + if err != nil { + t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err) + return + } + defer ef.Close() + + // Verify section types. + for _, sec := range ef.Sections { + want := elf.SHT_NULL + switch sec.Name { + case ".text", ".data": + want = elf.SHT_PROGBITS + case ".bss": + want = elf.SHT_NOBITS + case ".symtab": + want = elf.SHT_SYMTAB + case ".strtab": + want = elf.SHT_STRTAB + case ".init_array": + want = elf.SHT_INIT_ARRAY + case ".fini_array": + want = elf.SHT_FINI_ARRAY + case ".preinit_array": + want = elf.SHT_PREINIT_ARRAY + } + if want != elf.SHT_NULL && sec.Type != want { + t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want) + } + } +} + func TestInstall(t *testing.T) { if !testWork { defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) @@ -310,7 +503,7 @@ func TestEarlySignalHandler(t *testing.T) { defer func() { os.Remove("libgo2.a") os.Remove("libgo2.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -321,6 +514,7 @@ func TestEarlySignalHandler(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -350,7 +544,7 @@ func TestSignalForwarding(t *testing.T) { defer func() { os.Remove("libgo2.a") os.Remove("libgo2.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -361,6 +555,7 @@ func TestSignalForwarding(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -374,7 +569,7 @@ func TestSignalForwarding(t *testing.T) { cmd = exec.Command(bin[0], append(bin[1:], "1")...) out, err := cmd.CombinedOutput() - t.Logf("%s", out) + t.Logf("%v\n%s", cmd.Args, out) expectSignal(t, err, syscall.SIGSEGV) // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. @@ -383,7 +578,9 @@ func TestSignalForwarding(t *testing.T) { cmd = exec.Command(bin[0], append(bin[1:], "3")...) out, err = cmd.CombinedOutput() - t.Logf("%s", out) + if len(out) > 0 { + t.Logf("%s", out) + } expectSignal(t, err, syscall.SIGPIPE) } } @@ -400,7 +597,7 @@ func TestSignalForwardingExternal(t *testing.T) { defer func() { os.Remove("libgo2.a") os.Remove("libgo2.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -411,6 +608,7 @@ func TestSignalForwardingExternal(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -517,7 +715,7 @@ func TestOsSignal(t *testing.T) { defer func() { os.Remove("libgo3.a") os.Remove("libgo3.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -528,6 +726,7 @@ func TestOsSignal(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo3.h") + checkArchive(t, "libgo3.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") if runtime.Compiler == "gccgo" { @@ -554,7 +753,7 @@ func TestSigaltstack(t *testing.T) { defer func() { os.Remove("libgo4.a") os.Remove("libgo4.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -565,6 +764,7 @@ func TestSigaltstack(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo4.h") + checkArchive(t, "libgo4.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") if runtime.Compiler == "gccgo" { @@ -747,25 +947,30 @@ func TestSIGPROF(t *testing.T) { } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6") - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { t.Fatal(err) } checkLineComments(t, "libgo6.h") + checkArchive(t, "libgo6.a") ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a") if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } - if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { t.Fatal(err) } + checkIsExecutable(t, "./testp6"+exeSuffix) argv := cmdToRun("./testp6") cmd = exec.Command(argv[0], argv[1:]...) - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err = cmd.CombinedOutput() + t.Logf("%v\n%s", argv, out) + if err != nil { t.Fatal(err) } } @@ -788,13 +993,13 @@ func TestCompileWithoutShared(t *testing.T) { } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2") - t.Log(cmd.Args) out, err := cmd.CombinedOutput() - t.Logf("%s", out) + t.Logf("%v\n%s", cmd.Args, out) if err != nil { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") exe := "./testnoshared" + exeSuffix @@ -804,23 +1009,22 @@ func TestCompileWithoutShared(t *testing.T) { if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } - t.Log(ccArgs) out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) // If -no-pie unrecognized, try -nopie if this is possibly clang if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") { ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a") - t.Log(ccArgs) out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) } // Don't use either -no-pie or -nopie if err != nil && bytes.Contains(out, []byte("unrecognized")) { - ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a") - t.Log(ccArgs) + ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a") out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) } - t.Logf("%s", out) if err != nil { t.Fatal(err) } @@ -829,17 +1033,15 @@ func TestCompileWithoutShared(t *testing.T) { } binArgs := append(cmdToRun(exe), "1") - t.Log(binArgs) out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() - t.Logf("%s", out) + t.Logf("%v\n%s", binArgs, out) expectSignal(t, err, syscall.SIGSEGV) // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. if runtime.GOOS != "darwin" && runtime.GOOS != "ios" { binArgs := append(cmdToRun(exe), "3") - t.Log(binArgs) out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() - t.Logf("%s", out) + t.Logf("%v\n%s", binArgs, out) expectSignal(t, err, syscall.SIGPIPE) } } @@ -894,26 +1096,87 @@ func TestManyCalls(t *testing.T) { } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7") - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { t.Fatal(err) } checkLineComments(t, "libgo7.h") + checkArchive(t, "libgo7.a") ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a") if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } - if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { t.Fatal(err) } + checkIsExecutable(t, "./testp7"+exeSuffix) argv := cmdToRun("./testp7") cmd = exec.Command(argv[0], argv[1:]...) - var sb strings.Builder - cmd.Stdout = &sb - cmd.Stderr = &sb + sb := new(strings.Builder) + cmd.Stdout = sb + cmd.Stderr = sb + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + timer := time.AfterFunc(time.Minute, + func() { + t.Error("test program timed out") + cmd.Process.Kill() + }, + ) + defer timer.Stop() + + err = cmd.Wait() + t.Logf("%v\n%s", cmd.Args, sb) + if err != nil { + t.Error(err) + } +} + +// Issue 49288. +func TestPreemption(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("skipping asynchronous preemption test with gccgo") + } + + t.Parallel() + + if !testWork { + defer func() { + os.Remove("testp8" + exeSuffix) + os.Remove("libgo8.a") + os.Remove("libgo8.h") + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8") + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { + t.Fatal(err) + } + checkLineComments(t, "libgo8.h") + checkArchive(t, "libgo8.a") + + ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a") + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { + t.Fatal(err) + } + checkIsExecutable(t, "./testp8"+exeSuffix) + + argv := cmdToRun("./testp8") + cmd = exec.Command(argv[0], argv[1:]...) + sb := new(strings.Builder) + cmd.Stdout = sb + cmd.Stderr = sb if err := cmd.Start(); err != nil { t.Fatal(err) } @@ -926,8 +1189,9 @@ func TestManyCalls(t *testing.T) { ) defer timer.Stop() - if err := cmd.Wait(); err != nil { - t.Log(sb.String()) + err = cmd.Wait() + t.Logf("%v\n%s", cmd.Args, sb) + if err != nil { t.Error(err) } } diff --git a/libgo/misc/cgo/testcarchive/testdata/libgo8/a.go b/libgo/misc/cgo/testcarchive/testdata/libgo8/a.go new file mode 100644 index 00000000000..718418ecb87 --- /dev/null +++ b/libgo/misc/cgo/testcarchive/testdata/libgo8/a.go @@ -0,0 +1,36 @@ +// 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 "C" + +import ( + "os" + "runtime" + "sync/atomic" +) + +var started int32 + +// Start a goroutine that loops forever. +func init() { + runtime.GOMAXPROCS(1) + go func() { + for { + atomic.StoreInt32(&started, 1) + } + }() +} + +//export GoFunction8 +func GoFunction8() { + for atomic.LoadInt32(&started) == 0 { + runtime.Gosched() + } + os.Exit(0) +} + +func main() { +} diff --git a/libgo/misc/cgo/testcarchive/testdata/main8.c b/libgo/misc/cgo/testcarchive/testdata/main8.c new file mode 100644 index 00000000000..95fb7a349e1 --- /dev/null +++ b/libgo/misc/cgo/testcarchive/testdata/main8.c @@ -0,0 +1,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. + +// Test preemption. + +#include <stdlib.h> + +#include "libgo8.h" + +int main() { + GoFunction8(); + + // That should have exited the program. + abort(); +} diff --git a/libgo/misc/cgo/testcshared/cshared_test.go b/libgo/misc/cgo/testcshared/cshared_test.go index 19ad8c76a83..13ec8761e86 100644 --- a/libgo/misc/cgo/testcshared/cshared_test.go +++ b/libgo/misc/cgo/testcshared/cshared_test.go @@ -200,7 +200,7 @@ func adbRun(t *testing.T, env []string, adbargs ...string) string { args := append(adbCmd(), "exec-out") // Propagate LD_LIBRARY_PATH to the adb shell invocation. for _, e := range env { - if strings.Index(e, "LD_LIBRARY_PATH=") != -1 { + if strings.Contains(e, "LD_LIBRARY_PATH=") { adbargs = append([]string{e}, adbargs...) break } @@ -326,7 +326,7 @@ func createHeaders() error { base, name := filepath.Split(args[0]) args[0] = filepath.Join(base, "llvm-dlltool") var machine string - switch strings.SplitN(name, "-", 2)[0] { + switch prefix, _, _ := strings.Cut(name, "-"); prefix { case "i686": machine = "i386" case "x86_64": @@ -781,10 +781,10 @@ func copyFile(t *testing.T, dst, src string) { func TestGo2C2Go(t *testing.T) { switch GOOS { - case "darwin", "ios": - // Darwin shared libraries don't support the multiple + case "darwin", "ios", "windows": + // Non-ELF shared libraries don't support the multiple // copies of the runtime package implied by this test. - t.Skip("linking c-shared into Go programs not supported on Darwin; issue 29061") + t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS) case "android": t.Skip("test fails on android; issue 29087") } diff --git a/libgo/misc/cgo/testcshared/testdata/libgo2/dup2.go b/libgo/misc/cgo/testcshared/testdata/libgo2/dup2.go index 3b53e1ceea2..17432a33148 100644 --- a/libgo/misc/cgo/testcshared/testdata/libgo2/dup2.go +++ b/libgo/misc/cgo/testcshared/testdata/libgo2/dup2.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build darwin || dragonfly || freebsd || (linux && !arm64 && !riscv && !riscv64) || netbsd || openbsd // +build darwin dragonfly freebsd linux,!arm64,!riscv,!riscv64 netbsd openbsd package main diff --git a/libgo/misc/cgo/testcshared/testdata/libgo2/dup3.go b/libgo/misc/cgo/testcshared/testdata/libgo2/dup3.go index 79a37730c84..9b1e8391c18 100644 --- a/libgo/misc/cgo/testcshared/testdata/libgo2/dup3.go +++ b/libgo/misc/cgo/testcshared/testdata/libgo2/dup3.go @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux,arm64 linux,riscv,riscv64 +//go:build (linux && arm64) || (linux && riscv) || (linux && riscv64) +// +build linux,arm64 linux,riscv linux,riscv64 package main diff --git a/libgo/misc/cgo/testgodefs/testdata/issue48396.go b/libgo/misc/cgo/testgodefs/testdata/issue48396.go new file mode 100644 index 00000000000..d4c192403fd --- /dev/null +++ b/libgo/misc/cgo/testgodefs/testdata/issue48396.go @@ -0,0 +1,18 @@ +// 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. +// +// +build ignore + +package main + +/* +// from <linux/kcm.h> +struct issue48396 { + int fd; + int bpf_fd; +}; +*/ +import "C" + +type Issue48396 C.struct_issue48396 diff --git a/libgo/misc/cgo/testgodefs/testdata/main.go b/libgo/misc/cgo/testgodefs/testdata/main.go index 4a3f6a701cc..5c670f3d329 100644 --- a/libgo/misc/cgo/testgodefs/testdata/main.go +++ b/libgo/misc/cgo/testgodefs/testdata/main.go @@ -28,6 +28,9 @@ var v7 = S{} // Test that #define'd type is fully defined var _ = issue38649{X: 0} +// Test that prefixes do not cause duplicate field names. +var _ = Issue48396{Fd: 1, Bpf_fd: 2} + func main() { pass := true diff --git a/libgo/misc/cgo/testgodefs/testgodefs_test.go b/libgo/misc/cgo/testgodefs/testgodefs_test.go index aae34043605..7628ffc595b 100644 --- a/libgo/misc/cgo/testgodefs/testgodefs_test.go +++ b/libgo/misc/cgo/testgodefs/testgodefs_test.go @@ -25,6 +25,7 @@ var filePrefixes = []string{ "issue37621", "issue38649", "issue39534", + "issue48396", } func TestGoDefs(t *testing.T) { diff --git a/libgo/misc/cgo/testplugin/plugin_test.go b/libgo/misc/cgo/testplugin/plugin_test.go index 9697dbf7a78..10c5db2646b 100644 --- a/libgo/misc/cgo/testplugin/plugin_test.go +++ b/libgo/misc/cgo/testplugin/plugin_test.go @@ -265,10 +265,6 @@ func TestIssue25756(t *testing.T) { // Test with main using -buildmode=pie with plugin for issue #43228 func TestIssue25756pie(t *testing.T) { - if os.Getenv("GO_BUILDER_NAME") == "darwin-arm64-11_0-toothrot" { - t.Skip("broken on darwin/arm64 builder in sharded mode; see issue 46239") - } - goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin") goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go") run(t, "./issue25756pie.exe") @@ -293,3 +289,31 @@ func TestIssue44956(t *testing.T) { goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go") run(t, "./issue44956.exe") } + +func TestForkExec(t *testing.T) { + // Issue 38824: importing the plugin package causes it hang in forkExec on darwin. + + t.Parallel() + goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go") + + var cmd *exec.Cmd + done := make(chan int, 1) + + go func() { + for i := 0; i < 100; i++ { + cmd = exec.Command("./forkexec.exe", "1") + err := cmd.Run() + if err != nil { + t.Errorf("running command failed: %v", err) + break + } + } + done <- 1 + }() + select { + case <-done: + case <-time.After(5 * time.Minute): + cmd.Process.Kill() + t.Fatalf("subprocess hang") + } +} diff --git a/libgo/misc/cgo/testplugin/testdata/forkexec/main.go b/libgo/misc/cgo/testplugin/testdata/forkexec/main.go new file mode 100644 index 00000000000..3169ff5f04d --- /dev/null +++ b/libgo/misc/cgo/testplugin/testdata/forkexec/main.go @@ -0,0 +1,30 @@ +// 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 ( + "os" + "os/exec" + _ "plugin" + "sync" +) + +func main() { + if os.Args[1] != "1" { + return + } + + var wg sync.WaitGroup + for i := 0; i < 8; i++ { + wg.Add(1) + go func() { + defer wg.Done() + // does not matter what we exec, just exec itself + cmd := exec.Command("./forkexec.exe", "0") + cmd.Run() + }() + } + wg.Wait() +} diff --git a/libgo/misc/cgo/testsanitizers/asan_test.go b/libgo/misc/cgo/testsanitizers/asan_test.go new file mode 100644 index 00000000000..1b70bce3d11 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/asan_test.go @@ -0,0 +1,81 @@ +// 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 sanitizers_test + +import ( + "strings" + "testing" +) + +func TestASAN(t *testing.T) { + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + // The asan tests require support for the -asan option. + if !aSanSupported(goos, goarch) { + t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch) + } + + t.Parallel() + requireOvercommit(t) + config := configure("address") + config.skipIfCSanitizerBroken(t) + + mustRun(t, config.goCmd("build", "std")) + + cases := []struct { + src string + memoryAccessError string + errorLocation string + }{ + {src: "asan1_fail.go", memoryAccessError: "heap-use-after-free", errorLocation: "asan1_fail.go:25"}, + {src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow", errorLocation: "asan2_fail.go:31"}, + {src: "asan3_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan3_fail.go:13"}, + {src: "asan4_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan4_fail.go:13"}, + {src: "asan5_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan5_fail.go:18"}, + {src: "asan_useAfterReturn.go"}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + outPath := dir.Join(name) + mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src))) + + cmd := hangProneCmd(outPath) + if tc.memoryAccessError != "" { + outb, err := cmd.CombinedOutput() + out := string(outb) + if err != nil && strings.Contains(out, tc.memoryAccessError) { + // This string is output if the + // sanitizer library needs a + // symbolizer program and can't find it. + const noSymbolizer = "external symbolizer" + // Check if -asan option can correctly print where the error occured. + if tc.errorLocation != "" && + !strings.Contains(out, tc.errorLocation) && + !strings.Contains(out, noSymbolizer) && + compilerSupportsLocation() { + + t.Errorf("%#q exited without expected location of the error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.errorLocation, out) + } + return + } + t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out) + } + mustRun(t, cmd) + }) + } +} diff --git a/libgo/misc/cgo/testsanitizers/cc_test.go b/libgo/misc/cgo/testsanitizers/cc_test.go index 384b6250e1e..05b77932b4c 100644 --- a/libgo/misc/cgo/testsanitizers/cc_test.go +++ b/libgo/misc/cgo/testsanitizers/cc_test.go @@ -218,6 +218,23 @@ func compilerVersion() (version, error) { return compiler.version, compiler.err } +// compilerSupportsLocation reports whether the compiler should be +// able to provide file/line information in backtraces. +func compilerSupportsLocation() bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + switch compiler.name { + case "gcc": + return compiler.major >= 10 + case "clang": + return true + default: + return false + } +} + type compilerCheck struct { once sync.Once err error @@ -267,6 +284,11 @@ func configure(sanitizer string) *config { c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan") } + case "address": + c.goFlags = append(c.goFlags, "-asan") + // Set the debug mode to print the C stack trace. + c.cFlags = append(c.cFlags, "-g") + default: panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer)) } @@ -344,7 +366,7 @@ func (c *config) checkCSanitizer() (skip bool, err error) { if os.IsNotExist(err) { return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err) } - snippet := bytes.SplitN(out, []byte{'\n'}, 2)[0] + snippet, _, _ := bytes.Cut(out, []byte("\n")) return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet) } @@ -450,3 +472,14 @@ func mSanSupported(goos, goarch string) bool { return false } } + +// aSanSupported is a copy of the function cmd/internal/sys.ASanSupported, +// because the internal pacakage can't be used here. +func aSanSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "amd64" || goarch == "arm64" + default: + return false + } +} diff --git a/libgo/misc/cgo/testsanitizers/testdata/asan1_fail.go b/libgo/misc/cgo/testsanitizers/testdata/asan1_fail.go new file mode 100644 index 00000000000..80289e5c304 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/testdata/asan1_fail.go @@ -0,0 +1,28 @@ +// 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 + +/* +#include <stdlib.h> +#include <stdio.h> + +int *p; +int* test() { + p = (int *)malloc(2 * sizeof(int)); + free(p); + return p; +} +*/ +import "C" +import "fmt" + +func main() { + // C passes Go an invalid pointer. + a := C.test() + // Use after free + *a = 2 // BOOM + // We shouldn't get here; asan should stop us first. + fmt.Println(*a) +} diff --git a/libgo/misc/cgo/testsanitizers/testdata/asan2_fail.go b/libgo/misc/cgo/testsanitizers/testdata/asan2_fail.go new file mode 100644 index 00000000000..3ab06085710 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/testdata/asan2_fail.go @@ -0,0 +1,34 @@ +// 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 + +/* +#include <stdlib.h> +#include <stdio.h> + +int *p; +int* f() { + int i; + p = (int *)malloc(5*sizeof(int)); + for (i = 0; i < 5; i++) { + p[i] = i+10; + } + return p; +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +func main() { + a := C.f() + q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5)) + // Access to C pointer out of bounds. + *q5 = 100 // BOOM + // We shouldn't get here; asan should stop us first. + fmt.Printf("q5: %d, %x\n", *q5, q5) +} diff --git a/libgo/misc/cgo/testsanitizers/testdata/asan3_fail.go b/libgo/misc/cgo/testsanitizers/testdata/asan3_fail.go new file mode 100644 index 00000000000..9f6d26dd89d --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/testdata/asan3_fail.go @@ -0,0 +1,23 @@ +// 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 + +/* +#include <stdlib.h> +#include <stdio.h> + +void test(int *a) { + // Access Go pointer out of bounds. + int c = a[5]; // BOOM + // We shouldn't get here; asan should stop us first. + printf("a[5]=%d\n", c); +} +*/ +import "C" + +func main() { + cIntSlice := []C.int{200, 201, 203, 203, 204} + C.test(&cIntSlice[0]) +} diff --git a/libgo/misc/cgo/testsanitizers/testdata/asan4_fail.go b/libgo/misc/cgo/testsanitizers/testdata/asan4_fail.go new file mode 100644 index 00000000000..12098458ae9 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/testdata/asan4_fail.go @@ -0,0 +1,22 @@ +// 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 + +/* +#include <stdlib.h> +#include <stdio.h> + +void test(int* a) { + // Access Go pointer out of bounds. + a[3] = 300; // BOOM + // We shouldn't get here; asan should stop us first. + printf("a[3]=%d\n", a[3]); +}*/ +import "C" + +func main() { + var cIntArray [2]C.int + C.test(&cIntArray[0]) // cIntArray is moved to heap. +} diff --git a/libgo/misc/cgo/testsanitizers/testdata/asan5_fail.go b/libgo/misc/cgo/testsanitizers/testdata/asan5_fail.go new file mode 100644 index 00000000000..d6853eab733 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/testdata/asan5_fail.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "runtime" + "unsafe" +) + +func main() { + p := new([1024 * 1000]int) + p[0] = 10 + r := bar(&p[1024*1000-1]) + fmt.Printf("r value is %d", r) +} + +func bar(a *int) int { + p := unsafe.Add(unsafe.Pointer(a), 2*unsafe.Sizeof(int(1))) + runtime.ASanWrite(p, 8) // BOOM + *((*int)(p)) = 10 + return *((*int)(p)) +} diff --git a/libgo/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go b/libgo/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go new file mode 100644 index 00000000000..3d3d5a6ab1a --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go @@ -0,0 +1,26 @@ +// 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 + +// The -fsanitize=address option of C compier can detect stack-use-after-return bugs. +// In the following program, the local variable 'local' was moved to heap by the Go +// compiler because foo() is returning the reference to 'local', and return stack of +// foo() will be invalid. Thus for main() to use the reference to 'local', the 'local' +// must be available even after foo() has finished. Therefore, Go has no such issue. + +import "fmt" + +var ptr *int + +func main() { + foo() + fmt.Printf("ptr=%x, %v", *ptr, ptr) +} + +func foo() { + var local int + local = 1 + ptr = &local // local is moved to heap. +} diff --git a/libgo/misc/cgo/testshared/shared_test.go b/libgo/misc/cgo/testshared/shared_test.go index e77f8489154..b78083bc806 100644 --- a/libgo/misc/cgo/testshared/shared_test.go +++ b/libgo/misc/cgo/testshared/shared_test.go @@ -20,6 +20,7 @@ import ( "regexp" "runtime" "sort" + "strconv" "strings" "testing" "time" @@ -55,7 +56,7 @@ func runWithEnv(t *testing.T, msg string, env []string, args ...string) { // t.Fatalf if the command fails. func goCmd(t *testing.T, args ...string) string { newargs := []string{args[0]} - if *testX { + if *testX && args[0] != "env" { newargs = append(newargs, "-x") } newargs = append(newargs, args[1:]...) @@ -461,7 +462,9 @@ func TestTrivialExecutable(t *testing.T) { run(t, "trivial executable", "../../bin/trivial") AssertIsLinkedTo(t, "../../bin/trivial", soname) AssertHasRPath(t, "../../bin/trivial", gorootInstallDir) - checkSize(t, "../../bin/trivial", 100000) // it is 19K on linux/amd64, 100K should be enough + // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment + // 4*64k should be enough, but this might need revision eventually. + checkSize(t, "../../bin/trivial", 256000) } // Build a trivial program in PIE mode that links against the shared runtime and check it runs. @@ -470,7 +473,9 @@ func TestTrivialExecutablePIE(t *testing.T) { run(t, "trivial executable", "./trivial.pie") AssertIsLinkedTo(t, "./trivial.pie", soname) AssertHasRPath(t, "./trivial.pie", gorootInstallDir) - checkSize(t, "./trivial.pie", 100000) // it is 19K on linux/amd64, 100K should be enough + // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment + // 4*64k should be enough, but this might need revision eventually. + checkSize(t, "./trivial.pie", 256000) } // Check that the file size does not exceed a limit. @@ -694,7 +699,15 @@ func requireGccgo(t *testing.T) { if err != nil { t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output) } - if string(output) < "5" { + dot := bytes.Index(output, []byte{'.'}) + if dot > 0 { + output = output[:dot] + } + major, err := strconv.Atoi(string(output)) + if err != nil { + t.Skipf("can't parse gccgo version number %s", output) + } + if major < 5 { t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output))) } @@ -1033,7 +1046,7 @@ func TestGlobal(t *testing.T) { // Run a test using -linkshared of an installed shared package. // Issue 26400. func TestTestInstalledShared(t *testing.T) { - goCmd(nil, "test", "-linkshared", "-test.short", "sync/atomic") + goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic") } // Test generated pointer method with -linkshared. @@ -1045,8 +1058,8 @@ func TestGeneratedMethod(t *testing.T) { // Test use of shared library struct with generated hash function. // Issue 30768. func TestGeneratedHash(t *testing.T) { - goCmd(nil, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib") - goCmd(nil, "test", "-linkshared", "./issue30768") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib") + goCmd(t, "test", "-linkshared", "./issue30768") } // Test that packages can be added not in dependency order (here a depends on b, and a adds @@ -1070,3 +1083,11 @@ func TestIssue44031(t *testing.T) { goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b") goCmd(t, "run", "-linkshared", "./issue44031/main") } + +// Test that we use a variable from shared libraries (which implement an +// interface in shared libraries.). A weak reference is used in the itab +// in main process. It can cause unreacheble panic. See issue 47873. +func TestIssue47873(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a") + goCmd(t, "run", "-linkshared", "./issue47837/main") +} diff --git a/libgo/misc/cgo/testshared/testdata/issue47837/a/a.go b/libgo/misc/cgo/testshared/testdata/issue47837/a/a.go new file mode 100644 index 00000000000..68588eda2fa --- /dev/null +++ b/libgo/misc/cgo/testshared/testdata/issue47837/a/a.go @@ -0,0 +1,19 @@ +// 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 a + +type A interface { + M() +} + +//go:noinline +func TheFuncWithArgA(a A) { + a.M() +} + +type ImplA struct{} + +//go:noinline +func (A *ImplA) M() {} diff --git a/libgo/misc/cgo/testshared/testdata/issue47837/main/main.go b/libgo/misc/cgo/testshared/testdata/issue47837/main/main.go new file mode 100644 index 00000000000..77c6f343793 --- /dev/null +++ b/libgo/misc/cgo/testshared/testdata/issue47837/main/main.go @@ -0,0 +1,14 @@ +// 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 ( + "testshared/issue47837/a" +) + +func main() { + var vara a.ImplA + a.TheFuncWithArgA(&vara) +} diff --git a/libgo/misc/cgo/testso/noso_test.go b/libgo/misc/cgo/testso/noso_test.go index c88aebfb02a..1014534d62c 100644 --- a/libgo/misc/cgo/testso/noso_test.go +++ b/libgo/misc/cgo/testso/noso_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !cgo // +build !cgo package so_test diff --git a/libgo/misc/cgo/testsovar/noso_test.go b/libgo/misc/cgo/testsovar/noso_test.go index c88aebfb02a..1014534d62c 100644 --- a/libgo/misc/cgo/testsovar/noso_test.go +++ b/libgo/misc/cgo/testsovar/noso_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !cgo // +build !cgo package so_test diff --git a/libgo/misc/cgo/testtls/tls_test.go b/libgo/misc/cgo/testtls/tls_test.go index 3076c2d5943..a3b67c00441 100644 --- a/libgo/misc/cgo/testtls/tls_test.go +++ b/libgo/misc/cgo/testtls/tls_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotlstest |