diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2019-02-08 16:15:09 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2019-02-08 16:30:44 +0100 |
| commit | fa6c7b708014d8f73262837982e368f8d1f617b5 (patch) | |
| tree | baf22917ad354f32606ed5a62dfe30ae2697444e /sys/linux | |
| parent | 381ccbf2f8752e9369efc68aacae65f769378ba3 (diff) | |
sys/linux: prohibit opening /proc/self/exe
Fuzzer manages to open it and do bad things with it.
Prevent it from doing so.
Diffstat (limited to 'sys/linux')
| -rw-r--r-- | sys/linux/init.go | 75 | ||||
| -rw-r--r-- | sys/linux/init_test.go | 16 | ||||
| -rw-r--r-- | sys/linux/test/nonexec0 | 8 | ||||
| -rw-r--r-- | sys/linux/test/nonexec1 | 7 |
4 files changed, 82 insertions, 24 deletions
diff --git a/sys/linux/init.go b/sys/linux/init.go index e7fe9d78a..83a79a70e 100644 --- a/sys/linux/init.go +++ b/sys/linux/init.go @@ -4,6 +4,7 @@ package linux import ( + "bytes" "runtime" "github.com/google/syzkaller/prog" @@ -150,30 +151,7 @@ func (arch *arch) sanitizeCall(c *prog.Call) { cmd.Val = arch.SYSLOG_ACTION_SIZE_UNREAD } case "ioctl": - cmd := c.Args[1].(*prog.ConstArg) - // 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. - if uint64(uint32(cmd.Val)) == arch.FIFREEZE { - cmd.Val = arch.FITHAW - } - // SNAPSHOT_FREEZE freezes all processes and leaves the machine dead. - if uint64(uint32(cmd.Val)) == arch.SNAPSHOT_FREEZE { - cmd.Val = arch.SNAPSHOT_UNFREEZE - } - // EXT4_IOC_SHUTDOWN on root fs effectively brings the machine down in weird ways. - // Fortunately, the value does not conflict with any other ioctl commands for now. - if uint64(uint32(cmd.Val)) == arch.EXT4_IOC_SHUTDOWN { - cmd.Val = arch.EXT4_IOC_MIGRATE - } - // EXT4_IOC_RESIZE_FS on root fs can shrink it to 0 (or whatever is the minimum size) - // and then creation of new temp dirs for tests will fail. - // TODO: not necessary for sandbox=namespace as it tests in a tmpfs - // and/or if we mount tmpfs for sandbox=none (#971). - if uint64(uint32(cmd.Val)) == arch.EXT4_IOC_RESIZE_FS { - cmd.Val = arch.EXT4_IOC_MIGRATE - } + arch.sanitizeIoctl(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. @@ -212,6 +190,8 @@ func (arch *arch) sanitizeCall(c *prog.Call) { default: family.Val = ^uint64(0) } + case "syz_open_procfs": + arch.sanitizeSyzOpenProcfs(c) } switch c.Meta.Name { @@ -220,6 +200,53 @@ func (arch *arch) sanitizeCall(c *prog.Call) { } } +func (arch *arch) sanitizeIoctl(c *prog.Call) { + cmd := c.Args[1].(*prog.ConstArg) + // 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. + if uint64(uint32(cmd.Val)) == arch.FIFREEZE { + cmd.Val = arch.FITHAW + } + // SNAPSHOT_FREEZE freezes all processes and leaves the machine dead. + if uint64(uint32(cmd.Val)) == arch.SNAPSHOT_FREEZE { + cmd.Val = arch.SNAPSHOT_UNFREEZE + } + // EXT4_IOC_SHUTDOWN on root fs effectively brings the machine down in weird ways. + // Fortunately, the value does not conflict with any other ioctl commands for now. + if uint64(uint32(cmd.Val)) == arch.EXT4_IOC_SHUTDOWN { + cmd.Val = arch.EXT4_IOC_MIGRATE + } + // EXT4_IOC_RESIZE_FS on root fs can shrink it to 0 (or whatever is the minimum size) + // and then creation of new temp dirs for tests will fail. + // TODO: not necessary for sandbox=namespace as it tests in a tmpfs + // and/or if we mount tmpfs for sandbox=none (#971). + if uint64(uint32(cmd.Val)) == arch.EXT4_IOC_RESIZE_FS { + cmd.Val = arch.EXT4_IOC_MIGRATE + } +} + +func (arch *arch) sanitizeSyzOpenProcfs(c *prog.Call) { + // If fuzzer manages to open /proc/self/exe, it does some nasty things with it: + // - mark as non-executable + // - set some extended acl's that prevent execution + // - mark as immutable, etc + // As the result we fail to start executor again and recreate the VM. + // Don't let it open /proc/self/exe. + ptr := c.Args[1].(*prog.PointerArg) + if ptr.Res != nil { + arg := ptr.Res.(*prog.DataArg) + file := arg.Data() + for len(file) != 0 && (file[0] == '/' || file[0] == '.') { + file = file[1:] + } + if bytes.HasPrefix(file, []byte("exe")) { + arg.SetData([]byte("net\x00")) + } + } +} + func (arch *arch) generateTimespec(g *prog.Gen, typ0 prog.Type, old prog.Arg) (arg prog.Arg, calls []*prog.Call) { typ := typ0.(*prog.StructType) // We need to generate timespec/timeval that are either diff --git a/sys/linux/init_test.go b/sys/linux/init_test.go index 45b6397f8..7e4753115 100644 --- a/sys/linux/init_test.go +++ b/sys/linux/init_test.go @@ -136,6 +136,22 @@ exit_group(0x1) exit_group(0x1) `, }, + { + ` +syz_open_procfs(0x0, &(0x7f0000000000)='io') +syz_open_procfs(0x0, &(0x7f0000000000)='exe') +syz_open_procfs(0x0, &(0x7f0000000000)='exe\x00') +syz_open_procfs(0x0, &(0x7f0000000000)='/exe') +syz_open_procfs(0x0, &(0x7f0000000000)='./exe\x00') +`, + ` +syz_open_procfs(0x0, &(0x7f0000000000)='io') +syz_open_procfs(0x0, &(0x7f0000000000)='net\x00') +syz_open_procfs(0x0, &(0x7f0000000000)='net\x00') +syz_open_procfs(0x0, &(0x7f0000000000)='net\x00') +syz_open_procfs(0x0, &(0x7f0000000000)='net\x00') + `, + }, } for i, test := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { diff --git a/sys/linux/test/nonexec0 b/sys/linux/test/nonexec0 new file mode 100644 index 000000000..d4ce3ba4f --- /dev/null +++ b/sys/linux/test/nonexec0 @@ -0,0 +1,8 @@ +# This makes syz-executor non-executable. +# Does not work with repeat because on the second iteration the executor is still non-executable. +# setuid does not have permissions to set extended acl. +# FS_IOC_FSSETXATTR fails with EOPNOTSUPP, but it still changes the attrs. +# requires: -repeat -sandbox=setuid + +r0 = syz_open_procfs(0x0, &AUTO='exe\x00') +fsetxattr$system_posix_acl(r0, &AUTO='system.posix_acl_access\x00', &AUTO={{AUTO}, {0x1, 0x2, AUTO}, [{AUTO, 0x0, 0x0}, {0x2, 0x4, 0x0}], {AUTO, 0x0, AUTO}, [], {0x10, 0x2, AUTO}, {AUTO, 0x0, AUTO}}, 0x34, 0x0) # EOPNOTSUPP diff --git a/sys/linux/test/nonexec1 b/sys/linux/test/nonexec1 new file mode 100644 index 000000000..ec5d6417c --- /dev/null +++ b/sys/linux/test/nonexec1 @@ -0,0 +1,7 @@ +# This makes syz-executor non-executable. +# sandbox=namespace does not have permissions for FS_IOC_FSSETXATTR. +# requires: -repeat -sandbox=namespace -sandbox=setuid + +r0 = syz_open_procfs(0x0, &AUTO='exe\x00') +fsetxattr$system_posix_acl(r0, &AUTO='system.posix_acl_access\x00', &AUTO={{AUTO}, {0x1, 0x2, AUTO}, [{AUTO, 0x0, 0x0}, {0x2, 0x4, 0x0}], {AUTO, 0x0, AUTO}, [], {0x10, 0x2, AUTO}, {AUTO, 0x0, AUTO}}, 0x34, 0x0) # EOPNOTSUPP +ioctl$FS_IOC_FSSETXATTR(r0, 0x40086602, &AUTO={0x17e, 0x0, 0x0, 0x0, 0x0, 0x0}) # ENOTTY |
