Changeset View
Standalone View
sys/amd64/vmm/vmm_dev.c
Show All 27 Lines | |||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/jail.h> | |||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/libkern.h> | #include <sys/libkern.h> | ||||
#include <sys/ioccom.h> | #include <sys/ioccom.h> | ||||
#include <sys/mman.h> | #include <sys/mman.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/proc.h> | |||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_map.h> | #include <vm/vm_map.h> | ||||
#include <vm/vm_object.h> | #include <vm/vm_object.h> | ||||
#include <machine/vmparam.h> | #include <machine/vmparam.h> | ||||
#include <machine/vmm.h> | #include <machine/vmm.h> | ||||
Show All 29 Lines | |||||
static SLIST_HEAD(, vmmdev_softc) head; | static SLIST_HEAD(, vmmdev_softc) head; | ||||
static struct mtx vmmdev_mtx; | static struct mtx vmmdev_mtx; | ||||
static MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); | static MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); | ||||
SYSCTL_DECL(_hw_vmm); | SYSCTL_DECL(_hw_vmm); | ||||
static int vmm_priv_check(struct ucred *cred); | |||||
static int devmem_create_cdev(const char *vmname, int id, char *devmem); | static int devmem_create_cdev(const char *vmname, int id, char *devmem); | ||||
static void devmem_destroy(void *arg); | static void devmem_destroy(void *arg); | ||||
static int | static int | ||||
vmm_priv_check(struct ucred *ucred) | |||||
jamie: Instead of adding a new function, you should call priv_check_cred(9), which would involve… | |||||
Done Inline ActionsI thought about doing that. I passed on it due to limited scope. But since you brought it up, that probably means it's a good idea. :) I'll go ahead and do that and will update the patch once it's completed. I hope to have a new revision submitted by the end of next week. My interns at work are going to keep me _really_ busy the next few days. lattera-gmail.com: I thought about doing that. I passed on it due to limited scope. But since you brought it up… | |||||
Done Inline ActionsHaving established myself as someone to listen to, I will now completely reverse myself ;-). I neglected to consider the non-universal nature of this code. So it is indeed best to keep the checks entirely in this code. jamie: Having established myself as someone to listen to, I will now completely reverse myself ;-). I… | |||||
Done Inline ActionsHaha. Sounds good. I'll mark these comments as "done", then. lattera-gmail.com: Haha. Sounds good. I'll mark these comments as "done", then. | |||||
{ | |||||
struct prison *prison; | |||||
if (jailed(ucred)) { | |||||
prison = ucred->cr_prison; | |||||
if ((prison->pr_allow & PR_ALLOW_VMM) != PR_ALLOW_VMM) | |||||
return (EPERM); | |||||
} | |||||
return (0); | |||||
} | |||||
Done Inline ActionsThis check isn't necessary, if the one below is just "!(prison->pr_allow & pr_allow_flag)". jamie: This check isn't necessary, if the one below is just "!(prison->pr_allow & pr_allow_flag)". | |||||
Done Inline ActionsIt is to fail closed, like the comment above says. prison_add_allow can fail (and will if enough PR_ALLOW_* flags are added). This check future-proofs the code. Otherwise, the check below #include <stdio.h> int main(int argc, char *argv[]) { unsigned int val = 0xffffffff; unsigned int c1 = 0; printf("%d\n", val & c1); printf("%d\n", !(val & c1)); return (0); } Results in: 0 1 Your proposed solution would fail open. lattera-gmail.com: It is to fail closed, like the comment above says. prison_add_allow can fail (and will if… | |||||
Done Inline ActionsToo tired... I just realized the above statement was incorrect. Your proposed change works, too. I'll make it. lattera-gmail.com: Too tired... I just realized the above statement was incorrect. Your proposed change works, too. | |||||
static int | |||||
vcpu_lock_one(struct vmmdev_softc *sc, int vcpu) | vcpu_lock_one(struct vmmdev_softc *sc, int vcpu) | ||||
{ | { | ||||
int error; | int error; | ||||
if (vcpu < 0 || vcpu >= VM_MAXCPU) | if (vcpu < 0 || vcpu >= VM_MAXCPU) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); | error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) | vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) | ||||
{ | { | ||||
int error, off, c, prot; | int error, off, c, prot; | ||||
vm_paddr_t gpa, maxaddr; | vm_paddr_t gpa, maxaddr; | ||||
void *hpa, *cookie; | void *hpa, *cookie; | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
error = vmm_priv_check(curthread->td_ucred); | |||||
if (error) | |||||
return (error); | |||||
sc = vmmdev_lookup2(cdev); | sc = vmmdev_lookup2(cdev); | ||||
if (sc == NULL) | if (sc == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* | /* | ||||
* Get a read lock on the guest memory map by freezing any vcpu. | * Get a read lock on the guest memory map by freezing any vcpu. | ||||
*/ | */ | ||||
error = vcpu_lock_one(sc, VM_MAXCPU - 1); | error = vcpu_lock_one(sc, VM_MAXCPU - 1); | ||||
▲ Show 20 Lines • Show All 158 Lines • ▼ Show 20 Lines | vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, | ||||
struct vm_intinfo *vmii; | struct vm_intinfo *vmii; | ||||
struct vm_rtc_time *rtctime; | struct vm_rtc_time *rtctime; | ||||
struct vm_rtc_data *rtcdata; | struct vm_rtc_data *rtcdata; | ||||
struct vm_memmap *mm; | struct vm_memmap *mm; | ||||
struct vm_cpu_topology *topology; | struct vm_cpu_topology *topology; | ||||
uint64_t *regvals; | uint64_t *regvals; | ||||
int *regnums; | int *regnums; | ||||
error = vmm_priv_check(curthread->td_ucred); | |||||
if (error) | |||||
return (error); | |||||
sc = vmmdev_lookup2(cdev); | sc = vmmdev_lookup2(cdev); | ||||
if (sc == NULL) | if (sc == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
error = 0; | |||||
vcpu = -1; | vcpu = -1; | ||||
state_changed = 0; | state_changed = 0; | ||||
/* | /* | ||||
* Some VMM ioctls can operate only on vcpus that are not running. | * Some VMM ioctls can operate only on vcpus that are not running. | ||||
*/ | */ | ||||
switch (cmd) { | switch (cmd) { | ||||
case VM_RUN: | case VM_RUN: | ||||
▲ Show 20 Lines • Show All 405 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
vm_paddr_t gpa; | vm_paddr_t gpa; | ||||
size_t len; | size_t len; | ||||
vm_ooffset_t segoff, first, last; | vm_ooffset_t segoff, first, last; | ||||
int error, found, segid; | int error, found, segid; | ||||
bool sysmem; | bool sysmem; | ||||
error = vmm_priv_check(curthread->td_ucred); | |||||
if (error) | |||||
return (error); | |||||
first = *offset; | first = *offset; | ||||
last = first + mapsize; | last = first + mapsize; | ||||
if ((nprot & PROT_EXEC) || first < 0 || first >= last) | if ((nprot & PROT_EXEC) || first < 0 || first >= last) | ||||
return (EINVAL); | return (EINVAL); | ||||
sc = vmmdev_lookup2(cdev); | sc = vmmdev_lookup2(cdev); | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
/* virtual machine is in the process of being created */ | /* virtual machine is in the process of being created */ | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) | sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error; | int error; | ||||
char buf[VM_MAX_NAMELEN]; | char buf[VM_MAX_NAMELEN]; | ||||
struct devmem_softc *dsc; | struct devmem_softc *dsc; | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
struct cdev *cdev; | struct cdev *cdev; | ||||
error = vmm_priv_check(req->td->td_ucred); | |||||
if (error) | |||||
return (error); | |||||
strlcpy(buf, "beavis", sizeof(buf)); | strlcpy(buf, "beavis", sizeof(buf)); | ||||
error = sysctl_handle_string(oidp, buf, sizeof(buf), req); | error = sysctl_handle_string(oidp, buf, sizeof(buf), req); | ||||
if (error != 0 || req->newptr == NULL) | if (error != 0 || req->newptr == NULL) | ||||
return (error); | return (error); | ||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||
sc = vmmdev_lookup(buf); | sc = vmmdev_lookup(buf); | ||||
if (sc == NULL || sc->cdev == NULL) { | if (sc == NULL || sc->cdev == NULL) { | ||||
Show All 25 Lines | sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) | ||||
*/ | */ | ||||
SLIST_FOREACH(dsc, &sc->devmem, link) { | SLIST_FOREACH(dsc, &sc->devmem, link) { | ||||
KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); | KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); | ||||
destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); | destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); | ||||
} | } | ||||
destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); | destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); | ||||
return (0); | return (0); | ||||
} | } | ||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, CTLTYPE_STRING | CTLFLAG_RW, | SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, | ||||
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON, | |||||
NULL, 0, sysctl_vmm_destroy, "A", NULL); | NULL, 0, sysctl_vmm_destroy, "A", NULL); | ||||
static struct cdevsw vmmdevsw = { | static struct cdevsw vmmdevsw = { | ||||
.d_name = "vmmdev", | .d_name = "vmmdev", | ||||
.d_version = D_VERSION, | .d_version = D_VERSION, | ||||
.d_ioctl = vmmdev_ioctl, | .d_ioctl = vmmdev_ioctl, | ||||
.d_mmap_single = vmmdev_mmap_single, | .d_mmap_single = vmmdev_mmap_single, | ||||
.d_read = vmmdev_rw, | .d_read = vmmdev_rw, | ||||
.d_write = vmmdev_rw, | .d_write = vmmdev_rw, | ||||
}; | }; | ||||
static int | static int | ||||
sysctl_vmm_create(SYSCTL_HANDLER_ARGS) | sysctl_vmm_create(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error; | int error; | ||||
struct vm *vm; | struct vm *vm; | ||||
struct cdev *cdev; | struct cdev *cdev; | ||||
struct vmmdev_softc *sc, *sc2; | struct vmmdev_softc *sc, *sc2; | ||||
char buf[VM_MAX_NAMELEN]; | char buf[VM_MAX_NAMELEN]; | ||||
error = vmm_priv_check(req->td->td_ucred); | |||||
if (error) | |||||
return (error); | |||||
strlcpy(buf, "beavis", sizeof(buf)); | strlcpy(buf, "beavis", sizeof(buf)); | ||||
error = sysctl_handle_string(oidp, buf, sizeof(buf), req); | error = sysctl_handle_string(oidp, buf, sizeof(buf), req); | ||||
if (error != 0 || req->newptr == NULL) | if (error != 0 || req->newptr == NULL) | ||||
return (error); | return (error); | ||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||
sc = vmmdev_lookup(buf); | sc = vmmdev_lookup(buf); | ||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||
Show All 34 Lines | sysctl_vmm_create(SYSCTL_HANDLER_ARGS) | ||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||
sc->cdev = cdev; | sc->cdev = cdev; | ||||
sc->cdev->si_drv1 = sc; | sc->cdev->si_drv1 = sc; | ||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||
return (0); | return (0); | ||||
} | } | ||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, create, CTLTYPE_STRING | CTLFLAG_RW, | SYSCTL_PROC(_hw_vmm, OID_AUTO, create, | ||||
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON, | |||||
NULL, 0, sysctl_vmm_create, "A", NULL); | NULL, 0, sysctl_vmm_create, "A", NULL); | ||||
void | void | ||||
vmmdev_init(void) | vmmdev_init(void) | ||||
{ | { | ||||
mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); | mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 105 Lines • Show Last 20 Lines |
Instead of adding a new function, you should call priv_check_cred(9), which would involve adding a new privilege to sys/priv.h, and a corresponding check in prison_priv_check(). You might also need to add something to prison_check_cred() itself, but probably not.