diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2026-01-27 12:31:49 +0000 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2026-02-17 14:55:28 +0000 |
| commit | 72e0f1b67bdd3f89cf51e89a3c17dd4a7cb575f1 (patch) | |
| tree | c7e29b0a56ad5f81ed82f2bf47531c0d0b52812d /executor | |
| parent | 21b4b9b6789f7b255eb115d3757e82652bb33eaa (diff) | |
all: add a DumpMemory feature
On Linux, verify that makedumpfile and the second kernel are present,
then set up a kernel to be used on panic.
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/executor.cc | 1 | ||||
| -rw-r--r-- | executor/executor_common.h | 29 | ||||
| -rw-r--r-- | executor/executor_linux.h | 45 | ||||
| -rw-r--r-- | executor/test.h | 46 |
4 files changed, 121 insertions, 0 deletions
diff --git a/executor/executor.cc b/executor/executor.cc index d6bba7aa7..3ba21e846 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -298,6 +298,7 @@ static bool in_execute_one = false; #define SYZ_EXECUTOR 1 #include "common.h" +#include "executor_common.h" const size_t kMaxInput = 4 << 20; // keep in sync with prog.ExecBufferSize const size_t kMaxCommands = 1000; // prog package knows about this constant (prog.execMaxCommands) diff --git a/executor/executor_common.h b/executor/executor_common.h new file mode 100644 index 000000000..90f731dbf --- /dev/null +++ b/executor/executor_common.h @@ -0,0 +1,29 @@ +// Copyright 2025 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +#ifndef EXECUTOR_COMMON_H +#define EXECUTOR_COMMON_H + +#include <stdio.h> +#include <string.h> + +static void get_last_opt(const char* cmdline, const char* key, char* out, size_t out_len) +{ + char key_eq[128]; + snprintf(key_eq, sizeof(key_eq), "%s=", key); + const char* val = NULL; + for (const char* p = cmdline; (p = strstr(p, key_eq)); p += strlen(key_eq)) { + if (p == cmdline || p[-1] == ' ' || p[-1] == '\t' || p[-1] == '\n') + val = p + strlen(key_eq); + } + + if (val) { + size_t len = strcspn(val, " \t\n"); + if (len >= out_len) + len = out_len - 1; + memcpy(out, val, len); + out[len] = 0; + } +} + +#endif // EXECUTOR_COMMON_H diff --git a/executor/executor_linux.h b/executor/executor_linux.h index bbc5dfff0..9a5d22423 100644 --- a/executor/executor_linux.h +++ b/executor/executor_linux.h @@ -366,6 +366,50 @@ static const char* setup_kcov_reset_ioctl() return error; } +static const char* setup_kdump() +{ + if (access("/boot/bzImageKexec", F_OK) != 0) + return "/boot/bzImageKexec is missing"; + if (access("/usr/sbin/makedumpfile", F_OK) != 0) + return "/usr/sbin/makedumpfile is missing"; + char cmdline[4096]; + int fd = open("/proc/cmdline", O_RDONLY); + if (fd < 0) + return "failed to open /proc/cmdline"; + ssize_t n = read(fd, cmdline, sizeof(cmdline) - 1); + close(fd); + if (n <= 0) + return "failed to read /proc/cmdline"; + cmdline[n] = 0; + if (strstr(cmdline, "crashkernel=") == NULL) + return "crashkernel= is not present in /proc/cmdline"; + + // Current default values + char root[128] = "/dev/sda1"; + char console[128] = "ttyS0"; + get_last_opt(cmdline, "root", root, sizeof(root)); + get_last_opt(cmdline, "console", console, sizeof(console)); + + char cmd[1024]; + snprintf(cmd, sizeof(cmd), + "kexec -p /boot/bzImageKexec --append=\"earlyprintk=serial net.ifnames=0 ima_policy=tcb no_hash_pointers root=%s console=%s vsyscall=native watchdog_thresh=55 irqpoll nr_cpus=1 reset_devices\"", + root, console); + + if (system(cmd) != 0) + return "kexec failed"; + int s_fd = open("/sys/kernel/kexec_crash_loaded", O_RDONLY); + if (s_fd >= 0) { + char loaded_status[1]; + ssize_t sn = read(s_fd, loaded_status, sizeof(loaded_status)); + close(s_fd); + if (sn != 1) + return "failed to read /sys/kernel/kexec_crash_loaded"; + if (loaded_status[0] != '1') + return "/sys/kernel/kexec_crash_loaded is not 1"; + } + return NULL; +} + #define SYZ_HAVE_FEATURES 1 static feature_t features[] = { {rpc::Feature::DelayKcovMmap, setup_delay_kcov}, @@ -379,4 +423,5 @@ static feature_t features[] = { {rpc::Feature::Swap, setup_swap}, {rpc::Feature::NicVF, setup_nicvf}, {rpc::Feature::DevlinkPCI, setup_devlink_pci}, + {rpc::Feature::MemoryDump, setup_kdump}, }; diff --git a/executor/test.h b/executor/test.h index 8997ef52f..eb97aa5b9 100644 --- a/executor/test.h +++ b/executor/test.h @@ -10,6 +10,8 @@ #include <sys/stat.h> #include <unistd.h> +#include "executor_common.h" + static int test_copyin() { static uint16 buf[3]; @@ -365,6 +367,49 @@ static int test_glob() return 0; } +static int test_get_last_opt() +{ + struct { + const char* cmdline; + const char* key; + const char* want; + } tests[] = { + {"key=val", "key", "val"}, + {"key=val ", "key", "val"}, + {" key=val", "key", "val"}, + {" key=val ", "key", "val"}, + {"key=val1 key=val2", "key", "val2"}, + {"key=val1 key=val2 ", "key", "val2"}, + {"key=val1 key=val2 key=val3", "key", "val3"}, + {"other=val key=val", "key", "val"}, + {"key=val other=val", "key", "val"}, + {"foo=bar", "key", ""}, + {"nokey=val", "key", ""}, + {"key", "key", ""}, + {"full match", "full", ""}, // "full" != "full=" + }; + + char buf[128]; + for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + memset(buf, 0, sizeof(buf)); + get_last_opt(tests[i].cmdline, tests[i].key, buf, sizeof(buf)); + if (strcmp(buf, tests[i].want) != 0) { + printf("test_get_last_opt failed: cmdline='%s' key='%s' want='%s' got='%s'\n", + tests[i].cmdline, tests[i].key, tests[i].want, buf); + return 1; + } + } + + char small_buf[4]; + get_last_opt("key=value", "key", small_buf, sizeof(small_buf)); + if (strcmp(small_buf, "val") != 0) { + printf("test_get_last_opt truncation failed: want='val' got='%s'\n", small_buf); + return 1; + } + + return 0; +} + static struct { const char* name; int (*f)(); @@ -380,6 +425,7 @@ static struct { #endif {"test_cover_filter", test_cover_filter}, {"test_glob", test_glob}, + {"test_get_last_opt", test_get_last_opt}, }; static int run_tests(const char* test) |
