Add testcase for GHSA-gcx3-7m76-287p
diff --git a/tests/common/malloc_wrappers.c b/tests/common/malloc_wrappers.c
index fd12608..a5b7fe6 100644
--- a/tests/common/malloc_wrappers.c
+++ b/tests/common/malloc_wrappers.c
@@ -9,8 +9,6 @@
#include <assert.h>
#include <string.h>
-static size_t alloc_count = 0;
-
#define GUARD_SIZE (sizeof(size_t)*3)
#define PREFIX_SIZE (sizeof(size_t)*2)
#define CHECK1 ((size_t)0xDEADBEEF)
@@ -24,6 +22,9 @@
#define DEBUG_MALLOC 0
#endif
+static size_t g_alloc_count = 0;
+static size_t g_max_realloc_size = MAX_REALLOC_SIZE;
+
/* Allocate memory and place check values before and after. */
void* malloc_with_check(size_t size)
{
@@ -33,7 +34,7 @@
((size_t*)buf)[0] = size;
((size_t*)buf)[1] = CHECK1;
((size_t*)(buf + size))[2] = CHECK2;
- alloc_count++;
+ g_alloc_count++;
if (DEBUG_MALLOC) fprintf(stderr, "Alloc 0x%04x/%u\n", (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size);
return buf + PREFIX_SIZE;
}
@@ -51,11 +52,11 @@
{
char *buf = (char*)mem - PREFIX_SIZE;
size_t size = ((size_t*)buf)[0];
+ if (DEBUG_MALLOC) fprintf(stderr, "Release 0x%04x/%u\n", (unsigned)(uintptr_t)mem, (unsigned)size);
assert(((size_t*)buf)[1] == CHECK1);
assert(((size_t*)(buf + size))[2] == CHECK2);
- assert(alloc_count > 0);
- alloc_count--;
- if (DEBUG_MALLOC) fprintf(stderr, "Release 0x%04x/%u\n", (unsigned)(uintptr_t)mem, (unsigned)size);
+ assert(g_alloc_count > 0);
+ g_alloc_count--;
free(buf);
}
}
@@ -64,7 +65,7 @@
void* realloc_with_check(void *ptr, size_t size)
{
/* Don't allocate crazy amounts of RAM when fuzzing */
- if (size > MAX_REALLOC_SIZE)
+ if (size > g_max_realloc_size)
return NULL;
if (!ptr && size)
@@ -79,7 +80,7 @@
size_t oldsize = ((size_t*)buf)[0];
assert(((size_t*)buf)[1] == CHECK1);
assert(((size_t*)(buf + oldsize))[2] == CHECK2);
- assert(alloc_count > 0);
+ assert(g_alloc_count > 0);
buf = realloc(buf, size + GUARD_SIZE);
if (!buf)
@@ -111,7 +112,7 @@
/* Return total number of allocations not yet released */
size_t get_alloc_count()
{
- return alloc_count;
+ return g_alloc_count;
}
/* Return allocated size for a pointer returned from malloc(). */
@@ -120,3 +121,10 @@
char *buf = (char*)mem - PREFIX_SIZE;
return ((size_t*)buf)[0];
}
+
+/* Set limit for allocation size */
+void set_max_realloc_size(size_t max_size)
+{
+ g_max_realloc_size = max_size;
+}
+
diff --git a/tests/common/malloc_wrappers.h b/tests/common/malloc_wrappers.h
index 13dfb4f..b8c27ec 100644
--- a/tests/common/malloc_wrappers.h
+++ b/tests/common/malloc_wrappers.h
@@ -5,3 +5,4 @@
void* realloc_with_check(void *ptr, size_t size);
size_t get_alloc_count();
size_t get_allocation_size(const void *mem);
+void set_max_realloc_size(size_t max_size);
diff --git a/tests/regression/GHSA-gcx3-7m76-287p/SConscript b/tests/regression/GHSA-gcx3-7m76-287p/SConscript
new file mode 100644
index 0000000..27e9cac
--- /dev/null
+++ b/tests/regression/GHSA-gcx3-7m76-287p/SConscript
@@ -0,0 +1,13 @@
+# Regression test for security issue GHSA-gcx3-7m76-287p
+# "Out-of-memory condition on repeated field can result in invalid free()"
+
+Import("malloc_env")
+
+malloc_env.NanopbProto("test")
+
+test = malloc_env.Program(["test.c", "test.pb.c",
+ "$COMMON/pb_decode_with_malloc.o",
+ "$COMMON/pb_common_with_malloc.o",
+ "$COMMON/malloc_wrappers.o"])
+
+malloc_env.RunTest([test, 'test_input.pb'])
diff --git a/tests/regression/GHSA-gcx3-7m76-287p/test.c b/tests/regression/GHSA-gcx3-7m76-287p/test.c
new file mode 100644
index 0000000..7730f25
--- /dev/null
+++ b/tests/regression/GHSA-gcx3-7m76-287p/test.c
@@ -0,0 +1,39 @@
+#include <pb_decode.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "test_helpers.h"
+#include "test.pb.h"
+
+bool stream_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
+{
+ FILE *file = (FILE*)stream->state;
+ size_t len = fread(buf, 1, count, file);
+
+ if (len == count)
+ {
+ return true;
+ }
+ else
+ {
+ stream->bytes_left = 0;
+ return false;
+ }
+}
+
+int main()
+{
+ pb_istream_t stream = {&stream_callback, NULL, SIZE_MAX};
+ MyMessage msg = MyMessage_init_default;
+ bool status;
+ stream.state = stdin;
+ SET_BINARY_MODE(stdin);
+
+ set_max_realloc_size(512);
+
+ status = pb_decode(&stream, MyMessage_fields, &msg);
+ assert(!status);
+ assert(strcmp(stream.errmsg, "realloc failed") == 0);
+ return 0;
+}
+
diff --git a/tests/regression/GHSA-gcx3-7m76-287p/test.proto b/tests/regression/GHSA-gcx3-7m76-287p/test.proto
new file mode 100644
index 0000000..1f0626c
--- /dev/null
+++ b/tests/regression/GHSA-gcx3-7m76-287p/test.proto
@@ -0,0 +1,7 @@
+syntax = "proto2";
+import "nanopb.proto";
+
+message MyMessage {
+ repeated string values = 1 [(nanopb).type = FT_POINTER];
+}
+
diff --git a/tests/site_scons/platforms/avr/avr_io.c b/tests/site_scons/platforms/avr/avr_io.c
index a994132..30f40c1 100644
--- a/tests/site_scons/platforms/avr/avr_io.c
+++ b/tests/site_scons/platforms/avr/avr_io.c
@@ -30,8 +30,17 @@
return UDR0;
}
+FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
+static char g_malloc_heap[8192];
+extern uint32_t __bss_end;
+
void abort(void)
{
+ if (__bss_end != 0xDEADBEEF)
+ {
+ fprintf(stderr, "possible stack overflow\n");
+ }
+
fprintf(stderr, "abort() called\n");
DDRB = 3;
PORTB = 1;
@@ -39,10 +48,6 @@
while (1) sleep_mode();
}
-FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
-
-static char g_malloc_heap[8192];
-
int main(void)
{
const char *argv[4] = {"main", g_args.args[0], g_args.args[1], g_args.args[2]};
@@ -54,12 +59,20 @@
__malloc_heap_start = g_malloc_heap;
__malloc_heap_end = g_malloc_heap + sizeof(g_malloc_heap);
+ __bss_end = 0xDEADBEEF;
+
stdout = stdin = stderr = &uart_str;
fread((char*)&g_args, 1, sizeof(g_args), stdin);
status = app_main(g_args.argc + 1, argv);
+ if (__bss_end != 0xDEADBEEF)
+ {
+ status = 255;
+ fprintf(stderr, "possible stack overflow\n");
+ }
+
DDRB = 3;
if (status)
{