aboutsummaryrefslogtreecommitdiffstats
path: root/sys/linux/init.go
blob: 1427cebb10e768608f228386db4c12222e0f8bee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package linux

import (
	"github.com/google/syzkaller/prog"
	"github.com/google/syzkaller/sys/targets"
)

func InitTarget(target *prog.Target) {
	arch := &arch{
		unix:                        targets.MakeUnixNeutralizer(target),
		clockGettimeSyscall:         target.SyscallMap["clock_gettime"],
		MREMAP_MAYMOVE:              target.GetConst("MREMAP_MAYMOVE"),
		MREMAP_FIXED:                target.GetConst("MREMAP_FIXED"),
		SYSLOG_ACTION_CONSOLE_OFF:   target.GetConst("SYSLOG_ACTION_CONSOLE_OFF"),
		SYSLOG_ACTION_CONSOLE_ON:    target.GetConst("SYSLOG_ACTION_CONSOLE_ON"),
		SYSLOG_ACTION_CONSOLE_LEVEL: target.GetConst("SYSLOG_ACTION_CONSOLE_LEVEL"),
		SYSLOG_ACTION_CLEAR:         target.GetConst("SYSLOG_ACTION_CLEAR"),
		SYSLOG_ACTION_SIZE_UNREAD:   target.GetConst("SYSLOG_ACTION_SIZE_UNREAD"),
		FIFREEZE:                    target.GetConst("FIFREEZE"),
		FITHAW:                      target.GetConst("FITHAW"),
		SNAPSHOT_FREEZE:             target.GetConst("SNAPSHOT_FREEZE"),
		SNAPSHOT_POWER_OFF:          target.GetConst("SNAPSHOT_POWER_OFF"),
		FAN_OPEN_PERM:               target.GetConst("FAN_OPEN_PERM"),
		FAN_ACCESS_PERM:             target.GetConst("FAN_ACCESS_PERM"),
		FAN_OPEN_EXEC_PERM:          target.GetConst("FAN_OPEN_EXEC_PERM"),
		PTRACE_TRACEME:              target.GetConst("PTRACE_TRACEME"),
		CLOCK_REALTIME:              target.GetConst("CLOCK_REALTIME"),
		AF_NFC:                      target.GetConst("AF_NFC"),
		AF_LLC:                      target.GetConst("AF_LLC"),
		AF_BLUETOOTH:                target.GetConst("AF_BLUETOOTH"),
		AF_X25:                      target.GetConst("AF_X25"),
		AF_AX25:                     target.GetConst("AF_AX25"),
		AF_NETROM:                   target.GetConst("AF_NETROM"),
		AF_ROSE:                     target.GetConst("AF_ROSE"),
		AF_IEEE802154:               target.GetConst("AF_IEEE802154"),
		AF_NETLINK:                  target.GetConst("AF_NETLINK"),
		SOCK_RAW:                    target.GetConst("SOCK_RAW"),
		NETLINK_GENERIC:             target.GetConst("NETLINK_GENERIC"),
		TIOCSSERIAL:                 target.GetConst("TIOCSSERIAL"),
		TIOCGSERIAL:                 target.GetConst("TIOCGSERIAL"),
		// These are not present on all arches.
		ARCH_SET_FS: target.ConstMap["ARCH_SET_FS"],
		ARCH_SET_GS: target.ConstMap["ARCH_SET_GS"],
	}

	target.MakeDataMmap = targets.MakePosixMmap(target, true, true)
	target.Neutralize = arch.neutralize
	target.SpecialTypes = map[string]func(g *prog.Gen, typ prog.Type, dir prog.Dir, old prog.Arg) (
		prog.Arg, []*prog.Call){
		"timespec":                      arch.generateTimespec,
		"timeval":                       arch.generateTimespec,
		"sockaddr_alg":                  arch.generateSockaddrAlg,
		"alg_name":                      arch.generateAlgName,
		"alg_aead_name":                 arch.generateAlgAeadName,
		"alg_hash_name":                 arch.generateAlgHashName,
		"alg_skcipher_name":             arch.generateAlgSkcipherhName,
		"ipt_replace":                   arch.generateIptables,
		"ip6t_replace":                  arch.generateIptables,
		"arpt_replace":                  arch.generateArptables,
		"ebt_replace":                   arch.generateEbtables,
		"usb_device_descriptor":         arch.generateUsbDeviceDescriptor,
		"usb_device_descriptor_printer": arch.generateUsbPrinterDeviceDescriptor,
		"usb_device_descriptor_hid":     arch.generateUsbHidDeviceDescriptor,
	}

	target.AuxResources = map[string]bool{
		"uid":       true,
		"pid":       true,
		"gid":       true,
		"timespec":  true,
		"timeval":   true,
		"time_sec":  true,
		"time_usec": true,
		"time_nsec": true,
	}

	switch target.Arch {
	case targets.AMD64:
		target.SpecialPointers = []uint64{
			0xffffffff81000000, // kernel text
			0xffffffffff600000, // VSYSCALL_ADDR
		}
	case targets.RiscV64:
		target.SpecialPointers = []uint64{
			0xffffffe000000000, // PAGE_OFFSET
			0xffffff0000000000, // somewhere in VMEMMAP range
		}
	case targets.I386, targets.ARM64, targets.ARM, targets.PPC64LE, targets.MIPS64LE, targets.S390x:
	default:
		panic("unknown arch")
	}

	target.SpecialFileLenghts = []int{
		int(target.GetConst("PATH_MAX")),
		int(target.GetConst("UNIX_PATH_MAX")),
		int(target.GetConst("NAME_MAX")),
		int(target.GetConst("BTRFS_INO_LOOKUP_PATH_MAX")),
		int(target.GetConst("BTRFS_INO_LOOKUP_USER_PATH_MAX")),
		int(target.GetConst("SMB_PATH_MAX")),
		int(target.GetConst("XT_CGROUP_PATH_MAX")),
		int(target.GetConst("XENSTORE_REL_PATH_MAX")),
		1 << 16, // gVisor's MaxFilenameLen
	}
}

