KVM: PPC: Book3S HV: Avoid shifts by negative amounts
authorPaul Mackerras <paulus@ozlabs.org>
Fri, 10 Nov 2017 05:40:24 +0000 (16:40 +1100)
committerPaul Mackerras <paulus@ozlabs.org>
Thu, 23 Nov 2017 03:23:00 +0000 (14:23 +1100)
The kvmppc_hpte_page_shifts function decodes the actual and base page
sizes for a HPTE, returning -1 if it doesn't recognize the page size
encoding.  This then gets used as a shift amount in various places,
which is undefined behaviour.  This was reported by Coverity.

In fact this should never occur, since we should only get HPTEs in the
HPT which have a recognized page size encoding.  The only place where
this might not be true is in the call to kvmppc_actual_pgsz() near the
beginning of kvmppc_do_h_enter(), where we are validating the HPTE
value passed in from the guest.

So to fix this and eliminate the undefined behaviour, we make
kvmppc_hpte_page_shifts return 0 for unrecognized page size encodings,
and make kvmppc_actual_pgsz() detect that case and return 0 for the
page size, which will then cause kvmppc_do_h_enter() to return an
error and refuse to insert any HPTE with an unrecognized page size
encoding.

To ensure that we don't get undefined behaviour in compute_tlbie_rb(),
we take the 4k page size path for any unrecognized page size encoding.
This should never be hit in practice because it is only used on HPTE
values which have previously been checked for having a recognized
page size encoding.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
arch/powerpc/include/asm/kvm_book3s_64.h

index 735cfa35298ac73adca40d91ef058215f5600b1e..998f7b7aaa9e5c1e905d5b202d9e2b091fc037b4 100644 (file)
@@ -122,13 +122,13 @@ static inline int kvmppc_hpte_page_shifts(unsigned long h, unsigned long l)
        lphi = (l >> 16) & 0xf;
        switch ((l >> 12) & 0xf) {
        case 0:
-               return !lphi ? 24 : -1;         /* 16MB */
+               return !lphi ? 24 : 0;          /* 16MB */
                break;
        case 1:
                return 16;                      /* 64kB */
                break;
        case 3:
-               return !lphi ? 34 : -1;         /* 16GB */
+               return !lphi ? 34 : 0;          /* 16GB */
                break;
        case 7:
                return (16 << 8) + 12;          /* 64kB in 4kB */
@@ -140,7 +140,7 @@ static inline int kvmppc_hpte_page_shifts(unsigned long h, unsigned long l)
                        return (24 << 8) + 12;  /* 16MB in 4kB */
                break;
        }
-       return -1;
+       return 0;
 }
 
 static inline int kvmppc_hpte_base_page_shift(unsigned long h, unsigned long l)
@@ -159,7 +159,11 @@ static inline int kvmppc_hpte_actual_page_shift(unsigned long h, unsigned long l
 
 static inline unsigned long kvmppc_actual_pgsz(unsigned long v, unsigned long r)
 {
-       return 1ul << kvmppc_hpte_actual_page_shift(v, r);
+       int shift = kvmppc_hpte_actual_page_shift(v, r);
+
+       if (shift)
+               return 1ul << shift;
+       return 0;
 }
 
 static inline int kvmppc_pgsize_lp_encoding(int base_shift, int actual_shift)
@@ -232,7 +236,7 @@ static inline unsigned long compute_tlbie_rb(unsigned long v, unsigned long r,
                va_low ^= v >> (SID_SHIFT_1T - 16);
        va_low &= 0x7ff;
 
-       if (b_pgshift == 12) {
+       if (b_pgshift <= 12) {
                if (a_pgshift > 12) {
                        sllp = (a_pgshift == 16) ? 5 : 4;
                        rb |= sllp << 5;        /*  AP field */