DF-2835 / fix.diff
diff --git a/sys/sys/vkernel.h b/sys/sys/vkernel.h --- a/sys/sys/vkernel.h +++ b/sys/sys/vkernel.h +++ b/sys/sys/vkernel.h @@ -88,6 +88,7 @@ struct lwkt_token token; int refs; register_t vkernel_cr3; + int nvmspaces; /* live vmspace entries (DF-2834) */ }; struct vmspace_entry { diff --git a/sys/vm/vm_vmspace.c b/sys/vm/vm_vmspace.c --- a/sys/vm/vm_vmspace.c +++ b/sys/vm/vm_vmspace.c @@ -65,6 +65,17 @@ static MALLOC_DEFINE(M_VKERNEL, "vkernel", "VKernel structures"); /* + * Safety cap on live vmspaces per vkernel process (DF-2834). Each + * vmspace pins a wired PML4 page + KVA + slab until destroyed, so an + * uncapped vmspace_create() loop is an unprivileged kernel-memory + * exhaustion primitive. Mirrors the vm.max_proc_mmap pattern. + */ +static int max_vkernel_vmspaces = 64; +SYSCTL_INT(_vm, OID_AUTO, max_vkernel_vmspaces, CTLFLAG_RW, + &max_vkernel_vmspaces, 0, + "Maximum number of vmspaces per vkernel process"); + +/* * Cache helper, enter ve into per-lwp cache (avoid vkp->token use) */ static @@ -155,6 +166,10 @@ if (vkernel_enable == 0) return (EOPNOTSUPP); + /* Enforce the documented ABI (DF-2835), see comment above */ + if (uap->id == NULL || uap->type != 0 || uap->data != NULL) + return (EINVAL); + /* * Create a virtual kernel side-structure for the process if one * does not exist. @@ -187,6 +202,13 @@ pmap_pinit2(vmspace_pmap(ve->vmspace)); lwkt_gettoken(&vkp->token); + if (vkp->nvmspaces >= max_vkernel_vmspaces) { + lwkt_reltoken(&vkp->token); + vmspace_rel(ve->vmspace); + ve->vmspace = NULL; /* safety */ + kfree(ve, M_VKERNEL); + return (ETOOMANYREFS); + } if (RB_INSERT(vmspace_rb_tree, &vkp->root, ve)) { vmspace_rel(ve->vmspace); ve->vmspace = NULL; /* safety */ @@ -194,6 +216,7 @@ error = EEXIST; } else { error = 0; + ++vkp->nvmspaces; } lwkt_reltoken(&vkp->token); @@ -641,6 +664,7 @@ return EBUSY; } RB_REMOVE(vmspace_rb_tree, &vkp->root, ve); + --vkp->nvmspaces; /* * Shouldn't be any other references so we can drop the token |