* Copyright (C) 2025 SUSE LLC <andrea.cervesato@suse.com>
*/
* Test the robustness of the system generating random syscalls execution
* with random data and expecting that the current system is not crashing.
*/
#include <stdlib.h>
#include <limits.h>
#include "tst_test.h"
#include "lapi/syscalls.h"
#define MAX_SYSCALLS 465
static int *num_errors;
static char *str_num_tries;
static char *str_seed;
static int num_tries = 1000;
static int seed;
static int blacklist[] = {
__NR_vfork,
__NR_fork,
__NR_clone,
__NR_clone2,
__NR_clone3,
__NR_vhangup,
__NR_pause,
__NR_read,
__NR_kill,
__NR_restart_syscall,
__LTP__NR_INVALID_SYSCALL,
};
static long rand_number(void)
{
long num = 0;
for (size_t i = 0; i < sizeof(long); i++)
num |= ((rand() & 0xFFUL) << (i * 8));
return num;
}
static int in_blacklist(const int sysno)
{
for (size_t i = 0; i < ARRAY_SIZE(blacklist); i++)
if (sysno == blacklist[i])
return 1;
return 0;
}
static void try_crash(const int num)
{
long sysno, arg0, arg1, arg2, arg3, arg4, arg5, arg6;
int ret;
do {
sysno = rand() % MAX_SYSCALLS;
} while (in_blacklist(sysno));
arg0 = rand_number();
arg1 = rand_number();
arg2 = rand_number();
arg3 = rand_number();
arg4 = rand_number();
arg5 = rand_number();
arg6 = rand_number();
tst_res(TDEBUG,
"%d: syscall(%ld, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx)",
num, sysno, arg0, arg1, arg2, arg3, arg4, arg5, arg6);
ret = syscall(sysno, arg0, arg1, arg2, arg3, arg4, arg5, arg6);
if (ret == -1) {
(*num_errors)++;
tst_res(TDEBUG, "syscall error: %s", strerror(errno));
}
}
static void run(void)
{
pid_t pid;
int status;
int num_signals = 0;
*num_errors = 0;
pid = SAFE_FORK();
if (!pid) {
for (int i = 0; i < num_tries; i++)
try_crash(i);
exit(0);
}
SAFE_WAITPID(pid, &status, 0);
if (WIFSIGNALED(status)) {
num_signals++;
tst_res(TDEBUG, "syscall signaled: %s",
strsignal(WTERMSIG(status)));
}
tst_res(TINFO, "Detected errors: %d", *num_errors);
tst_res(TINFO, "Detected signals: %d", num_signals);
tst_res(TPASS, "System is still up and running");
}
static void setup(void)
{
if (tst_parse_int(str_num_tries, &num_tries, 1, INT_MAX))
tst_brk(TBROK, "Invalid number of entries '%s'", str_num_tries);
if (tst_parse_int(str_seed, &seed, 0, INT_MAX))
tst_brk(TBROK, "Invalid seed number '%s'", str_num_tries);
num_errors = SAFE_MMAP(
NULL, sizeof(int),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
seed = str_seed ? seed : time(NULL);
srand(seed);
tst_res(TINFO, "Random seed: %d", seed);
tst_res(TINFO, "Number of tries: %d", num_tries);
tst_set_runtime((num_tries / 1000) + 1);
}
static void cleanup(void)
{
if (num_errors)
SAFE_MUNMAP(num_errors, sizeof(int));
}
static struct tst_test test = {
.test_all = run,
.setup = setup,
.cleanup = cleanup,
.forks_child = 1,
.options = (struct tst_option []) {
{"n:", &str_num_tries, "Number of retries (default: 1000)"},
{"s:", &str_seed, "Initial seed for random generator"},
{}
},
};