[image] Check delimiters when parsing command-line key-value arguments

The Linux kernel bzImage image format and the CPIO archive constructor
will parse the image command line for certain arguments of the form
"key=value".  This parsing is currently implemented using strstr() in
a way that can cause a false positive suffix match.  For example, a
command line containing "highmem=<n>" would erroneously be treated as
containing a value for "mem=<n>".

Fix by centralising the logic used for parsing such arguments, and
including a check that the argument immediately follows a whitespace
delimiter (or is at the start of the string).

Reported-by: Filippo Giunchedi <filippo@esaurito.net>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/892/head
Michael Brown 2023-02-13 20:40:42 +00:00
parent 3c83843e11
commit 76a286530a
4 changed files with 50 additions and 28 deletions

View File

@ -247,19 +247,17 @@ static void bzimage_update_header ( struct image *image,
* *
* @v image bzImage file * @v image bzImage file
* @v bzimg bzImage context * @v bzimg bzImage context
* @v cmdline Kernel command line
* @ret rc Return status code * @ret rc Return status code
*/ */
static int bzimage_parse_cmdline ( struct image *image, static int bzimage_parse_cmdline ( struct image *image,
struct bzimage_context *bzimg, struct bzimage_context *bzimg ) {
char *cmdline ) { const char *vga;
const char *mem;
char *sep; char *sep;
char *vga; char *end;
char *mem;
/* Look for "vga=" */ /* Look for "vga=" */
if ( ( vga = strstr ( cmdline, "vga=" ) ) ) { if ( ( vga = image_argument ( image, "vga=" ) ) ) {
vga += 4;
sep = strchr ( vga, ' ' ); sep = strchr ( vga, ' ' );
if ( sep ) if ( sep )
*sep = '\0'; *sep = '\0';
@ -270,10 +268,10 @@ static int bzimage_parse_cmdline ( struct image *image,
} else if ( strcmp ( vga, "ask" ) == 0 ) { } else if ( strcmp ( vga, "ask" ) == 0 ) {
bzimg->vid_mode = BZI_VID_MODE_ASK; bzimg->vid_mode = BZI_VID_MODE_ASK;
} else { } else {
bzimg->vid_mode = strtoul ( vga, &vga, 0 ); bzimg->vid_mode = strtoul ( vga, &end, 0 );
if ( *vga ) { if ( *end ) {
DBGC ( image, "bzImage %p strange \"vga=\" " DBGC ( image, "bzImage %p strange \"vga=\" "
"terminator '%c'\n", image, *vga ); "terminator '%c'\n", image, *end );
} }
} }
if ( sep ) if ( sep )
@ -281,10 +279,9 @@ static int bzimage_parse_cmdline ( struct image *image,
} }
/* Look for "mem=" */ /* Look for "mem=" */
if ( ( mem = strstr ( cmdline, "mem=" ) ) ) { if ( ( mem = image_argument ( image, "mem=" ) ) ) {
mem += 4; bzimg->mem_limit = strtoul ( mem, &end, 0 );
bzimg->mem_limit = strtoul ( mem, &mem, 0 ); switch ( *end ) {
switch ( *mem ) {
case 'G': case 'G':
case 'g': case 'g':
bzimg->mem_limit <<= 10; bzimg->mem_limit <<= 10;
@ -302,7 +299,7 @@ static int bzimage_parse_cmdline ( struct image *image,
break; break;
default: default:
DBGC ( image, "bzImage %p strange \"mem=\" " DBGC ( image, "bzImage %p strange \"mem=\" "
"terminator '%c'\n", image, *mem ); "terminator '%c'\n", image, *end );
break; break;
} }
bzimg->mem_limit -= 1; bzimg->mem_limit -= 1;
@ -316,11 +313,10 @@ static int bzimage_parse_cmdline ( struct image *image,
* *
* @v image bzImage image * @v image bzImage image
* @v bzimg bzImage context * @v bzimg bzImage context
* @v cmdline Kernel command line
*/ */
static void bzimage_set_cmdline ( struct image *image, static void bzimage_set_cmdline ( struct image *image,
struct bzimage_context *bzimg, struct bzimage_context *bzimg ) {
const char *cmdline ) { const char *cmdline = ( image->cmdline ? image->cmdline : "" );
size_t cmdline_len; size_t cmdline_len;
/* Copy command line down to real-mode portion */ /* Copy command line down to real-mode portion */
@ -528,7 +524,6 @@ static void bzimage_load_initrds ( struct image *image,
*/ */
static int bzimage_exec ( struct image *image ) { static int bzimage_exec ( struct image *image ) {
struct bzimage_context bzimg; struct bzimage_context bzimg;
char *cmdline = ( image->cmdline ? image->cmdline : "" );
int rc; int rc;
/* Read and parse header from image */ /* Read and parse header from image */
@ -551,7 +546,7 @@ static int bzimage_exec ( struct image *image ) {
} }
/* Parse command line for bootloader parameters */ /* Parse command line for bootloader parameters */
if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0) if ( ( rc = bzimage_parse_cmdline ( image, &bzimg ) ) != 0)
return rc; return rc;
/* Check that initrds can be loaded */ /* Check that initrds can be loaded */
@ -568,7 +563,7 @@ static int bzimage_exec ( struct image *image ) {
bzimg.rm_filesz, bzimg.pm_sz ); bzimg.rm_filesz, bzimg.pm_sz );
/* Store command line */ /* Store command line */
bzimage_set_cmdline ( image, &bzimg, cmdline ); bzimage_set_cmdline ( image, &bzimg );
/* Prepare for exiting. Must do this before loading initrds, /* Prepare for exiting. Must do this before loading initrds,
* since loading the initrds will corrupt the external heap. * since loading the initrds will corrupt the external heap.

View File

@ -77,17 +77,12 @@ size_t cpio_name_len ( struct image *image ) {
*/ */
static void cpio_parse_cmdline ( struct image *image, static void cpio_parse_cmdline ( struct image *image,
struct cpio_header *cpio ) { struct cpio_header *cpio ) {
const char *cmdline; const char *arg;
char *arg;
char *end; char *end;
unsigned int mode; unsigned int mode;
/* Skip image filename */
cmdline = ( cpio_name ( image ) + cpio_name_len ( image ) );
/* Look for "mode=" */ /* Look for "mode=" */
if ( ( arg = strstr ( cmdline, "mode=" ) ) ) { if ( ( arg = image_argument ( image, "mode=" ) ) ) {
arg += 5;
mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
if ( *end && ( *end != ' ' ) ) { if ( *end && ( *end != ' ' ) ) {
DBGC ( image, "CPIO %p strange \"mode=\" " DBGC ( image, "CPIO %p strange \"mode=\" "

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <assert.h> #include <assert.h>
#include <libgen.h> #include <libgen.h>
@ -569,3 +570,33 @@ struct image * image_memory ( const char *name, userptr_t data, size_t len ) {
err_alloc_image: err_alloc_image:
return NULL; return NULL;
} }
/**
* Find argument within image command line
*
* @v image Image
* @v key Argument search key (including trailing delimiter)
* @ret value Argument value, or NULL if not found
*/
const char * image_argument ( struct image *image, const char *key ) {
const char *cmdline = image->cmdline;
const char *search;
const char *match;
const char *next;
/* Find argument */
for ( search = cmdline ; search ; search = next ) {
/* Find next occurrence, if any */
match = strstr ( search, key );
if ( ! match )
break;
next = ( match + strlen ( key ) );
/* Check preceding delimiter, if any */
if ( ( match == cmdline ) || isspace ( match[-1] ) )
return next;
}
return NULL;
}

View File

@ -195,6 +195,7 @@ extern struct image * image_find_selected ( void );
extern int image_set_trust ( int require_trusted, int permanent ); extern int image_set_trust ( int require_trusted, int permanent );
extern struct image * image_memory ( const char *name, userptr_t data, extern struct image * image_memory ( const char *name, userptr_t data,
size_t len ); size_t len );
extern const char * image_argument ( struct image *image, const char *key );
extern int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ); extern int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf );
extern int image_asn1 ( struct image *image, size_t offset, extern int image_asn1 ( struct image *image, size_t offset,
struct asn1_cursor **cursor ); struct asn1_cursor **cursor );