Blame SOURCES/kvm-opts-don-t-silently-truncate-long-parameter-keys.patch

016a62
From 5fe3c58c3a57a04254b3083b070fdf99fba82c93 Mon Sep 17 00:00:00 2001
016a62
From: Laszlo Ersek <lersek@redhat.com>
016a62
Date: Thu, 12 Sep 2019 13:04:59 +0100
016a62
Subject: [PATCH 02/22] opts: don't silently truncate long parameter keys
016a62
MIME-Version: 1.0
016a62
Content-Type: text/plain; charset=UTF-8
016a62
Content-Transfer-Encoding: 8bit
016a62
016a62
RH-Author: Laszlo Ersek <lersek@redhat.com>
016a62
Message-id: <20190912130503.14094-3-lersek@redhat.com>
016a62
Patchwork-id: 90435
016a62
O-Subject: [RHEL-8.2.0 qemu-kvm PATCH 2/6] opts: don't silently truncate long parameter keys
016a62
Bugzilla: 1749022
016a62
RH-Acked-by: Stefano Garzarella <sgarzare@redhat.com>
016a62
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
016a62
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
016a62
016a62
From: Daniel P. Berrangé <berrange@redhat.com>
016a62
016a62
The existing QemuOpts parsing code uses a fixed size 128 byte buffer
016a62
for storing the parameter keys. If a key exceeded this size it was
016a62
silently truncate and no error reported to the user. This behaviour was
016a62
reasonable & harmless because traditionally the key names are all
016a62
statically declared, and it was known that no code was declaring a key
016a62
longer than 127 bytes. This assumption, however, ceased to be valid once
016a62
the block layer added support for dot-separate compound keys. This
016a62
syntax allows for keys that can be arbitrarily long, limited only by the
016a62
number of block drivers you can stack up. With this usage, silently
016a62
truncating the key name can never lead to correct behaviour.
016a62
016a62
Hopefully such truncation would turn into an error, when the block code
016a62
then tried to extract options later, but there's no guarantee that will
016a62
happen. It is conceivable that an option specified by the user may be
016a62
truncated and then ignored. This could have serious consequences,
016a62
possibly even leading to security problems if the ignored option set a
016a62
security relevant parameter.
016a62
016a62
If the operating system didn't limit the user's argv when spawning QEMU,
016a62
the code should honour whatever length arguments were given without
016a62
imposing its own length restrictions. This patch thus changes the code
016a62
to use a heap allocated buffer for storing the keys during parsing,
016a62
lifting the arbitrary length restriction.
016a62
016a62
RHEL8 notes:
016a62
016a62
- Fix up upstream's obviously garbled UTF8 sequences in Dan's name (Author
016a62
  meta-datum, Signed-off-by tags).
016a62
016a62
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
016a62
Message-Id: <20180416111743.8473-3-berrange@redhat.com>
016a62
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
016a62
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
016a62
(cherry picked from commit e652714f98f22e8882e88e3d563b025c5b00feec)
016a62
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
016a62
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
016a62
---
016a62
 tests/test-qemu-opts.c | 18 ------------------
016a62
 util/qemu-option.c     | 44 ++++++++++++++++++++++----------------------
016a62
 2 files changed, 22 insertions(+), 40 deletions(-)
016a62
016a62
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
016a62
index 77dd72b..7092e21 100644
016a62
--- a/tests/test-qemu-opts.c
016a62
+++ b/tests/test-qemu-opts.c
016a62
@@ -459,8 +459,6 @@ static void test_opts_parse(void)
016a62
 {
016a62
     Error *err = NULL;
016a62
     QemuOpts *opts;
016a62
-    char long_key[129];
016a62
-    char *params;
016a62
 
016a62
     /* Nothing */
016a62
     opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort);
016a62
@@ -471,22 +469,6 @@ static void test_opts_parse(void)
016a62
     g_assert_cmpuint(opts_count(opts), ==, 1);
016a62
     g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val");
016a62
 
016a62
-    /* Long key */
016a62
-    memset(long_key, 'a', 127);
016a62
-    long_key[127] = 'z';
016a62
-    long_key[128] = 0;
016a62
-    params = g_strdup_printf("%s=v", long_key);
016a62
-    opts = qemu_opts_parse(&opts_list_03, params + 1, NULL, &error_abort);
016a62
-    g_assert_cmpuint(opts_count(opts), ==, 1);
016a62
-    g_assert_cmpstr(qemu_opt_get(opts, long_key + 1), ==, "v");
016a62
-
016a62
-    /* Overlong key gets truncated */
016a62
-    opts = qemu_opts_parse(&opts_list_03, params, NULL, &error_abort);
016a62
-    g_assert(opts_count(opts) == 1);
016a62
-    long_key[127] = 0;
016a62
-    g_assert_cmpstr(qemu_opt_get(opts, long_key), ==, "v");
016a62
-    g_free(params);
016a62
-
016a62
     /* Multiple keys, last one wins */
