|
|
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 |
|