Skip to content

Commit f1033f1

Browse files
authored
Merge pull request #70 from wolfSSL/parser_boundaries
Fixed manifest header boundary checks
2 parents d897a8b + c30d675 commit f1033f1

File tree

5 files changed

+270
-2
lines changed

5 files changed

+270
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ include/target.h
8181
tools/test-expect-version/test-expect-version
8282
tools/test-update-server/server
8383
tools/uart-flash-server/ufserver
84+
tools/unit-tests/unit-parser
8485
config/*.ld
8586

8687
# Generated confiuguration file

src/libwolfboot.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
#include "wolfboot/wolfboot.h"
2727
#include "image.h"
2828

29+
#ifdef UNIT_TEST
30+
# define unit_dbg printf
31+
#else
32+
# define unit_dbg(...) do{}while(0)
33+
#endif
34+
2935
#if defined(EXT_ENCRYPTED)
3036
#if defined(__WOLFBOOT)
3137
#include "encrypt.h"
@@ -342,8 +348,19 @@ uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr)
342348
{
343349
uint8_t *p = haystack;
344350
uint16_t len;
345-
while (((p[0] != 0) || (p[1] != 0)) && ((p - haystack) < IMAGE_HEADER_SIZE)) {
351+
const volatile uint8_t *max_p = (haystack - IMAGE_HEADER_OFFSET) + IMAGE_HEADER_SIZE;
352+
*ptr = NULL;
353+
if (p > max_p) {
354+
unit_dbg("Illegal address (too high)\n");
355+
return 0;
356+
}
357+
while ((p + 4) < max_p) {
358+
if ((p[0] == 0) && (p[1] == 0)) {
359+
unit_dbg("Explicit end of options reached\n");
360+
break;
361+
}
346362
if (*p == HDR_PADDING) {
363+
/* Padding byte (skip one position) */
347364
p++;
348365
continue;
349366
}
@@ -353,13 +370,20 @@ uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr)
353370
continue;
354371
}
355372
len = p[2] | (p[3] << 8);
373+
if ((4 + len) > (IMAGE_HEADER_SIZE - IMAGE_HEADER_OFFSET)) {
374+
unit_dbg("This field is too large (bigger than the space available in the current header)\n");
375+
break;
376+
}
377+
if (p + 4 + len > max_p) {
378+
unit_dbg("This field is too large and would overflow the image header\n");
379+
break;
380+
}
356381
if ((p[0] | (p[1] << 8)) == type) {
357382
*ptr = (p + 4);
358383
return len;
359384
}
360385
p += 4 + len;
361386
}
362-
*ptr = NULL;
363387
return 0;
364388
}
365389

tools/unit-tests/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
LDFLAGS=-lcheck -lm -pthread
2+
3+
UNAME_S := $(shell uname -s)
4+
ifneq ($(UNAME_S),Darwin)
5+
LDFLAGS+=-lrt -lsubunit
6+
endif
7+
8+
CFLAGS=-I../../src -I../../include
9+
10+
all: unit-parser
11+
12+
13+
unit-parser: unit-parser.o
14+
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
15+
16+
%.o:%.c
17+
gcc -c -o $@ $^ $(CFLAGS)
18+
19+
clean:
20+
rm -f unit-parser unit-parser.o

tools/unit-tests/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Unit Test Tools
2+
3+
This uses the "check" unit test framework for C.
4+
5+
You may need to run "apt install check", "yum install check" or "brew install check".
6+
7+
## Building
8+
9+
Use `make` to build.
10+
11+
## Expected output
12+
13+
```sh
14+
$ ./unit-parser
15+
Running suite(s): wolfBoot
16+
Explicit end of options reached
17+
This field is too large (bigger than the space available in the current header)
18+
This field is too large and would overflow the image header
19+
Illegal address (too high)
20+
Illegal address (too high)
21+
100%: Checks: 2, Failures: 0, Errors: 0
22+
```

