-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
init.go
389 lines (372 loc) · 13.7 KB
/
init.go
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
// 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_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
}