Skip to content

Commit

Permalink
arm64/vmm: Avoid returning valid capabilities from VM_GET_REGISTER
Browse files Browse the repository at this point in the history
The VM_GET_REGISTER and VM_GET_REGISTER_SET ioctls return guest register
values; on arm64 they are used only by bhyve's gdb stub.

The ioctls may return valid capabilities, which is a mistake since they
belong to a different address space.  Modify the ioctl handlers to strip
capability tags before returning to userspace.

Since the gdb stub still wants the tag values, provide two new ioctls
for this purpose: VM_GET_REGISTER_CHERI_CAPABILITY_TAG and
VM_GET_REGISTER_CHERI_CAPABILITY_TAG_SET.  They are not atomic, but for
practical purposes the target vCPU will be stopped anyway, and this lets
us side-step compatibility concerns.
  • Loading branch information
markjdb committed Jan 5, 2025
1 parent 0f503b0 commit 7c11272
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 0 deletions.
4 changes: 4 additions & 0 deletions sys/arm64/include/vmm.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ void vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores,
int vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores,
uint16_t threads, uint16_t maxcpus);
int vm_get_register(struct vcpu *vcpu, int reg, uintcap_t *retval);
#if __has_feature(capabilities)
int vm_get_register_cheri_capability_tag(struct vcpu *vcpu, int reg,
uint8_t *tagp);
#endif
int vm_set_register(struct vcpu *vcpu, int reg, uintcap_t val);
int vm_run(struct vcpu *vcpu);
int vm_suspend(struct vm *vm, enum vm_suspend_how how);
Expand Down
25 changes: 25 additions & 0 deletions sys/arm64/include/vmm_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,30 @@ struct vm_register {
kuintcap_t regval;
};

#if __has_feature(capabilities)
struct vm_register_cheri_capability_tag {
int cpuid;
int regnum; /* enum vm_reg_name */
uint8_t tag;
};
#endif

struct vm_register_set {
int cpuid;
unsigned int count;
const int * __kerncap regnums; /* enum vm_reg_name */
uintcap_t * __kerncap regvals;
};

#if __has_feature(capabilities)
struct vm_register_cheri_capability_tag_set {
int cpuid;
unsigned int count;
const int * __kerncap regnums; /* enum vm_reg_name */
uint8_t * __kerncap tags;
};
#endif

