mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-25 04:19:53 +00:00
91ea49ce25
This commit adds Odroid C2 support to syzkaller. It's now possible to specify "type": "odroid" in manager config. Documentation on how to setup fuzzing with Odroid C2 board is here: https://github.com/google/syzkaller/wiki/Setup:-Odroid-C2 Note, that after this change libusb-1.0-0-dev package should be installed to build syzkaller.
1672 lines
52 KiB
Go
1672 lines
52 KiB
Go
// AUTOGENERATED FROM executor/common.h
|
|
package csource
|
|
|
|
var commonHeader = `
|
|
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <linux/capability.h>
|
|
#include <linux/if.h>
|
|
#include <linux/if_tun.h>
|
|
#include <linux/kvm.h>
|
|
#include <linux/sched.h>
|
|
#include <net/if_arp.h>
|
|
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <grp.h>
|
|
#include <pthread.h>
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
const int kFailStatus = 67;
|
|
const int kErrorStatus = 68;
|
|
const int kRetryStatus = 69;
|
|
|
|
__attribute__((noreturn)) void doexit(int status)
|
|
{
|
|
volatile unsigned i;
|
|
syscall(__NR_exit_group, status);
|
|
for (i = 0;; i++) {
|
|
}
|
|
}
|
|
|
|
#if defined(SYZ_EXECUTOR)
|
|
#define exit use_doexit_instead
|
|
#define _exit use_doexit_instead
|
|
#endif
|
|
|
|
__attribute__((noreturn)) void fail(const char* msg, ...)
|
|
{
|
|
int e = errno;
|
|
fflush(stdout);
|
|
va_list args;
|
|
va_start(args, msg);
|
|
vfprintf(stderr, msg, args);
|
|
va_end(args);
|
|
fprintf(stderr, " (errno %d)\n", e);
|
|
doexit((e == ENOMEM || e == EAGAIN) ? kRetryStatus : kFailStatus);
|
|
}
|
|
|
|
#if defined(SYZ_EXECUTOR)
|
|
__attribute__((noreturn)) void error(const char* msg, ...)
|
|
{
|
|
fflush(stdout);
|
|
va_list args;
|
|
va_start(args, msg);
|
|
vfprintf(stderr, msg, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
doexit(kErrorStatus);
|
|
}
|
|
#endif
|
|
|
|
__attribute__((noreturn)) void exitf(const char* msg, ...)
|
|
{
|
|
int e = errno;
|
|
fflush(stdout);
|
|
va_list args;
|
|
va_start(args, msg);
|
|
vfprintf(stderr, msg, args);
|
|
va_end(args);
|
|
fprintf(stderr, " (errno %d)\n", e);
|
|
doexit(kRetryStatus);
|
|
}
|
|
|
|
static int flag_debug;
|
|
|
|
void debug(const char* msg, ...)
|
|
{
|
|
if (!flag_debug)
|
|
return;
|
|
va_list args;
|
|
va_start(args, msg);
|
|
vfprintf(stdout, msg, args);
|
|
va_end(args);
|
|
fflush(stdout);
|
|
}
|
|
|
|
__thread int skip_segv;
|
|
__thread jmp_buf segv_env;
|
|
|
|
static void segv_handler(int sig, siginfo_t* info, void* uctx)
|
|
{
|
|
uintptr_t addr = (uintptr_t)info->si_addr;
|
|
const uintptr_t prog_start = 1 << 20;
|
|
const uintptr_t prog_end = 100 << 20;
|
|
if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED) && (addr < prog_start || addr > prog_end)) {
|
|
debug("SIGSEGV on %p, skipping\n", addr);
|
|
_longjmp(segv_env, 1);
|
|
}
|
|
debug("SIGSEGV on %p, exiting\n", addr);
|
|
doexit(sig);
|
|
for (;;) {
|
|
}
|
|
}
|
|
|
|
static void install_segv_handler()
|
|
{
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_sigaction = segv_handler;
|
|
sa.sa_flags = SA_NODEFER | SA_SIGINFO;
|
|
sigaction(SIGSEGV, &sa, NULL);
|
|
sigaction(SIGBUS, &sa, NULL);
|
|
}
|
|
|
|
#define NONFAILING(...) \
|
|
{ \
|
|
__atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \
|
|
if (_setjmp(segv_env) == 0) { \
|
|
__VA_ARGS__; \
|
|
} \
|
|
__atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \
|
|
}
|
|
|
|
#define BITMASK_LEN(type, bf_len) (type)((1ull << (bf_len)) - 1)
|
|
|
|
#define BITMASK_LEN_OFF(type, bf_off, bf_len) (type)(BITMASK_LEN(type, (bf_len)) << (bf_off))
|
|
|
|
#define STORE_BY_BITMASK(type, addr, val, bf_off, bf_len) \
|
|
if ((bf_off) == 0 && (bf_len) == 0) { \
|
|
*(type*)(addr) = (type)(val); \
|
|
} else { \
|
|
type new_val = *(type*)(addr); \
|
|
new_val &= ~BITMASK_LEN_OFF(type, (bf_off), (bf_len)); \
|
|
new_val |= ((type)(val)&BITMASK_LEN(type, (bf_len))) << (bf_off); \
|
|
*(type*)(addr) = new_val; \
|
|
}
|
|
|
|
#ifdef __NR_syz_emit_ethernet
|
|
static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
|
|
{
|
|
int rv;
|
|
|
|
rv = vsnprintf(str, size, format, args);
|
|
if (rv < 0)
|
|
fail("tun: snprintf failed");
|
|
if ((size_t)rv >= size)
|
|
fail("tun: string '%s...' doesn't fit into buffer", str);
|
|
}
|
|
|
|
static void snprintf_check(char* str, size_t size, const char* format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vsnprintf_check(str, size, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
#define COMMAND_MAX_LEN 128
|
|
|
|
static void execute_command(const char* format, ...)
|
|
{
|
|
va_list args;
|
|
char command[COMMAND_MAX_LEN];
|
|
int rv;
|
|
|
|
va_start(args, format);
|
|
|
|
vsnprintf_check(command, sizeof(command), format, args);
|
|
rv = system(command);
|
|
if (rv != 0)
|
|
fail("tun: command \"%s\" failed with code %d", &command[0], rv);
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
int tunfd = -1;
|
|
|
|
#define MAX_PIDS 32
|
|
#define ADDR_MAX_LEN 32
|
|
|
|
#define LOCAL_MAC "aa:aa:aa:aa:aa:%02hx"
|
|
#define REMOTE_MAC "bb:bb:bb:bb:bb:%02hx"
|
|
|
|
#define LOCAL_IPV4 "172.20.%d.170"
|
|
#define REMOTE_IPV4 "172.20.%d.187"
|
|
|
|
#define LOCAL_IPV6 "fd00::%02hxaa"
|
|
#define REMOTE_IPV6 "fd00::%02hxbb"
|
|
|
|
static void initialize_tun(uint64_t pid)
|
|
{
|
|
if (pid >= MAX_PIDS)
|
|
fail("tun: no more than %d executors", MAX_PIDS);
|
|
int id = pid;
|
|
|
|
tunfd = open("/dev/net/tun", O_RDWR);
|
|
if (tunfd == -1)
|
|
fail("tun: can't open /dev/net/tun");
|
|
|
|
char iface[IFNAMSIZ];
|
|
snprintf_check(iface, sizeof(iface), "syz%d", id);
|
|
|
|
struct ifreq ifr;
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
|
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
|
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0)
|
|
fail("tun: ioctl(TUNSETIFF) failed");
|
|
|
|
char local_mac[ADDR_MAX_LEN];
|
|
snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC, id);
|
|
char remote_mac[ADDR_MAX_LEN];
|
|
snprintf_check(remote_mac, sizeof(remote_mac), REMOTE_MAC, id);
|
|
|
|
char local_ipv4[ADDR_MAX_LEN];
|
|
snprintf_check(local_ipv4, sizeof(local_ipv4), LOCAL_IPV4, id);
|
|
char remote_ipv4[ADDR_MAX_LEN];
|
|
snprintf_check(remote_ipv4, sizeof(remote_ipv4), REMOTE_IPV4, id);
|
|
|
|
char local_ipv6[ADDR_MAX_LEN];
|
|
snprintf_check(local_ipv6, sizeof(local_ipv6), LOCAL_IPV6, id);
|
|
char remote_ipv6[ADDR_MAX_LEN];
|
|
snprintf_check(remote_ipv6, sizeof(remote_ipv6), REMOTE_IPV6, id);
|
|
|
|
execute_command("ip link set dev %s address %s", iface, local_mac);
|
|
execute_command("ip addr add %s/24 dev %s", local_ipv4, iface);
|
|
execute_command("ip -6 addr add %s/120 dev %s", local_ipv6, iface);
|
|
execute_command("ip neigh add %s lladdr %s dev %s nud permanent", remote_ipv4, remote_mac, iface);
|
|
execute_command("ip -6 neigh add %s lladdr %s dev %s nud permanent", remote_ipv6, remote_mac, iface);
|
|
execute_command("ip link set %s up", iface);
|
|
}
|
|
|
|
static void setup_tun(uint64_t pid, bool enable_tun)
|
|
{
|
|
if (enable_tun)
|
|
initialize_tun(pid);
|
|
}
|
|
|
|
static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
|
|
{
|
|
if (tunfd < 0)
|
|
return (uintptr_t)-1;
|
|
|
|
int64_t length = a0;
|
|
char* data = (char*)a1;
|
|
return write(tunfd, data, length);
|
|
}
|
|
#endif
|
|
|
|
#ifdef __NR_syz_open_dev
|
|
static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2)
|
|
{
|
|
if (a0 == 0xc || a0 == 0xb) {
|
|
char buf[128];
|
|
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2);
|
|
return open(buf, O_RDWR, 0);
|
|
} else {
|
|
char buf[1024];
|
|
char* hash;
|
|
NONFAILING(strncpy(buf, (char*)a0, sizeof(buf)));
|
|
buf[sizeof(buf) - 1] = 0;
|
|
while ((hash = strchr(buf, '#'))) {
|
|
*hash = '0' + (char)(a1 % 10);
|
|
a1 /= 10;
|
|
}
|
|
return open(buf, a2, 0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef __NR_syz_open_pts
|
|
static uintptr_t syz_open_pts(uintptr_t a0, uintptr_t a1)
|
|
{
|
|
int ptyno = 0;
|
|
if (ioctl(a0, TIOCGPTN, &ptyno))
|
|
return -1;
|
|
char buf[128];
|
|
sprintf(buf, "/dev/pts/%d", ptyno);
|
|
return open(buf, a1, 0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef __NR_syz_fuse_mount
|
|
static uintptr_t syz_fuse_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
|
|
{
|
|
uint64_t target = a0;
|
|
uint64_t mode = a1;
|
|
uint64_t uid = a2;
|
|
uint64_t gid = a3;
|
|
uint64_t maxread = a4;
|
|
uint64_t flags = a5;
|
|
|
|
int fd = open("/dev/fuse", O_RDWR);
|
|
if (fd == -1)
|
|
return fd;
|
|
char buf[1024];
|
|
sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u);
|
|
if (maxread != 0)
|
|
sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
|
|
if (mode & 1)
|
|
strcat(buf, ",default_permissions");
|
|
if (mode & 2)
|
|
strcat(buf, ",allow_other");
|
|
syscall(SYS_mount, "", target, "fuse", flags, buf);
|
|
return fd;
|
|
}
|
|
#endif
|
|
|
|
#ifdef __NR_syz_fuseblk_mount
|
|
static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7)
|
|
{
|
|
uint64_t target = a0;
|
|
uint64_t blkdev = a1;
|
|
uint64_t mode = a2;
|
|
uint64_t uid = a3;
|
|
uint64_t gid = a4;
|
|
uint64_t maxread = a5;
|
|
uint64_t blksize = a6;
|
|
uint64_t flags = a7;
|
|
|
|
int fd = open("/dev/fuse", O_RDWR);
|
|
if (fd == -1)
|
|
return fd;
|
|
if (syscall(SYS_mknodat, AT_FDCWD, blkdev, S_IFBLK, makedev(7, 199)))
|
|
return fd;
|
|
char buf[256];
|
|
sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u);
|
|
if (maxread != 0)
|
|
sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
|
|
if (blksize != 0)
|
|
sprintf(buf + strlen(buf), ",blksize=%ld", (long)blksize);
|
|
if (mode & 1)
|
|
strcat(buf, ",default_permissions");
|
|
if (mode & 2)
|
|
strcat(buf, ",allow_other");
|
|
syscall(SYS_mount, blkdev, target, "fuseblk", flags, buf);
|
|
return fd;
|
|
}
|
|
#endif
|
|
|
|
#ifdef __NR_syz_kvm_setup_cpu
|
|
#if defined(__x86_64__)
|
|
|
|
|
|
|
|
const char kvm_asm16_cpl3[] = "\x0f\x20\xc0\x66\x83\xc8\x01\x0f\x22\xc0\xb8\xa0\x00\x0f\x00\xd8\xb8\x2b\x00\x8e\xd8\x8e\xc0\x8e\xe0\x8e\xe8\xbc\x00\x01\xc7\x06\x00\x01\x1d\xba\xc7\x06\x02\x01\x23\x00\xc7\x06\x04\x01\x00\x01\xc7\x06\x06\x01\x2b\x00\xcb";
|
|
const char kvm_asm32_paged[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0";
|
|
const char kvm_asm32_vm86[] = "\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00";
|
|
const char kvm_asm32_paged_vm86[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00";
|
|
const char kvm_asm64_vm86[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00";
|
|
const char kvm_asm64_enable_long[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8";
|
|
const char kvm_asm64_init_vm[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8\x48\xc7\xc1\x3a\x00\x00\x00\x0f\x32\x48\x83\xc8\x05\x0f\x30\x0f\x20\xe0\x48\x0d\x00\x20\x00\x00\x0f\x22\xe0\x48\xc7\xc1\x80\x04\x00\x00\x0f\x32\x48\xc7\xc2\x00\x60\x00\x00\x89\x02\x48\xc7\xc2\x00\x70\x00\x00\x89\x02\x48\xc7\xc0\x00\x5f\x00\x00\xf3\x0f\xc7\x30\x48\xc7\xc0\x08\x5f\x00\x00\x66\x0f\xc7\x30\x0f\xc7\x30\x48\xc7\xc1\x81\x04\x00\x00\x0f\x32\x48\x83\xc8\x3f\x48\x21\xd0\x48\xc7\xc2\x00\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x40\x00\x00\x48\xb8\x84\x9e\x99\xf3\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x40\x00\x00\x48\xc7\xc0\x81\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x83\x04\x00\x00\x0f\x32\x48\x0d\xff\x6f\x03\x00\x48\x21\xd0\x48\xc7\xc2\x0c\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x84\x04\x00\x00\x0f\x32\x48\x0d\xff\x17\x00\x00\x48\x21\xd0\x48\xc7\xc2\x12\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x2c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x28\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x02\x0c\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc0\x58\x00\x00\x00\x48\xc7\xc2\x00\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc0\xd8\x00\x00\x00\x48\xc7\xc2\x0c\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x2c\x00\x00\x48\xc7\xc0\x00\x05\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x4c\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x0f\x20\xc0\x48\xc7\xc2\x00\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xd8\x48\xc7\xc2\x02\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xe0\x48\xc7\xc2\x04\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc2\x06\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x6c\x00\x00\x48\xc7\xc0\x00\x3a\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x6c\x00\x00\x48\xc7\xc0\x00\x10\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x6c\x00\x00\x48\xc7\xc0\x00\x38\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x6c\x00\x00\x48\x8b\x04\x25\x10\x5f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x77\x02\x00\x00\x0f\x32\x48\xc1\xe2\x20\x48\x09\xd0\x48\xc7\xc2\x00\x2c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc2\x04\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x60\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x02\x60\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x1c\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x22\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x08\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x08\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x08\x00\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x68\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x68\x00\x00\x48\xc7\xc0\x00\x3a\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x68\x00\x00\x48\xc7\xc0\x00\x10\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x18\x68\x00\x00\x48\xc7\xc0\x00\x38\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x48\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x48\x00\x00\x48\xc7\xc0\x9b\x20\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x18\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1a\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1c\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x48\x00\x00\x48\xc7\xc0\x82\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x22\x48\x00\x00\x48\xc7\xc0\x8b\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1c\x68\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x68\x00\x00\x48\xc7\xc0\x00\x91\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x68\x00\x00\x48\xc7\xc0\x02\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x28\x00\x00\x48\xc7\xc0\x00\x05\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x0f\x20\xc0\x48\xc7\xc2\x00\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xd8\x48\xc7\xc2\x02\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xe0\x48\xc7\xc2\x04\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc0\x18\x5f\x00\x00\x48\x8b\x10\x48\xc7\xc0\x20\x5f\x00\x00\x48\x8b\x08\x48\x31\xc0\x0f\x78\xd0\x48\x31\xc8\x0f\x79\xd0\x0f\x01\xc2\x48\xc7\xc2\x00\x44\x00\x00\x0f\x78\xd0\xf4";
|
|
const char kvm_asm64_vm_exit[] = "\x48\xc7\xc3\x00\x44\x00\x00\x0f\x78\xda\x48\xc7\xc3\x02\x44\x00\x00\x0f\x78\xd9\x48\xc7\xc0\x00\x64\x00\x00\x0f\x78\xc0\x48\xc7\xc3\x1e\x68\x00\x00\x0f\x78\xdb\xf4";
|
|
const char kvm_asm64_cpl3[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8\x48\xc7\xc0\x6b\x00\x00\x00\x8e\xd8\x8e\xc0\x8e\xe0\x8e\xe8\x48\xc7\xc4\x80\x0f\x00\x00\x48\xc7\x04\x24\x1d\xba\x00\x00\x48\xc7\x44\x24\x04\x63\x00\x00\x00\x48\xc7\x44\x24\x08\x80\x0f\x00\x00\x48\xc7\x44\x24\x0c\x6b\x00\x00\x00\xcb";
|
|
|
|
#define ADDR_TEXT 0x0000
|
|
#define ADDR_GDT 0x1000
|
|
#define ADDR_LDT 0x1800
|
|
#define ADDR_PML4 0x2000
|
|
#define ADDR_PDP 0x3000
|
|
#define ADDR_PD 0x4000
|
|
#define ADDR_STACK0 0x0f80
|
|
#define ADDR_VAR_HLT 0x2800
|
|
#define ADDR_VAR_SYSRET 0x2808
|
|
#define ADDR_VAR_SYSEXIT 0x2810
|
|
#define ADDR_VAR_IDT 0x3800
|
|
#define ADDR_VAR_TSS64 0x3a00
|
|
#define ADDR_VAR_TSS64_CPL3 0x3c00
|
|
#define ADDR_VAR_TSS16 0x3d00
|
|
#define ADDR_VAR_TSS16_2 0x3e00
|
|
#define ADDR_VAR_TSS16_CPL3 0x3f00
|
|
#define ADDR_VAR_TSS32 0x4800
|
|
#define ADDR_VAR_TSS32_2 0x4a00
|
|
#define ADDR_VAR_TSS32_CPL3 0x4c00
|
|
#define ADDR_VAR_TSS32_VM86 0x4e00
|
|
#define ADDR_VAR_VMXON_PTR 0x5f00
|
|
#define ADDR_VAR_VMCS_PTR 0x5f08
|
|
#define ADDR_VAR_VMEXIT_PTR 0x5f10
|
|
#define ADDR_VAR_VMWRITE_FLD 0x5f18
|
|
#define ADDR_VAR_VMWRITE_VAL 0x5f20
|
|
#define ADDR_VAR_VMXON 0x6000
|
|
#define ADDR_VAR_VMCS 0x7000
|
|
#define ADDR_VAR_VMEXIT_CODE 0x9000
|
|
#define ADDR_VAR_USER_CODE 0x9100
|
|
#define ADDR_VAR_USER_CODE2 0x9120
|
|
|
|
#define SEL_LDT (1 << 3)
|
|
#define SEL_CS16 (2 << 3)
|
|
#define SEL_DS16 (3 << 3)
|
|
#define SEL_CS16_CPL3 ((4 << 3) + 3)
|
|
#define SEL_DS16_CPL3 ((5 << 3) + 3)
|
|
#define SEL_CS32 (6 << 3)
|
|
#define SEL_DS32 (7 << 3)
|
|
#define SEL_CS32_CPL3 ((8 << 3) + 3)
|
|
#define SEL_DS32_CPL3 ((9 << 3) + 3)
|
|
#define SEL_CS64 (10 << 3)
|
|
#define SEL_DS64 (11 << 3)
|
|
#define SEL_CS64_CPL3 ((12 << 3) + 3)
|
|
#define SEL_DS64_CPL3 ((13 << 3) + 3)
|
|
#define SEL_CGATE16 (14 << 3)
|
|
#define SEL_TGATE16 (15 << 3)
|
|
#define SEL_CGATE32 (16 << 3)
|
|
#define SEL_TGATE32 (17 << 3)
|
|
#define SEL_CGATE64 (18 << 3)
|
|
#define SEL_CGATE64_HI (19 << 3)
|
|
#define SEL_TSS16 (20 << 3)
|
|
#define SEL_TSS16_2 (21 << 3)
|
|
#define SEL_TSS16_CPL3 ((22 << 3) + 3)
|
|
#define SEL_TSS32 (23 << 3)
|
|
#define SEL_TSS32_2 (24 << 3)
|
|
#define SEL_TSS32_CPL3 ((25 << 3) + 3)
|
|
#define SEL_TSS32_VM86 (26 << 3)
|
|
#define SEL_TSS64 (27 << 3)
|
|
#define SEL_TSS64_HI (28 << 3)
|
|
#define SEL_TSS64_CPL3 ((29 << 3) + 3)
|
|
#define SEL_TSS64_CPL3_HI (30 << 3)
|
|
|
|
#define MSR_IA32_FEATURE_CONTROL 0x3a
|
|
#define MSR_IA32_VMX_BASIC 0x480
|
|
#define MSR_IA32_SMBASE 0x9e
|
|
#define MSR_IA32_SYSENTER_CS 0x174
|
|
#define MSR_IA32_SYSENTER_ESP 0x175
|
|
#define MSR_IA32_SYSENTER_EIP 0x176
|
|
#define MSR_IA32_STAR 0xC0000081
|
|
#define MSR_IA32_LSTAR 0xC0000082
|
|
#define MSR_IA32_VMX_PROCBASED_CTLS2 0x48B
|
|
|
|
#define NEXT_INSN $0xbadc0de
|
|
#define PREFIX_SIZE 0xba1d
|
|
|
|
#ifndef KVM_SMI
|
|
#define KVM_SMI _IO(KVMIO, 0xb7)
|
|
#endif
|
|
|
|
#define CR0_PE 1
|
|
#define CR0_MP (1 << 1)
|
|
#define CR0_EM (1 << 2)
|
|
#define CR0_TS (1 << 3)
|
|
#define CR0_ET (1 << 4)
|
|
#define CR0_NE (1 << 5)
|
|
#define CR0_WP (1 << 16)
|
|
#define CR0_AM (1 << 18)
|
|
#define CR0_NW (1 << 29)
|
|
#define CR0_CD (1 << 30)
|
|
#define CR0_PG (1 << 31)
|
|
|
|
#define CR4_VME 1
|
|
#define CR4_PVI (1 << 1)
|
|
#define CR4_TSD (1 << 2)
|
|
#define CR4_DE (1 << 3)
|
|
#define CR4_PSE (1 << 4)
|
|
#define CR4_PAE (1 << 5)
|
|
#define CR4_MCE (1 << 6)
|
|
#define CR4_PGE (1 << 7)
|
|
#define CR4_PCE (1 << 8)
|
|
#define CR4_OSFXSR (1 << 8)
|
|
#define CR4_OSXMMEXCPT (1 << 10)
|
|
#define CR4_UMIP (1 << 11)
|
|
#define CR4_VMXE (1 << 13)
|
|
#define CR4_SMXE (1 << 14)
|
|
#define CR4_FSGSBASE (1 << 16)
|
|
#define CR4_PCIDE (1 << 17)
|
|
#define CR4_OSXSAVE (1 << 18)
|
|
#define CR4_SMEP (1 << 20)
|
|
#define CR4_SMAP (1 << 21)
|
|
#define CR4_PKE (1 << 22)
|
|
|
|
#define EFER_SCE 1
|
|
#define EFER_LME (1 << 8)
|
|
#define EFER_LMA (1 << 10)
|
|
#define EFER_NXE (1 << 11)
|
|
#define EFER_SVME (1 << 12)
|
|
#define EFER_LMSLE (1 << 13)
|
|
#define EFER_FFXSR (1 << 14)
|
|
#define EFER_TCE (1 << 15)
|
|
|
|
#define PDE32_PRESENT 1
|
|
#define PDE32_RW (1 << 1)
|
|
#define PDE32_USER (1 << 2)
|
|
#define PDE32_PS (1 << 7)
|
|
|
|
#define PDE64_PRESENT 1
|
|
#define PDE64_RW (1 << 1)
|
|
#define PDE64_USER (1 << 2)
|
|
#define PDE64_ACCESSED (1 << 5)
|
|
#define PDE64_DIRTY (1 << 6)
|
|
#define PDE64_PS (1 << 7)
|
|
#define PDE64_G (1 << 8)
|
|
|
|
struct tss16 {
|
|
uint16_t prev;
|
|
uint16_t sp0;
|
|
uint16_t ss0;
|
|
uint16_t sp1;
|
|
uint16_t ss1;
|
|
uint16_t sp2;
|
|
uint16_t ss2;
|
|
uint16_t ip;
|
|
uint16_t flags;
|
|
uint16_t ax;
|
|
uint16_t cx;
|
|
uint16_t dx;
|
|
uint16_t bx;
|
|
uint16_t sp;
|
|
uint16_t bp;
|
|
uint16_t si;
|
|
uint16_t di;
|
|
uint16_t es;
|
|
uint16_t cs;
|
|
uint16_t ss;
|
|
uint16_t ds;
|
|
uint16_t ldt;
|
|
} __attribute__((packed));
|
|
|
|
struct tss32 {
|
|
uint16_t prev, prevh;
|
|
uint32_t sp0;
|
|
uint16_t ss0, ss0h;
|
|
uint32_t sp1;
|
|
uint16_t ss1, ss1h;
|
|
uint32_t sp2;
|
|
uint16_t ss2, ss2h;
|
|
uint32_t cr3;
|
|
uint32_t ip;
|
|
uint32_t flags;
|
|
uint32_t ax;
|
|
uint32_t cx;
|
|
uint32_t dx;
|
|
uint32_t bx;
|
|
uint32_t sp;
|
|
uint32_t bp;
|
|
uint32_t si;
|
|
uint32_t di;
|
|
uint16_t es, esh;
|
|
uint16_t cs, csh;
|
|
uint16_t ss, ssh;
|
|
uint16_t ds, dsh;
|
|
uint16_t fs, fsh;
|
|
uint16_t gs, gsh;
|
|
uint16_t ldt, ldth;
|
|
uint16_t trace;
|
|
uint16_t io_bitmap;
|
|
} __attribute__((packed));
|
|
|
|
struct tss64 {
|
|
uint32_t reserved0;
|
|
uint64_t rsp[3];
|
|
uint64_t reserved1;
|
|
uint64_t ist[7];
|
|
uint64_t reserved2;
|
|
uint32_t reserved3;
|
|
uint32_t io_bitmap;
|
|
} __attribute__((packed));
|
|
|
|
static void fill_segment_descriptor(uint64_t* dt, uint64_t* lt, struct kvm_segment* seg)
|
|
{
|
|
uint16_t index = seg->selector >> 3;
|
|
uint64_t limit = seg->g ? seg->limit >> 12 : seg->limit;
|
|
uint64_t sd = (limit & 0xffff) | (seg->base & 0xffffff) << 16 | (uint64_t)seg->type << 40 | (uint64_t)seg->s << 44 | (uint64_t)seg->dpl << 45 | (uint64_t)seg->present << 47 | (limit & 0xf0000ULL) << 48 | (uint64_t)seg->avl << 52 | (uint64_t)seg->l << 53 | (uint64_t)seg->db << 54 | (uint64_t)seg->g << 55 | (seg->base & 0xff000000ULL) << 56;
|
|
NONFAILING(dt[index] = sd);
|
|
NONFAILING(lt[index] = sd);
|
|
}
|
|
|
|
static void fill_segment_descriptor_dword(uint64_t* dt, uint64_t* lt, struct kvm_segment* seg)
|
|
{
|
|
fill_segment_descriptor(dt, lt, seg);
|
|
uint16_t index = seg->selector >> 3;
|
|
NONFAILING(dt[index + 1] = 0);
|
|
NONFAILING(lt[index + 1] = 0);
|
|
}
|
|
|
|
static void setup_syscall_msrs(int cpufd, uint16_t sel_cs, uint16_t sel_cs_cpl3)
|
|
{
|
|
char buf[sizeof(struct kvm_msrs) + 5 * sizeof(struct kvm_msr_entry)];
|
|
memset(buf, 0, sizeof(buf));
|
|
struct kvm_msrs* msrs = (struct kvm_msrs*)buf;
|
|
msrs->nmsrs = 5;
|
|
msrs->entries[0].index = MSR_IA32_SYSENTER_CS;
|
|
msrs->entries[0].data = sel_cs;
|
|
msrs->entries[1].index = MSR_IA32_SYSENTER_ESP;
|
|
msrs->entries[1].data = ADDR_STACK0;
|
|
msrs->entries[2].index = MSR_IA32_SYSENTER_EIP;
|
|
msrs->entries[2].data = ADDR_VAR_SYSEXIT;
|
|
msrs->entries[3].index = MSR_IA32_STAR;
|
|
msrs->entries[3].data = ((uint64_t)sel_cs << 32) | ((uint64_t)sel_cs_cpl3 << 48);
|
|
msrs->entries[4].index = MSR_IA32_LSTAR;
|
|
msrs->entries[4].data = ADDR_VAR_SYSRET;
|
|
ioctl(cpufd, KVM_SET_MSRS, msrs);
|
|
}
|
|
|
|
static void setup_32bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t guest_mem)
|
|
{
|
|
sregs->idt.base = guest_mem + ADDR_VAR_IDT;
|
|
sregs->idt.limit = 0x1ff;
|
|
uint64_t* idt = (uint64_t*)(host_mem + sregs->idt.base);
|
|
int i;
|
|
for (i = 0; i < 32; i++) {
|
|
struct kvm_segment gate;
|
|
gate.selector = i << 3;
|
|
switch (i % 6) {
|
|
case 0:
|
|
gate.type = 6;
|
|
gate.base = SEL_CS16;
|
|
break;
|
|
case 1:
|
|
gate.type = 7;
|
|
gate.base = SEL_CS16;
|
|
break;
|
|
case 2:
|
|
gate.type = 3;
|
|
gate.base = SEL_TGATE16;
|
|
break;
|
|
case 3:
|
|
gate.type = 14;
|
|
gate.base = SEL_CS32;
|
|
break;
|
|
case 4:
|
|
gate.type = 15;
|
|
gate.base = SEL_CS32;
|
|
break;
|
|
case 6:
|
|
gate.type = 11;
|
|
gate.base = SEL_TGATE32;
|
|
break;
|
|
}
|
|
gate.limit = guest_mem + ADDR_VAR_USER_CODE2;
|
|
gate.present = 1;
|
|
gate.dpl = 0;
|
|
gate.s = 0;
|
|
gate.g = 0;
|
|
gate.db = 0;
|
|
gate.l = 0;
|
|
gate.avl = 0;
|
|
fill_segment_descriptor(idt, idt, &gate);
|
|
}
|
|
}
|
|
|
|
static void setup_64bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t guest_mem)
|
|
{
|
|
sregs->idt.base = guest_mem + ADDR_VAR_IDT;
|
|
sregs->idt.limit = 0x1ff;
|
|
uint64_t* idt = (uint64_t*)(host_mem + sregs->idt.base);
|
|
int i;
|
|
for (i = 0; i < 32; i++) {
|
|
struct kvm_segment gate;
|
|
gate.selector = (i * 2) << 3;
|
|
gate.type = (i & 1) ? 14 : 15;
|
|
gate.base = SEL_CS64;
|
|
gate.limit = guest_mem + ADDR_VAR_USER_CODE2;
|
|
gate.present = 1;
|
|
gate.dpl = 0;
|
|
gate.s = 0;
|
|
gate.g = 0;
|
|
gate.db = 0;
|
|
gate.l = 0;
|
|
gate.avl = 0;
|
|
fill_segment_descriptor_dword(idt, idt, &gate);
|
|
}
|
|
}
|
|
|
|
struct kvm_text {
|
|
uintptr_t typ;
|
|
const void* text;
|
|
uintptr_t size;
|
|
};
|
|
|
|
struct kvm_opt {
|
|
uint64_t typ;
|
|
uint64_t val;
|
|
};
|
|
|
|
#define KVM_SETUP_PAGING (1 << 0)
|
|
#define KVM_SETUP_PAE (1 << 1)
|
|
#define KVM_SETUP_PROTECTED (1 << 2)
|
|
#define KVM_SETUP_CPL3 (1 << 3)
|
|
#define KVM_SETUP_VIRT86 (1 << 4)
|
|
#define KVM_SETUP_SMM (1 << 5)
|
|
#define KVM_SETUP_VM (1 << 6)
|
|
|
|
static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7)
|
|
{
|
|
const int vmfd = a0;
|
|
const int cpufd = a1;
|
|
char* const host_mem = (char*)a2;
|
|
const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3;
|
|
const uintptr_t text_count = a4;
|
|
const uintptr_t flags = a5;
|
|
const struct kvm_opt* const opt_array_ptr = (struct kvm_opt*)a6;
|
|
uintptr_t opt_count = a7;
|
|
|
|
const uintptr_t page_size = 4 << 10;
|
|
const uintptr_t ioapic_page = 10;
|
|
const uintptr_t guest_mem_size = 24 * page_size;
|
|
const uintptr_t guest_mem = 0;
|
|
|
|
(void)text_count;
|
|
int text_type = 0;
|
|
const void* text = 0;
|
|
uintptr_t text_size = 0;
|
|
NONFAILING(text_type = text_array_ptr[0].typ);
|
|
NONFAILING(text = text_array_ptr[0].text);
|
|
NONFAILING(text_size = text_array_ptr[0].size);
|
|
|
|
uintptr_t i;
|
|
for (i = 0; i < guest_mem_size / page_size; i++) {
|
|
struct kvm_userspace_memory_region memreg;
|
|
memreg.slot = i;
|
|
memreg.flags = 0;
|
|
memreg.guest_phys_addr = guest_mem + i * page_size;
|
|
if (i == ioapic_page)
|
|
memreg.guest_phys_addr = 0xfec00000;
|
|
memreg.memory_size = page_size;
|
|
memreg.userspace_addr = (uintptr_t)host_mem + i * page_size;
|
|
ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
|
|
}
|
|
struct kvm_userspace_memory_region memreg;
|
|
memreg.slot = 1 + (1 << 16);
|
|
memreg.flags = 0;
|
|
memreg.guest_phys_addr = 0x30000;
|
|
memreg.memory_size = 64 << 10;
|
|
memreg.userspace_addr = (uintptr_t)host_mem;
|
|
ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
|
|
|
|
struct kvm_sregs sregs;
|
|
if (ioctl(cpufd, KVM_GET_SREGS, &sregs))
|
|
return -1;
|
|
|
|
struct kvm_regs regs;
|
|
memset(®s, 0, sizeof(regs));
|
|
regs.rip = guest_mem + ADDR_TEXT;
|
|
regs.rsp = ADDR_STACK0;
|
|
|
|
sregs.gdt.base = guest_mem + ADDR_GDT;
|
|
sregs.gdt.limit = 256 * sizeof(uint64_t) - 1;
|
|
uint64_t* gdt = (uint64_t*)(host_mem + sregs.gdt.base);
|
|
|
|
struct kvm_segment seg_ldt;
|
|
seg_ldt.selector = SEL_LDT;
|
|
seg_ldt.type = 2;
|
|
seg_ldt.base = guest_mem + ADDR_LDT;
|
|
seg_ldt.limit = 256 * sizeof(uint64_t) - 1;
|
|
seg_ldt.present = 1;
|
|
seg_ldt.dpl = 0;
|
|
seg_ldt.s = 0;
|
|
seg_ldt.g = 0;
|
|
seg_ldt.db = 1;
|
|
seg_ldt.l = 0;
|
|
sregs.ldt = seg_ldt;
|
|
uint64_t* ldt = (uint64_t*)(host_mem + sregs.ldt.base);
|
|
|
|
struct kvm_segment seg_cs16;
|
|
seg_cs16.selector = SEL_CS16;
|
|
seg_cs16.type = 11;
|
|
seg_cs16.base = 0;
|
|
seg_cs16.limit = 0xfffff;
|
|
seg_cs16.present = 1;
|
|
seg_cs16.dpl = 0;
|
|
seg_cs16.s = 1;
|
|
seg_cs16.g = 0;
|
|
seg_cs16.db = 0;
|
|
seg_cs16.l = 0;
|
|
|
|
struct kvm_segment seg_ds16 = seg_cs16;
|
|
seg_ds16.selector = SEL_DS16;
|
|
seg_ds16.type = 3;
|
|
|
|
struct kvm_segment seg_cs16_cpl3 = seg_cs16;
|
|
seg_cs16_cpl3.selector = SEL_CS16_CPL3;
|
|
seg_cs16_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_ds16_cpl3 = seg_ds16;
|
|
seg_ds16_cpl3.selector = SEL_DS16_CPL3;
|
|
seg_ds16_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_cs32 = seg_cs16;
|
|
seg_cs32.selector = SEL_CS32;
|
|
seg_cs32.db = 1;
|
|
|
|
struct kvm_segment seg_ds32 = seg_ds16;
|
|
seg_ds32.selector = SEL_DS32;
|
|
seg_ds32.db = 1;
|
|
|
|
struct kvm_segment seg_cs32_cpl3 = seg_cs32;
|
|
seg_cs32_cpl3.selector = SEL_CS32_CPL3;
|
|
seg_cs32_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_ds32_cpl3 = seg_ds32;
|
|
seg_ds32_cpl3.selector = SEL_DS32_CPL3;
|
|
seg_ds32_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_cs64 = seg_cs16;
|
|
seg_cs64.selector = SEL_CS64;
|
|
seg_cs64.l = 1;
|
|
|
|
struct kvm_segment seg_ds64 = seg_ds32;
|
|
seg_ds64.selector = SEL_DS64;
|
|
|
|
struct kvm_segment seg_cs64_cpl3 = seg_cs64;
|
|
seg_cs64_cpl3.selector = SEL_CS64_CPL3;
|
|
seg_cs64_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_ds64_cpl3 = seg_ds64;
|
|
seg_ds64_cpl3.selector = SEL_DS64_CPL3;
|
|
seg_ds64_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_tss32;
|
|
seg_tss32.selector = SEL_TSS32;
|
|
seg_tss32.type = 9;
|
|
seg_tss32.base = ADDR_VAR_TSS32;
|
|
seg_tss32.limit = 0x1ff;
|
|
seg_tss32.present = 1;
|
|
seg_tss32.dpl = 0;
|
|
seg_tss32.s = 0;
|
|
seg_tss32.g = 0;
|
|
seg_tss32.db = 0;
|
|
seg_tss32.l = 0;
|
|
|
|
struct kvm_segment seg_tss32_2 = seg_tss32;
|
|
seg_tss32_2.selector = SEL_TSS32_2;
|
|
seg_tss32_2.base = ADDR_VAR_TSS32_2;
|
|
|
|
struct kvm_segment seg_tss32_cpl3 = seg_tss32;
|
|
seg_tss32_cpl3.selector = SEL_TSS32_CPL3;
|
|
seg_tss32_cpl3.base = ADDR_VAR_TSS32_CPL3;
|
|
|
|
struct kvm_segment seg_tss32_vm86 = seg_tss32;
|
|
seg_tss32_vm86.selector = SEL_TSS32_VM86;
|
|
seg_tss32_vm86.base = ADDR_VAR_TSS32_VM86;
|
|
|
|
struct kvm_segment seg_tss16 = seg_tss32;
|
|
seg_tss16.selector = SEL_TSS16;
|
|
seg_tss16.base = ADDR_VAR_TSS16;
|
|
seg_tss16.limit = 0xff;
|
|
seg_tss16.type = 1;
|
|
|
|
struct kvm_segment seg_tss16_2 = seg_tss16;
|
|
seg_tss16_2.selector = SEL_TSS16_2;
|
|
seg_tss16_2.base = ADDR_VAR_TSS16_2;
|
|
seg_tss16_2.dpl = 0;
|
|
|
|
struct kvm_segment seg_tss16_cpl3 = seg_tss16;
|
|
seg_tss16_cpl3.selector = SEL_TSS16_CPL3;
|
|
seg_tss16_cpl3.base = ADDR_VAR_TSS16_CPL3;
|
|
seg_tss16_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_tss64 = seg_tss32;
|
|
seg_tss64.selector = SEL_TSS64;
|
|
seg_tss64.base = ADDR_VAR_TSS64;
|
|
seg_tss64.limit = 0x1ff;
|
|
|
|
struct kvm_segment seg_tss64_cpl3 = seg_tss64;
|
|
seg_tss64_cpl3.selector = SEL_TSS64_CPL3;
|
|
seg_tss64_cpl3.base = ADDR_VAR_TSS64_CPL3;
|
|
seg_tss64_cpl3.dpl = 3;
|
|
|
|
struct kvm_segment seg_cgate16;
|
|
seg_cgate16.selector = SEL_CGATE16;
|
|
seg_cgate16.type = 4;
|
|
seg_cgate16.base = SEL_CS16 | (2 << 16);
|
|
seg_cgate16.limit = ADDR_VAR_USER_CODE2;
|
|
seg_cgate16.present = 1;
|
|
seg_cgate16.dpl = 0;
|
|
seg_cgate16.s = 0;
|
|
seg_cgate16.g = 0;
|
|
seg_cgate16.db = 0;
|
|
seg_cgate16.l = 0;
|
|
seg_cgate16.avl = 0;
|
|
|
|
struct kvm_segment seg_tgate16 = seg_cgate16;
|
|
seg_tgate16.selector = SEL_TGATE16;
|
|
seg_tgate16.type = 3;
|
|
seg_cgate16.base = SEL_TSS16_2;
|
|
seg_tgate16.limit = 0;
|
|
|
|
struct kvm_segment seg_cgate32 = seg_cgate16;
|
|
seg_cgate32.selector = SEL_CGATE32;
|
|
seg_cgate32.type = 12;
|
|
seg_cgate32.base = SEL_CS32 | (2 << 16);
|
|
|
|
struct kvm_segment seg_tgate32 = seg_cgate32;
|
|
seg_tgate32.selector = SEL_TGATE32;
|
|
seg_tgate32.type = 11;
|
|
seg_tgate32.base = SEL_TSS32_2;
|
|
seg_tgate32.limit = 0;
|
|
|
|
struct kvm_segment seg_cgate64 = seg_cgate16;
|
|
seg_cgate64.selector = SEL_CGATE64;
|
|
seg_cgate64.type = 12;
|
|
seg_cgate64.base = SEL_CS64;
|
|
|
|
int kvmfd = open("/dev/kvm", O_RDWR);
|
|
char buf[sizeof(struct kvm_cpuid2) + 128 * sizeof(struct kvm_cpuid_entry2)];
|
|
memset(buf, 0, sizeof(buf));
|
|
struct kvm_cpuid2* cpuid = (struct kvm_cpuid2*)buf;
|
|
cpuid->nent = 128;
|
|
ioctl(kvmfd, KVM_GET_SUPPORTED_CPUID, cpuid);
|
|
ioctl(cpufd, KVM_SET_CPUID2, cpuid);
|
|
close(kvmfd);
|
|
|
|
const char* text_prefix = 0;
|
|
int text_prefix_size = 0;
|
|
char* host_text = host_mem + ADDR_TEXT;
|
|
|
|
if (text_type == 8) {
|
|
if (flags & KVM_SETUP_SMM) {
|
|
if (flags & KVM_SETUP_PROTECTED) {
|
|
sregs.cs = seg_cs16;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
|
|
sregs.cr0 |= CR0_PE;
|
|
} else {
|
|
sregs.cs.selector = 0;
|
|
sregs.cs.base = 0;
|
|
}
|
|
|
|
NONFAILING(*(host_mem + ADDR_TEXT) = 0xf4);
|
|
host_text = host_mem + 0x8000;
|
|
|
|
ioctl(cpufd, KVM_SMI, 0);
|
|
} else if (flags & KVM_SETUP_VIRT86) {
|
|
sregs.cs = seg_cs32;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
|
|
sregs.cr0 |= CR0_PE;
|
|
sregs.efer |= EFER_SCE;
|
|
|
|
setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3);
|
|
setup_32bit_idt(&sregs, host_mem, guest_mem);
|
|
|
|
if (flags & KVM_SETUP_PAGING) {
|
|
uint64_t pd_addr = guest_mem + ADDR_PD;
|
|
uint64_t* pd = (uint64_t*)(host_mem + ADDR_PD);
|
|
NONFAILING(pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS);
|
|
sregs.cr3 = pd_addr;
|
|
sregs.cr4 |= CR4_PSE;
|
|
|
|
text_prefix = kvm_asm32_paged_vm86;
|
|
text_prefix_size = sizeof(kvm_asm32_paged_vm86) - 1;
|
|
} else {
|
|
text_prefix = kvm_asm32_vm86;
|
|
text_prefix_size = sizeof(kvm_asm32_vm86) - 1;
|
|
}
|
|
} else {
|
|
sregs.cs.selector = 0;
|
|
sregs.cs.base = 0;
|
|
}
|
|
} else if (text_type == 16) {
|
|
if (flags & KVM_SETUP_CPL3) {
|
|
sregs.cs = seg_cs16;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
|
|
|
|
text_prefix = kvm_asm16_cpl3;
|
|
text_prefix_size = sizeof(kvm_asm16_cpl3) - 1;
|
|
} else {
|
|
sregs.cr0 |= CR0_PE;
|
|
sregs.cs = seg_cs16;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
|
|
}
|
|
} else if (text_type == 32) {
|
|
sregs.cr0 |= CR0_PE;
|
|
sregs.efer |= EFER_SCE;
|
|
|
|
setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3);
|
|
setup_32bit_idt(&sregs, host_mem, guest_mem);
|
|
|
|
if (flags & KVM_SETUP_SMM) {
|
|
sregs.cs = seg_cs32;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
|
|
|
|
NONFAILING(*(host_mem + ADDR_TEXT) = 0xf4);
|
|
host_text = host_mem + 0x8000;
|
|
|
|
ioctl(cpufd, KVM_SMI, 0);
|
|
} else if (flags & KVM_SETUP_PAGING) {
|
|
sregs.cs = seg_cs32;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
|
|
|
|
uint64_t pd_addr = guest_mem + ADDR_PD;
|
|
uint64_t* pd = (uint64_t*)(host_mem + ADDR_PD);
|
|
NONFAILING(pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS);
|
|
sregs.cr3 = pd_addr;
|
|
sregs.cr4 |= CR4_PSE;
|
|
|
|
text_prefix = kvm_asm32_paged;
|
|
text_prefix_size = sizeof(kvm_asm32_paged) - 1;
|
|
} else if (flags & KVM_SETUP_CPL3) {
|
|
sregs.cs = seg_cs32_cpl3;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32_cpl3;
|
|
} else {
|
|
sregs.cs = seg_cs32;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
|
|
}
|
|
} else {
|
|
sregs.efer |= EFER_LME | EFER_SCE;
|
|
sregs.cr0 |= CR0_PE;
|
|
|
|
setup_syscall_msrs(cpufd, SEL_CS64, SEL_CS64_CPL3);
|
|
setup_64bit_idt(&sregs, host_mem, guest_mem);
|
|
|
|
sregs.cs = seg_cs32;
|
|
sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
|
|
|
|
uint64_t pml4_addr = guest_mem + ADDR_PML4;
|
|
uint64_t* pml4 = (uint64_t*)(host_mem + ADDR_PML4);
|
|
uint64_t pdpt_addr = guest_mem + ADDR_PDP;
|
|
uint64_t* pdpt = (uint64_t*)(host_mem + ADDR_PDP);
|
|
uint64_t pd_addr = guest_mem + ADDR_PD;
|
|
uint64_t* pd = (uint64_t*)(host_mem + ADDR_PD);
|
|
NONFAILING(pml4[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pdpt_addr);
|
|
NONFAILING(pdpt[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pd_addr);
|
|
NONFAILING(pd[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_PS);
|
|
sregs.cr3 = pml4_addr;
|
|
sregs.cr4 |= CR4_PAE;
|
|
|
|
if (flags & KVM_SETUP_VM) {
|
|
sregs.cr0 |= CR0_NE;
|
|
|
|
NONFAILING(*((uint64_t*)(host_mem + ADDR_VAR_VMXON_PTR)) = ADDR_VAR_VMXON);
|
|
NONFAILING(*((uint64_t*)(host_mem + ADDR_VAR_VMCS_PTR)) = ADDR_VAR_VMCS);
|
|
NONFAILING(memcpy(host_mem + ADDR_VAR_VMEXIT_CODE, kvm_asm64_vm_exit, sizeof(kvm_asm64_vm_exit) - 1));
|
|
NONFAILING(*((uint64_t*)(host_mem + ADDR_VAR_VMEXIT_PTR)) = ADDR_VAR_VMEXIT_CODE);
|
|
|
|
text_prefix = kvm_asm64_init_vm;
|
|
text_prefix_size = sizeof(kvm_asm64_init_vm) - 1;
|
|
} else if (flags & KVM_SETUP_CPL3) {
|
|
text_prefix = kvm_asm64_cpl3;
|
|
text_prefix_size = sizeof(kvm_asm64_cpl3) - 1;
|
|
} else {
|
|
text_prefix = kvm_asm64_enable_long;
|
|
text_prefix_size = sizeof(kvm_asm64_enable_long) - 1;
|
|
}
|
|
}
|
|
|
|
struct tss16* tss16 = (struct tss16*)(host_mem + seg_tss16_2.base);
|
|
NONFAILING(
|
|
struct tss16* tss = tss16;
|
|
memset(tss, 0, sizeof(*tss));
|
|
tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS16;
|
|
tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
|
|
tss->ip = ADDR_VAR_USER_CODE2;
|
|
tss->flags = (1 << 1);
|
|
tss->cs = SEL_CS16;
|
|
tss->es = tss->ds = tss->ss = SEL_DS16;
|
|
tss->ldt = SEL_LDT);
|
|
struct tss16* tss16_cpl3 = (struct tss16*)(host_mem + seg_tss16_cpl3.base);
|
|
NONFAILING(
|
|
struct tss16* tss = tss16_cpl3;
|
|
memset(tss, 0, sizeof(*tss));
|
|
tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS16;
|
|
tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
|
|
tss->ip = ADDR_VAR_USER_CODE2;
|
|
tss->flags = (1 << 1);
|
|
tss->cs = SEL_CS16_CPL3;
|
|
tss->es = tss->ds = tss->ss = SEL_DS16_CPL3;
|
|
tss->ldt = SEL_LDT);
|
|
struct tss32* tss32 = (struct tss32*)(host_mem + seg_tss32_vm86.base);
|
|
NONFAILING(
|
|
struct tss32* tss = tss32;
|
|
memset(tss, 0, sizeof(*tss));
|
|
tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS32;
|
|
tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
|
|
tss->ip = ADDR_VAR_USER_CODE;
|
|
tss->flags = (1 << 1) | (1 << 17);
|
|
tss->ldt = SEL_LDT;
|
|
tss->cr3 = sregs.cr3;
|
|
tss->io_bitmap = offsetof(struct tss32, io_bitmap));
|
|
struct tss32* tss32_cpl3 = (struct tss32*)(host_mem + seg_tss32_2.base);
|
|
NONFAILING(
|
|
struct tss32* tss = tss32_cpl3;
|
|
memset(tss, 0, sizeof(*tss));
|
|
tss->ss0 = tss->ss1 = tss->ss2 = SEL_DS32;
|
|
tss->sp0 = tss->sp1 = tss->sp2 = ADDR_STACK0;
|
|
tss->ip = ADDR_VAR_USER_CODE;
|
|
tss->flags = (1 << 1);
|
|
tss->cr3 = sregs.cr3;
|
|
tss->es = tss->ds = tss->ss = tss->gs = tss->fs = SEL_DS32;
|
|
tss->cs = SEL_CS32;
|
|
tss->ldt = SEL_LDT;
|
|
tss->cr3 = sregs.cr3;
|
|
tss->io_bitmap = offsetof(struct tss32, io_bitmap));
|
|
struct tss64* tss64 = (struct tss64*)(host_mem + seg_tss64.base);
|
|
NONFAILING(
|
|
struct tss64* tss = tss64;
|
|
memset(tss, 0, sizeof(*tss));
|
|
tss->rsp[0] = ADDR_STACK0;
|
|
tss->rsp[1] = ADDR_STACK0;
|
|
tss->rsp[2] = ADDR_STACK0;
|
|
tss->io_bitmap = offsetof(struct tss64, io_bitmap));
|
|
struct tss64* tss64_cpl3 = (struct tss64*)(host_mem + seg_tss64_cpl3.base);
|
|
NONFAILING(
|
|
struct tss64* tss = tss64_cpl3;
|
|
memset(tss, 0, sizeof(*tss));
|
|
tss->rsp[0] = ADDR_STACK0;
|
|
tss->rsp[1] = ADDR_STACK0;
|
|
tss->rsp[2] = ADDR_STACK0;
|
|
tss->io_bitmap = offsetof(struct tss64, io_bitmap));
|
|
|
|
if (text_size > 1000)
|
|
text_size = 1000;
|
|
if (text_prefix) {
|
|
NONFAILING(memcpy(host_text, text_prefix, text_prefix_size));
|
|
void* patch = 0;
|
|
NONFAILING(patch = memmem(host_text, text_prefix_size, "\xde\xc0\xad\x0b", 4));
|
|
if (patch)
|
|
NONFAILING(*((uint32_t*)patch) = guest_mem + ADDR_TEXT + ((char*)patch - host_text) + 6);
|
|
uint16_t magic = PREFIX_SIZE;
|
|
patch = 0;
|
|
NONFAILING(patch = memmem(host_text, text_prefix_size, &magic, sizeof(magic)));
|
|
if (patch)
|
|
NONFAILING(*((uint16_t*)patch) = guest_mem + ADDR_TEXT + text_prefix_size);
|
|
}
|
|
NONFAILING(memcpy((void*)(host_text + text_prefix_size), text, text_size));
|
|
NONFAILING(*(host_text + text_prefix_size + text_size) = 0xf4);
|
|
|
|
NONFAILING(memcpy(host_mem + ADDR_VAR_USER_CODE, text, text_size));
|
|
NONFAILING(*(host_mem + ADDR_VAR_USER_CODE + text_size) = 0xf4);
|
|
|
|
NONFAILING(*(host_mem + ADDR_VAR_HLT) = 0xf4);
|
|
NONFAILING(memcpy(host_mem + ADDR_VAR_SYSRET, "\x0f\x07\xf4", 3));
|
|
NONFAILING(memcpy(host_mem + ADDR_VAR_SYSEXIT, "\x0f\x35\xf4", 3));
|
|
|
|
NONFAILING(*(uint64_t*)(host_mem + ADDR_VAR_VMWRITE_FLD) = 0);
|
|
NONFAILING(*(uint64_t*)(host_mem + ADDR_VAR_VMWRITE_VAL) = 0);
|
|
|
|
if (opt_count > 2)
|
|
opt_count = 2;
|
|
for (i = 0; i < opt_count; i++) {
|
|
uint64_t typ = 0;
|
|
uint64_t val = 0;
|
|
NONFAILING(typ = opt_array_ptr[i].typ);
|
|
NONFAILING(val = opt_array_ptr[i].val);
|
|
switch (typ % 9) {
|
|
case 0:
|
|
sregs.cr0 ^= val & (CR0_MP | CR0_EM | CR0_ET | CR0_NE | CR0_WP | CR0_AM | CR0_NW | CR0_CD);
|
|
break;
|
|
case 1:
|
|
sregs.cr4 ^= val & (CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_MCE | CR4_PGE | CR4_PCE |
|
|
CR4_OSFXSR | CR4_OSXMMEXCPT | CR4_UMIP | CR4_VMXE | CR4_SMXE | CR4_FSGSBASE | CR4_PCIDE |
|
|
CR4_OSXSAVE | CR4_SMEP | CR4_SMAP | CR4_PKE);
|
|
break;
|
|
case 2:
|
|
sregs.efer ^= val & (EFER_SCE | EFER_NXE | EFER_SVME | EFER_LMSLE | EFER_FFXSR | EFER_TCE);
|
|
break;
|
|
case 3:
|
|
val &= ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13) | (1 << 14) |
|
|
(1 << 15) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21));
|
|
regs.rflags ^= val;
|
|
NONFAILING(tss16->flags ^= val);
|
|
NONFAILING(tss16_cpl3->flags ^= val);
|
|
NONFAILING(tss32->flags ^= val);
|
|
NONFAILING(tss32_cpl3->flags ^= val);
|
|
break;
|
|
case 4:
|
|
seg_cs16.type = val & 0xf;
|
|
seg_cs32.type = val & 0xf;
|
|
seg_cs64.type = val & 0xf;
|
|
break;
|
|
case 5:
|
|
seg_cs16_cpl3.type = val & 0xf;
|
|
seg_cs32_cpl3.type = val & 0xf;
|
|
seg_cs64_cpl3.type = val & 0xf;
|
|
break;
|
|
case 6:
|
|
seg_ds16.type = val & 0xf;
|
|
seg_ds32.type = val & 0xf;
|
|
seg_ds64.type = val & 0xf;
|
|
break;
|
|
case 7:
|
|
seg_ds16_cpl3.type = val & 0xf;
|
|
seg_ds32_cpl3.type = val & 0xf;
|
|
seg_ds64_cpl3.type = val & 0xf;
|
|
break;
|
|
case 8:
|
|
NONFAILING(*(uint64_t*)(host_mem + ADDR_VAR_VMWRITE_FLD) = (val & 0xffff));
|
|
NONFAILING(*(uint64_t*)(host_mem + ADDR_VAR_VMWRITE_VAL) = (val >> 16));
|
|
break;
|
|
default:
|
|
fail("bad kvm setup opt");
|
|
}
|
|
}
|
|
regs.rflags |= 2;
|
|
|
|
fill_segment_descriptor(gdt, ldt, &seg_ldt);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cs16);
|
|
fill_segment_descriptor(gdt, ldt, &seg_ds16);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cs16_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_ds16_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cs32);
|
|
fill_segment_descriptor(gdt, ldt, &seg_ds32);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cs32_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_ds32_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cs64);
|
|
fill_segment_descriptor(gdt, ldt, &seg_ds64);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cs64_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_ds64_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tss32);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tss32_2);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tss32_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tss32_vm86);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tss16);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tss16_2);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tss16_cpl3);
|
|
fill_segment_descriptor_dword(gdt, ldt, &seg_tss64);
|
|
fill_segment_descriptor_dword(gdt, ldt, &seg_tss64_cpl3);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cgate16);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tgate16);
|
|
fill_segment_descriptor(gdt, ldt, &seg_cgate32);
|
|
fill_segment_descriptor(gdt, ldt, &seg_tgate32);
|
|
fill_segment_descriptor_dword(gdt, ldt, &seg_cgate64);
|
|
|
|
if (ioctl(cpufd, KVM_SET_SREGS, &sregs))
|
|
return -1;
|
|
if (ioctl(cpufd, KVM_SET_REGS, ®s))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
#elif defined(__aarch64__)
|
|
|
|
|
|
|
|
struct kvm_text {
|
|
uintptr_t typ;
|
|
const void* text;
|
|
uintptr_t size;
|
|
};
|
|
|
|
struct kvm_opt {
|
|
uint64_t typ;
|
|
uint64_t val;
|
|
};
|
|
|
|
static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7)
|
|
{
|
|
const int vmfd = a0;
|
|
const int cpufd = a1;
|
|
char* const host_mem = (char*)a2;
|
|
const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3;
|
|
const uintptr_t text_count = a4;
|
|
const uintptr_t flags = a5;
|
|
const struct kvm_opt* const opt_array_ptr = (struct kvm_opt*)a6;
|
|
uintptr_t opt_count = a7;
|
|
|
|
(void)flags;
|
|
(void)opt_count;
|
|
|
|
const uintptr_t page_size = 4 << 10;
|
|
const uintptr_t guest_mem = 0;
|
|
const uintptr_t guest_mem_size = 24 * page_size;
|
|
|
|
(void)text_count;
|
|
int text_type = 0;
|
|
const void* text = 0;
|
|
int text_size = 0;
|
|
NONFAILING(text_type = text_array_ptr[0].typ);
|
|
NONFAILING(text = text_array_ptr[0].text);
|
|
NONFAILING(text_size = text_array_ptr[0].size);
|
|
(void)text_type;
|
|
(void)opt_array_ptr;
|
|
|
|
uint32_t features = 0;
|
|
if (opt_count > 1)
|
|
opt_count = 1;
|
|
uintptr_t i;
|
|
for (i = 0; i < opt_count; i++) {
|
|
uint64_t typ = 0;
|
|
uint64_t val = 0;
|
|
NONFAILING(typ = opt_array_ptr[i].typ);
|
|
NONFAILING(val = opt_array_ptr[i].val);
|
|
switch (typ) {
|
|
case 1:
|
|
features = val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < guest_mem_size / page_size; i++) {
|
|
struct kvm_userspace_memory_region memreg;
|
|
memreg.slot = i;
|
|
memreg.flags = 0;
|
|
memreg.guest_phys_addr = guest_mem + i * page_size;
|
|
memreg.memory_size = page_size;
|
|
memreg.userspace_addr = (uintptr_t)host_mem + i * page_size;
|
|
ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
|
|
}
|
|
|
|
struct kvm_vcpu_init init;
|
|
ioctl(cpufd, KVM_ARM_PREFERRED_TARGET, &init);
|
|
init.features[0] = features;
|
|
ioctl(cpufd, KVM_ARM_VCPU_INIT, &init);
|
|
|
|
if (text_size > 1000)
|
|
text_size = 1000;
|
|
NONFAILING(memcpy(host_mem, text, text_size));
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8)
|
|
{
|
|
switch (nr) {
|
|
default:
|
|
return syscall(nr, a0, a1, a2, a3, a4, a5);
|
|
#ifdef __NR_syz_test
|
|
case __NR_syz_test:
|
|
return 0;
|
|
#endif
|
|
#ifdef __NR_syz_open_dev
|
|
case __NR_syz_open_dev:
|
|
return syz_open_dev(a0, a1, a2);
|
|
#endif
|
|
#ifdef __NR_syz_open_pts
|
|
case __NR_syz_open_pts:
|
|
return syz_open_pts(a0, a1);
|
|
#endif
|
|
#ifdef __NR_syz_fuse_mount
|
|
case __NR_syz_fuse_mount:
|
|
return syz_fuse_mount(a0, a1, a2, a3, a4, a5);
|
|
#endif
|
|
#ifdef __NR_syz_fuseblk_mount
|
|
case __NR_syz_fuseblk_mount:
|
|
return syz_fuseblk_mount(a0, a1, a2, a3, a4, a5, a6, a7);
|
|
#endif
|
|
#ifdef __NR_syz_emit_ethernet
|
|
case __NR_syz_emit_ethernet:
|
|
return syz_emit_ethernet(a0, a1);
|
|
#endif
|
|
#ifdef __NR_syz_kvm_setup_cpu
|
|
case __NR_syz_kvm_setup_cpu:
|
|
return syz_kvm_setup_cpu(a0, a1, a2, a3, a4, a5, a6, a7);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void setup_main_process()
|
|
{
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_handler = SIG_IGN;
|
|
syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8);
|
|
syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8);
|
|
install_segv_handler();
|
|
|
|
char tmpdir_template[] = "./syzkaller.XXXXXX";
|
|
char* tmpdir = mkdtemp(tmpdir_template);
|
|
if (!tmpdir)
|
|
fail("failed to mkdtemp");
|
|
if (chmod(tmpdir, 0777))
|
|
fail("failed to chmod");
|
|
if (chdir(tmpdir))
|
|
fail("failed to chdir");
|
|
}
|
|
|
|
static void loop();
|
|
|
|
static void sandbox_common()
|
|
{
|
|
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
|
|
setpgrp();
|
|
setsid();
|
|
|
|
struct rlimit rlim;
|
|
rlim.rlim_cur = rlim.rlim_max = 128 << 20;
|
|
setrlimit(RLIMIT_AS, &rlim);
|
|
rlim.rlim_cur = rlim.rlim_max = 1 << 20;
|
|
setrlimit(RLIMIT_FSIZE, &rlim);
|
|
rlim.rlim_cur = rlim.rlim_max = 1 << 20;
|
|
setrlimit(RLIMIT_STACK, &rlim);
|
|
rlim.rlim_cur = rlim.rlim_max = 0;
|
|
setrlimit(RLIMIT_CORE, &rlim);
|
|
|
|
unshare(CLONE_NEWNS);
|
|
unshare(CLONE_NEWIPC);
|
|
unshare(CLONE_IO);
|
|
}
|
|
|
|
#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE)
|
|
static int do_sandbox_none(int executor_pid, bool enable_tun)
|
|
{
|
|
int pid = fork();
|
|
if (pid)
|
|
return pid;
|
|
|
|
sandbox_common();
|
|
#ifdef __NR_syz_emit_ethernet
|
|
setup_tun(executor_pid, enable_tun);
|
|
#endif
|
|
|
|
loop();
|
|
doexit(1);
|
|
}
|
|
#endif
|
|
|
|
#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_SETUID)
|
|
static int do_sandbox_setuid(int executor_pid, bool enable_tun)
|
|
{
|
|
int pid = fork();
|
|
if (pid)
|
|
return pid;
|
|
|
|
sandbox_common();
|
|
#ifdef __NR_syz_emit_ethernet
|
|
setup_tun(executor_pid, enable_tun);
|
|
#endif
|
|
|
|
const int nobody = 65534;
|
|
if (setgroups(0, NULL))
|
|
fail("failed to setgroups");
|
|
if (syscall(SYS_setresgid, nobody, nobody, nobody))
|
|
fail("failed to setresgid");
|
|
if (syscall(SYS_setresuid, nobody, nobody, nobody))
|
|
fail("failed to setresuid");
|
|
|
|
loop();
|
|
doexit(1);
|
|
}
|
|
#endif
|
|
|
|
#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE)
|
|
static int real_uid;
|
|
static int real_gid;
|
|
static int epid;
|
|
static bool etun;
|
|
__attribute__((aligned(64 << 10))) static char sandbox_stack[1 << 20];
|
|
|
|
static bool write_file(const char* file, const char* what, ...)
|
|
{
|
|
char buf[1024];
|
|
va_list args;
|
|
va_start(args, what);
|
|
vsnprintf(buf, sizeof(buf), what, args);
|
|
va_end(args);
|
|
buf[sizeof(buf) - 1] = 0;
|
|
int len = strlen(buf);
|
|
|
|
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
|
if (fd == -1)
|
|
return false;
|
|
if (write(fd, buf, len) != len) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
close(fd);
|
|
return true;
|
|
}
|
|
|
|
static int namespace_sandbox_proc(void* arg)
|
|
{
|
|
sandbox_common();
|
|
|
|
write_file("/proc/self/setgroups", "deny");
|
|
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid))
|
|
fail("write of /proc/self/uid_map failed");
|
|
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid))
|
|
fail("write of /proc/self/gid_map failed");
|
|
|
|
#ifdef __NR_syz_emit_ethernet
|
|
setup_tun(epid, etun);
|
|
#endif
|
|
|
|
if (mkdir("./syz-tmp", 0777))
|
|
fail("mkdir(syz-tmp) failed");
|
|
if (mount("", "./syz-tmp", "tmpfs", 0, NULL))
|
|
fail("mount(tmpfs) failed");
|
|
if (mkdir("./syz-tmp/newroot", 0777))
|
|
fail("mkdir failed");
|
|
if (mkdir("./syz-tmp/newroot/dev", 0700))
|
|
fail("mkdir failed");
|
|
if (mount("/dev", "./syz-tmp/newroot/dev", NULL, MS_BIND | MS_REC | MS_PRIVATE, NULL))
|
|
fail("mount(dev) failed");
|
|
if (mkdir("./syz-tmp/pivot", 0777))
|
|
fail("mkdir failed");
|
|
if (syscall(SYS_pivot_root, "./syz-tmp", "./syz-tmp/pivot")) {
|
|
debug("pivot_root failed");
|
|
if (chdir("./syz-tmp"))
|
|
fail("chdir failed");
|
|
} else {
|
|
if (chdir("/"))
|
|
fail("chdir failed");
|
|
if (umount2("./pivot", MNT_DETACH))
|
|
fail("umount failed");
|
|
}
|
|
if (chroot("./newroot"))
|
|
fail("chroot failed");
|
|
if (chdir("/"))
|
|
fail("chdir failed");
|
|
|
|
struct __user_cap_header_struct cap_hdr = {};
|
|
struct __user_cap_data_struct cap_data[2] = {};
|
|
cap_hdr.version = _LINUX_CAPABILITY_VERSION_3;
|
|
cap_hdr.pid = getpid();
|
|
if (syscall(SYS_capget, &cap_hdr, &cap_data))
|
|
fail("capget failed");
|
|
cap_data[0].effective &= ~(1 << CAP_SYS_PTRACE);
|
|
cap_data[0].permitted &= ~(1 << CAP_SYS_PTRACE);
|
|
cap_data[0].inheritable &= ~(1 << CAP_SYS_PTRACE);
|
|
if (syscall(SYS_capset, &cap_hdr, &cap_data))
|
|
fail("capset failed");
|
|
|
|
loop();
|
|
doexit(1);
|
|
}
|
|
|
|
static int do_sandbox_namespace(int executor_pid, bool enable_tun)
|
|
{
|
|
real_uid = getuid();
|
|
real_gid = getgid();
|
|
epid = executor_pid;
|
|
etun = enable_tun;
|
|
mprotect(sandbox_stack, 4096, PROT_NONE);
|
|
return clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64],
|
|
CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, NULL);
|
|
}
|
|
#endif
|
|
|
|
#if defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT)
|
|
static void remove_dir(const char* dir)
|
|
{
|
|
DIR* dp;
|
|
struct dirent* ep;
|
|
int iter = 0;
|
|
retry:
|
|
dp = opendir(dir);
|
|
if (dp == NULL) {
|
|
if (errno == EMFILE) {
|
|
exitf("opendir(%s) failed due to NOFILE, exiting");
|
|
}
|
|
exitf("opendir(%s) failed", dir);
|
|
}
|
|
while ((ep = readdir(dp))) {
|
|
if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
|
|
continue;
|
|
char filename[FILENAME_MAX];
|
|
snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name);
|
|
struct stat st;
|
|
if (lstat(filename, &st))
|
|
exitf("lstat(%s) failed", filename);
|
|
if (S_ISDIR(st.st_mode)) {
|
|
remove_dir(filename);
|
|
continue;
|
|
}
|
|
int i;
|
|
for (i = 0;; i++) {
|
|
debug("unlink(%s)\n", filename);
|
|
if (unlink(filename) == 0)
|
|
break;
|
|
if (errno == EROFS) {
|
|
debug("ignoring EROFS\n");
|
|
break;
|
|
}
|
|
if (errno != EBUSY || i > 100)
|
|
exitf("unlink(%s) failed", filename);
|
|
debug("umount(%s)\n", filename);
|
|
if (umount2(filename, MNT_DETACH))
|
|
exitf("umount(%s) failed", filename);
|
|
}
|
|
}
|
|
closedir(dp);
|
|
int i;
|
|
for (i = 0;; i++) {
|
|
debug("rmdir(%s)\n", dir);
|
|
if (rmdir(dir) == 0)
|
|
break;
|
|
if (i < 100) {
|
|
if (errno == EROFS) {
|
|
debug("ignoring EROFS\n");
|
|
break;
|
|
}
|
|
if (errno == EBUSY) {
|
|
debug("umount(%s)\n", dir);
|
|
if (umount2(dir, MNT_DETACH))
|
|
exitf("umount(%s) failed", dir);
|
|
continue;
|
|
}
|
|
if (errno == ENOTEMPTY) {
|
|
if (iter < 100) {
|
|
iter++;
|
|
goto retry;
|
|
}
|
|
}
|
|
}
|
|
exitf("rmdir(%s) failed", dir);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(SYZ_EXECUTOR) || defined(SYZ_REPEAT)
|
|
static uint64_t current_time_ms()
|
|
{
|
|
struct timespec ts;
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts))
|
|
fail("clock_gettime failed");
|
|
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
|
|
}
|
|
#endif
|
|
|
|
#if defined(SYZ_REPEAT)
|
|
static void test();
|
|
|
|
void loop()
|
|
{
|
|
int iter;
|
|
for (iter = 0;; iter++) {
|
|
char cwdbuf[256];
|
|
sprintf(cwdbuf, "./%d", iter);
|
|
if (mkdir(cwdbuf, 0777))
|
|
fail("failed to mkdir");
|
|
int pid = fork();
|
|
if (pid < 0)
|
|
fail("clone failed");
|
|
if (pid == 0) {
|
|
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
|
|
setpgrp();
|
|
if (chdir(cwdbuf))
|
|
fail("failed to chdir");
|
|
test();
|
|
doexit(0);
|
|
}
|
|
int status = 0;
|
|
uint64_t start = current_time_ms();
|
|
for (;;) {
|
|
int res = waitpid(-1, &status, __WALL | WNOHANG);
|
|
if (res == pid)
|
|
break;
|
|
usleep(1000);
|
|
if (current_time_ms() - start > 5 * 1000) {
|
|
kill(-pid, SIGKILL);
|
|
kill(pid, SIGKILL);
|
|
while (waitpid(-1, &status, __WALL) != pid) {
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
remove_dir(cwdbuf);
|
|
}
|
|
}
|
|
#endif
|
|
`
|