Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/build: incorrect use of t.Fatal()/t.Fatalf() in tests #4315

Open
ajdlinux opened this issue Nov 7, 2023 · 2 comments
Open

pkg/build: incorrect use of t.Fatal()/t.Fatalf() in tests #4315

ajdlinux opened this issue Nov 7, 2023 · 2 comments
Labels

Comments

@ajdlinux
Copy link
Contributor

ajdlinux commented Nov 7, 2023

Describe the bug

At least one test in pkg/build (but possibly in other parts) incorrectly calls t.Fatal()/t.Fatalf() from within a goroutine.

Per the documentation (https://pkg.go.dev/testing#T.FailNow), t.FailNow() (which is wrapped by t.Fatal() and t.Fatalf()) shouldn't be called from within a goroutine, other than the original test goroutine.

pkg/build/linux_test.go:43:

func enumerateFlags(t *testing.T, flags, allFlags []string) {
	if len(allFlags) != 0 {
		enumerateFlags(t, flags, allFlags[1:])
		enumerateFlags(t, append(flags, allFlags[0]), allFlags[1:])
		return
	}
	t.Run(strings.Join(flags, "-"), func(t *testing.T) {
		t.Parallel()
		sign1, sign2 := "", ""
		var wg sync.WaitGroup
		wg.Add(2)
		go func() {
			sign1 = sign(t, flags, false, false)
			wg.Done()
		}()
		go func() {
			sign2 = sign(t, flags, false, true)
			wg.Done()
		}()
		sign3 := sign(t, flags, true, false)
		wg.Wait()
		if sign1 != sign2 {
			t.Errorf("signature has changed after a comment-only change")
		}
		if sign1 == sign3 {
			t.Errorf("signature has not changed after a change")
		}
	})
}

func sign(t *testing.T, flags []string, changed, comment bool) string {
	buf := new(bytes.Buffer)
	if err := srcTemplate.Execute(buf, SrcParams{Changed: changed, Comment: comment}); err != nil {
		t.Fatal(err)
	}
	src := buf.Bytes()
	bin, err := osutil.TempFile("syz-build-test")
	if err != nil {
		t.Fatal(err) // <--- here
	}
	defer os.Remove(bin)
	cmd := osutil.Command("gcc", append(flags, "-pthread", "-o", bin, "-x", "c", "-")...)
	cmd.Stdin = buf
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("compiler failed: %v\n%s\n\n%s", err, src, out) // <--- here
	}
	sign, err := elfBinarySignature(bin, &debugtracer.TestTracer{T: t})
	if err != nil {
		t.Fatal(err) // <--- here
	}
	return sign
}

I'm fairly sure this is why, when the test fails on line 88, you only see:

panic: Fail in goroutine after TestElfBinarySignature/-g--static has completed

followed by a stack trace, rather than the format string that is supposed to be printed.

To Reproduce
n/a

Expected behavior
t.Fatal()/t.Fatalf() should only be called from the top-level goroutine

Additional context
n/a

@ajdlinux ajdlinux added the bug label Nov 7, 2023
@a-nogikh
Copy link
Collaborator

Thanks for reporting the problem!
Indeed, the docs say that Fatal/Fatalf should not be used from other goroutines.

I'm curious why go vet tool did not detect this problem (it's reportedly able to do this since Go 1.16).

@a-nogikh
Copy link
Collaborator

Looked a bit closer: the vet tool does indeed check for Fatal calls, but does not yet detect the cases when Fatal() is called indirectly. So far it was deemed low-priority.

a-nogikh added a commit to a-nogikh/syzkaller that referenced this issue Nov 28, 2023
It's prohibited by the Go testing library. Use T.Error() instead.

Cc google#4315

Reported-by: Andrew Donnellan
github-merge-queue bot pushed a commit that referenced this issue Nov 28, 2023
It's prohibited by the Go testing library. Use T.Error() instead.

Cc #4315

Reported-by: Andrew Donnellan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants