]> www.infradead.org Git - users/dwmw2/qemu.git/commitdiff
s390x/mmu: Check table length and offset fields
authorThomas Huth <thuth@linux.vnet.ibm.com>
Thu, 12 Feb 2015 17:09:21 +0000 (18:09 +0100)
committerChristian Borntraeger <borntraeger@de.ibm.com>
Wed, 18 Feb 2015 08:37:14 +0000 (09:37 +0100)
The ACSEs have a table length field and the region entries have
table length and offset fields which must be checked during
translation to see whether the given virtual address is really
covered by the translation table.

Signed-off-by: Thomas Huth <thuth@linux.vnet.ibm.com>
Signed-off-by: Jens Freimann <jfrei@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
target-s390x/cpu.h
target-s390x/mmu_helper.c

index 95d0f3b601e7709b8a60b37d0d3056004fd56368..556304241a9696c14f5c55a1b4626b8b36a7e7d0 100644 (file)
@@ -837,6 +837,7 @@ struct sysib_322 {
 #define _ASCE_TABLE_LENGTH      0x03      /* region table length              */
 
 #define _REGION_ENTRY_ORIGIN    ~0xfffULL /* region/segment table origin      */
+#define _REGION_ENTRY_TF        0xc0      /* region/segment table offset      */
 #define _REGION_ENTRY_INV       0x20      /* invalid region table entry       */
 #define _REGION_ENTRY_TYPE_MASK 0x0c      /* region/segment table type mask   */
 #define _REGION_ENTRY_TYPE_R1   0x0c      /* region first table type          */
index 01d819e0268e0a790d883c9bc1108ad20f716613..d4087ba3ce074e096a3d7050cf201b0adaef8ca5 100644 (file)
@@ -171,6 +171,10 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
 {
     CPUState *cs = CPU(s390_env_get_cpu(env));
     uint64_t origin, offs, new_entry;
+    const int pchks[4] = {
+        PGM_SEGMENT_TRANS, PGM_REG_THIRD_TRANS,
+        PGM_REG_SEC_TRANS, PGM_REG_FIRST_TRANS
+    };
 
     PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, entry);
 
@@ -201,6 +205,15 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
                                      rw);
     }
 
+    /* Check region table offset and length */
+    offs = (vaddr >> (28 + 11 * (level - 4) / 4)) & 3;
+    if (offs < ((new_entry & _REGION_ENTRY_TF) >> 6)
+        || offs > (new_entry & _REGION_ENTRY_LENGTH)) {
+        DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry);
+        trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw);
+        return -1;
+    }
+
     /* yet another region */
     return mmu_translate_region(env, vaddr, asc, new_entry, level - 4,
                                 raddr, flags, rw);
@@ -238,6 +251,10 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
     level = asce & _ASCE_TYPE_MASK;
     switch (level) {
     case _ASCE_TYPE_REGION1:
+        if ((vaddr >> 62) > (asce & _ASCE_TABLE_LENGTH)) {
+            trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw);
+            return -1;
+        }
         break;
     case _ASCE_TYPE_REGION2:
         if (vaddr & 0xffe0000000000000ULL) {
@@ -246,6 +263,10 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
             trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
             return -1;
         }
+        if ((vaddr >> 51 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
+            trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw);
+            return -1;
+        }
         break;
     case _ASCE_TYPE_REGION3:
         if (vaddr & 0xfffffc0000000000ULL) {
@@ -254,6 +275,10 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
             trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
             return -1;
         }
+        if ((vaddr >> 40 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
+            trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw);
+            return -1;
+        }
         break;
     case _ASCE_TYPE_SEGMENT:
         if (vaddr & 0xffffffff80000000ULL) {
@@ -262,6 +287,10 @@ static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
             trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
             return -1;
         }
+        if ((vaddr >> 29 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
+            trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw);
+            return -1;
+        }
         break;
     }