diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2025-08-29 14:29:23 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2025-08-29 16:37:53 +0000 |
| commit | 807a3b61ca22f8988561c180eb47268ea6e244db (patch) | |
| tree | c29598ca28b49bca60e3961dc49d41ff7142cd8f /syz-cluster/workflow | |
| parent | b0c32b0591eb98d9fd9a2590b140eb771cf86c46 (diff) | |
syz-cluster: consider global/static variable values
When determining whether a patch series is worth fuzzing, consider not
only the hashes of .text symbols, but also the hashes of the global
(static and non-static) variables.
As before, calculate the hashes during build and process them at the
beginning of the fuzz step.
Diffstat (limited to 'syz-cluster/workflow')
| -rw-r--r-- | syz-cluster/workflow/build-step/main.go | 3 | ||||
| -rw-r--r-- | syz-cluster/workflow/fuzz-step/main.go | 50 | ||||
| -rw-r--r-- | syz-cluster/workflow/fuzz-step/main_test.go | 81 |
3 files changed, 109 insertions, 25 deletions
diff --git a/syz-cluster/workflow/build-step/main.go b/syz-cluster/workflow/build-step/main.go index 943b18700..0fb89225f 100644 --- a/syz-cluster/workflow/build-step/main.go +++ b/syz-cluster/workflow/build-step/main.go @@ -293,7 +293,8 @@ func saveSymbolHashes(tracer debugtracer.DebugTracer) error { if err != nil { return fmt.Errorf("failed to query symbol hashes: %w", err) } - tracer.Log("extracted hashes for %d symbols", len(hashes)) + tracer.Log("extracted hashes for %d text symbols and %d data symbols", + len(hashes.Text), len(hashes.Data)) file, err := os.Create(filepath.Join(*flagOutput, "symbol_hashes.json")) if err != nil { return fmt.Errorf("failed to open symbol_hashes.json: %w", err) diff --git a/syz-cluster/workflow/fuzz-step/main.go b/syz-cluster/workflow/fuzz-step/main.go index 7e1589115..efbbdd3d4 100644 --- a/syz-cluster/workflow/fuzz-step/main.go +++ b/syz-cluster/workflow/fuzz-step/main.go @@ -16,6 +16,7 @@ import ( "path/filepath" "time" + "github.com/google/syzkaller/pkg/build" "github.com/google/syzkaller/pkg/config" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/manager" @@ -108,7 +109,7 @@ func run(baseCtx context.Context, client *api.Client, timeout time.Duration, if shouldSkipFuzzing(baseSymbols, patchedSymbols) { return errSkipFuzzing } - manager.PatchFocusAreas(patched, series.PatchBodies(), baseSymbols, patchedSymbols) + manager.PatchFocusAreas(patched, series.PatchBodies(), baseSymbols.Text, patchedSymbols.Text) if *flagCorpusURL != "" { err := downloadCorpus(baseCtx, patched.Workdir, *flagCorpusURL) @@ -311,15 +312,32 @@ func reportFinding(ctx context.Context, client *api.Client, bug *manager.UniqueB return client.UploadFinding(ctx, finding) } -func shouldSkipFuzzing(baseSymbols, patchedSymbols map[string]string) bool { - if len(baseSymbols) == 0 || len(patchedSymbols) == 0 { +var ignoreLinuxVariables = map[string]bool{ + "raw_data": true, // from arch/x86/entry/vdso/vdso-image + // Build versions / timestamps. + "linux_banner": true, + "vermagic": true, + "init_uts_ns": true, +} + +func shouldSkipFuzzing(base, patched build.SectionHashes) bool { + if len(base.Text) == 0 || len(patched.Text) == 0 { // Likely, something went wrong during the kernel build step. log.Logf(0, "skipped the binary equality check because some of them have 0 symbols") return false } - same := len(baseSymbols) == len(patchedSymbols) - for name, hash := range baseSymbols { - if patchedSymbols[name] != hash { + same := len(base.Text) == len(patched.Text) && len(base.Data) == len(patched.Data) + // For .text, demand all symbols to be equal. + for name, hash := range base.Text { + if patched.Text[name] != hash { + same = false + break + } + } + // For data sections ignore some of them. + for name, hash := range base.Data { + if !ignoreLinuxVariables[name] && patched.Data[name] != hash { + log.Logf(1, "symbol %q has different values in base vs patch", name) same = false break } @@ -332,31 +350,31 @@ func shouldSkipFuzzing(baseSymbols, patchedSymbols map[string]string) bool { return false } -func readSymbolHashes() (base, patched map[string]string, err error) { +func readSymbolHashes() (base, patched build.SectionHashes, err error) { // These are saved by the build step. - base, err = readJSONMap("/base/symbol_hashes.json") + base, err = readSectionHashes("/base/symbol_hashes.json") if err != nil { - return nil, nil, fmt.Errorf("failed to read base hashes: %w", err) + return build.SectionHashes{}, build.SectionHashes{}, fmt.Errorf("failed to read base hashes: %w", err) } - patched, err = readJSONMap("/patched/symbol_hashes.json") + patched, err = readSectionHashes("/patched/symbol_hashes.json") if err != nil { - return nil, nil, fmt.Errorf("failed to read patched hashes: %w", err) + return build.SectionHashes{}, build.SectionHashes{}, fmt.Errorf("failed to read patched hashes: %w", err) } - log.Logf(0, "extracted %d symbol hashes for base and %d for patched", len(base), len(patched)) + log.Logf(0, "extracted %d text symbol hashes for base and %d for patched", len(base.Text), len(patched.Text)) return } -func readJSONMap(file string) (map[string]string, error) { +func readSectionHashes(file string) (build.SectionHashes, error) { f, err := os.Open(file) if err != nil { - return nil, err + return build.SectionHashes{}, err } defer f.Close() - var data map[string]string + var data build.SectionHashes err = json.NewDecoder(f).Decode(&data) if err != nil { - return nil, err + return build.SectionHashes{}, err } return data, nil } diff --git a/syz-cluster/workflow/fuzz-step/main_test.go b/syz-cluster/workflow/fuzz-step/main_test.go index 0336281b8..038690bd6 100644 --- a/syz-cluster/workflow/fuzz-step/main_test.go +++ b/syz-cluster/workflow/fuzz-step/main_test.go @@ -4,11 +4,16 @@ package main import ( + "encoding/json" "io/fs" + "os" "path/filepath" "testing" + "github.com/google/syzkaller/pkg/build" + "github.com/google/syzkaller/pkg/osutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestConfigLoad(t *testing.T) { @@ -29,26 +34,86 @@ func TestConfigLoad(t *testing.T) { }) } +func TestReadSectionHashes(t *testing.T) { + hashes := build.SectionHashes{ + Text: map[string]string{"A": "1"}, + Data: map[string]string{"B": "2"}, + } + + jsonData, err := json.Marshal(hashes) + require.NoError(t, err) + + file, err := osutil.WriteTempFile(jsonData) + require.NoError(t, err) + defer os.Remove(file) + + fromFile, err := readSectionHashes(file) + require.NoError(t, err) + assert.Equal(t, hashes, fromFile) +} + +// nolint: dupl func TestShouldSkipFuzzing(t *testing.T) { t.Run("one empty", func(t *testing.T) { - assert.False(t, shouldSkipFuzzing(nil, map[string]string{"A": "1"})) + assert.False(t, shouldSkipFuzzing( + build.SectionHashes{}, + build.SectionHashes{ + Text: map[string]string{"A": "1"}, + }, + )) + }) + t.Run("equal symbols", func(t *testing.T) { + assert.True(t, shouldSkipFuzzing( + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + Data: map[string]string{"C": "1", "D": "2"}, + }, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + Data: map[string]string{"C": "1", "D": "2"}, + }, + )) }) - t.Run("equal", func(t *testing.T) { + t.Run("ignore known variables", func(t *testing.T) { assert.True(t, shouldSkipFuzzing( - map[string]string{"A": "1", "B": "2"}, - map[string]string{"A": "1", "B": "2"}, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + Data: map[string]string{"C": "1", "raw_data": "A", "vermagic": "A"}, + }, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + Data: map[string]string{"C": "1", "raw_data": "B", "vermagic": "B"}, + }, )) }) t.Run("same len, different hashes", func(t *testing.T) { assert.False(t, shouldSkipFuzzing( - map[string]string{"A": "1", "B": "2"}, - map[string]string{"A": "1", "B": "different"}, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + }, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "different"}, + }, + )) + assert.False(t, shouldSkipFuzzing( + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + Data: map[string]string{"C": "1", "D": "2"}, + }, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + Data: map[string]string{"C": "1", "D": "different"}, + }, )) }) t.Run("different len, same hashes", func(t *testing.T) { assert.False(t, shouldSkipFuzzing( - map[string]string{"A": "1", "B": "2", "C": "3"}, - map[string]string{"A": "1", "B": "2"}, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2"}, + }, + build.SectionHashes{ + Text: map[string]string{"A": "1", "B": "2", "C": "new"}, + }, )) }) } |
