diff mbox series

[RFC,v6,03/92] kvm: introspection: add permission access ioctls

Message ID 20190809160047.8319-4-alazar@bitdefender.com
State New, archived
Headers show
Series VM introspection | expand

Commit Message

Adalbert Lazăr Aug. 9, 2019, 3:59 p.m. UTC
KVM_INTROSPECTION_COMMAND and KVM_INTROSPECTION_EVENTS should be used
by userspace/QEMU to allow access to specific (or all) introspection
commands and events.

By default, all introspection events and almost all introspection commands
are disallowed. There are a couple of commands that are always allowed
(those querying the introspection capabilities).

Signed-off-by: Adalbert Lazăr <alazar@bitdefender.com>
---
 Documentation/virtual/kvm/api.txt | 56 +++++++++++++++++++-
 include/uapi/linux/kvm.h          |  6 +++
 virt/kvm/kvm_main.c               |  6 +++
 virt/kvm/kvmi.c                   | 85 +++++++++++++++++++++++++++++++
 virt/kvm/kvmi_int.h               | 51 +++++++++++++++++++
 5 files changed, 203 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 28d4429f9ae9..ea3135d365c7 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -3889,7 +3889,61 @@  It will fail with -EINVAL if padding is not zero.
 The KVMI version can be retrieved using the KVM_CAP_INTROSPECTION of
 the KVM_CHECK_EXTENSION ioctl() at run-time.
 
-4.997 KVM_INTROSPECTION_UNHOOK
+4.997 KVM_INTROSPECTION_COMMAND
+
+Capability: KVM_CAP_INTROSPECTION
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_introspection_feature (in)
+Returns: 0 on success, a negative value on error
+
+This ioctl is used to allow or disallow introspection commands
+for the current VM. By default, almost all commands are disallowed
+except for those used to query the API.
+
+struct kvm_introspection_feature {
+	__u32 allow;
+	__s32 id;
+};
+
+If allow is 1, the command specified by id is allowed. If allow is 0,
+the command is disallowed.
+
+Unless set to -1 (meaning all commands), id must be a command ID
+(e.g. KVMI_GET_VERSION, KVMI_GET_GUEST_INFO etc.)
+
+Errors:
+
+  -EINVAL if the command is unknown
+  -EPERM if the command can't be disallowed (e.g. KVMI_GET_VERSION)
+
+4.998 KVM_INTROSPECTION_EVENT
+
+Capability: KVM_CAP_INTROSPECTION
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_introspection_feature (in)
+Returns: 0 on success, a negative value on error
+
+This ioctl is used to allow or disallow introspection events
+for the current VM. By default, all events are disallowed.
+
+struct kvm_introspection_feature {
+	__u32 allow;
+	__s32 id;
+};
+
+If allow is 1, the event specified by id is allowed. If allow is 0,
+the event is disallowed.
+
+Unless set to -1 (meaning all event), id must be a event ID
+(e.g. KVMI_EVENT_UNHOOK, KVMI_EVENT_CR, etc.)
+
+Errors:
+
+  -EINVAL if the event is unknown
+
+4.999 KVM_INTROSPECTION_UNHOOK
 
 Capability: KVM_CAP_INTROSPECTION
 Architectures: x86
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index bae37bf37338..2ff05fd123e3 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1527,9 +1527,15 @@  struct kvm_introspection {
 	__u32 padding;
 	__u8 uuid[16];
 };
+struct kvm_introspection_feature {
+	__u32 allow;
+	__s32 id;
+};
 #define KVM_INTROSPECTION_HOOK    _IOW(KVMIO, 0xff, struct kvm_introspection)
 #define KVM_INTROSPECTION_UNHOOK  _IO(KVMIO, 0xfe)
 /* write true on force-reset, false otherwise */
+#define KVM_INTROSPECTION_COMMAND _IOW(KVMIO, 0xfd, struct kvm_introspection_feature)
+#define KVM_INTROSPECTION_EVENT   _IOW(KVMIO, 0xfc, struct kvm_introspection_feature)
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3		(1 << 1)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 09a930ac007d..8399b826f2d2 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3270,6 +3270,12 @@  static long kvm_vm_ioctl(struct file *filp,
 	case KVM_INTROSPECTION_HOOK:
 		r = kvmi_ioctl_hook(kvm, argp);
 		break;
+	case KVM_INTROSPECTION_COMMAND:
+		r = kvmi_ioctl_command(kvm, argp);
+		break;
+	case KVM_INTROSPECTION_EVENT:
+		r = kvmi_ioctl_event(kvm, argp);
+		break;
 	case KVM_INTROSPECTION_UNHOOK:
 		r = kvmi_ioctl_unhook(kvm, arg);
 		break;
diff --git a/virt/kvm/kvmi.c b/virt/kvm/kvmi.c
index 591f6ee22135..dc64f975998f 100644
--- a/virt/kvm/kvmi.c
+++ b/virt/kvm/kvmi.c
@@ -169,6 +169,91 @@  int kvmi_ioctl_hook(struct kvm *kvm, void __user *argp)
 	return kvmi_hook(kvm, &i);
 }
 
