aboutsummaryrefslogtreecommitdiffstats
path: root/sys/linux
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-02-08 16:15:09 +0100
committerDmitry Vyukov <dvyukov@google.com>2019-02-08 16:30:44 +0100
commitfa6c7b708014d8f73262837982e368f8d1f617b5 (patch)
treebaf22917ad354f32606ed5a62dfe30ae2697444e /sys/linux
parent381ccbf2f8752e9369efc68aacae65f769378ba3 (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.go75
-rw-r--r--sys/linux/init_test.go16
-rw-r--r--sys/linux/test/nonexec08
-rw-r--r--sys/linux/test/nonexec17
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