Blame SOURCES/kvm-nvram-Exit-QEMU-if-NVRAM-cannot-contain-all-prom-env.patch

a19a21
From aac48d07764ce73c2ba23e3f05ccd29db190024a Mon Sep 17 00:00:00 2001
a19a21
From: Greg Kurz <gkurz@redhat.com>
a19a21
Date: Thu, 8 Oct 2020 11:06:43 -0400
a19a21
Subject: [PATCH 04/14] nvram: Exit QEMU if NVRAM cannot contain all -prom-env
a19a21
 data
a19a21
a19a21
RH-Author: Greg Kurz <gkurz@redhat.com>
a19a21
Message-id: <20201008110643.155902-2-gkurz@redhat.com>
a19a21
Patchwork-id: 98577
a19a21
O-Subject: [RHEL-8.4.0 qemu-kvm PATCH 1/1] nvram: Exit QEMU if NVRAM cannot contain all -prom-env data
a19a21
Bugzilla: 1874780
a19a21
RH-Acked-by: David Gibson <dgibson@redhat.com>
a19a21
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
a19a21
RH-Acked-by: Thomas Huth <thuth@redhat.com>
a19a21
a19a21
From: Greg Kurz <groug@kaod.org>
a19a21
a19a21
Since commit 61f20b9dc5b7 ("spapr_nvram: Pre-initialize the NVRAM to
a19a21
support the -prom-env parameter"), pseries machines can pre-initialize
a19a21
the "system" partition in the NVRAM with the data passed to all -prom-env
a19a21
parameters on the QEMU command line.
a19a21
a19a21
In this case it is assumed that all the data fits in 64 KiB, but the user
a19a21
can easily pass more and crash QEMU:
a19a21
a19a21
$ qemu-system-ppc64 -M pseries $(for ((x=0;x<128;x++)); do \
a19a21
  echo -n " -prom-env " ; printf "%0.sx" {1..1024}; \
a19a21
  done) # this requires ~128 Kib
a19a21
malloc(): corrupted top size
a19a21
Aborted (core dumped)
a19a21
a19a21
This happens because we don't check if all the prom-env data fits in
a19a21
the NVRAM and chrp_nvram_set_var() happily memcpy() it passed the
a19a21
buffer.
a19a21
a19a21
This crash affects basically all ppc/ppc64 machine types that use -prom-env:
a19a21
- pseries (all versions)
a19a21
- g3beige
a19a21
- mac99
a19a21
a19a21
and also sparc/sparc64 machine types:
a19a21
- LX
a19a21
- SPARCClassic
a19a21
- SPARCbook
a19a21
- SS-10
a19a21
- SS-20
a19a21
- SS-4
a19a21
- SS-5
a19a21
- SS-600MP
a19a21
- Voyager
a19a21
- sun4u
a19a21
- sun4v
a19a21
a19a21
Add a max_len argument to chrp_nvram_create_system_partition() so that
a19a21
it can check the available size before writing to memory.
a19a21
a19a21
Since NVRAM is populated at machine init, it seems reasonable to consider
a19a21
this error as fatal. So, instead of reporting an error when we detect that
a19a21
the NVRAM is too small and adapt all machine types to handle it, we simply
a19a21
exit QEMU in all cases. This is still better than crashing. If someone
a19a21
wants another behavior, I guess this can be reworked later.
a19a21
a19a21
Tested with:
a19a21
a19a21
$ yes q | \
a19a21
  (for arch in ppc ppc64 sparc sparc64; do \
a19a21
       echo == $arch ==; \
a19a21
       qemu=${arch}-softmmu/qemu-system-$arch; \
a19a21
       for mach in $($qemu -M help | awk '! /^Supported/ { print $1 }'); do \
a19a21
           echo $mach; \
a19a21
           $qemu -M $mach -monitor stdio -nodefaults -nographic \
a19a21
           $(for ((x=0;x<128;x++)); do \
a19a21
                 echo -n " -prom-env " ; printf "%0.sx" {1..1024}; \
a19a21
             done) >/dev/null; \
a19a21
        done; echo; \
a19a21
   done)
a19a21
a19a21
Without the patch, affected machine types cause QEMU to report some
a19a21
memory corruption and crash:
a19a21
a19a21
malloc(): corrupted top size
a19a21
a19a21
free(): invalid size
a19a21
a19a21
*** stack smashing detected ***: terminated
a19a21
a19a21
With the patch, QEMU prints the following message and exits:
a19a21
a19a21
NVRAM is too small. Try to pass less data to -prom-env
a19a21
a19a21
It seems that the conditions for the crash have always existed, but it
a19a21
affects pseries, the machine type I care for, since commit 61f20b9dc5b7
a19a21
only.
a19a21
a19a21
Fixes: 61f20b9dc5b7 ("spapr_nvram: Pre-initialize the NVRAM to support the -prom-env parameter")
a19a21
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1867739
a19a21
Reported-by: John Snow <jsnow@redhat.com>
a19a21
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
a19a21
Signed-off-by: Greg Kurz <groug@kaod.org>
a19a21
Message-Id: <159736033937.350502.12402444542194031035.stgit@bahia.lan>
a19a21
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
a19a21
(cherry picked from commit 37035df51eaabb8d26b71da75b88a1c6727de8fa)
a19a21
Signed-off-by: Greg Kurz <gkurz@redhat.com>
a19a21
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
a19a21
---
a19a21
 hw/nvram/chrp_nvram.c         | 24 +++++++++++++++++++++---
a19a21
 hw/nvram/mac_nvram.c          |  2 +-
a19a21
 hw/nvram/spapr_nvram.c        |  3 ++-
a19a21
 hw/sparc/sun4m.c              |  2 +-
a19a21
 hw/sparc64/sun4u.c            |  2 +-
a19a21
 include/hw/nvram/chrp_nvram.h |  3 ++-
a19a21
 6 files changed, 28 insertions(+), 8 deletions(-)
a19a21
a19a21
diff --git a/hw/nvram/chrp_nvram.c b/hw/nvram/chrp_nvram.c
a19a21
index d969f26704..d4d10a7c03 100644
a19a21
--- a/hw/nvram/chrp_nvram.c
a19a21
+++ b/hw/nvram/chrp_nvram.c
a19a21
@@ -21,14 +21,21 @@
a19a21
 
a19a21
 #include "qemu/osdep.h"
a19a21
 #include "qemu/cutils.h"
a19a21
+#include "qemu/error-report.h"
a19a21
 #include "hw/nvram/chrp_nvram.h"
a19a21
 #include "sysemu/sysemu.h"
a19a21
 
a19a21
-static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str)
a19a21
+static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str,
a19a21
+                              int max_len)
a19a21
 {
a19a21
     int len;
a19a21
 
a19a21
     len = strlen(str) + 1;
a19a21
+
a19a21
+    if (max_len < len) {
a19a21
+        return -1;
a19a21
+    }
a19a21
+
a19a21
     memcpy(&nvram[addr], str, len);
a19a21
 
a19a21
     return addr + len;
a19a21
@@ -38,19 +45,26 @@ static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str)
a19a21
  * Create a "system partition", used for the Open Firmware