tools/unit-tests/unit-parser.c

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/* unit-parser.c
2+
*
3+
* Unit test for parser functions in libwolfboot.c
4+
*
5+
*
6+
* Copyright (C) 2020 wolfSSL Inc.
7+
*
8+
* This file is part of wolfBoot.
9+
*
10+
* wolfBoot is free software; you can redistribute it and/or modify
11+
* it under the terms of the GNU General Public License as published by
12+
* the Free Software Foundation; either version 2 of the License, or
13+
* (at your option) any later version.
14+
*
15+
* wolfBoot is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU General Public License
21+
* along with this program; if not, write to the Free Software
22+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
23+
*/
24+
25+
/* Option to enable sign tool debugging */
26+
/* Must also define DEBUG_WOLFSSL in user_settings.h */
27+
#define WOLFBOOT_HASH_SHA256
28+
#define IMAGE_HEADER_SIZE 256
29+
#define UNIT_TEST
30+
#include <stdio.h>
31+
#include "libwolfboot.c"
32+
#include <check.h>
33+
static int locked = 0;
34+
35+
/* Mocks */
36+
void hal_init(void)
37+
{
38+
}
39+
int hal_flash_write(uint32_t address, const uint8_t *data, int len)
40+
{
41+
return 0;
42+
}
43+
int hal_flash_erase(uint32_t address, int len)
44+
{
45+
return 0;
46+
}
47+
void hal_flash_unlock(void)
48+
{
49+
fail_unless(locked, "Double unlock detected\n");
50+
locked--;
51+
}
52+
void hal_flash_lock(void)
53+
{
54+
fail_if(locked, "Double lock detected\n");
55+
locked++;
56+
}
57+
58+
void hal_prepare_boot(void)
59+
{
60+
}
61+
/* End Mocks */
62+
63+
Suite *wolfboot_suite(void);
64+
65+
static uint8_t test_buffer[512] = {
66+
'W', 'O', 'L', 'F', 0x00, 0x00, 0x01, 0x00,
67+
0x01, 0x00, 0x04, 0x00, 0x0d, 0x0c, 0x0b, 0x0a,
68+
0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x08, 0x00,
69+
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
70+
0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x20, 0x00,
71+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
72+
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
73+
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
74+
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
75+
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*<-- end of options */
76+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
77+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
78+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
79+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
80+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
81+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
82+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
83+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
84+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
85+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
86+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
87+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
88+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
89+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
90+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
91+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
92+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
93+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
94+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
95+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
96+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
97+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
98+
/* End HDR */
99+
0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
100+
};
101+
102+
START_TEST (test_parser_sunny)
103+
{
104+
uint8_t *p;
105+
int i;
106+
107+
/* Check version */
108+
fail_if(wolfBoot_find_header(test_buffer + 8, HDR_VERSION, &p) != 4, "Parser error: cannot locate version");
109+
fail_if((p[0] != 0x0d) || (p[1] != 0x0c) || (p[2] != 0x0b) || (p[3] != 0x0a), "Parser error: version doesn't match");
110+
111+
/* Check timestamp */
112+
fail_if(wolfBoot_find_header(test_buffer + 8, HDR_TIMESTAMP, &p) != 8, "Parser error: cannot locate timestamp");
113+
fail_if((p[0] != 0x07) || (p[1] != 0x06) || (p[2] != 0x05) || (p[3] != 0x04), "Parser error: timestamp doesn't match");
114+
fail_if((p[4] != 0x03) || (p[5] != 0x02) || (p[6] != 0x01) || (p[7] != 0x00), "Parser error: timestamp doesn't match");
115+
116+
/* Check sha256 field */
117+
fail_if(wolfBoot_find_header(test_buffer + 8, HDR_SHA256, &p) != 32, "Parser error: cannot locate hash");
118+
for (i = 0; i < 32; i++)
119+
fail_unless(p[i] == i, "Parser error: hash does not match");
120+
121+
/* Check non-existing field */
122+
fail_if(wolfBoot_find_header(test_buffer + 8, HDR_SHA3_384, &p) != 0, "Parser error: found a non-existing field");
123+
}
124+
END_TEST
125+
126+
START_TEST (test_parser_borders)
127+
{
128+
uint8_t *p;
129+
int i;
130+
uint8_t bad_buff[512];
131+
memset(bad_buff, 0xFF, 256);
132+
133+
/* Field out of bounds */
134+
bad_buff[256] = 0x02;
135+
bad_buff[257] = 0x00;
136+
bad_buff[258] = 0x04;
137+
bad_buff[259] = 0x00;
138+
fail_if(wolfBoot_find_header(bad_buff + 8, HDR_VERSION, &p) != 0, "Parser error: accessing version field out of bounds");
139+
140+
/* Single field too large */
141+
bad_buff[8] = 0x02;
142+
bad_buff[9] = 0x00;
143+
bad_buff[10] = 0xF8;
144+
bad_buff[11] = 0x00;
145+
fail_if(wolfBoot_find_header(bad_buff + 8, HDR_VERSION, &p) != 0, "Parser error: accessing version field out of bounds");
146+
147+
/* Second field too large */
148+
bad_buff[8] = 0x01;
149+
bad_buff[9] = 0x00;
150+
bad_buff[10] = 0x04;
151+
bad_buff[11] = 0x00;
152+
bad_buff[12] = 0x05;
153+
bad_buff[13] = 0x05;
154+
bad_buff[14] = 0x05;
155+
bad_buff[15] = 0x05;
156+
bad_buff[16] = 0x02;
157+
bad_buff[17] = 0x00;
158+
bad_buff[18] = 0xf0; /** Timestamp field too large **/
159+
bad_buff[19] = 0x00;
160+
fail_if(wolfBoot_find_header(bad_buff + 8, HDR_TIMESTAMP, &p) != 0, "Parser error: accessing version field out of bounds");
161+
162+
/* High memory access */
163+
fail_if(wolfBoot_find_header(((void *)(0 - 0xF8)), HDR_VERSION, &p) != 0);
164+
fail_if(wolfBoot_find_header(((void *)(0 - 0x10)), HDR_VERSION, &p) != 0);
165+
166+
}
167+
END_TEST
168+
169+
Suite *wolfboot_suite(void)
170+
{
171+
172+
/* Suite initialization */
173+
Suite *s = suite_create("wolfBoot");
174+
175+
/* Test cases */
176+
TCase *parser_sunny = tcase_create("Parser Sunny-day case");
177+
TCase *parser_borders = tcase_create("Parser test buffer borders");
178+
179+
/* Test function <-> Test case */
180+
tcase_add_test(parser_sunny, test_parser_sunny);
181+
tcase_add_test(parser_borders, test_parser_borders);
182+
183+
/* Set parameters + add to suite */
184+
tcase_set_timeout(parser_sunny, 20);
185+
suite_add_tcase(s, parser_sunny);
186+
suite_add_tcase(s, parser_borders);
187+
188+
return s;
189+
}
190+
191+
192+
int main(void)
193+
{
194+
int fails;
195+
Suite *s = wolfboot_suite();
196+
SRunner *sr = srunner_create(s);
197+
srunner_run_all(sr, CK_NORMAL);
198+
fails = srunner_ntests_failed(sr);
199+
srunner_free(sr);
200+
return fails;
201+
}

0 commit comments

Comments
 (0)