struct vm_run {
int cpuid;
cpuset_t * __kerncap cpuset; /* CPU set storage */
Expand Down Expand Up @@ -216,6 +233,8 @@ enum {

#if __has_feature(capabilities)
IOCNUM_GET_CHERI_CAPABILITY_TAG = 200,
IOCNUM_GET_REGISTER_CHERI_CAPABILITY_TAG = 201,
IOCNUM_GET_REGISTER_CHERI_CAPABILITY_TAG_SET = 202,
#endif
};

Expand Down Expand Up @@ -281,5 +300,11 @@ enum {
#define VM_GET_CHERI_CAPABILITY_TAG \
_IOWR('v', IOCNUM_GET_CHERI_CAPABILITY_TAG, \
struct vm_cheri_capability_tag)
#define VM_GET_REGISTER_CHERI_CAPABILITY_TAG \
_IOWR('v', IOCNUM_GET_REGISTER_CHERI_CAPABILITY_TAG, \
struct vm_register_cheri_capability_tag)
#define VM_GET_REGISTER_CHERI_CAPABILITY_TAG_SET \
_IOW('v', IOCNUM_GET_REGISTER_CHERI_CAPABILITY_TAG_SET, \
struct vm_register_cheri_capability_tag_set)
#endif
#endif
17 changes: 17 additions & 0 deletions sys/arm64/vmm/vmm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,23 @@ vm_get_register(struct vcpu *vcpu, int reg, uintcap_t *retval)
return (vmmops_getreg(vcpu->cookie, reg, retval));
}

#if __has_feature(capabilities)
int
vm_get_register_cheri_capability_tag(struct vcpu *vcpu, int reg, uint8_t *tagp)
{
uintcap_t val;
int error;

if (reg >= VM_REG_LAST)
return (EINVAL);

error = vmmops_getreg(vcpu->cookie, reg, &val);
if (error == 0)
*tagp = cheri_gettag(val);
return (error);
}
#endif

int
vm_set_register(struct vcpu *vcpu, int reg, uintcap_t val)
{
Expand Down
63 changes: 63 additions & 0 deletions sys/dev/vmm/vmm_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,31 @@ vm_get_register_set(struct vcpu *vcpu, unsigned int count, int *regnum,
error = vm_get_register(vcpu, regnum[i], &regval[i]);
if (error)
break;
#if __has_feature(capabilities)
regval[i] = cheri_cleartag(regval[i]);
#endif
}
return (error);
}

#if __has_feature(capabilities)
static int
vm_get_register_cheri_capability_tag_set(struct vcpu *vcpu, unsigned int count,
int *regnum, uint8_t *tags)
{
int error, i;

error = 0;
for (i = 0; i < count; i++) {
error = vm_get_register_cheri_capability_tag(vcpu, regnum[i],
&tags[i]);
if (error)
break;
}
return (error);
}
#endif

static int
vm_set_register_set(struct vcpu *vcpu, unsigned int count, int *regnum,
uintcap_t *regval)
Expand Down Expand Up @@ -378,6 +399,10 @@ static const struct vmmdev_ioctl vmmdev_ioctls[] = {

#if __has_feature(capabilities)
VMMDEV_IOCTL(VM_GET_CHERI_CAPABILITY_TAG, VMMDEV_IOCTL_SLOCK_MEMSEGS),
VMMDEV_IOCTL(VM_GET_REGISTER_CHERI_CAPABILITY_TAG,
VMMDEV_IOCTL_LOCK_ONE_VCPU),
VMMDEV_IOCTL(VM_GET_REGISTER_CHERI_CAPABILITY_TAG_SET,
VMMDEV_IOCTL_LOCK_ONE_VCPU),
#endif
};

Expand Down Expand Up @@ -522,6 +547,10 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,

vmreg = (struct vm_register *)data;
error = vm_get_register(vcpu, vmreg->regnum, &vmreg->regval);
#if __has_feature(capabilities)
if (error == 0)
vmreg->regval = cheri_cleartag(vmreg->regval);
#endif
break;
}
case VM_SET_REGISTER: {
Expand Down Expand Up @@ -660,6 +689,40 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
error = vm_get_cheri_capability_tag(sc->vm, vt);
break;
}
case VM_GET_REGISTER_CHERI_CAPABILITY_TAG: {
struct vm_register_cheri_capability_tag *vrct;

vrct = (struct vm_register_cheri_capability_tag *)data;
error = vm_get_register_cheri_capability_tag(vcpu, vrct->regnum,
&vrct->tag);
break;
}
case VM_GET_REGISTER_CHERI_CAPABILITY_TAG_SET: {
struct vm_register_cheri_capability_tag_set *vmtagset;
uint8_t *tags;
int *regnums;

vmtagset = (struct vm_register_cheri_capability_tag_set *)data;
if (vmtagset->count > VM_REG_LAST) {
error = EINVAL;
break;
}
tags = malloc(sizeof(tags[0]) * vmtagset->count, M_VMMDEV,
M_WAITOK);
regnums = malloc(sizeof(regnums[0]) * vmtagset->count, M_VMMDEV,
M_WAITOK);
error = copyin(vmtagset->regnums, regnums, sizeof(regnums[0]) *
vmtagset->count);
if (error == 0)
error = vm_get_register_cheri_capability_tag_set(vcpu,
vmtagset->count, regnums, tags);
if (error == 0)
error = copyout(tags, vmtagset->tags,
sizeof(tags[0]) * vmtagset->count);
free(tags, M_VMMDEV);
free(regnums, M_VMMDEV);
break;
}
#endif
default:
error = vmmdev_machdep_ioctl(sc->vm, vcpu, cmd, data, fflag,
Expand Down

0 comments on commit 7c11272

Please sign in to comment.