diff options
Diffstat (limited to 'drivers/virt')
-rw-r--r-- | drivers/virt/coco/sev-guest/sev-guest.c | 44 |
1 files changed, 32 insertions, 12 deletions
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c index 5bee58ef5f1e..e5f8f115f4af 100644 --- a/drivers/virt/coco/sev-guest/sev-guest.c +++ b/drivers/virt/coco/sev-guest/sev-guest.c @@ -19,6 +19,7 @@ #include <crypto/aead.h> #include <linux/scatterlist.h> #include <linux/psp-sev.h> +#include <linux/sockptr.h> #include <uapi/linux/sev-guest.h> #include <uapi/linux/psp-sev.h> @@ -475,6 +476,11 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, return 0; } +struct snp_req_resp { + sockptr_t req_data; + sockptr_t resp_data; +}; + static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) { struct snp_guest_crypto *crypto = snp_dev->crypto; @@ -555,22 +561,25 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque return rc; } -static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) +static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg, + struct snp_req_resp *io) + { struct snp_ext_report_req *req = &snp_dev->req.ext_report; struct snp_guest_crypto *crypto = snp_dev->crypto; struct snp_report_resp *resp; int ret, npages = 0, resp_len; + sockptr_t certs_address; lockdep_assert_held(&snp_cmd_mutex); - if (!arg->req_data || !arg->resp_data) + if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data)) return -EINVAL; - if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req))) + if (copy_from_sockptr(req, io->req_data, sizeof(*req))) return -EFAULT; - /* userspace does not want certificate data */ + /* caller does not want certificate data */ if (!req->certs_len || !req->certs_address) goto cmd; @@ -578,8 +587,13 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques !IS_ALIGNED(req->certs_len, PAGE_SIZE)) return -EINVAL; - if (!access_ok((const void __user *)req->certs_address, req->certs_len)) - return -EFAULT; + if (sockptr_is_kernel(io->resp_data)) { + certs_address = KERNEL_SOCKPTR((void *)req->certs_address); + } else { + certs_address = USER_SOCKPTR((void __user *)req->certs_address); + if (!access_ok(certs_address.user, req->certs_len)) + return -EFAULT; + } /* * Initialize the intermediate buffer with all zeros. This buffer @@ -609,21 +623,19 @@ cmd: if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) { req->certs_len = snp_dev->input.data_npages << PAGE_SHIFT; - if (copy_to_user((void __user *)arg->req_data, req, sizeof(*req))) + if (copy_to_sockptr(io->req_data, req, sizeof(*req))) ret = -EFAULT; } if (ret) goto e_free; - if (npages && - copy_to_user((void __user *)req->certs_address, snp_dev->certs_data, - req->certs_len)) { + if (npages && copy_to_sockptr(certs_address, snp_dev->certs_data, req->certs_len)) { ret = -EFAULT; goto e_free; } - if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp))) + if (copy_to_sockptr(io->resp_data, resp, sizeof(*resp))) ret = -EFAULT; e_free: @@ -636,6 +648,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long struct snp_guest_dev *snp_dev = to_snp_dev(file); void __user *argp = (void __user *)arg; struct snp_guest_request_ioctl input; + struct snp_req_resp io; int ret = -ENOTTY; if (copy_from_user(&input, argp, sizeof(input))) @@ -664,7 +677,14 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long ret = get_derived_key(snp_dev, &input); break; case SNP_GET_EXT_REPORT: - ret = get_ext_report(snp_dev, &input); + /* + * As get_ext_report() may be called from the ioctl() path and a + * kernel internal path (configfs-tsm), decorate the passed + * buffers as user pointers. + */ + io.req_data = USER_SOCKPTR((void __user *)input.req_data); + io.resp_data = USER_SOCKPTR((void __user *)input.resp_data); + ret = get_ext_report(snp_dev, &input, &io); break; default: break; |