• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 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
4package main
5
6import (
7	"bytes"
8	"flag"
9	"fmt"
10	"io/ioutil"
11	"os"
12	"path/filepath"
13	"runtime"
14	"sort"
15	"strings"
16
17	"github.com/google/syzkaller/pkg/ast"
18	"github.com/google/syzkaller/pkg/compiler"
19	"github.com/google/syzkaller/pkg/osutil"
20	"github.com/google/syzkaller/sys/targets"
21)
22
23var (
24	flagOS        = flag.String("os", "", "target OS")
25	flagBuild     = flag.Bool("build", false, "regenerate arch-specific kernel headers")
26	flagSourceDir = flag.String("sourcedir", "", "path to kernel source checkout dir")
27	flagBuildDir  = flag.String("builddir", "", "path to kernel build dir")
28	flagArch      = flag.String("arch", "", "comma-separated list of arches to generate (all by default)")
29)
30
31type Arch struct {
32	target    *targets.Target
33	sourceDir string
34	buildDir  string
35	build     bool
36	files     []*File
37	err       error
38	done      chan bool
39}
40
41type File struct {
42	arch       *Arch
43	name       string
44	consts     map[string]uint64
45	undeclared map[string]bool
46	info       *compiler.ConstInfo
47	err        error
48	done       chan bool
49}
50
51type Extractor interface {
52	prepare(sourcedir string, build bool, arches []string) error
53	prepareArch(arch *Arch) error
54	processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error)
55}
56
57var extractors = map[string]Extractor{
58	"akaros":  new(akaros),
59	"linux":   new(linux),
60	"freebsd": new(freebsd),
61	"netbsd":  new(netbsd),
62	"android": new(linux),
63	"fuchsia": new(fuchsia),
64	"windows": new(windows),
65}
66
67func main() {
68	failf := func(msg string, args ...interface{}) {
69		fmt.Fprintf(os.Stderr, msg+"\n", args...)
70		os.Exit(1)
71	}
72	flag.Parse()
73	if *flagBuild && *flagBuildDir != "" {
74		failf("-build and -builddir is an invalid combination")
75	}
76
77	OS, archArray, files, err := archFileList(*flagOS, *flagArch, flag.Args())
78	if err != nil {
79		failf("%v", err)
80	}
81
82	extractor := extractors[OS]
83	if extractor == nil {
84		failf("unknown os: %v", OS)
85	}
86	if err := extractor.prepare(*flagSourceDir, *flagBuild, archArray); err != nil {
87		failf("%v", err)
88	}
89
90	arches, err := createArches(OS, archArray, files)
91	if err != nil {
92		failf("%v", err)
93	}
94	jobC := make(chan interface{}, len(archArray)*len(files))
95	for _, arch := range arches {
96		jobC <- arch
97	}
98
99	for p := 0; p < runtime.GOMAXPROCS(0); p++ {
100		go func() {
101			for job := range jobC {
102				switch j := job.(type) {
103				case *Arch:
104					infos, err := processArch(extractor, j)
105					j.err = err
106					close(j.done)
107					if j.err == nil {
108						for _, f := range j.files {
109							f.info = infos[f.name]
110							jobC <- f
111						}
112					}
113				case *File:
114					j.consts, j.undeclared, j.err = processFile(extractor, j.arch, j)
115					close(j.done)
116				}
117			}
118		}()
119	}
120
121	failed := false
122	for _, arch := range arches {
123		fmt.Printf("generating %v/%v...\n", arch.target.OS, arch.target.Arch)
124		<-arch.done
125		if arch.err != nil {
126			failed = true
127			fmt.Printf("	%v\n", arch.err)
128			continue
129		}
130		for _, f := range arch.files {
131			fmt.Printf("extracting from %v\n", f.name)
132			<-f.done
133			if f.err != nil {
134				failed = true
135				fmt.Printf("	%v\n", f.err)
136				continue
137			}
138		}
139		fmt.Printf("\n")
140	}
141
142	if !failed {
143		failed = checkUnsupportedCalls(arches)
144	}
145	for _, arch := range arches {
146		if arch.build {
147			os.RemoveAll(arch.buildDir)
148		}
149	}
150	if failed {
151		os.Exit(1)
152	}
153}
154
155func createArches(OS string, archArray, files []string) ([]*Arch, error) {
156	var arches []*Arch
157	for _, archStr := range archArray {
158		buildDir := ""
159		if *flagBuild {
160			dir, err := ioutil.TempDir("", "syzkaller-kernel-build")
161			if err != nil {
162				return nil, fmt.Errorf("failed to create temp dir: %v", err)
163			}
164			buildDir = dir
165		} else if *flagBuildDir != "" {
166			buildDir = *flagBuildDir
167		} else {
168			buildDir = *flagSourceDir
169		}
170
171		target := targets.Get(OS, archStr)
172		if target == nil {
173			return nil, fmt.Errorf("unknown arch: %v", archStr)
174		}
175
176		arch := &Arch{
177			target:    target,
178			sourceDir: *flagSourceDir,
179			buildDir:  buildDir,
180			build:     *flagBuild,
181			done:      make(chan bool),
182		}
183		for _, f := range files {
184			arch.files = append(arch.files, &File{
185				arch: arch,
186				name: f,
187				done: make(chan bool),
188			})
189		}
190		arches = append(arches, arch)
191	}
192	return arches, nil
193}
194
195func checkUnsupportedCalls(arches []*Arch) bool {
196	supported := make(map[string]bool)
197	unsupported := make(map[string]string)
198	for _, arch := range arches {
199		for _, f := range arch.files {
200			for name := range f.consts {
201				supported[name] = true
202			}
203			for name := range f.undeclared {
204				unsupported[name] = f.name
205			}
206		}
207	}
208	failed := false
209	for name, file := range unsupported {
210		if supported[name] {
211			continue
212		}
213		failed = true
214		fmt.Printf("%v: %v is unsupported on all arches (typo?)\n",
215			file, name)
216	}
217	return failed
218}
219
220func archFileList(os, arch string, files []string) (string, []string, []string, error) {
221	android := false
222	if os == "android" {
223		android = true
224		os = "linux"
225	}
226	var arches []string
227	if arch != "" {
228		arches = strings.Split(arch, ",")
229	} else {
230		for arch := range targets.List[os] {
231			arches = append(arches, arch)
232		}
233		if android {
234			arches = []string{"386", "amd64", "arm", "arm64"}
235		}
236		sort.Strings(arches)
237	}
238	if len(files) == 0 {
239		matches, err := filepath.Glob(filepath.Join("sys", os, "*.txt"))
240		if err != nil || len(matches) == 0 {
241			return "", nil, nil, fmt.Errorf("failed to find sys files: %v", err)
242		}
243		androidFiles := map[string]bool{
244			"tlk_device.txt": true,
245			// This was generated on:
246			// https://source.codeaurora.org/quic/la/kernel/msm-4.9 msm-4.9
247			"video4linux.txt": true,
248		}
249		for _, f := range matches {
250			f = filepath.Base(f)
251			if os == "linux" && android != androidFiles[f] {
252				continue
253			}
254			files = append(files, f)
255		}
256		sort.Strings(files)
257	}
258	return os, arches, files, nil
259}
260
261func processArch(extractor Extractor, arch *Arch) (map[string]*compiler.ConstInfo, error) {
262	errBuf := new(bytes.Buffer)
263	eh := func(pos ast.Pos, msg string) {
264		fmt.Fprintf(errBuf, "%v: %v\n", pos, msg)
265	}
266	top := ast.ParseGlob(filepath.Join("sys", arch.target.OS, "*.txt"), eh)
267	if top == nil {
268		return nil, fmt.Errorf("%v", errBuf.String())
269	}
270	infos := compiler.ExtractConsts(top, arch.target, eh)
271	if infos == nil {
272		return nil, fmt.Errorf("%v", errBuf.String())
273	}
274	if err := extractor.prepareArch(arch); err != nil {
275		return nil, err
276	}
277	return infos, nil
278}
279
280func processFile(extractor Extractor, arch *Arch, file *File) (map[string]uint64, map[string]bool, error) {
281	inname := filepath.Join("sys", arch.target.OS, file.name)
282	outname := strings.TrimSuffix(inname, ".txt") + "_" + arch.target.Arch + ".const"
283	if file.info == nil {
284		return nil, nil, fmt.Errorf("input file %v is missing", inname)
285	}
286	if len(file.info.Consts) == 0 {
287		os.Remove(outname)
288		return nil, nil, nil
289	}
290	consts, undeclared, err := extractor.processFile(arch, file.info)
291	if err != nil {
292		return nil, nil, err
293	}
294	data := compiler.SerializeConsts(consts, undeclared)
295	if err := osutil.WriteFile(outname, data); err != nil {
296		return nil, nil, fmt.Errorf("failed to write output file: %v", err)
297	}
298	return consts, undeclared, nil
299}
300