aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2022-01-12 14:40:51 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2022-01-13 17:03:14 +0100
commitd6f3385b1a3f2fba8e14d6794bece1dcdd9e479e (patch)
treec3163643d321b913450df84f41731b71d5874243 /pkg
parent6baa7baec59a790c452b0b1c97447475eb6c1afe (diff)
all: add syz_clone() and syz_clone3() pseudo calls
As was pointed out in #2921, the current approach of limiting the number of pids per process does not work on all Linux-based kernels. We could just treat fork, clone and clone3 in a special way (e.g. exit on a zero return). However, in that case we also need to sanitize the arguments for clone and clone3 - if CLONE_VM is passed and stack is 0, the forked child processes (threads) will become nearly unindentifiable and will corrupt syz-executor's memory. While we could sanitize clone's arguments, we cannot do so for clone3 - nothing can guarantee that they will not be changed concurrently. Instead of calling those syscalls directly, introduce a special pseudo syscall syz_clone3. It copies and sanitizes the arguments and then executes clone3 (or fork, if we're on an older kernel) in such a way so as to prevent fork bombs from happening. Also introduce syz_clone() to still be able to fuzz it on older systems.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/csource/csource.go1
-rw-r--r--pkg/csource/generated.go47
-rw-r--r--pkg/host/syscalls_linux.go2
3 files changed, 50 insertions, 0 deletions
diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go
index 1ba433f45..9c820a2e8 100644
--- a/pkg/csource/csource.go
+++ b/pkg/csource/csource.go
@@ -116,6 +116,7 @@ func (ctx *context) generateSource() ([]byte, error) {
timeouts := ctx.sysTarget.Timeouts(ctx.opts.Slowdown)
replacements["PROGRAM_TIMEOUT_MS"] = fmt.Sprint(int(timeouts.Program / time.Millisecond))
timeoutExpr := fmt.Sprint(int(timeouts.Syscall / time.Millisecond))
+ replacements["BASE_CALL_TIMEOUT_MS"] = timeoutExpr
for i, call := range ctx.p.Calls {
if timeout := call.Meta.Attrs.Timeout; timeout != 0 {
timeoutExpr += fmt.Sprintf(" + (call == %v ? %v : 0)", i, timeout*uint64(timeouts.Scale))
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index 7c3b35ca5..c535f04ed 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -10173,6 +10173,53 @@ static long syz_80211_join_ibss(volatile long a0, volatile long a1, volatile lon
#endif
+#if SYZ_EXECUTOR || __NR_syz_clone || __NR_syz_clone3
+#if SYZ_EXECUTOR
+#define USLEEP_FORKED_CHILD (3 * syscall_timeout_ms * 1000)
+#else
+#define USLEEP_FORKED_CHILD (3 * /*{{{BASE_CALL_TIMEOUT_MS}}}*/ *1000)
+#endif
+
+static long handle_clone_ret(long ret)
+{
+ if (ret != 0)
+ return ret;
+ usleep(USLEEP_FORKED_CHILD);
+ syscall(__NR_exit, 0);
+ while (1) {
+ }
+}
+#endif
+
+#if SYZ_EXECUTOR || __NR_syz_clone
+static long syz_clone(volatile long flags, volatile long stack, volatile long stack_len,
+ volatile long ptid, volatile long ctid, volatile long tls)
+{
+ long sp = (stack + stack_len) & ~15;
+ long ret = (long)syscall(__NR_clone, flags & ~CLONE_VM, sp, ptid, ctid, tls);
+ return handle_clone_ret(ret);
+}
+#endif
+
+#if SYZ_EXECUTOR || __NR_syz_clone3
+#include <linux/sched.h>
+#include <sched.h>
+
+#define MAX_CLONE_ARGS_BYTES 256
+static long syz_clone3(volatile long a0, volatile long a1)
+{
+ unsigned long copy_size = a1;
+ if (copy_size < sizeof(uint64) || copy_size > MAX_CLONE_ARGS_BYTES)
+ return -1;
+ char clone_args[MAX_CLONE_ARGS_BYTES];
+ memcpy(&clone_args, (void*)a0, copy_size);
+ uint64* flags = (uint64*)&clone_args;
+ *flags &= ~CLONE_VM;
+ return handle_clone_ret((long)syscall(__NR_clone3, &clone_args, copy_size));
+}
+
+#endif
+
#elif GOOS_test
#include <stdlib.h>
diff --git a/pkg/host/syscalls_linux.go b/pkg/host/syscalls_linux.go
index f43f1ada3..c628d20ab 100644
--- a/pkg/host/syscalls_linux.go
+++ b/pkg/host/syscalls_linux.go
@@ -316,6 +316,8 @@ var syzkallSupport = map[string]func(*prog.Syscall, *prog.Target, string) (bool,
"syz_80211_inject_frame": isWifiEmulationSupported,
"syz_80211_join_ibss": isWifiEmulationSupported,
"syz_usbip_server_init": isSyzUsbIPSupported,
+ "syz_clone": alwaysSupported,
+ "syz_clone3": alwaysSupported,
}
func isSupportedSyzkall(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {