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)
     {