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

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