| From 85b18d7b5e7ffefb2f076186511d39c4990aa005 Mon Sep 17 00:00:00 2001 |
| From: Janosch Frank <frankja@linux.ibm.com> |
| Date: Tue, 12 Jan 2021 05:40:53 -0500 |
| Subject: s390: mm: Fix secure storage access exception handling |
| |
| From: Janosch Frank <frankja@linux.ibm.com> |
| |
| commit 85b18d7b5e7ffefb2f076186511d39c4990aa005 upstream. |
| |
| Turns out that the bit 61 in the TEID is not always 1 and if that's |
| the case the address space ID and the address are |
| unpredictable. Without an address and its address space ID we can't |
| export memory and hence we can only send a SIGSEGV to the process or |
| panic the kernel depending on who caused the exception. |
| |
| Unfortunately bit 61 is only reliable if we have the "misc" UV feature |
| bit. |
| |
| Signed-off-by: Janosch Frank <frankja@linux.ibm.com> |
| Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com> |
| Fixes: 084ea4d611a3d ("s390/mm: add (non)secure page access exceptions handlers") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Vasily Gorbik <gor@linux.ibm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/s390/boot/uv.c | 1 + |
| arch/s390/include/asm/uv.h | 8 +++++++- |
| arch/s390/kernel/uv.c | 10 ++++++++++ |
| arch/s390/mm/fault.c | 26 ++++++++++++++++++++++++++ |
| 4 files changed, 44 insertions(+), 1 deletion(-) |
| |
| --- a/arch/s390/boot/uv.c |
| +++ b/arch/s390/boot/uv.c |
| @@ -36,6 +36,7 @@ void uv_query_info(void) |
| uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE); |
| uv_info.max_num_sec_conf = uvcb.max_num_sec_conf; |
| uv_info.max_guest_cpu_id = uvcb.max_guest_cpu_id; |
| + uv_info.uv_feature_indications = uvcb.uv_feature_indications; |
| } |
| |
| #ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST |
| --- a/arch/s390/include/asm/uv.h |
| +++ b/arch/s390/include/asm/uv.h |
| @@ -73,6 +73,10 @@ enum uv_cmds_inst { |
| BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, |
| }; |
| |
| +enum uv_feat_ind { |
| + BIT_UV_FEAT_MISC = 0, |
| +}; |
| + |
| struct uv_cb_header { |
| u16 len; |
| u16 cmd; /* Command Code */ |
| @@ -97,7 +101,8 @@ struct uv_cb_qui { |
| u64 max_guest_stor_addr; |
| u8 reserved88[158 - 136]; |
| u16 max_guest_cpu_id; |
| - u8 reserveda0[200 - 160]; |
| + u64 uv_feature_indications; |
| + u8 reserveda0[200 - 168]; |
| } __packed __aligned(8); |
| |
| /* Initialize Ultravisor */ |
| @@ -274,6 +279,7 @@ struct uv_info { |
| unsigned long max_sec_stor_addr; |
| unsigned int max_num_sec_conf; |
| unsigned short max_guest_cpu_id; |
| + unsigned long uv_feature_indications; |
| }; |
| |
| extern struct uv_info uv_info; |
| --- a/arch/s390/kernel/uv.c |
| +++ b/arch/s390/kernel/uv.c |
| @@ -364,6 +364,15 @@ static ssize_t uv_query_facilities(struc |
| static struct kobj_attribute uv_query_facilities_attr = |
| __ATTR(facilities, 0444, uv_query_facilities, NULL); |
| |
| +static ssize_t uv_query_feature_indications(struct kobject *kobj, |
| + struct kobj_attribute *attr, char *buf) |
| +{ |
| + return sysfs_emit(buf, "%lx\n", uv_info.uv_feature_indications); |
| +} |
| + |
| +static struct kobj_attribute uv_query_feature_indications_attr = |
| + __ATTR(feature_indications, 0444, uv_query_feature_indications, NULL); |
| + |
| static ssize_t uv_query_max_guest_cpus(struct kobject *kobj, |
| struct kobj_attribute *attr, char *page) |
| { |
| @@ -396,6 +405,7 @@ static struct kobj_attribute uv_query_ma |
| |
| static struct attribute *uv_query_attrs[] = { |
| &uv_query_facilities_attr.attr, |
| + &uv_query_feature_indications_attr.attr, |
| &uv_query_max_guest_cpus_attr.attr, |
| &uv_query_max_guest_vms_attr.attr, |
| &uv_query_max_guest_addr_attr.attr, |
| --- a/arch/s390/mm/fault.c |
| +++ b/arch/s390/mm/fault.c |
| @@ -805,6 +805,32 @@ void do_secure_storage_access(struct pt_ |
| struct page *page; |
| int rc; |
| |
| + /* |
| + * bit 61 tells us if the address is valid, if it's not we |
| + * have a major problem and should stop the kernel or send a |
| + * SIGSEGV to the process. Unfortunately bit 61 is not |
| + * reliable without the misc UV feature so we need to check |
| + * for that as well. |
| + */ |
| + if (test_bit_inv(BIT_UV_FEAT_MISC, &uv_info.uv_feature_indications) && |
| + !test_bit_inv(61, ®s->int_parm_long)) { |
| + /* |
| + * When this happens, userspace did something that it |
| + * was not supposed to do, e.g. branching into secure |
| + * memory. Trigger a segmentation fault. |
| + */ |
| + if (user_mode(regs)) { |
| + send_sig(SIGSEGV, current, 0); |
| + return; |
| + } |
| + |
| + /* |
| + * The kernel should never run into this case and we |
| + * have no way out of this situation. |
| + */ |
| + panic("Unexpected PGM 0x3d with TEID bit 61=0"); |
| + } |
| + |
| switch (get_fault_type(regs)) { |
| case USER_FAULT: |
| mm = current->mm; |