+static int kvmi_ioctl_get_feature(void __user *argp, bool *allow, int *id,
+				  unsigned long *bitmask)
+{
+	struct kvm_introspection_feature feat;
+	int all_bits = -1;
+
+	if (copy_from_user(&feat, argp, sizeof(feat)))
+		return -EFAULT;
+
+	if (feat.id < 0 && feat.id != all_bits)
+		return -EINVAL;
+
+	*allow = !!(feat.allow & 1);
+	*id = feat.id;
+	*bitmask = *id == all_bits ? -1 : BIT(feat.id);
+
+	return 0;
+}
+
+static int kvmi_ioctl_feature(struct kvm *kvm,
+			      bool allow, unsigned long *requested,
+			      size_t off_dest, unsigned int nbits)
+{
+	unsigned long *dest;
+	struct kvmi *ikvm;
+
+	if (bitmap_empty(requested, nbits))
+		return -EINVAL;
+
+	ikvm = kvmi_get(kvm);
+	if (!ikvm)
+		return -EFAULT;
+
+	dest = (unsigned long *)((char *)ikvm + off_dest);
+
+	if (allow)
+		bitmap_or(dest, dest, requested, nbits);
+	else
+		bitmap_andnot(dest, dest, requested, nbits);
+
+	kvmi_put(kvm);
+
+	return 0;
+}
+
+int kvmi_ioctl_event(struct kvm *kvm, void __user *argp)
+{
+	DECLARE_BITMAP(requested, KVMI_NUM_EVENTS);
+	DECLARE_BITMAP(known, KVMI_NUM_EVENTS);
+	bool allow;
+	int err;
+	int id;
+
+	err = kvmi_ioctl_get_feature(argp, &allow, &id, requested);
+	if (err)
+		return err;
+
+	bitmap_from_u64(known, KVMI_KNOWN_EVENTS);
+	bitmap_and(requested, requested, known, KVMI_NUM_EVENTS);
+
+	return kvmi_ioctl_feature(kvm, allow, requested,
+				  offsetof(struct kvmi, event_allow_mask),
+				  KVMI_NUM_EVENTS);
+}
+
+int kvmi_ioctl_command(struct kvm *kvm, void __user *argp)
+{
+	DECLARE_BITMAP(requested, KVMI_NUM_COMMANDS);
+	DECLARE_BITMAP(known, KVMI_NUM_COMMANDS);
+	bool allow;
+	int err;
+	int id;
+
+	err = kvmi_ioctl_get_feature(argp, &allow, &id, requested);
+	if (err)
+		return err;
+
+	bitmap_from_u64(known, KVMI_KNOWN_COMMANDS);
+	bitmap_and(requested, requested, known, KVMI_NUM_COMMANDS);
+
+	return kvmi_ioctl_feature(kvm, allow, requested,
+				  offsetof(struct kvmi, cmd_allow_mask),
+				  KVMI_NUM_COMMANDS);
+}
+
 void kvmi_create_vm(struct kvm *kvm)
 {
 	init_completion(&kvm->kvmi_completed);
diff --git a/virt/kvm/kvmi_int.h b/virt/kvm/kvmi_int.h
index 9bc5205c8714..bd8b539e917a 100644
--- a/virt/kvm/kvmi_int.h
+++ b/virt/kvm/kvmi_int.h
@@ -23,6 +23,54 @@ 
 #define kvmi_err(ikvm, fmt, ...) \
 	kvm_info("%pU ERROR: " fmt, &ikvm->uuid, ## __VA_ARGS__)
 
+#define KVMI_KNOWN_VCPU_EVENTS ( \
+		BIT(KVMI_EVENT_CR) | \
+		BIT(KVMI_EVENT_MSR) | \
+		BIT(KVMI_EVENT_XSETBV) | \
+		BIT(KVMI_EVENT_BREAKPOINT) | \
+		BIT(KVMI_EVENT_HYPERCALL) | \
+		BIT(KVMI_EVENT_PF) | \
+		BIT(KVMI_EVENT_TRAP) | \
+		BIT(KVMI_EVENT_DESCRIPTOR) | \
+		BIT(KVMI_EVENT_PAUSE_VCPU) | \
+		BIT(KVMI_EVENT_SINGLESTEP))
+
+#define KVMI_KNOWN_VM_EVENTS ( \
+		BIT(KVMI_EVENT_CREATE_VCPU) | \
+		BIT(KVMI_EVENT_UNHOOK))
+
+#define KVMI_KNOWN_EVENTS (KVMI_KNOWN_VCPU_EVENTS | KVMI_KNOWN_VM_EVENTS)
+
+#define KVMI_KNOWN_COMMANDS ( \
+		BIT(KVMI_GET_VERSION) | \
+		BIT(KVMI_CHECK_COMMAND) | \
+		BIT(KVMI_CHECK_EVENT) | \
+		BIT(KVMI_GET_GUEST_INFO) | \
+		BIT(KVMI_PAUSE_VCPU) | \
+		BIT(KVMI_CONTROL_VM_EVENTS) | \
+		BIT(KVMI_CONTROL_EVENTS) | \
+		BIT(KVMI_CONTROL_CR) | \
+		BIT(KVMI_CONTROL_MSR) | \
+		BIT(KVMI_CONTROL_VE) | \
+		BIT(KVMI_GET_REGISTERS) | \
+		BIT(KVMI_SET_REGISTERS) | \
+		BIT(KVMI_GET_CPUID) | \
+		BIT(KVMI_GET_XSAVE) | \
+		BIT(KVMI_READ_PHYSICAL) | \
+		BIT(KVMI_WRITE_PHYSICAL) | \
+		BIT(KVMI_INJECT_EXCEPTION) | \
+		BIT(KVMI_GET_PAGE_ACCESS) | \
+		BIT(KVMI_SET_PAGE_ACCESS) | \
+		BIT(KVMI_GET_MAP_TOKEN) | \
+		BIT(KVMI_CONTROL_SPP) | \
+		BIT(KVMI_GET_PAGE_WRITE_BITMAP) | \
+		BIT(KVMI_SET_PAGE_WRITE_BITMAP) | \
+		BIT(KVMI_GET_MTRR_TYPE) | \
+		BIT(KVMI_CONTROL_CMD_RESPONSE) | \
+		BIT(KVMI_GET_VCPU_INFO))
+
+#define KVMI_NUM_COMMANDS KVMI_NEXT_AVAILABLE_COMMAND
+
 #define IKVM(kvm) ((struct kvmi *)((kvm)->kvmi))
 
 struct kvmi {
@@ -32,6 +80,9 @@  struct kvmi {
 	struct task_struct *recv;
 
 	uuid_t uuid;
+
+	DECLARE_BITMAP(cmd_allow_mask, KVMI_NUM_COMMANDS);
+	DECLARE_BITMAP(event_allow_mask, KVMI_NUM_EVENTS);
 };
 
 /* kvmi_msg.c */