efi/libstub: Don't parse overlong command lines
authorArvind Sankar <nivedita@alum.mit.edu>
Thu, 21 May 2020 00:29:21 +0000 (20:29 -0400)
committerArd Biesheuvel <ardb@kernel.org>
Fri, 22 May 2020 14:50:03 +0000 (16:50 +0200)
Check if the command line passed in is larger than COMMAND_LINE_SIZE,
and truncate it to the last full argument if so.

Signed-off-by: Arvind Sankar <nivedita@alum.mit.edu>
Link: https://lore.kernel.org/r/20200521002921.69650-1-nivedita@alum.mit.edu
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
drivers/firmware/efi/libstub/efi-stub-helper.c

index 9fb107e..dbfaf8d 100644 (file)
@@ -9,10 +9,12 @@
 
 #include <stdarg.h>
 
+#include <linux/ctype.h>
 #include <linux/efi.h>
 #include <linux/kernel.h>
 #include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */
 #include <asm/efi.h>
+#include <asm/setup.h>
 
 #include "efistub.h"
 
@@ -217,22 +219,33 @@ char *efi_convert_cmdline(efi_loaded_image_t *image,
        unsigned long cmdline_addr = 0;
        int options_chars = efi_table_attr(image, load_options_size) / 2;
        const u16 *options = efi_table_attr(image, load_options);
-       int options_bytes = 0;  /* UTF-8 bytes */
+       int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
+       bool in_quote = false;
        efi_status_t status;
 
        if (options) {
                s2 = options;
-               while (options_chars--) {
+               while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
                        u16 c = *s2++;
 
-                       if (c == L'\0' || c == L'\n')
-                               break;
+                       if (c < 0x80) {
+                               if (c == L'\0' || c == L'\n')
+                                       break;
+                               if (c == L'"')
+                                       in_quote = !in_quote;
+                               else if (!in_quote && isspace((char)c))
+                                       safe_options_bytes = options_bytes;
+
+                               options_bytes++;
+                               continue;
+                       }
+
                        /*
                         * Get the number of UTF-8 bytes corresponding to a
                         * UTF-16 character.
                         * The first part handles everything in the BMP.
                         */
-                       options_bytes += 1 + (c >= 0x80) + (c >= 0x800);
+                       options_bytes += 2 + (c >= 0x800);
                        /*
                         * Add one more byte for valid surrogate pairs. Invalid
                         * surrogates will be replaced with 0xfffd and take up
@@ -253,6 +266,11 @@ char *efi_convert_cmdline(efi_loaded_image_t *image,
                                }
                        }
                }
+               if (options_bytes >= COMMAND_LINE_SIZE) {
+                       options_bytes = safe_options_bytes;
+                       efi_err("Command line is too long: truncated to %d bytes\n",
+                               options_bytes);
+               }
        }
 
        options_bytes++;        /* NUL termination */