type arch struct {
	unix *targets.UnixNeutralizer

	clockGettimeSyscall *prog.Syscall

	MREMAP_MAYMOVE              uint64
	MREMAP_FIXED                uint64
	SYSLOG_ACTION_CONSOLE_OFF   uint64
	SYSLOG_ACTION_CONSOLE_ON    uint64
	SYSLOG_ACTION_CONSOLE_LEVEL uint64
	SYSLOG_ACTION_CLEAR         uint64
	SYSLOG_ACTION_SIZE_UNREAD   uint64
	FIFREEZE                    uint64
	FITHAW                      uint64
	SNAPSHOT_FREEZE             uint64
	SNAPSHOT_POWER_OFF          uint64
	FAN_OPEN_PERM               uint64
	FAN_ACCESS_PERM             uint64
	FAN_OPEN_EXEC_PERM          uint64
	PTRACE_TRACEME              uint64
	CLOCK_REALTIME              uint64
	ARCH_SET_FS                 uint64
	ARCH_SET_GS                 uint64
	AF_NFC                      uint64
	AF_LLC                      uint64
	AF_BLUETOOTH                uint64
	AF_X25                      uint64
	AF_AX25                     uint64
	AF_NETROM                   uint64
	AF_ROSE                     uint64
	AF_IEEE802154               uint64
	AF_NETLINK                  uint64
	SOCK_RAW                    uint64
	NETLINK_GENERIC             uint64
	TIOCSSERIAL                 uint64
	TIOCGSERIAL                 uint64
}

