| From 13f7752c18f4de973433e0c046caaf4ac1a12d4b Mon Sep 17 00:00:00 2001 |
| From: David Hildenbrand <david@redhat.com> |
| Date: Fri, 3 Apr 2020 17:30:46 +0200 |
| Subject: [PATCH] KVM: s390: vsie: Fix region 1 ASCE sanity shadow address |
| checks |
| |
| commit a1d032a49522cb5368e5dfb945a85899b4c74f65 upstream. |
| |
| In case we have a region 1 the following calculation |
| (31 + ((gmap->asce & _ASCE_TYPE_MASK) >> 2)*11) |
| results in 64. As shifts beyond the size are undefined the compiler is |
| free to use instructions like sllg. sllg will only use 6 bits of the |
| shift value (here 64) resulting in no shift at all. That means that ALL |
| addresses will be rejected. |
| |
| The can result in endless loops, e.g. when prefix cannot get mapped. |
| |
| Fixes: 4be130a08420 ("s390/mm: add shadow gmap support") |
| Tested-by: Janosch Frank <frankja@linux.ibm.com> |
| Reported-by: Janosch Frank <frankja@linux.ibm.com> |
| Cc: <stable@vger.kernel.org> # v4.8+ |
| Signed-off-by: David Hildenbrand <david@redhat.com> |
| Link: https://lore.kernel.org/r/20200403153050.20569-2-david@redhat.com |
| Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com> |
| Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com> |
| [borntraeger@de.ibm.com: fix patch description, remove WARN_ON_ONCE] |
| Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c |
| index 1e668b95e0c6..84b667b984bb 100644 |
| --- a/arch/s390/mm/gmap.c |
| +++ b/arch/s390/mm/gmap.c |
| @@ -787,14 +787,18 @@ static void gmap_call_notifier(struct gmap *gmap, unsigned long start, |
| static inline unsigned long *gmap_table_walk(struct gmap *gmap, |
| unsigned long gaddr, int level) |
| { |
| + const int asce_type = gmap->asce & _ASCE_TYPE_MASK; |
| unsigned long *table; |
| |
| if ((gmap->asce & _ASCE_TYPE_MASK) + 4 < (level * 4)) |
| return NULL; |
| if (gmap_is_shadow(gmap) && gmap->removed) |
| return NULL; |
| - if (gaddr & (-1UL << (31 + ((gmap->asce & _ASCE_TYPE_MASK) >> 2)*11))) |
| + |
| + if (asce_type != _ASCE_TYPE_REGION1 && |
| + gaddr & (-1UL << (31 + (asce_type >> 2) * 11))) |
| return NULL; |
| + |
| table = gmap->table; |
| switch (gmap->asce & _ASCE_TYPE_MASK) { |
| case _ASCE_TYPE_REGION1: |
| -- |
| 2.7.4 |
| |