s390: mm: Fix secure storage access exception handling
authorJanosch Frank <frankja@linux.ibm.com>
Tue, 12 Jan 2021 10:40:53 +0000 (05:40 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 14 Jul 2021 14:55:43 +0000 (16:55 +0200)
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
arch/s390/include/asm/uv.h
arch/s390/kernel/uv.c
arch/s390/mm/fault.c

index 87641dd65ccf915e8d11aa5062dcc0433f34eeb4..b3501ea5039e464b6da2e8425581586f514e488b 100644 (file)
@@ -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
index 7b98d4caee779cfd128182f62a6f56fbc4dae1e2..12c5f006c1364b714c4a7187b659c3c2fb71d0d2 100644 (file)
@@ -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;
index b2d2ad1530676ddad2142c3963bdd41bd0a72da7..c811b2313100be65524202ef8194cd30e685ec62 100644 (file)
@@ -364,6 +364,15 @@ static ssize_t uv_query_facilities(struct kobject *kobj,
 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_max_guest_addr_attr =
 
 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,
index 996884dcc9fdb0bbdb9cf1033ad56ed6df6289cc..ed517fad0d035f6f99ca9b8a8408d243807ee3f6 100644 (file)
@@ -805,6 +805,32 @@ void do_secure_storage_access(struct pt_regs *regs)
        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, &regs->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;