func (arch *arch) neutralize(c *prog.Call, fixStructure bool) error {
	err := arch.unix.Neutralize(c, fixStructure)
	if err != nil {
		return err
	}
	switch c.Meta.CallName {
	case "mremap":
		// Add MREMAP_FIXED flag, otherwise it produces non-deterministic results.
		flags := c.Args[3].(*prog.ConstArg)
		if flags.Val&arch.MREMAP_MAYMOVE != 0 {
			flags.Val |= arch.MREMAP_FIXED
		}
	case "syslog":
		cmd := c.Args[0].(*prog.ConstArg)
		cmd.Val = uint64(uint32(cmd.Val))
		// These disable console output, but we need it.
		if cmd.Val == arch.SYSLOG_ACTION_CONSOLE_OFF ||
			cmd.Val == arch.SYSLOG_ACTION_CONSOLE_ON ||
			cmd.Val == arch.SYSLOG_ACTION_CONSOLE_LEVEL ||
			cmd.Val == arch.SYSLOG_ACTION_CLEAR {
			cmd.Val = arch.SYSLOG_ACTION_SIZE_UNREAD
		}
	case "ioctl":
		arch.neutralizeIoctl(c)
	case "fanotify_mark":
		// FAN_*_PERM require the program to reply to open requests.
		// If that does not happen, the program will hang in an unkillable state forever.
		// See the following bug for details:
		// https://groups.google.com/d/msg/syzkaller-bugs/pD-vbqJu6U0/kGH30p3lBgAJ
		mask := c.Args[2].(*prog.ConstArg)
		mask.Val &^= arch.FAN_OPEN_PERM | arch.FAN_ACCESS_PERM | arch.FAN_OPEN_EXEC_PERM
	case "ptrace":
		req := c.Args[0].(*prog.ConstArg)
		// PTRACE_TRACEME leads to unkillable processes, see:
		// https://groups.google.com/forum/#!topic/syzkaller/uGzwvhlCXAw
		if uint64(uint32(req.Val)) == arch.PTRACE_TRACEME {
			req.Val = ^uint64(0)
		}
	case "arch_prctl":
		// fs holds address of tls, if a program messes it at least signal
		// handling will break. This also allows a program to do writes
		// at arbitrary addresses, which usually leads to machine outbreak.
		cmd := c.Args[0].(*prog.ConstArg)
		if uint64(uint32(cmd.Val)) == arch.ARCH_SET_FS {
			cmd.Val = arch.ARCH_SET_GS
		}
	case "init_module":
		// Kernel tries to vmalloc whatever we pass as size and it's not accounted against memcg.
		// As the result it can lead to massive OOM kills of everything running on the machine.
		// Strictly saying, the same applies to finit_module with a sparse file too,
		// but there is no simple way to handle that.
		sz := c.Args[1].(*prog.ConstArg)
		sz.Val %= 1 << 20
	case "syz_init_net_socket":
		// Don't let it mess with arbitrary sockets in init namespace.
		family := c.Args[0].(*prog.ConstArg)
		switch uint64(uint32(family.Val)) {
		case arch.AF_NFC, arch.AF_LLC, arch.AF_BLUETOOTH, arch.AF_IEEE802154,
			arch.AF_X25, arch.AF_AX25, arch.AF_NETROM, arch.AF_ROSE:
		case arch.AF_NETLINK:
			c.Args[1].(*prog.ConstArg).Val = arch.SOCK_RAW
			c.Args[2].(*prog.ConstArg).Val = arch.NETLINK_GENERIC
		default:
			family.Val = ^uint64(0)
		}
	case "syz_open_dev":
		enforceIntArg(c.Args[0])
		enforceIntArg(c.Args[1])
		enforceIntArg(c.Args[2])
	case "sched_setattr":
		// Enabling a SCHED_FIFO or a SCHED_RR policy may lead to false positive stall-related crashes.
		neutralizeSchedAttr(c.Args[1])
	}

	switch c.Meta.Name {
	case "setsockopt$EBT_SO_SET_ENTRIES":
		arch.neutralizeEbtables(c)
	}
	return nil
}

func neutralizeSchedAttr(a prog.Arg) {
	switch attr := a.(type) {
	case *prog.PointerArg:
		if attr.Res == nil {
			// If it's just a pointer to somewhere, still set it to NULL as there's a risk that
			// it points to the valid memory and it can be interpreted as a sched_attr struct.
			attr.Address = 0
			return
		}
		groupArg, ok := attr.Res.(*prog.GroupArg)
		if !ok || len(groupArg.Inner) == 0 {
			return
		}
		if unionArg, ok := groupArg.Inner[0].(*prog.UnionArg); ok {
			dataArg, ok := unionArg.Option.(*prog.DataArg)
			if !ok {
				return
			}
			if dataArg.Dir() == prog.DirOut {
				return
			}
			// Clear the first 16 bytes to prevent overcoming the limitation by squashing the struct.
			data := append([]byte{}, dataArg.Data()...)
			for i := 0; i < 16 && i < len(data); i++ {
				data[i] = 0
			}
			dataArg.SetData(data)
		}

		// Most likely it's the intended sched_attr structure.
		if len(groupArg.Inner) > 1 {
			policyField, ok := groupArg.Inner[1].(*prog.ConstArg)
			if !ok {
				return
			}
			const SCHED_FIFO = 0x1
			const SCHED_RR = 0x2
			if policyField.Val == SCHED_FIFO || policyField.Val == SCHED_RR {
				policyField.Val = 0
			}
		}
	case *prog.ConstArg:
		attr.Val = 0
	}
}

func enforceIntArg(a prog.Arg) {
	arg, ok := a.(*prog.ConstArg)
	if !ok {
		return
	}
	switch typ := arg.Type().(type) {
	case *prog.ConstType:
		arg.Val = typ.Val
	case *prog.IntType:
		if typ.Kind == prog.IntRange && (arg.Val < typ.RangeBegin || arg.Val > typ.RangeEnd) {
			arg.Val = typ.RangeBegin
		}
	}
}

