diff options
| author | Alexander Potapenko <glider@google.com> | 2024-01-10 16:17:08 +0100 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2024-01-29 09:09:17 +0000 |
| commit | 991a98f41ea59aba246c9165c8d9539d780a2544 (patch) | |
| tree | c10a0763b334f6b0beb52b6a663785768f2e96d7 /pkg/cover/report_test.go | |
| parent | e9cba3e0b8c082d74feee5139584c29866f4df60 (diff) | |
pkg/cover: ensure that all PCs returned by kcov have matching callbacks
In the case some modules' addresses are off, certain kernel addresses
returned by kcov may not have corresponding coverage callbacks in the
.ko files. Keep an additional map in the backend to verify those
addresses and report an error if that is the case.
Because GCC < 14 may tail-call coverage callbacks, the described check
is not performed for binaries which mention GCC in their .comment
section.
Also adjust text expectations in pkg/cover/report_test.go, so that
non-GCC targets check for PCs matching the callbacks.
See https://github.com/google/syzkaller/issues/4447 for more details.
Diffstat (limited to 'pkg/cover/report_test.go')
| -rw-r--r-- | pkg/cover/report_test.go | 49 |
1 files changed, 44 insertions, 5 deletions
diff --git a/pkg/cover/report_test.go b/pkg/cover/report_test.go index 64baf3ee2..c6c9d28f1 100644 --- a/pkg/cover/report_test.go +++ b/pkg/cover/report_test.go @@ -11,6 +11,7 @@ package cover import ( "bytes" "encoding/csv" + "fmt" "os" "path/filepath" "reflect" @@ -36,8 +37,15 @@ type Test struct { Progs []Prog DebugInfo bool AddCover bool - Result string - Supports func(target *targets.Target) bool + AddBadPc bool + // Set by the testing environment depending on the target compiler. + // See https://github.com/google/syzkaller/issues/4447. + KcovIsBroken bool + // Set to true if the test should be skipped under broken kcov. + SkipIfKcovIsBroken bool + // Inexact coverage generated by AddCover=true may override empty Result. + Result string + Supports func(target *targets.Target) bool } func TestReportGenerator(t *testing.T) { @@ -74,6 +82,15 @@ func TestReportGenerator(t *testing.T) { DebugInfo: true, }, { + Name: "mismatch-pcs", + AddCover: true, + AddBadPc: true, + CFlags: []string{"-fsanitize-coverage=trace-pc"}, + DebugInfo: true, + SkipIfKcovIsBroken: true, + Result: `.* do not have matching coverage callbacks`, + }, + { Name: "good-pie", AddCover: true, CFlags: []string{"-fsanitize-coverage=trace-pc", "-fpie"}, @@ -116,12 +133,17 @@ func TestReportGenerator(t *testing.T) { if target.BrokenCompiler != "" { t.Skip("skipping the test due to broken cross-compiler:\n" + target.BrokenCompiler) } + kcovIsBroken := targetKcovIsBroken(t, target) for _, test := range tests { test := test + test.KcovIsBroken = kcovIsBroken t.Run(test.Name, func(t *testing.T) { if test.Supports != nil && !test.Supports(target) { t.Skip("unsupported target") } + if test.SkipIfKcovIsBroken && test.KcovIsBroken { + t.Skip("coverage testing requested, but kcov is broken") + } t.Parallel() testReportGenerator(t, target, test) }) @@ -132,7 +154,7 @@ func TestReportGenerator(t *testing.T) { } func testReportGenerator(t *testing.T, target *targets.Target, test Test) { - rep, csv, err := generateReport(t, target, test) + rep, csv, err := generateReport(t, target, &test) if err != nil { if test.Result == "" { t.Fatalf("expected no error, but got:\n%v", err) @@ -177,7 +199,7 @@ void* aslr_base() { return NULL; } void __sanitizer_cov_trace_pc() { printf("%llu", (long long)(__builtin_return_address(0) - aslr_base())); } ` -func buildTestBinary(t *testing.T, target *targets.Target, test Test, dir string) string { +func buildTestBinary(t *testing.T, target *targets.Target, test *Test, dir string) string { kcovSrc := filepath.Join(dir, "kcov.c") kcovObj := filepath.Join(dir, "kcov.o") if err := osutil.WriteFile(kcovSrc, []byte(kcovCode)); err != nil { @@ -257,7 +279,15 @@ func buildTestBinary(t *testing.T, target *targets.Target, test Test, dir string return bin } -func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, []byte, error) { +// Work around https://github.com/google/syzkaller/issues/4447. Do not use for anything else. +func targetKcovIsBroken(t *testing.T, target *targets.Target) bool { + if out, err := osutil.RunCmd(time.Hour, "", target.CCompiler, "--version"); err == nil { + return backend.IsKcovBrokenInCompiler(string(out)) + } + return true +} + +func generateReport(t *testing.T, target *targets.Target, test *Test) ([]byte, []byte, error) { dir := t.TempDir() bin := buildTestBinary(t, target, test, dir) cfg := &mgrconfig.Config{ @@ -292,6 +322,7 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, [] } if test.AddCover { var pcs []uint64 + Inexact := false // Sanitizers crash when installing signal handlers with static libc. const sanitizerOptions = "handle_segv=0:handle_sigbus=0:handle_sigfpe=0" cmd := osutil.Command(bin) @@ -322,6 +353,14 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, [] pcs = append(pcs, main.Addr+uint64(off)) } t.Logf("using inexact coverage range 0x%x-0x%x", main.Addr, main.Addr+uint64(main.Size)) + Inexact = true + } + if Inexact && test.Result == "" && !test.KcovIsBroken { + test.Result = fmt.Sprintf("%d out of %d PCs returned by kcov do not have matching coverage callbacks", + len(pcs)-1, len(pcs)) + } + if test.AddBadPc { + pcs = append(pcs, 0xdeadbeef) } progs = append(progs, Prog{Data: "main", PCs: pcs}) } |
