aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2023-04-03 07:45:09 +0200
committerDmitry Vyukov <dvyukov@google.com>2023-04-03 10:29:09 +0200
commitd04ac3a54895a36998e3f1da27c2ce33f7a80c5a (patch)
treee79bc6b62402f2b9e1b4cf91bc38b2a6c404e7b1
parent7c00f48c0c766f7abd4601bd9848527dd1e4be77 (diff)
sys/linux: add syz_pkey_set syscalls
The syscall sets PKRU register which is part of protection keys (pkey).
-rw-r--r--executor/common_linux.h24
-rw-r--r--pkg/csource/generated.go19
-rw-r--r--pkg/host/syscalls_linux.go20
-rw-r--r--sys/linux/sys.txt2
-rw-r--r--sys/linux/test/pkey10
5 files changed, 75 insertions, 0 deletions
diff --git a/executor/common_linux.h b/executor/common_linux.h
index e53a908ab..a2736624a 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -5547,3 +5547,27 @@ static long syz_clone3(volatile long a0, volatile long a1)
}
#endif
+
+#if SYZ_EXECUTOR || __NR_syz_pkey_set
+// syz_pkey_set(key pkey, val flags[pkey_flags])
+static long syz_pkey_set(volatile long pkey, volatile long val)
+{
+#if GOARCH_amd64 || GOARCH_386
+ uint32 eax = 0;
+ uint32 ecx = 0;
+ asm volatile("rdpkru"
+ : "=a"(eax)
+ : "c"(ecx)
+ : "edx");
+ // PKRU register contains 2 bits per key.
+ // Max number of keys is 16.
+ // Clear old bits for the key:
+ eax &= ~(3 << ((pkey % 16) * 2));
+ // Set new bits for the key:
+ eax |= (val & 3) << ((pkey % 16) * 2);
+ uint32 edx = 0;
+ asm volatile("wrpkru" ::"a"(eax), "c"(ecx), "d"(edx));
+#endif
+ return 0;
+}
+#endif
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index 552cc9191..204d6d2ee 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -11378,6 +11378,25 @@ static long syz_clone3(volatile long a0, volatile long a1)
#endif
+#if SYZ_EXECUTOR || __NR_syz_pkey_set
+static long syz_pkey_set(volatile long pkey, volatile long val)
+{
+#if GOARCH_amd64 || GOARCH_386
+ uint32 eax = 0;
+ uint32 ecx = 0;
+ asm volatile("rdpkru"
+ : "=a"(eax)
+ : "c"(ecx)
+ : "edx");
+ eax &= ~(3 << ((pkey % 16) * 2));
+ eax |= (val & 3) << ((pkey % 16) * 2);
+ uint32 edx = 0;
+ asm volatile("wrpkru" ::"a"(eax), "c"(ecx), "d"(edx));
+#endif
+ return 0;
+}
+#endif
+
#elif GOOS_test
#include <stdlib.h>
diff --git a/pkg/host/syscalls_linux.go b/pkg/host/syscalls_linux.go
index 67efd9295..7037dc001 100644
--- a/pkg/host/syscalls_linux.go
+++ b/pkg/host/syscalls_linux.go
@@ -18,6 +18,7 @@ import (
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys/targets"
+ "golang.org/x/sys/unix"
)
func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
@@ -37,6 +38,9 @@ func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, st
if strings.HasPrefix(c.Name, "mount$") {
return isSupportedMount(c, sandbox)
}
+ if c.CallName == "pkey_alloc" {
+ return isSyzPkeySetSupported(c, target, sandbox)
+ }
if c.Name == "ioctl$EXT4_IOC_SHUTDOWN" && sandbox == "none" {
// Don't shutdown root filesystem.
return false, "unsafe with sandbox=none"
@@ -317,6 +321,7 @@ var syzkallSupport = map[string]func(*prog.Syscall, *prog.Target, string) (bool,
"syz_usbip_server_init": isSyzUsbIPSupported,
"syz_clone": alwaysSupported,
"syz_clone3": alwaysSupported,
+ "syz_pkey_set": isSyzPkeySetSupported,
}
func isSupportedSyzkall(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
@@ -544,3 +549,18 @@ func extractStringConst(typ prog.Type) (string, bool) {
}
return v, true
}
+
+func isSyzPkeySetSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
+ if target.Arch != targets.AMD64 && target.Arch != targets.I386 {
+ return false, "unsupported arch"
+ }
+ key, _, err := syscall.Syscall6(unix.SYS_PKEY_ALLOC, 0, 0, 0, 0, 0, 0)
+ if err != 0 {
+ return false, fmt.Sprintf("pkey_alloc failed: %v", err)
+ }
+ _, _, err = syscall.Syscall6(unix.SYS_PKEY_FREE, key, 0, 0, 0, 0, 0)
+ if err != 0 {
+ return false, fmt.Sprintf("pkey_free failed: %v", err)
+ }
+ return true, ""
+}
diff --git a/sys/linux/sys.txt b/sys/linux/sys.txt
index 80ece3188..4b403ddd9 100644
--- a/sys/linux/sys.txt
+++ b/sys/linux/sys.txt
@@ -212,6 +212,8 @@ resource pkey[int32]: -1
pkey_alloc(flags const[0], val flags[pkey_flags]) pkey
pkey_free(key pkey)
pkey_mprotect(addr vma, len len[addr], prot flags[mmap_prot], key pkey)
+syz_pkey_set(key pkey, val flags[pkey_flags])
+
pkey_flags = PKEY_DISABLE_ACCESS, PKEY_DISABLE_WRITE
restart_syscall()
diff --git a/sys/linux/test/pkey b/sys/linux/test/pkey
new file mode 100644
index 000000000..063e878cc
--- /dev/null
+++ b/sys/linux/test/pkey
@@ -0,0 +1,10 @@
+# requires: arch=amd64
+
+r0 = pkey_alloc(0x0, 0x0)
+mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)
+pkey_mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, r0)
+syz_pkey_set(r0, 0x3)
+pipe(&(0x7f0000000000)={0x0, 0x0}) # EFAULT
+syz_pkey_set(r0, 0x0)
+pipe(&(0x7f0000000000)={0x0, 0x0})
+pkey_free(r0)