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

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