diff mbox series

grubenv: fix buffer overflow on strtok function

Message ID 20210323151505.10045-1-e.kapllaj@asem.it
State Changes Requested
Headers show
Series grubenv: fix buffer overflow on strtok function | expand

Commit Message

Elvis Kapllaj March 23, 2021, 3:15 p.m. UTC
grubenv file is a fixed block of memory (1024) not null-terminated.
Using strtok on this buffer can have unpredicted results based on
the memory content immediately after the buffer.

During an intensive SWUPDATE test, we noticed some random crashes
with the following error:

[ERROR] : SWUPDATE failed [0] ERROR bootloader/grub.c : grubenv_write : 174 : Not enough free space in envblk file, 1031
[ERROR] : SWUPDATE failed [0] ERROR corelib/installer.c : update_bootloader_env : 191 : Bootloader-specific error -1 updating its environment Software updated failed

It turned out to be related to strtok overrunning the env buffer.
The bottom of the grubenv file is filled with "#" characters, and
the latest cycle on the do_while loop was detecting the "####" string as
the `key` env token (very long string). This file is not terminated with
'\0' so strtok was not stopping until it went beyound the buffer area.
On a lucky run, the memory immediately after the buffer was filled with
0, so strtok returned a very long string for the `key` string, and a
null for `value`.

Sometimes the memory after the buffer was filled with some other data,
which made the strtok call go on finding a valid `value` string outside
the buffer, resulting in the swupdate crash. Since strtok is destructive
on the string it uses (replaces the string separator with '\0') this
could also lead to memory corruption.

With this patch, we allocate an extra byte for the '\0' character on the
buffer, to make sure strtok works as expected.

Signed-off-by: Elvis Kapllaj <e.kapllaj@asem.it>
---
 bootloader/grub.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

Comments

Stefano Babic March 23, 2021, 3:19 p.m. UTC | #1
Hi Elvis,

On 23.03.21 16:15, Elvis Kapllaj wrote:
> grubenv file is a fixed block of memory (1024) not null-terminated.
> Using strtok on this buffer can have unpredicted results based on
> the memory content immediately after the buffer.
> 
> During an intensive SWUPDATE test, we noticed some random crashes
> with the following error:
> 
> [ERROR] : SWUPDATE failed [0] ERROR bootloader/grub.c : grubenv_write : 174 : Not enough free space in envblk file, 1031
> [ERROR] : SWUPDATE failed [0] ERROR corelib/installer.c : update_bootloader_env : 191 : Bootloader-specific error -1 updating its environment Software updated failed
> 
> It turned out to be related to strtok overrunning the env buffer.
> The bottom of the grubenv file is filled with "#" characters, and
> the latest cycle on the do_while loop was detecting the "####" string as
> the `key` env token (very long string). This file is not terminated with
> '\0' so strtok was not stopping until it went beyound the buffer area.
> On a lucky run, the memory immediately after the buffer was filled with
> 0, so strtok returned a very long string for the `key` string, and a
> null for `value`.
> 
> Sometimes the memory after the buffer was filled with some other data,
> which made the strtok call go on finding a valid `value` string outside
> the buffer, resulting in the swupdate crash. Since strtok is destructive
> on the string it uses (replaces the string separator with '\0') this
> could also lead to memory corruption.
> 
> With this patch, we allocate an extra byte for the '\0' character on the
> buffer, to make sure strtok works as expected.
> 
> Signed-off-by: Elvis Kapllaj <e.kapllaj@asem.it>
> ---
>   bootloader/grub.c | 9 ++++++++-
>   1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/bootloader/grub.c b/bootloader/grub.c
> index 45fb102..a75af5b 100644
> --- a/bootloader/grub.c
> +++ b/bootloader/grub.c
> @@ -44,7 +44,12 @@ static int grubenv_open(struct grubenv_t *grubenv)
>   		goto cleanup;
>   	}
>   
> -	buf = malloc(size);

Agree, code is wrong, thanks for fixing it.

> +	/* grubenv is a block of 'size' bytes not null-terminated, and this is a
> +	 * problem when using strtok, which is expecting a null-terminated
> +	 * string. Allocate an extra byte to terminate the buffer or strtok will
> +	 * overrun it.
> +	 * . */
> +	buf = malloc(size+1);

Then switch to calloc() and the buffer is automatically zeroed.

>   	if (!buf) {
>   		ERROR("Not enough memory for environment");
>   		fclose(fp);
> @@ -57,6 +62,8 @@ static int grubenv_open(struct grubenv_t *grubenv)
>   		ret = 1;
>   		goto cleanup;
>   	}
> +	/* terminate buf */
> +	buf[size]='\0';
>   
>   	if (memcmp(buf, GRUBENV_HEADER, strlen(GRUBENV_HEADER) -1)) {
>   		ERROR("Invalid grubenv header");
> 

Best regards,
Stefano Babic
Elvis Kapllaj March 23, 2021, 3:53 p.m. UTC | #2
Hi stefano,


Yes we could also use calloc to allocate the memory, but it would be less efficient i think.

What we are doing after allocating that block of memory is:

if (fread(buf, 1, size, fp) != size) {
printf("Failed to read file %s\n", GRUBENV_PATH);
ret = 1;
goto cleanup;
}
+buf[size]='\0';


So we are reading size bytes from the file and writing it into the buffer, so it doesn't matter what the previous content was.

To be on the safe side, and fix this issue, we only need to make sure we have allocated an extra byte at the end, and it contains

'\0', so I think using malloc and terminating the buffer would be more efficient than using calloc() to clear all 1025 bytes, on which

the first 1024 are being written by fread() anyway.


Best regards.


Elvis Kapllaj


[1596469214269]


Elvis Kapllaj
R&D - Software Engineer

ASEM S.r.l.
Via Buia 4, 33011 Artegna (UD) Italia
tel +39 0432 967 423
email: e.kapllaj@asem.it<mailto:l.dandrea@asem.it>

website: www.asemautomation.com<http://www.asemautomation.com/>
diff mbox series

Patch

diff --git a/bootloader/grub.c b/bootloader/grub.c
index 45fb102..a75af5b 100644
--- a/bootloader/grub.c
+++ b/bootloader/grub.c
@@ -44,7 +44,12 @@  static int grubenv_open(struct grubenv_t *grubenv)
 		goto cleanup;
 	}
 
-	buf = malloc(size);
+	/* grubenv is a block of 'size' bytes not null-terminated, and this is a
+	 * problem when using strtok, which is expecting a null-terminated
+	 * string. Allocate an extra byte to terminate the buffer or strtok will
+	 * overrun it.
+	 * . */
+	buf = malloc(size+1);
 	if (!buf) {
 		ERROR("Not enough memory for environment");
 		fclose(fp);
@@ -57,6 +62,8 @@  static int grubenv_open(struct grubenv_t *grubenv)
 		ret = 1;
 		goto cleanup;
 	}
+	/* terminate buf */
+	buf[size]='\0';
 
 	if (memcmp(buf, GRUBENV_HEADER, strlen(GRUBENV_HEADER) -1)) {
 		ERROR("Invalid grubenv header");