diff mbox

[U-Boot,v2] bootm: Avoid 256-byte overflow in fixup_silent_linux()

Message ID 1319133298-30249-1-git-send-email-dianders@chromium.org
State Changes Requested
Headers show

Commit Message

Doug Anderson Oct. 20, 2011, 5:54 p.m. UTC
This makes fixup_silent_linux() use malloc() to allocate its
working space, meaning that our maximum kernel command line
should only be limited by malloc().  Previously it was silently
overflowing the stack.

Signed-off-by: Doug Anderson <dianders@chromium.org>
---
v2: This is a simpler version of patch 3/4 in my previous patchset that just
uses malloc() without using the general command line munging funcs.  We can
separately continue to discuss about the general command func if desired.

 common/cmd_bootm.c |   44 ++++++++++++++++++++++++++++++++++----------
 1 files changed, 34 insertions(+), 10 deletions(-)

Comments

Wolfgang Denk Jan. 10, 2012, 10:28 p.m. UTC | #1
Dear Doug Anderson,

In message <1319133298-30249-1-git-send-email-dianders@chromium.org> you wrote:
> This makes fixup_silent_linux() use malloc() to allocate its
> working space, meaning that our maximum kernel command line
> should only be limited by malloc().  Previously it was silently
> overflowing the stack.
...
>  static void fixup_silent_linux(void)
>  {
> -	char buf[256], *start, *end;

Are you sure that the kernel's buffer is long enough?

For example on PowerPC, there is a current hard limit on 512
characters:

	arch/powerpc/boot/ops.h:#define COMMAND_LINE_SIZE       512
	arch/powerpc/kernel/setup-common.c:char cmd_line[COMMAND_LINE_SIZE];

On SPARC, we have 256 bytes hard limit, see arch/sparc/prom/bootstr_64.c:

	#define BARG_LEN  256
	...
	prom_getstring(prom_chosen_node, "bootargs",
			bootstr_info.bootstr_buf, BARG_LEN);

And so on for other architectures, for example:

arch/score/include/asm/setup.h:#define COMMAND_LINE_SIZE        256

arch/m68k/include/asm/setup.h:#define COMMAND_LINE_SIZE 256

arch/avr32/include/asm/setup.h:#define COMMAND_LINE_SIZE 256

arch/microblaze/include/asm/setup.h:#define COMMAND_LINE_SIZE   256

arch/mn10300/include/asm/param.h:#define COMMAND_LINE_SIZE 256

arch/sparc/include/asm/setup.h:# define COMMAND_LINE_SIZE 256

arch/cris/include/asm/setup.h:#define COMMAND_LINE_SIZE 256

arch/xtensa/include/asm/setup.h:#define COMMAND_LINE_SIZE       256

arch/alpha/include/asm/setup.h:#define COMMAND_LINE_SIZE        256


I think your patch is likely to break all these architectures?


Best regards,

Wolfgang Denk
Doug Anderson Jan. 10, 2012, 10:51 p.m. UTC | #2
Dear Wolfgang Denk,

On Tue, Jan 10, 2012 at 2:28 PM, Wolfgang Denk <wd@denx.de> wrote:
>> This makes fixup_silent_linux() use malloc() to allocate its
>> working space, meaning that our maximum kernel command line
>> should only be limited by malloc().  Previously it was silently
>> overflowing the stack.
> ...
>>  static void fixup_silent_linux(void)
>>  {
>> -     char buf[256], *start, *end;
>
> Are you sure that the kernel's buffer is long enough?

The kernel's buffer might be big enough, depending on the
architecture.  For ARM (which is what I'm on), I see that the command
line is 1024 bytes.

Here's where I'm looking:
<http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob;f=arch/arm/include/asm/setup.h;h=23ebc0c82a3975ae5c455dd39598e93ab33922e7;hb=refs/heads/master#l19>


> I think your patch is likely to break all these architectures?

I'm not sure how my patch would break these architectures.  My patch
removes a buffer overrun--it doesn't actually increase any particular
board's command line length.  I need this because my board uses a
command line that is ~300 bytes--under the kernel limit but currently
over u-boot's.

I agree completely that this patch doesn't remove all limits on Linux
command line length.  However, it does allow you to use the full Linux
command line length on kernels that have a #define with something
greater than 256.

In addition, a buffer overrun is a particularly gnarly failure case
(opens you up to all sorts of security attacks if someone can figure
out how to manipulate the command line), so is generally good to fix.

If you'd rather, I'd be happy to rework the patch to change the
hardcoded 256 to a CONFIG_COMMAND_LINE_SIZE, then add overflow
checking to the function.  That would allow my use case and also
prevent future buffer overruns.


-Doug
Mike Frysinger Jan. 10, 2012, 11:30 p.m. UTC | #3
On Tuesday 10 January 2012 17:28:05 Wolfgang Denk wrote:
> Doug Anderson wrote:
> > This makes fixup_silent_linux() use malloc() to allocate its
> > working space, meaning that our maximum kernel command line
> > should only be limited by malloc().  Previously it was silently
> > overflowing the stack.
> 
> ...
> 
> >  static void fixup_silent_linux(void)
> >  {
> > 
> > -	char buf[256], *start, *end;
> 
> Are you sure that the kernel's buffer is long enough?
> 
> For example on PowerPC, there is a current hard limit on 512
> characters:
> 
> 	arch/powerpc/boot/ops.h:#define COMMAND_LINE_SIZE       512
> 	arch/powerpc/kernel/setup-common.c:char cmd_line[COMMAND_LINE_SIZE];
> 
> On SPARC, we have 256 bytes hard limit, see arch/sparc/prom/bootstr_64.c:
> 
> 	#define BARG_LEN  256
> 	...
> 	prom_getstring(prom_chosen_node, "bootargs",
> 			bootstr_info.bootstr_buf, BARG_LEN);

i think this does len checking ...

> I think your patch is likely to break all these architectures?

i don't know about others, but on Blackfin, we don't care.  we just copy the 
first COMMAND_LINE_SIZE bytes out and ignore the rest.
-mike
Mike Frysinger Jan. 10, 2012, 11:31 p.m. UTC | #4
On Tuesday 10 January 2012 17:51:15 Doug Anderson wrote:
> On Tue, Jan 10, 2012 at 2:28 PM, Wolfgang Denk wrote:
> > I think your patch is likely to break all these architectures?
> 
> I'm not sure how my patch would break these architectures.

if the kernel doesn't do len checking on the input string and only looks for 
trailing NUL byte, you could trigger a buffer overflow in the kernel.  
personally, i'd say that's poor behavior on the part of the kernel, but we 
should still be nice if possible ...
-mike
diff mbox

Patch

diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c
index ece1b9a..5bddea4 100644
--- a/common/cmd_bootm.c
+++ b/common/cmd_bootm.c
@@ -1200,9 +1200,13 @@  U_BOOT_CMD(
 /* helper routines */
 /*******************************************************************/
 #ifdef CONFIG_SILENT_CONSOLE
+
+#define CONSOLE_ARG     "console="
+#define CONSOLE_ARG_LEN (sizeof(CONSOLE_ARG) - 1)
+
 static void fixup_silent_linux(void)
 {
-	char buf[256], *start, *end;
+	char *buf;
 	char *cmdline = getenv("bootargs");
 
 	/* Only fix cmdline when requested */
@@ -1210,25 +1214,45 @@  static void fixup_silent_linux(void)
 		return;
 
 	debug("before silent fix-up: %s\n", cmdline);
-	if (cmdline) {
-		start = strstr(cmdline, "console=");
+	if (cmdline && (cmdline[0] != '\0')) {
+		char *start = strstr(cmdline, "console=");
 		if (start) {
-			end = strchr(start, ' ');
-			strncpy(buf, cmdline, (start - cmdline + 8));
+			char *end = strchr(start, ' ');
+			int num_start_bytes = start - cmdline + CONSOLE_ARG_LEN;
+
+			/* We know cmdline bytes will be more than enough. */
+			buf = malloc(strlen(cmdline) + 1);
+			if (!buf) {
+				debug("WARNING: %s failed to alloc cmdline\n",
+				      __func__);
+				return;
+			}
+
+			strncpy(buf, cmdline, num_start_bytes);
 			if (end)
-				strcpy(buf + (start - cmdline + 8), end);
+				strcpy(buf + num_start_bytes, end);
 			else
-				buf[start - cmdline + 8] = '\0';
+				buf[num_start_bytes] = '\0';
 		} else {
-			strcpy(buf, cmdline);
-			strcat(buf, " console=");
+			buf = malloc(strlen(cmdline) + 1 + CONSOLE_ARG_LEN + 1);
+			if (!buf) {
+				debug("WARNING: %s failed to alloc cmdline\n",
+				      __func__);
+				return;
+			}
+			sprintf(buf, "%s %s", cmdline, CONSOLE_ARG);
 		}
 	} else {
-		strcpy(buf, "console=");
+		buf = strdup("console=");
+		if (!buf) {
+			debug("WARNING: strdup failed in fixup_silent_linux\n");
+			return;
+		}
 	}
 
 	setenv("bootargs", buf);
 	debug("after silent fix-up: %s\n", buf);
+	free(buf);
 }
 #endif /* CONFIG_SILENT_CONSOLE */