016a62
     opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3",
016a62
                            false, &error_abort);
016a62
diff --git a/util/qemu-option.c b/util/qemu-option.c
016a62
index a8db173..b99568f 100644
016a62
--- a/util/qemu-option.c
016a62
+++ b/util/qemu-option.c
016a62
@@ -43,27 +43,23 @@
016a62
  * first byte of the option name)
016a62
  *
016a62
  * The option name is delimited by delim (usually , or =) or the string end
016a62
- * and is copied into buf. If the option name is longer than buf_size, it is
016a62
- * truncated. buf is always zero terminated.
016a62
+ * and is copied into option. The caller is responsible for free'ing option
016a62
+ * when no longer required.
016a62
  *
016a62
  * The return value is the position of the delimiter/zero byte after the option
016a62
  * name in p.
016a62
  */
016a62
-static const char *get_opt_name(char *buf, int buf_size, const char *p,
016a62
-                                char delim)
016a62
+static const char *get_opt_name(const char *p, char **option, char delim)
016a62
 {
016a62
-    char *q;
016a62
+    char *offset = strchr(p, delim);
016a62
 
016a62
-    q = buf;
016a62
-    while (*p != '\0' && *p != delim) {
016a62
-        if (q && (q - buf) < buf_size - 1)
016a62
-            *q++ = *p;
016a62
-        p++;
016a62
+    if (offset) {
016a62
+        *option = g_strndup(p, offset - p);
016a62
+        return offset;
016a62
+    } else {
016a62
+        *option = g_strdup(p);
016a62
+        return p + strlen(p);
016a62
     }
016a62
-    if (q)
016a62
-        *q = '\0';
016a62
-
016a62
-    return p;
016a62
 }
016a62
 
016a62
 /*
016a62
@@ -757,7 +753,8 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
016a62
 static void opts_do_parse(QemuOpts *opts, const char *params,
016a62
                           const char *firstname, bool prepend, Error **errp)
016a62
 {
016a62
-    char option[128], value[1024];
016a62
+    char *option = NULL;
016a62
+    char value[1024];
016a62
     const char *p,*pe,*pc;
016a62
     Error *local_err = NULL;
016a62
 
016a62
@@ -768,11 +765,11 @@ static void opts_do_parse(QemuOpts *opts, const char *params,
016a62
             /* found "foo,more" */
016a62
             if (p == params && firstname) {
016a62
                 /* implicitly named first option */
016a62
-                pstrcpy(option, sizeof(option), firstname);
016a62
+                option = g_strdup(firstname);
016a62
                 p = get_opt_value(value, sizeof(value), p);
016a62
             } else {
016a62
                 /* option without value, probably a flag */
016a62
-                p = get_opt_name(option, sizeof(option), p, ',');
016a62
+                p = get_opt_name(p, &option, ',');
016a62
                 if (strncmp(option, "no", 2) == 0) {
016a62
                     memmove(option, option+2, strlen(option+2)+1);
016a62
                     pstrcpy(value, sizeof(value), "off");
016a62
@@ -782,10 +779,8 @@ static void opts_do_parse(QemuOpts *opts, const char *params,
016a62
             }
016a62
         } else {
016a62
             /* found "foo=bar,more" */
016a62
-            p = get_opt_name(option, sizeof(option), p, '=');
016a62
-            if (*p != '=') {
016a62
-                break;
016a62
-            }
016a62
+            p = get_opt_name(p, &option, '=');
016a62
+            assert(*p == '=');
016a62
             p++;
016a62
             p = get_opt_value(value, sizeof(value), p);
016a62
         }
016a62
@@ -794,13 +789,18 @@ static void opts_do_parse(QemuOpts *opts, const char *params,
016a62
             opt_set(opts, option, value, prepend, &local_err);
016a62
             if (local_err) {
016a62
                 error_propagate(errp, local_err);
016a62
-                return;
016a62
+                goto cleanup;
016a62
             }
016a62
         }
016a62
         if (*p != ',') {
016a62
             break;
016a62
         }
016a62
+        g_free(option);
016a62
+        option = NULL;
016a62
     }
016a62
+
016a62
+ cleanup:
016a62
+    g_free(option);
016a62
 }
016a62
 
016a62
 /**
016a62
-- 
016a62
1.8.3.1
016a62