Skip to content

Commit 422c7aa

Browse files
authored
Merge branch 'master' into qemugo-todo-patch
2 parents 78e38df + ef766cd commit 422c7aa

File tree

23 files changed

+244
-175
lines changed

23 files changed

+244
-175
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ format_cpp:
272272
clang-format --style=file -i executor/*.cc executor/*.h \
273273
executor/android/android_seccomp.h \
274274
tools/kcovtrace/*.c tools/kcovfuzzer/*.c tools/fops_probe/*.cc \
275-
tools/syz-declextract/clangtool/*.cpp tools/syz-declextract/clangtool/*.h
275+
tools/clang/*.h \
276+
tools/clang/declextract/*.h tools/clang/declextract/*.cpp
276277

277278
format_sys: bin/syz-fmt
278279
bin/syz-fmt all

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
cloud.google.com/go/compute/metadata v0.9.0
1010
cloud.google.com/go/logging v1.13.1
1111
cloud.google.com/go/profiler v0.4.3
12-
cloud.google.com/go/secretmanager v1.15.1
12+
cloud.google.com/go/secretmanager v1.16.0
1313
cloud.google.com/go/spanner v1.82.0
1414
cloud.google.com/go/storage v1.57.1
1515
github.com/VividCortex/gohistogram v1.0.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,8 @@ cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISI
504504
cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4=
505505
cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4=
506506
cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU=
507-
cloud.google.com/go/secretmanager v1.15.1 h1:OC9KtdV7eZ4SGQOzFR/qltXbSW6mYiRF4O+ajKYMs1s=
508-
cloud.google.com/go/secretmanager v1.15.1/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q=
507+
cloud.google.com/go/secretmanager v1.16.0 h1:19QT7ZsLJ8FSP1k+4esQvuCD7npMJml6hYzilxVyT+k=
508+
cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q=
509509
cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
510510
cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
511511
cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=

pkg/clangtool/clangtool.go

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package clangtool
55

66
import (
77
"bytes"
8+
"crypto/sha256"
89
"encoding/json"
910
"errors"
1011
"fmt"
@@ -18,7 +19,6 @@ import (
1819
"strings"
1920
"time"
2021

21-
"github.com/google/syzkaller/pkg/declextract"
2222
"github.com/google/syzkaller/pkg/osutil"
2323
)
2424

@@ -30,17 +30,21 @@ type Config struct {
3030
DebugTrace io.Writer
3131
}
3232

33+
type OutputDataPtr[T any] interface {
34+
*T
35+
Merge(*T)
36+
SetSourceFile(string, func(filename string) string)
37+
SortAndDedup()
38+
}
39+
3340
// Run runs the clang tool on all files in the compilation database
3441
// in the kernel build dir and returns combined output for all files.
3542
// It always caches results, and optionally reuses previously cached results.
36-
func Run(cfg *Config) (*declextract.Output, error) {
43+
func Run[Output any, OutputPtr OutputDataPtr[Output]](cfg *Config) (OutputPtr, error) {
3744
if cfg.CacheFile != "" {
38-
data, err := os.ReadFile(cfg.CacheFile)
45+
out, err := osutil.ReadJSON[OutputPtr](cfg.CacheFile)
3946
if err == nil {
40-
out, err := unmarshal(data)
41-
if err == nil {
42-
return out, nil
43-
}
47+
return out, nil
4448
}
4549
}
4650

@@ -51,15 +55,15 @@ func Run(cfg *Config) (*declextract.Output, error) {
5155
}
5256

5357
type result struct {
54-
out *declextract.Output
58+
out OutputPtr
5559
err error
5660
}
5761
results := make(chan *result, 10)
5862
files := make(chan string, len(cmds))
5963
for w := 0; w < runtime.NumCPU(); w++ {
6064
go func() {
6165
for file := range files {
62-
out, err := runTool(cfg, dbFile, file)
66+
out, err := runTool[Output, OutputPtr](cfg, dbFile, file)
6367
results <- &result{out, err}
6468
}
6569
}()
@@ -69,7 +73,7 @@ func Run(cfg *Config) (*declextract.Output, error) {
6973
}
7074
close(files)
7175

72-
out := new(declextract.Output)
76+
out := OutputPtr(new(Output))
7377
for range cmds {
7478
res := <-results
7579
if res.err != nil {
@@ -91,7 +95,7 @@ func Run(cfg *Config) (*declextract.Output, error) {
9195
return out, nil
9296
}
9397

94-
func runTool(cfg *Config, dbFile, file string) (*declextract.Output, error) {
98+
func runTool[Output any, OutputPtr OutputDataPtr[Output]](cfg *Config, dbFile, file string) (OutputPtr, error) {
9599
relFile := strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(filepath.Clean(file),
96100
cfg.KernelSrc), cfg.KernelObj), "/")
97101
// Suppress warning since we may build the tool on a different clang
@@ -104,33 +108,20 @@ func runTool(cfg *Config, dbFile, file string) (*declextract.Output, error) {
104108
}
105109
return nil, err
106110
}
107-
out, err := unmarshal(data)
111+
out, err := osutil.ParseJSON[OutputPtr](data)
108112
if err != nil {
109113
return nil, err
110114
}
111-
fixupFileNames(cfg, out, relFile)
112-
return out, nil
113-
}
114-
115-
func unmarshal(data []byte) (*declextract.Output, error) {
116-
dec := json.NewDecoder(bytes.NewReader(data))
117-
dec.DisallowUnknownFields()
118-
out := new(declextract.Output)
119-
if err := dec.Decode(out); err != nil {
120-
return nil, fmt.Errorf("failed to unmarshal clang tool output: %w\n%s", err, data)
121-
}
122-
return out, nil
123-
}
124-
125-
func fixupFileNames(cfg *Config, out *declextract.Output, file string) {
126115
// All includes in the tool output are relative to the build dir.
127116
// Make them relative to the source dir.
128-
out.SetSourceFile(file, func(filename string) string {
129-
if res, err := filepath.Rel(cfg.KernelSrc, filepath.Join(cfg.KernelObj, filename)); err == nil {
130-
return res
117+
out.SetSourceFile(relFile, func(filename string) string {
118+
rel, err := filepath.Rel(cfg.KernelSrc, filepath.Join(cfg.KernelObj, filename))
119+
if err == nil && filename != "" {
120+
return rel
131121
}
132122
return filename
133123
})
124+
return out, nil
134125
}
135126

136127
type compileCommand struct {
@@ -170,3 +161,21 @@ func loadCompileCommands(dbFile string) ([]compileCommand, error) {
170161
}
171162
return cmds, nil
172163
}
164+
165+
func SortAndDedupSlice[Slice ~[]E, E comparable](s Slice) Slice {
166+
dedup := make(map[[sha256.Size]byte]E)
167+
text := make(map[E][]byte)
168+
for _, e := range s {
169+
t, _ := json.Marshal(e)
170+
dedup[sha256.Sum256(t)] = e
171+
text[e] = t
172+
}
173+
s = make([]E, 0, len(dedup))
174+
for _, e := range dedup {
175+
s = append(s, e)
176+
}
177+
slices.SortFunc(s, func(a, b E) int {
178+
return bytes.Compare(text[a], text[b])
179+
})
180+
return s
181+
}

pkg/clangtool/tooltest/tooltest.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2025 syzkaller project authors. All rights reserved.
2+
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3+
4+
package tooltest
5+
6+
import (
7+
"encoding/json"
8+
"flag"
9+
"fmt"
10+
"os"
11+
"path/filepath"
12+
"testing"
13+
14+
"github.com/google/go-cmp/cmp"
15+
"github.com/google/syzkaller/pkg/clangtool"
16+
"github.com/google/syzkaller/pkg/osutil"
17+
"github.com/google/syzkaller/pkg/testutil"
18+
)
19+
20+
var (
21+
FlagBin = flag.String("bin", "", "path to the clang tool binary to use")
22+
FlagUpdate = flag.Bool("update", false, "update golden files")
23+
)
24+
25+
func TestClangTool[Output any, OutputPtr clangtool.OutputDataPtr[Output]](t *testing.T) {
26+
if *FlagBin == "" {
27+
t.Skipf("clang tool path is not specified, run with -bin=clangtool flag")
28+
}
29+
ForEachTestFile(t, func(t *testing.T, cfg *clangtool.Config, file string) {
30+
out, err := clangtool.Run[Output, OutputPtr](cfg)
31+
if err != nil {
32+
t.Fatal(err)
33+
}
34+
got, err := json.MarshalIndent(out, "", "\t")
35+
if err != nil {
36+
t.Fatal(err)
37+
}
38+
CompareGoldenData(t, file+".json", got)
39+
})
40+
}
41+
42+
func LoadOutput[Output any, OutputPtr clangtool.OutputDataPtr[Output]](t *testing.T) OutputPtr {
43+
out := OutputPtr(new(Output))
44+
forEachTestFile(t, func(t *testing.T, file string) {
45+
tmp, err := osutil.ReadJSON[OutputPtr](file + ".json")
46+
if err != nil {
47+
t.Fatal(err)
48+
}
49+
out.Merge(tmp)
50+
})
51+
out.SortAndDedup()
52+
return out
53+
}
54+
55+
func ForEachTestFile(t *testing.T, fn func(t *testing.T, cfg *clangtool.Config, file string)) {
56+
forEachTestFile(t, func(t *testing.T, file string) {
57+
t.Run(filepath.Base(file), func(t *testing.T) {
58+
t.Parallel()
59+
buildDir := t.TempDir()
60+
commands := fmt.Sprintf(`[{
61+
"file": "%s",
62+
"directory": "%s",
63+
"command": "clang -c %s -DKBUILD_BASENAME=foo"
64+
}]`,
65+
file, buildDir, file)
66+
dbFile := filepath.Join(buildDir, "compile_commands.json")
67+
if err := os.WriteFile(dbFile, []byte(commands), 0600); err != nil {
68+
t.Fatal(err)
69+
}
70+
cfg := &clangtool.Config{
71+
ToolBin: *FlagBin,
72+
KernelSrc: osutil.Abs("testdata"),
73+
KernelObj: buildDir,
74+
CacheFile: filepath.Join(buildDir, filepath.Base(file)+".json"),
75+
DebugTrace: &testutil.Writer{TB: t},
76+
}
77+
fn(t, cfg, file)
78+
})
79+
})
80+
}
81+
82+
func forEachTestFile(t *testing.T, fn func(t *testing.T, file string)) {
83+
files, err := filepath.Glob(filepath.Join(osutil.Abs("testdata"), "*.c"))
84+
if err != nil {
85+
t.Fatal(err)
86+
}
87+
if len(files) == 0 {
88+
t.Fatal("found no source files")
89+
}
90+
for _, file := range files {
91+
fn(t, file)
92+
}
93+
}
94+
95+
func CompareGoldenFile(t *testing.T, goldenFile, gotFile string) {
96+
got, err := os.ReadFile(gotFile)
97+
if err != nil {
98+
t.Fatal(err)
99+
}
100+
CompareGoldenData(t, goldenFile, got)
101+
}
102+
103+
func CompareGoldenData(t *testing.T, goldenFile string, got []byte) {
104+
if *FlagUpdate {
105+
if err := os.WriteFile(goldenFile, got, 0644); err != nil {
106+
t.Fatal(err)
107+
}
108+
}
109+
want, err := os.ReadFile(goldenFile)
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
if diff := cmp.Diff(want, got); diff != "" {
114+
t.Fatal(diff)
115+
}
116+
}

pkg/declextract/declextract.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"slices"
1313
"strings"
1414

15+
"github.com/google/syzkaller/pkg/clangtool"
1516
"github.com/google/syzkaller/pkg/cover"
1617
"github.com/google/syzkaller/pkg/ifaceprobe"
1718
)
@@ -136,8 +137,8 @@ func (ctx *context) processConsts() map[string]string {
136137
Value: fmt.Sprint(ci.Value),
137138
})
138139
}
139-
ctx.includes = sortAndDedupSlice(ctx.includes)
140-
ctx.defines = sortAndDedupSlice(ctx.defines)
140+
ctx.includes = clangtool.SortAndDedupSlice(ctx.includes)
141+
ctx.defines = clangtool.SortAndDedupSlice(ctx.defines)
141142
// These additional includes must be at the top, because other kernel headers
142143
// are broken and won't compile without these additional ones included first.
143144
ctx.includes = append([]string{
@@ -194,7 +195,7 @@ func (ctx *context) processSyscalls() {
194195
}
195196
ctx.emitSyscall(&syscalls, call, "", "", -1, "")
196197
}
197-
ctx.Syscalls = sortAndDedupSlice(syscalls)
198+
ctx.Syscalls = clangtool.SortAndDedupSlice(syscalls)
198199
}
199200

200201
func (ctx *context) emitSyscall(syscalls *[]*Syscall, call *Syscall,

pkg/declextract/entity.go

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
package declextract
55

66
import (
7-
"bytes"
8-
"crypto/sha256"
9-
"encoding/json"
10-
"slices"
117
"strings"
8+
9+
"github.com/google/syzkaller/pkg/clangtool"
1210
)
1311

1412
type Output struct {
@@ -244,16 +242,16 @@ func (out *Output) Merge(other *Output) {
244242
}
245243

246244
func (out *Output) SortAndDedup() {
247-
out.Functions = sortAndDedupSlice(out.Functions)
248-
out.Consts = sortAndDedupSlice(out.Consts)
249-
out.Enums = sortAndDedupSlice(out.Enums)
250-
out.Structs = sortAndDedupSlice(out.Structs)
251-
out.Syscalls = sortAndDedupSlice(out.Syscalls)
252-
out.FileOps = sortAndDedupSlice(out.FileOps)
253-
out.Ioctls = sortAndDedupSlice(out.Ioctls)
254-
out.IouringOps = sortAndDedupSlice(out.IouringOps)
255-
out.NetlinkFamilies = sortAndDedupSlice(out.NetlinkFamilies)
256-
out.NetlinkPolicies = sortAndDedupSlice(out.NetlinkPolicies)
245+
out.Functions = clangtool.SortAndDedupSlice(out.Functions)
246+
out.Consts = clangtool.SortAndDedupSlice(out.Consts)
247+
out.Enums = clangtool.SortAndDedupSlice(out.Enums)
248+
out.Structs = clangtool.SortAndDedupSlice(out.Structs)
249+
out.Syscalls = clangtool.SortAndDedupSlice(out.Syscalls)
250+
out.FileOps = clangtool.SortAndDedupSlice(out.FileOps)
251+
out.Ioctls = clangtool.SortAndDedupSlice(out.Ioctls)
252+
out.IouringOps = clangtool.SortAndDedupSlice(out.IouringOps)
253+
out.NetlinkFamilies = clangtool.SortAndDedupSlice(out.NetlinkFamilies)
254+
out.NetlinkPolicies = clangtool.SortAndDedupSlice(out.NetlinkPolicies)
257255
}
258256

259257
// SetSoureFile attaches the source file to the entities that need it.
@@ -285,21 +283,3 @@ func (out *Output) SetSourceFile(file string, updatePath func(string) string) {
285283
op.SourceFile = file
286284
}
287285
}
288-
289-
func sortAndDedupSlice[Slice ~[]E, E comparable](s Slice) Slice {
290-
dedup := make(map[[sha256.Size]byte]E)
291-
text := make(map[E][]byte)
292-
for _, e := range s {
293-
t, _ := json.Marshal(e)
294-
dedup[sha256.Sum256(t)] = e
295-
text[e] = t
296-
}
297-
s = make([]E, 0, len(dedup))
298-
for _, e := range dedup {
299-
s = append(s, e)
300-
}
301-
slices.SortFunc(s, func(a, b E) int {
302-
return bytes.Compare(text[a], text[b])
303-
})
304-
return s
305-
}

pkg/declextract/fileops.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/google/syzkaller/pkg/ast"
12+
"github.com/google/syzkaller/pkg/clangtool"
1213
)
1314

1415
const (
@@ -243,7 +244,7 @@ func (ctx *context) mapFileToFops(funcs map[*Function]bool, funcToFops map[*Func
243244
best = append(best, fops)
244245
}
245246
}
246-
best = sortAndDedupSlice(best)
247+
best = clangtool.SortAndDedupSlice(best)
247248
// Now, filter out some excessive file_operations.
248249
// An example of an excessive case is if we have 2 file_operations with just read+write,
249250
// currently we emit generic read/write operations, so we would emit completly equal

0 commit comments

Comments
 (0)