diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,7 @@ static unsigned pr_allow_flag; static struct mtx vmmdev_mtx; +static unsigned vmmdev_prison_slot; static MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); @@ -99,6 +101,7 @@ static int vmm_priv_check(struct ucred *ucred); static int devmem_create_cdev(const char *vmname, int id, char *devmem); static void devmem_destroy(void *arg); +static int vmm_destroy(struct vmmdev_softc *sc); static int vmm_priv_check(struct ucred *ucred) @@ -175,9 +178,7 @@ struct vmmdev_softc *sc; struct vmmdev_softc *found = NULL; -#ifdef notyet /* XXX kernel is not compiled with invariants */ mtx_assert(&vmmdev_mtx, MA_OWNED); -#endif SLIST_FOREACH(sc, &head, link) { if (strcmp(name, vm_name(sc->vm)) == 0) { @@ -997,31 +998,15 @@ } static int -sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) +vmm_destroy(struct vmmdev_softc *sc) { struct devmem_softc *dsc; - struct vmmdev_softc *sc; struct cdev *cdev; - char *buf; - int error, buflen; - - error = vmm_priv_check(req->td->td_ucred); - if (error) - return (error); - buflen = VM_MAX_NAMELEN + 1; - buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); - strlcpy(buf, "beavis", buflen); - error = sysctl_handle_string(oidp, buf, buflen, req); - if (error != 0 || req->newptr == NULL) - goto out; + mtx_assert(&vmmdev_mtx, MA_OWNED); - mtx_lock(&vmmdev_mtx); - sc = vmmdev_lookup(buf); if (sc == NULL || sc->cdev == NULL) { - mtx_unlock(&vmmdev_mtx); - error = EINVAL; - goto out; + return (EINVAL); } /* @@ -1033,7 +1018,6 @@ */ cdev = sc->cdev; sc->cdev = NULL; - mtx_unlock(&vmmdev_mtx); /* * Schedule all cdevs to be destroyed: @@ -1051,8 +1035,35 @@ destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); } destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); - error = 0; + return (0); +} + +static int +sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) +{ + struct vmmdev_softc *sc; + char *buf; + int error; + size_t buflen; + + error = vmm_priv_check(req->td->td_ucred); + if (error) + return (error); + + buflen = VM_MAX_NAMELEN + 1; + buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); + strlcpy(buf, "beavis", buflen); + error = sysctl_handle_string(oidp, buf, buflen, req); + if (error != 0 || req->newptr == NULL) + goto out; + + mtx_lock(&vmmdev_mtx); + + sc = vmmdev_lookup(buf); + error = vmm_destroy(sc); + + mtx_unlock(&vmmdev_mtx); out: free(buf, M_VMMDEV); return (error); @@ -1078,7 +1089,8 @@ struct cdev *cdev; struct vmmdev_softc *sc, *sc2; char *buf; - int error, buflen; + int error; + size_t buflen; error = vmm_priv_check(req->td->td_ucred); if (error) @@ -1147,12 +1159,56 @@ NULL, 0, sysctl_vmm_create, "A", NULL); +static void +vmmdev_prison_cleanup(struct prison *pr) +{ + struct vmmdev_softc *sc; + struct vmmdev_softc *tsc; + + mtx_lock(&vmmdev_mtx); + + SLIST_FOREACH_SAFE(sc, &head, link, tsc) { + if (sc->ucred->cr_prison == pr) + vmm_destroy(sc); + } + + mtx_unlock(&vmmdev_mtx); +} + +static int +vmmdev_prison_remove(void *obj, void *data __unused) +{ + struct prison *pr = obj; + + vmmdev_prison_cleanup(pr); + return (0); +} + +static int +vmmdev_prison_set(void *obj, void *data) +{ + struct prison *pr = obj; + struct vfsoptlist *opts = data; + + if (vfs_flagopt(opts, "allow.novmm", NULL, 0)) + vmmdev_prison_cleanup(pr); + + return (0); +} + void vmmdev_init(void) { + osd_method_t methods[PR_MAXMETHOD] = { + [PR_METHOD_SET] = vmmdev_prison_set, + [PR_METHOD_REMOVE] = vmmdev_prison_remove, + }; + mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); pr_allow_flag = prison_add_allow(NULL, "vmm", NULL, "Allow use of vmm in a jail."); + + vmmdev_prison_slot = osd_jail_register(NULL, methods); } int @@ -1160,10 +1216,12 @@ { int error; - if (SLIST_EMPTY(&head)) + if (SLIST_EMPTY(&head)) { + osd_jail_deregister(vmmdev_prison_slot); error = 0; - else + } else { error = EBUSY; + } return (error); } diff --git a/tests/sys/vmm/Makefile b/tests/sys/vmm/Makefile --- a/tests/sys/vmm/Makefile +++ b/tests/sys/vmm/Makefile @@ -4,7 +4,7 @@ BINDIR= ${TESTSDIR} -ATF_TESTS_SH+= vmm_cred_jail +ATF_TESTS_SH+= vmm_jail ${PACKAGE}FILES+= utils.subr diff --git a/tests/sys/vmm/vmm_cred_jail.sh b/tests/sys/vmm/vmm_jail.sh rename from tests/sys/vmm/vmm_cred_jail.sh rename to tests/sys/vmm/vmm_jail.sh --- a/tests/sys/vmm/vmm_cred_jail.sh +++ b/tests/sys/vmm/vmm_jail.sh @@ -73,8 +73,54 @@ vmm_cleanup } +atf_test_case vmm_remove_jail cleanup +vmm_remove_jail_head() +{ + atf_set "descr" "Tests deleting a jail that has VM objects" + atf_set "require.user" "root" +} +vmm_remove_jail_body() +{ + if ! kldstat -qn vmm; then + atf_skip "vmm is not loaded" + fi + jail -c name=myjail allow.vmm persist + jexec myjail bhyvectl --vm=testvm --create + jail -r myjail + atf_check -s exit:1 -e ignore ls /dev/vmm +} +vmm_remove_jail_cleanup() +{ + bhyvectl --vm=testvm --destroy + vmm_cleanup +} + +atf_test_case vmm_unenable_jail cleanup +vmm_unenable_jail_head() +{ + atf_set "descr" "Tests allow.vmm=false on a jail that has VM objects" + atf_set "require.user" "root" +} +vmm_unenable_jail_body() +{ + if ! kldstat -qn vmm; then + atf_skip "vmm is not loaded" + fi + vmm_mkjail myjail + jexec myjail bhyvectl --vm=testvm --create + jail -m name=myjail allow.vmm=false + atf_check -s exit:1 -e ignore ls /dev/vmm +} +vmm_unenable_jail_cleanup() +{ + bhyvectl --vm=testvm --destroy + vmm_cleanup +} + atf_init_test_cases() { atf_add_test_case vmm_cred_jail_host atf_add_test_case vmm_cred_jail_other + atf_add_test_case vmm_remove_jail + atf_add_test_case vmm_unenable_jail }