Remove artificial length limits when parsing options
cover Remove artificial length limits when parsing options
A user trying out SMBIOS "OEM strings" feature reported that the data they are exposing to the guest was truncated at 1023 bytes, which breaks the app consuming in the guest. After searching for the cause I eventually found that the QemuOpts parsing is using fixed length 1024 byte array for option values and 128 byte array for key names.
We can certainly debate whether it is sane to have such long command line argument values (it is not sane), but if the OS was capable of exec'ing QEMU with such an ARGV array, there is little good reason for imposing an artificial length restriction when parsing it. Even worse is that we silently truncate without reporting an error when hitting limits resulting in a semantically incorrect behaviour, possibly even leading to security flaws depending on the data that was truncated.
Thus this patch series removes the artificial length limits by killing the fixed length buffers.
Separately I intend to make it possible to read "OEM strings" data from a file, to avoid need to have long command line args.
Daniel P. Berrangé (3):
accel: use g_strsplit for parsing accelerator names
opts: don't silently truncate long parameter keys
opts: don't silently truncate long option values
accel/accel.c | 16 +++--- hw/i386/multiboot.c | 33 +++++++---- include/qemu/option.h | 3 +- tests/test-qemu-opts.c | 18 ------ util/qemu-option.c | 150 ++++++++++++++++++++++++++----------------------- 5 files changed, 108 insertions(+), 112 deletions(-)
Signature · 1 line
2.14.3
PATCH 1/3 accel: use g_strsplit for parsing accelerator names
Instead of re-using the get_opt_name() method from QemuOpts to split a string on ':', just use g_strsplit().
--- accel/accel.c | 16 +++++++--------- include/qemu/option.h | 1 - util/qemu-option.c | 3 ++- 3 files changed, 9 insertions(+), 11 deletions(-)
accel/accel.c +7 -9
@@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)void configure_accelerator (MachineState *ms) { -const char *accel, *p; -char buf[10 ]; +const char *accel; +char **accel_list, **tmp;int ret;bool accel_initialised = false;bool init_failed = false;
@@ -83,13 +83,10 @@ void configure_accelerator(MachineState *ms) accel ="tcg" ; } - p = accel; -while (!accel_initialised && *p !='\0' ) { -if (*p ==':' ) { - p++; - } - p =get_opt_name (buf,sizeof (buf), p,':' ); - acc =accel_find (buf); + accel_list =g_strsplit (accel,":" ,0 ); + +for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { + acc =accel_find (*tmp);if (!acc) {continue ; }
@@ -107,6 +104,7 @@ void configure_accelerator(MachineState *ms) accel_initialised = true; } } +g_strfreev (accel_list);if (!accel_initialised) {if (!init_failed) {
include/qemu/option.h +0 -1
@@ -28,7 +28,6 @@#include "qemu/queue.h" -const char *get_opt_name (char *buf,int buf_size,const char *p,char delim);const char *get_opt_value (char *buf,int buf_size,const char *p); void parse_option_size(const char *name, const char *value,
util/qemu-option.c +2 -1
@@ -49,7 +49,8 @@* The return value is the position of the delimiter/zero byte after the option * name in p. */ -const char *get_opt_name (char *buf,int buf_size,const char *p,char delim) +static const char *get_opt_name (char *buf,int buf_size,const char *p, +char delim) {char *q;
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 1/3] accel: use g_strsplit for parsing accelerator names
Quoted 4 lines · Daniel P. Berrangé
Instead of re-using the
get_opt_name()method from QemuOpts to split a string on ':', just useg_strsplit().Signed-off-by Daniel P. Berrangé <[email protected]>
Quoted 74 lines
--- accel/accel.c | 16 +++++++--------- include/qemu/option.h | 1 - util/qemu-option.c | 3 ++- 3 files changed, 9 insertions(+), 11 deletions(-)accel/accel.c +7
-9@@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)void configure_accelerator (MachineState *ms) { -const char *accel, *p; -char buf[10 ]; +const char *accel; +char **accel_list, **tmp;int ret;bool accel_initialised = false;bool init_failed = false;@@ -83,13 +83,10 @@ void configure_accelerator(MachineState *ms) accel ="tcg" ; } - p = accel; -while (!accel_initialised && *p !='\0' ) { -if (*p ==':' ) { - p++; - } - p =get_opt_name (buf,sizeof (buf), p,':' ); - acc =accel_find (buf); + accel_list =g_strsplit (accel,":" ,0 ); + +for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { + acc =accel_find (*tmp);if (!acc) {continue ; }@@ -107,6 +104,7 @@ void configure_accelerator(MachineState *ms) accel_initialised = true; } } +g_strfreev (accel_list);if (!accel_initialised) {if (!init_failed) {include/qemu/option.h +0
-1@@ -28,7 +28,6 @@#include "qemu/queue.h" -const char *get_opt_name (char *buf,int buf_size,const char *p,char delim);const char *get_opt_value (char *buf,int buf_size,const char *p); void parse_option_size(const char *name, const char *value,util/qemu-option.c +2
-1@@ -49,7 +49,8 @@* The return value is the position of the delimiter/zero byte after the option * name in p. */ -const char *get_opt_name (char *buf,int buf_size,const char *p,char delim) +static const char *get_opt_name (char *buf,int buf_size,const char *p, +char delim) {char *q;
PATCH 2/3 opts: don't silently truncate long parameter keys
In reply to Daniel P. BerrangéThe existing QemuOpts parsing code uses a fixed size 128 byte buffer for storing the parameter keys. If a key exceeded this size it was silently truncate and no error reported to the user. This behaviour was reasonable & harmless because traditionally the key names are all statically declared, and it was known that no code was declaring a key longer than 127 bytes. This assumption, however, ceased to be valid once the block layer added support for dot-separate compound keys. This syntax allows for keys that can be arbitrarily long, limited only by the number of block drivers you can stack up. With this usage, silently truncating the key name can never lead to correct behaviour.
Hopefully such truncation would turn into an error, when the block code then tried to extract options later, but there's no guarantee that will happen. It is conceivable that an option specified by the user may be truncated and then ignored. This could have serious consequences, possibly even leading to security problems if the ignored option set a security relevant parameter.
If the operating system didn't limit the user's argv when spawning QEMU, the code should honour whatever length arguments were given without imposing its own length restrictions. This patch thus changes the code to use a heap allocated buffer for storing the keys during parsing, lifting the arbitrary length restriction.
--- tests/test-qemu-opts.c | 18 ------------------ util/qemu-option.c | 44 ++++++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 40 deletions(-)
tests/test-qemu-opts.c +0 -18
@@ -459,8 +459,6 @@ static void test_opts_parse(void) {Error *err =NULL ;QemuOpts *opts; -char long_key[129 ]; -char *params;/* Nothing */ opts =qemu_opts_parse (&opts_list_03,"" , false, &error_abort);
@@ -471,22 +469,6 @@ static void test_opts_parse(void) g_assert_cmpuint(opts_count(opts), ==, 1); g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); -/* Long key */ -memset (long_key,'a' ,127 ); - long_key[127 ] ='z' ; - long_key[128 ] =0 ; - params =g_strdup_printf ("%s=v" , long_key); - opts =qemu_opts_parse (&opts_list_03, params +1 ,NULL , &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, long_key + 1), ==, "v"); - -/* Overlong key gets truncated */ - opts =qemu_opts_parse (&opts_list_03, params,NULL , &error_abort); -g_assert (opts_count (opts) ==1 ); - long_key[127 ] =0 ; - g_assert_cmpstr(qemu_opt_get(opts, long_key), ==, "v"); -g_free (params); -/* Multiple keys, last one wins */ opts =qemu_opts_parse (&opts_list_03,"a=1,b=2,,x,a=3" , false, &error_abort);
util/qemu-option.c +22 -22
90 lines not on this page · raw message
Signature · 1 line
2.14.3
PATCH 3/3 opts: don't silently truncate long option values
In reply to Daniel P. BerrangéThe existing QemuOpts parsing code uses a fixed size 1024 byte buffer for storing the option values. If a value exceeded this size it was silently truncated and no error reported to the user. Long option values is not a common scenario, but it is conceivable that they will happen. eg if the user has a very deeply nested filesystem it would be possible to come up with a disk path that was > 1024 bytes. Most of the time if such data was silently truncated, the user would get an error about opening a non-existant disk. If they're unlucky though, QEMU might use a completely different disk image from another VM, which could be considered a security issue. Another example program was in using the -smbios command line arg with very large data blobs. In this case the silent truncation will be providing semantically incorrect data to the guest OS for SMBIOS tables.
If the operating system didn't limit the user's argv when spawning QEMU, the code should honour whatever length arguments were given without imposing its own length restrictions. This patch thus changes the code to use a heap allocated buffer for storing the values during parsing, lifting the arbitrary length restriction.
--- hw/i386/multiboot.c | 33 +++++++++------ include/qemu/option.h | 2 +- util/qemu-option.c | 111 +++++++++++++++++++++++++++----------------------- 3 files changed, 81 insertions(+), 65 deletions(-)
hw/i386/multiboot.c +21 -12
@@ -291,12 +291,16 @@ int load_multiboot(FWCfgState *fw_cfg, cmdline_len =strlen (kernel_filename) +1 ; cmdline_len +=strlen (kernel_cmdline) +1 ;if (initrd_filename) { -const char *r = initrd_filename; +const char *r =get_opt_value (initrd_filename,NULL ); cmdline_len +=strlen (r) +1 ; mbs.mb_mods_avail =1 ; -while (*(r =get_opt_value (NULL ,0 , r))) { - mbs.mb_mods_avail++; - r++; +while (1 ) { + mbs.mb_mods_avail++; + r =get_opt_value (r,NULL ); +if (!*r) { +break ; + } + r++; } }
@@ -313,7 +317,8 @@ int load_multiboot(FWCfgState *fw_cfg,if (initrd_filename) {const char *next_initrd; -char not_last, tmpbuf[strlen (initrd_filename) +1 ]; +char not_last; +char *one_file =NULL ; mbs.offset_mods = mbs.mb_buf_size;
@@ -322,24 +327,26 @@ int load_multiboot(FWCfgState *fw_cfg,int mb_mod_length;uint32_t offs = mbs.mb_buf_size; - next_initrd =get_opt_value (tmpbuf,sizeof (tmpbuf), initrd_filename); + next_initrd =get_opt_value (initrd_filename, &one_file); not_last = *next_initrd;/* if a space comes after the module filename, treat everything after that as parameters */ -hwaddr c =mb_add_cmdline (&mbs, tmpbuf); -if ((next_space =strchr (tmpbuf,' ' ))) +hwaddr c =mb_add_cmdline (&mbs, one_file); + next_space =strchr (one_file,' ' ); +if (next_space) { *next_space ='\0' ; -mb_debug ("multiboot loading module: %s" , tmpbuf); - mb_mod_length =get_image_size (tmpbuf); + } +mb_debug ("multiboot loading module: %s" , one_file); + mb_mod_length =get_image_size (one_file);if (mb_mod_length <0 ) { -error_report ("Failed to open file '%s'" , tmpbuf); +error_report ("Failed to open file '%s'" , one_file);exit (1 ); } mbs.mb_buf_size =TARGET_PAGE_ALIGN (mb_mod_length + mbs.mb_buf_size); mbs.mb_buf =g_realloc (mbs.mb_buf, mbs.mb_buf_size); -load_image (tmpbuf, (unsigned char *)mbs.mb_buf + offs); +load_image (one_file, (unsigned char *)mbs.mb_buf + offs);mb_add_mod (&mbs, mbs.mb_buf_phys + offs, mbs.mb_buf_phys + offs + mb_mod_length, c);
@@ -347,6 +354,8 @@ int load_multiboot(FWCfgState *fw_cfg, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); initrd_filename = next_initrd+1 ; +g_free (one_file); + one_file =NULL ; }while (not_last); }
include/qemu/option.h +1 -1
@@ -28,7 +28,7 @@#include "qemu/queue.h" -const char *get_opt_value (char *buf,int buf_size,const char *p); +const char *get_opt_value (const char *p,char **value);void parse_option_size (const char *name,const char *value,uint64_t *ret,Error **errp);
util/qemu-option.c +59 -52
230 lines not on this page · raw message
Signature · 1 line
2.14.3
Paolo Bonzini
AppliedQuoted 25 lines · Daniel P. Berrangé
A user trying out SMBIOS "OEM strings" feature reported that the data they are exposing to the guest was truncated at 1023 bytes, which breaks the app consuming in the guest. After searching for the cause I eventually found that the QemuOpts parsing is using fixed length 1024 byte array for option values and 128 byte array for key names.
We can certainly debate whether it is sane to have such long command line argument values (it is not sane), but if the OS was capable of exec'ing QEMU with such an ARGV array, there is little good reason for imposing an artificial length restriction when parsing it. Even worse is that we silently truncate without reporting an error when hitting limits resulting in a semantically incorrect behaviour, possibly even leading to security flaws depending on the data that was truncated.
Thus this patch series removes the artificial length limits by killing the fixed length buffers.
Separately I intend to make it possible to read "OEM strings" data from a file, to avoid need to have long command line args.
Daniel P. Berrangé (3):
accel: useg_strsplitfor parsing accelerator names
opts: don't silently truncate long parameter keys
opts: don't silently truncate long option values
Quoted 7 lines
accel/accel.c | 16 +++--- hw/i386/multiboot.c | 33 +++++++---- include/qemu/option.h | 3 +- tests/test-qemu-opts.c | 18 ------ util/qemu-option.c | 150 ++++++++++++++++++++++++++----------------------- 5 files changed, 108 insertions(+), 112 deletions(-)
Queued, thanks.
Paolo
Quoted 14 lines · Daniel P. Berrangé
A user trying out SMBIOS "OEM strings" feature reported that the data they are exposing to the guest was truncated at 1023 bytes, which breaks the app consuming in the guest. After searching for the cause I eventually found that the QemuOpts parsing is using fixed length 1024 byte array for option values and 128 byte array for key names.
We can certainly debate whether it is sane to have such long command line argument values (it is not sane), but if the OS was capable of exec'ing QEMU with such an ARGV array, there is little good reason for imposing an artificial length restriction when parsing it. Even worse is that we silently truncate without reporting an error when hitting limits resulting in a semantically incorrect behaviour, possibly even leading to security flaws depending on the data that was truncated.
Quoted 5 lines
Thus this patch series removes the artificial length limits by killing the fixed length buffers.
Separately I intend to make it possible to read "OEM strings" data from a file, to avoid need to have long command line args.
Too bad I haven't been able to complete my quest to kill QemuOpts.
As far as I know, keyval.c's only arbitrary limit is the length of a key fragment (the things separated by '.').
Quoted 19 lines · Markus Armbruster
Quoted 17 lines · Daniel P. Berrangé
A user trying out SMBIOS "OEM strings" feature reported that the data they are exposing to the guest was truncated at 1023 bytes, which breaks the app consuming in the guest. After searching for the cause I eventually found that the QemuOpts parsing is using fixed length 1024 byte array for option values and 128 byte array for key names.
We can certainly debate whether it is sane to have such long command line argument values (it is not sane), but if the OS was capable of exec'ing QEMU with such an ARGV array, there is little good reason for imposing an artificial length restriction when parsing it. Even worse is that we silently truncate without reporting an error when hitting limits resulting in a semantically incorrect behaviour, possibly even leading to security flaws depending on the data that was truncated.
Thus this patch series removes the artificial length limits by killing the fixed length buffers.
Quoted 7 lines
Quoted 2 lines
Separately I intend to make it possible to read "OEM strings" data from a file, to avoid need to have long command line args.
Too bad I haven't been able to complete my quest to kill QemuOpts.
As far as I know, keyval.c's only arbitrary limit is the length of a key fragment (the things separated by '.').
Looks like that's the same scenario I tried to address in patch 2. The 'key' part in QemuOpts has the same 128 byte limit as in the keyval.c code. I fear that could be hit with -blockdev when setting params on very deeply nested block backends. On the plus side keyval.c actually reports an error when it hits its 128 byte limit, instead of silently carrying on as if all was well like QemuOpts did :-)
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Quoted 28 lines · Daniel P. Berrangé
Quoted 19 lines · Markus Armbruster
Quoted 17 lines · Daniel P. Berrangé
A user trying out SMBIOS "OEM strings" feature reported that the data they are exposing to the guest was truncated at 1023 bytes, which breaks the app consuming in the guest. After searching for the cause I eventually found that the QemuOpts parsing is using fixed length 1024 byte array for option values and 128 byte array for key names.
We can certainly debate whether it is sane to have such long command line argument values (it is not sane), but if the OS was capable of exec'ing QEMU with such an ARGV array, there is little good reason for imposing an artificial length restriction when parsing it. Even worse is that we silently truncate without reporting an error when hitting limits resulting in a semantically incorrect behaviour, possibly even leading to security flaws depending on the data that was truncated.
Thus this patch series removes the artificial length limits by killing the fixed length buffers.
Quoted 7 lines
Quoted 2 lines
Separately I intend to make it possible to read "OEM strings" data from a file, to avoid need to have long command line args.
Too bad I haven't been able to complete my quest to kill QemuOpts.
As far as I know, keyval.c's only arbitrary limit is the length of a key fragment (the things separated by '.').
Quoted 6 lines
Looks like that's the same scenario I tried to address in patch 2. The 'key' part in QemuOpts has the same 128 byte limit as in the
keyval.ccode. I fear that could be hit with -blockdev when setting params on very deeply nested block backends. On the plus sidekeyval.cactually reports an error when it hits its 128 byte limit, instead of silently carrying on as if all was well like QemuOpts did :-)
In keyval.c, the key (things like "a.b.c") can be arbitrarily long (well, until g_malloc() throws in the towel), but each key fragment ("a", "b" and "c") is limited to 128 bytes.
If key length was limited there, I would've asked you to fix it there, too.
Quoted 37 lines · Markus Armbruster
Quoted 28 lines · Daniel P. Berrangé
Quoted 19 lines · Markus Armbruster
Quoted 17 lines · Daniel P. Berrangé
A user trying out SMBIOS "OEM strings" feature reported that the data they are exposing to the guest was truncated at 1023 bytes, which breaks the app consuming in the guest. After searching for the cause I eventually found that the QemuOpts parsing is using fixed length 1024 byte array for option values and 128 byte array for key names.
We can certainly debate whether it is sane to have such long command line argument values (it is not sane), but if the OS was capable of exec'ing QEMU with such an ARGV array, there is little good reason for imposing an artificial length restriction when parsing it. Even worse is that we silently truncate without reporting an error when hitting limits resulting in a semantically incorrect behaviour, possibly even leading to security flaws depending on the data that was truncated.
Thus this patch series removes the artificial length limits by killing the fixed length buffers.
Quoted 7 lines
Quoted 2 lines
Separately I intend to make it possible to read "OEM strings" data from a file, to avoid need to have long command line args.
Too bad I haven't been able to complete my quest to kill QemuOpts.
As far as I know, keyval.c's only arbitrary limit is the length of a key fragment (the things separated by '.').
Quoted 6 lines
Looks like that's the same scenario I tried to address in patch 2. The 'key' part in QemuOpts has the same 128 byte limit as in the
keyval.ccode. I fear that could be hit with -blockdev when setting params on very deeply nested block backends. On the plus sidekeyval.cactually reports an error when it hits its 128 byte limit, instead of silently carrying on as if all was well like QemuOpts did :-)
Quoted 6 lines
In
keyval.c, the key (things like "a.b.c") can be arbitrarily long (well, untilg_malloc()throws in the towel), but each key fragment ("a", "b" and "c") is limited to 128 bytes.If key length was limited there, I would've asked you to fix it there, too.
Agreed, if only fragments are limited, that's fine because we know that no code ever declares a key long enough to exceed the individual fragment size.
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|