summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreyk <reyk@openbsd.org>2018-10-15 10:35:41 +0000
committerreyk <reyk@openbsd.org>2018-10-15 10:35:41 +0000
commit75cf143ab306db172df3f037074fa6bca7aca85c (patch)
tree495d476dd936f64bb43add35bc3002b6e9dc7849
parentOmit HSTS headers over unencrypted connections, per RFC 6797. (diff)
downloadwireguard-openbsd-75cf143ab306db172df3f037074fa6bca7aca85c.tar.xz
wireguard-openbsd-75cf143ab306db172df3f037074fa6bca7aca85c.zip
Prevent VM reboot loops by rate-limiting the interval a VM can reboot.
This looping has been experienced by people who run VMs with a broken kernel or boot loader that trigger a very fast reboot loop (triple fault) of a VM that ends up using a lot of CPU and resources on the host. Some fixes in vmm(4) and vmd(8) helped to avoid such conditions but it can still occur if something is wrong in the guest VM itself. If the VM restarts after less than VM_START_RATE_SEC (6) seconds, we increment the limit counter. After VM_START_RATE_LIMIT (3) of suchs fast reboots the VM is stopped. There are only very few people who intentionally want to reboot-loop a VM very quickly (many times within a second); mostly for fuzzing. They will have to recompile and adjust the stated #defines in the code as we don't have a config option to disable it. OK mlarkin@
-rw-r--r--usr.sbin/vmd/config.c36
-rw-r--r--usr.sbin/vmd/vmd.c13
-rw-r--r--usr.sbin/vmd/vmd.h11
3 files changed, 57 insertions, 3 deletions
diff --git a/usr.sbin/vmd/config.c b/usr.sbin/vmd/config.c
index eb1aa3a91df..373e027b425 100644
--- a/usr.sbin/vmd/config.c
+++ b/usr.sbin/vmd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.51 2018/10/08 16:32:01 reyk Exp $ */
+/* $OpenBSD: config.c,v 1.52 2018/10/15 10:35:41 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -193,6 +193,7 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
char base[PATH_MAX];
char expanded[PATH_MAX];
unsigned int unit;
+ struct timeval tv, rate, since_last;
errno = 0;
@@ -211,6 +212,39 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
}
}
+ /*
+ * Rate-limit the VM so that it cannot restart in a loop:
+ * if the VM restarts after less than VM_START_RATE_SEC seconds,
+ * we increment the limit counter. After VM_START_RATE_LIMIT
+ * of suchs fast reboots the VM is stopped.
+ */
+ getmonotime(&tv);
+ if (vm->vm_start_tv.tv_sec) {
+ timersub(&tv, &vm->vm_start_tv, &since_last);
+
+ rate.tv_sec = VM_START_RATE_SEC;
+ rate.tv_usec = 0;
+ if (timercmp(&since_last, &rate, <))
+ vm->vm_start_limit++;
+ else {
+ /* Reset counter */
+ vm->vm_start_limit = 0;
+ }
+
+ log_debug("%s: vm %u restarted after %lld.%ld seconds,"
+ " limit %d/%d", __func__, vcp->vcp_id, since_last.tv_sec,
+ since_last.tv_usec, vm->vm_start_limit,
+ VM_START_RATE_LIMIT);
+
+ if (vm->vm_start_limit >= VM_START_RATE_LIMIT) {
+ log_warnx("%s: vm %u restarted too quickly",
+ __func__, vcp->vcp_id);
+ errno = EPERM;
+ goto fail;
+ }
+ }
+ vm->vm_start_tv = tv;
+
for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++)
for (j = 0; j < VM_MAX_BASE_PER_DISK; j++)
diskfds[i][j] = -1;
diff --git a/usr.sbin/vmd/vmd.c b/usr.sbin/vmd/vmd.c
index 057b67770c9..8053b02620f 100644
--- a/usr.sbin/vmd/vmd.c
+++ b/usr.sbin/vmd/vmd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmd.c,v 1.103 2018/10/08 16:32:01 reyk Exp $ */
+/* $OpenBSD: vmd.c,v 1.104 2018/10/15 10:35:41 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -1921,3 +1921,14 @@ prefixlen2mask(uint8_t prefixlen)
return (htonl(0xffffffff << (32 - prefixlen)));
}
+
+void
+getmonotime(struct timeval *tv)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ fatal("clock_gettime");
+
+ TIMESPEC_TO_TIMEVAL(tv, &ts);
+}
diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h
index 391b042f2f8..4d7b0380294 100644
--- a/usr.sbin/vmd/vmd.h
+++ b/usr.sbin/vmd/vmd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmd.h,v 1.82 2018/10/08 16:32:01 reyk Exp $ */
+/* $OpenBSD: vmd.h,v 1.83 2018/10/15 10:35:41 reyk Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -55,6 +55,10 @@
#define VMD_SWITCH_TYPE "bridge"
#define VM_DEFAULT_MEMORY 512
+/* Rate-limit fast reboots */
+#define VM_START_RATE_SEC 6 /* min. seconds since last reboot */
+#define VM_START_RATE_LIMIT 3 /* max. number of fast reboots */
+
/* default user instance limits */
#define VM_DEFAULT_USER_MAXCPU 4
#define VM_DEFAULT_USER_MAXMEM 2048
@@ -261,6 +265,10 @@ struct vmd_vm {
int vm_receive_fd;
struct vmd_user *vm_user;
+ /* For rate-limiting */
+ struct timeval vm_start_tv;
+ int vm_start_limit;
+
TAILQ_ENTRY(vmd_vm) vm_entry;
};
TAILQ_HEAD(vmlist, vmd_vm);
@@ -364,6 +372,7 @@ void user_inc(struct vm_create_params *, struct vmd_user *, int);
int user_checklimit(struct vmd_user *, struct vm_create_params *);
char *get_string(uint8_t *, size_t);
uint32_t prefixlen2mask(uint8_t);
+void getmonotime(struct timeval *);
/* priv.c */
void priv(struct privsep *, struct privsep_proc *);