func (arch *arch) neutralizeIoctl(c *prog.Call) {
	cmd := c.Args[1].(*prog.ConstArg)
	switch uint64(uint32(cmd.Val)) {
	case arch.FIFREEZE:
		// Freeze kills machine. Though, it is an interesting functions,
		// so we need to test it somehow.
		// TODO: not required if executor drops privileges.
		// Fortunately, the value does not conflict with any other ioctl commands for now.
		cmd.Val = arch.FITHAW
	case arch.SNAPSHOT_FREEZE:
		// SNAPSHOT_FREEZE freezes all processes and leaves the machine dead.
		cmd.Val = arch.FITHAW
	case arch.SNAPSHOT_POWER_OFF:
		// SNAPSHOT_POWER_OFF shuts down the machine.
		cmd.Val = arch.FITHAW
	case arch.TIOCSSERIAL:
		// TIOCSSERIAL can do nasty things under root, like causing writes to random memory
		// pretty much like /dev/mem, but this is also working as intended.
		// For details see:
		// https://groups.google.com/g/syzkaller-bugs/c/1rVENJf9P4U/m/QtGpapRxAgAJ
		// https://syzkaller.appspot.com/bug?extid=f4f1e871965064ae689e
		// TODO: TIOCSSERIAL does some other things that are not dangerous
		// and would be nice to test, if/when we can neutralize based on sandbox value
		// we could prohibit it only under sandbox=none.
		cmd.Val = arch.TIOCGSERIAL
	}
}

func (arch *arch) generateTimespec(g *prog.Gen, typ0 prog.Type, dir prog.Dir, old prog.Arg) (
	arg prog.Arg, calls []*prog.Call) {
	typ := typ0.(*prog.StructType)
	// We need to generate timespec/timeval that are either
	// (1) definitely in the past, or
	// (2) definitely in unreachable fututre, or
	// (3) few ms ahead of now.
	// Note: timespec/timeval can be absolute or relative to now.
	// Note: executor has blocking syscall timeout of 45 ms,
	// so we generate both 10ms and 60ms.
	// TODO(dvyukov): this is now all outdated with tunable timeouts.
	const (
		timeout1 = uint64(10)
		timeout2 = uint64(60)
	)
	usec := typ.Name() == "timeval"
	switch {
	case g.NOutOf(1, 4):
		// Now for relative, past for absolute.
		arg = prog.MakeGroupArg(typ, dir, []prog.Arg{
			prog.MakeResultArg(typ.Fields[0].Type, dir, nil, 0),
			prog.MakeResultArg(typ.Fields[1].Type, dir, nil, 0),
		})
	case g.NOutOf(1, 3):
		// Few ms ahead for relative, past for absolute.
		nsec := timeout1 * 1e6
		if g.NOutOf(1, 2) {
			nsec = timeout2 * 1e6
		}
		if usec {
			nsec /= 1e3
		}
		arg = prog.MakeGroupArg(typ, dir, []prog.Arg{
			prog.MakeResultArg(typ.Fields[0].Type, dir, nil, 0),
			prog.MakeResultArg(typ.Fields[1].Type, dir, nil, nsec),
		})
	case g.NOutOf(1, 2):
		// Unreachable fututre for both relative and absolute.
		arg = prog.MakeGroupArg(typ, dir, []prog.Arg{
			prog.MakeResultArg(typ.Fields[0].Type, dir, nil, 2e9),
			prog.MakeResultArg(typ.Fields[1].Type, dir, nil, 0),
		})
	default:
		// Few ms ahead for absolute.
		meta := arch.clockGettimeSyscall
		ptrArgType := meta.Args[1].Type.(*prog.PtrType)
		argType := ptrArgType.Elem.(*prog.StructType)
		tp := prog.MakeGroupArg(argType, prog.DirOut, []prog.Arg{
			prog.MakeResultArg(argType.Fields[0].Type, prog.DirOut, nil, 0),
			prog.MakeResultArg(argType.Fields[1].Type, prog.DirOut, nil, 0),
		})
		var tpaddr prog.Arg
		tpaddr, calls = g.Alloc(ptrArgType, prog.DirIn, tp)
		gettime := prog.MakeCall(meta, []prog.Arg{
			prog.MakeConstArg(meta.Args[0].Type, prog.DirIn, arch.CLOCK_REALTIME),
			tpaddr,
		})
		calls = append(calls, gettime)
		sec := prog.MakeResultArg(typ.Fields[0].Type, dir, tp.Inner[0].(*prog.ResultArg), 0)
		nsec := prog.MakeResultArg(typ.Fields[1].Type, dir, tp.Inner[1].(*prog.ResultArg), 0)
		msec := timeout1
		if g.NOutOf(1, 2) {
			msec = timeout2
		}
		if usec {
			nsec.OpDiv = 1e3
			nsec.OpAdd = msec * 1e3
		} else {
			nsec.OpAdd = msec * 1e6
		}
		arg = prog.MakeGroupArg(typ, dir, []prog.Arg{sec, nsec})
	}
	return
}