From fa6c7b708014d8f73262837982e368f8d1f617b5 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 8 Feb 2019 16:15:09 +0100 Subject: sys/linux: prohibit opening /proc/self/exe Fuzzer manages to open it and do bad things with it. Prevent it from doing so. --- sys/linux/init.go | 75 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 24 deletions(-) (limited to 'sys/linux/init.go') 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 -- cgit mrf-deployment