a19a21
  * environment variables.
a19a21
  */
a19a21
-int chrp_nvram_create_system_partition(uint8_t *data, int min_len)
a19a21
+int chrp_nvram_create_system_partition(uint8_t *data, int min_len, int max_len)
a19a21
 {
a19a21
     ChrpNvramPartHdr *part_header;
a19a21
     unsigned int i;
a19a21
     int end;
a19a21
 
a19a21
+    if (max_len < sizeof(*part_header)) {
a19a21
+        goto fail;
a19a21
+    }
a19a21
+
a19a21
     part_header = (ChrpNvramPartHdr *)data;
a19a21
     part_header->signature = CHRP_NVPART_SYSTEM;
a19a21
     pstrcpy(part_header->name, sizeof(part_header->name), "system");
a19a21
 
a19a21
     end = sizeof(ChrpNvramPartHdr);
a19a21
     for (i = 0; i < nb_prom_envs; i++) {
a19a21
-        end = chrp_nvram_set_var(data, end, prom_envs[i]);
a19a21
+        end = chrp_nvram_set_var(data, end, prom_envs[i], max_len - end);
a19a21
+        if (end == -1) {
a19a21
+            goto fail;
a19a21
+        }
a19a21
     }
a19a21
 
a19a21
     /* End marker */
a19a21
@@ -65,6 +79,10 @@ int chrp_nvram_create_system_partition(uint8_t *data, int min_len)
a19a21
     chrp_nvram_finish_partition(part_header, end);
a19a21
 
a19a21
     return end;
a19a21
+
a19a21
+fail:
a19a21
+    error_report("NVRAM is too small. Try to pass less data to -prom-env");
a19a21
+    exit(EXIT_FAILURE);
a19a21
 }
