• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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 csource
5
6import (
7	"bytes"
8	"encoding/json"
9	"errors"
10	"fmt"
11
12	"github.com/google/syzkaller/pkg/mgrconfig"
13)
14
15// Options control various aspects of source generation.
16// Dashboard also provides serialized Options along with syzkaller reproducers.
17type Options struct {
18	Threaded    bool   `json:"threaded,omitempty"`
19	Collide     bool   `json:"collide,omitempty"`
20	Repeat      bool   `json:"repeat,omitempty"`
21	RepeatTimes int    `json:"repeat_times,omitempty"` // if non-0, repeat that many times
22	Procs       int    `json:"procs"`
23	Sandbox     string `json:"sandbox"`
24
25	Fault     bool `json:"fault,omitempty"` // inject fault into FaultCall/FaultNth
26	FaultCall int  `json:"fault_call,omitempty"`
27	FaultNth  int  `json:"fault_nth,omitempty"`
28
29	// These options allow for a more fine-tuned control over the generated C code.
30	EnableTun     bool `json:"tun,omitempty"`
31	UseTmpDir     bool `json:"tmpdir,omitempty"`
32	EnableCgroups bool `json:"cgroups,omitempty"`
33	EnableNetdev  bool `json:"netdev,omitempty"`
34	ResetNet      bool `json:"resetnet,omitempty"`
35	HandleSegv    bool `json:"segv,omitempty"`
36
37	// Generate code for use with repro package to prints log messages,
38	// which allows to detect hangs.
39	Repro bool `json:"repro,omitempty"`
40	Trace bool `json:"trace,omitempty"`
41}
42
43// Check checks if the opts combination is valid or not.
44// For example, Collide without Threaded is not valid.
45// Invalid combinations must not be passed to Write.
46func (opts Options) Check(OS string) error {
47	switch opts.Sandbox {
48	case "", sandboxNone, sandboxNamespace, sandboxSetuid:
49	default:
50		return fmt.Errorf("unknown sandbox %v", opts.Sandbox)
51	}
52	if !opts.Threaded && opts.Collide {
53		// Collide requires threaded.
54		return errors.New("Collide without Threaded")
55	}
56	if !opts.Repeat {
57		if opts.Procs > 1 {
58			// This does not affect generated code.
59			return errors.New("Procs>1 without Repeat")
60		}
61		if opts.ResetNet {
62			return errors.New("ResetNet without Repeat")
63		}
64		if opts.RepeatTimes > 1 {
65			return errors.New("RepeatTimes without Repeat")
66		}
67	}
68	if opts.Sandbox == "" {
69		if opts.EnableTun {
70			return errors.New("EnableTun without sandbox")
71		}
72		if opts.EnableCgroups {
73			return errors.New("EnableCgroups without sandbox")
74		}
75		if opts.EnableNetdev {
76			return errors.New("EnableNetdev without sandbox")
77		}
78	}
79	if opts.Sandbox == sandboxNamespace && !opts.UseTmpDir {
80		// This is borken and never worked.
81		// This tries to create syz-tmp dir in cwd,
82		// which will fail if procs>1 and on second run of the program.
83		return errors.New("Sandbox=namespace without UseTmpDir")
84	}
85	if opts.EnableCgroups && !opts.UseTmpDir {
86		return errors.New("EnableCgroups without UseTmpDir")
87	}
88	if opts.ResetNet && (opts.Sandbox == "" || opts.Sandbox == sandboxSetuid) {
89		return errors.New("ResetNet without sandbox")
90	}
91	return opts.checkLinuxOnly(OS)
92}
93
94func (opts Options) checkLinuxOnly(OS string) error {
95	if OS == linux {
96		return nil
97	}
98	if opts.EnableTun {
99		return fmt.Errorf("EnableTun is not supported on %v", OS)
100	}
101	if opts.EnableCgroups {
102		return fmt.Errorf("EnableCgroups is not supported on %v", OS)
103	}
104	if opts.EnableNetdev {
105		return fmt.Errorf("EnableNetdev is not supported on %v", OS)
106	}
107	if opts.ResetNet {
108		return fmt.Errorf("ResetNet is not supported on %v", OS)
109	}
110	if opts.Sandbox == sandboxNamespace || opts.Sandbox == sandboxSetuid {
111		return fmt.Errorf("Sandbox=%v is not supported on %v", opts.Sandbox, OS)
112	}
113	if opts.Fault {
114		return fmt.Errorf("Fault is not supported on %v", OS)
115	}
116	return nil
117}
118
119func DefaultOpts(cfg *mgrconfig.Config) Options {
120	opts := Options{
121		Threaded:      true,
122		Collide:       true,
123		Repeat:        true,
124		Procs:         cfg.Procs,
125		Sandbox:       cfg.Sandbox,
126		EnableTun:     true,
127		EnableCgroups: true,
128		EnableNetdev:  true,
129		ResetNet:      true,
130		UseTmpDir:     true,
131		HandleSegv:    true,
132		Repro:         true,
133	}
134	if cfg.TargetOS != linux {
135		opts.EnableTun = false
136		opts.EnableCgroups = false
137		opts.EnableNetdev = false
138		opts.ResetNet = false
139	}
140	if cfg.Sandbox == "" || cfg.Sandbox == "setuid" {
141		opts.ResetNet = false
142	}
143	if err := opts.Check(cfg.TargetOS); err != nil {
144		panic(fmt.Sprintf("DefaultOpts created bad opts: %v", err))
145	}
146	return opts
147}
148
149func (opts Options) Serialize() []byte {
150	data, err := json.Marshal(opts)
151	if err != nil {
152		panic(err)
153	}
154	return data
155}
156
157func DeserializeOptions(data []byte) (Options, error) {
158	var opts Options
159	if err := json.Unmarshal(data, &opts); err == nil {
160		return opts, nil
161	}
162	// Support for legacy formats.
163	data = bytes.Replace(data, []byte("Sandbox: "), []byte("Sandbox:empty "), -1)
164	waitRepeat, debug := false, false
165	n, err := fmt.Sscanf(string(data),
166		"{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s"+
167			" Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+
168			" HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}",
169		&opts.Threaded, &opts.Collide, &opts.Repeat, &opts.Procs, &opts.Sandbox,
170		&opts.Fault, &opts.FaultCall, &opts.FaultNth, &opts.EnableTun, &opts.UseTmpDir,
171		&opts.HandleSegv, &waitRepeat, &debug, &opts.Repro)
172	if err == nil {
173		if want := 14; n != want {
174			return opts, fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want)
175		}
176		if opts.Sandbox == "empty" {
177			opts.Sandbox = ""
178		}
179		return opts, nil
180	}
181	n, err = fmt.Sscanf(string(data),
182		"{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s"+
183			" Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+
184			" EnableCgroups:%t HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}",
185		&opts.Threaded, &opts.Collide, &opts.Repeat, &opts.Procs, &opts.Sandbox,
186		&opts.Fault, &opts.FaultCall, &opts.FaultNth, &opts.EnableTun, &opts.UseTmpDir,
187		&opts.EnableCgroups, &opts.HandleSegv, &waitRepeat, &debug, &opts.Repro)
188	if err == nil {
189		if want := 15; n != want {
190			return opts, fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want)
191		}
192		if opts.Sandbox == "empty" {
193			opts.Sandbox = ""
194		}
195		return opts, nil
196	}
197	return opts, err
198}
199