a19a21
 
a19a21
 /**
a19a21
diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c
a19a21
index 9a47e35b8e..ecfb36182f 100644
a19a21
--- a/hw/nvram/mac_nvram.c
a19a21
+++ b/hw/nvram/mac_nvram.c
a19a21
@@ -152,7 +152,7 @@ static void pmac_format_nvram_partition_of(MacIONVRAMState *nvr, int off,
a19a21
 
a19a21
     /* OpenBIOS nvram variables partition */
a19a21
     sysp_end = chrp_nvram_create_system_partition(&nvr->data[off],
a19a21
-                                                  DEF_SYSTEM_SIZE) + off;
a19a21
+                                                  DEF_SYSTEM_SIZE, len) + off;
a19a21
 
a19a21
     /* Free space partition */
a19a21
     chrp_nvram_create_free_partition(&nvr->data[sysp_end], len - sysp_end);
a19a21
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
a19a21
index 838082b451..225cd69b49 100644
a19a21
--- a/hw/nvram/spapr_nvram.c
a19a21
+++ b/hw/nvram/spapr_nvram.c
a19a21
@@ -188,7 +188,8 @@ static void spapr_nvram_realize(SpaprVioDevice *dev, Error **errp)
a19a21
         }
a19a21
     } else if (nb_prom_envs > 0) {
a19a21
         /* Create a system partition to pass the -prom-env variables */
a19a21
-        chrp_nvram_create_system_partition(nvram->buf, MIN_NVRAM_SIZE / 4);
a19a21
+        chrp_nvram_create_system_partition(nvram->buf, MIN_NVRAM_SIZE / 4,
a19a21
+                                           nvram->size);
a19a21
         chrp_nvram_create_free_partition(&nvram->buf[MIN_NVRAM_SIZE / 4],
a19a21
                                          nvram->size - MIN_NVRAM_SIZE / 4);
a19a21
     }
a19a21
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
a19a21
index 2aaa5bf1ae..cf2d0762d9 100644
a19a21
--- a/hw/sparc/sun4m.c
a19a21
+++ b/hw/sparc/sun4m.c
a19a21
@@ -142,7 +142,7 @@ static void nvram_init(Nvram *nvram, uint8_t *macaddr,
a19a21
     memset(image, '\0', sizeof(image));
a19a21
 
a19a21
     /* OpenBIOS nvram variables partition */
a19a21
-    sysp_end = chrp_nvram_create_system_partition(image, 0);
a19a21
+    sysp_end = chrp_nvram_create_system_partition(image, 0, 0x1fd0);
a19a21
 
a19a21
     /* Free space partition */
a19a21
     chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end);
a19a21
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
a19a21
index 955082773b..f5295a687e 100644
a19a21
--- a/hw/sparc64/sun4u.c
a19a21
+++ b/hw/sparc64/sun4u.c
a19a21
@@ -137,7 +137,7 @@ static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size,
a19a21
     memset(image, '\0', sizeof(image));
a19a21
 
a19a21
     /* OpenBIOS nvram variables partition */
a19a21
-    sysp_end = chrp_nvram_create_system_partition(image, 0);
a19a21
+    sysp_end = chrp_nvram_create_system_partition(image, 0, 0x1fd0);
a19a21
 
a19a21
     /* Free space partition */
a19a21
     chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end);
a19a21
diff --git a/include/hw/nvram/chrp_nvram.h b/include/hw/nvram/chrp_nvram.h
a19a21
index 09941a9be4..4a0f5c21b8 100644
a19a21
--- a/include/hw/nvram/chrp_nvram.h
a19a21
+++ b/include/hw/nvram/chrp_nvram.h
a19a21
@@ -50,7 +50,8 @@ chrp_nvram_finish_partition(ChrpNvramPartHdr *header, uint32_t size)
a19a21
     header->checksum = sum & 0xff;
a19a21
 }
a19a21
 
a19a21
-int chrp_nvram_create_system_partition(uint8_t *data, int min_len);
a19a21
+/* chrp_nvram_create_system_partition() failure is fatal */
a19a21
+int chrp_nvram_create_system_partition(uint8_t *data, int min_len, int max_len);
a19a21
 int chrp_nvram_create_free_partition(uint8_t *data, int len);
a19a21
 
a19a21
 #endif
a19a21
-- 
a19a21
2.27.0
a19a21