Add more extensive validation as part of fuzz test.

This should help detect errors such as invalid pointer in decoded structure.
diff --git a/tests/common/malloc_wrappers.c b/tests/common/malloc_wrappers.c
index 30720b2..93b9840 100644
--- a/tests/common/malloc_wrappers.c
+++ b/tests/common/malloc_wrappers.c
@@ -94,7 +94,15 @@
     }
 }
 
+/* Return total number of allocations not yet released */
 size_t get_alloc_count()
 {
     return alloc_count;
 }
+
+/* Return allocated size for a pointer returned from malloc(). */
+size_t get_allocation_size(const void *mem)
+{
+    char *buf = (char*)mem - PREFIX_SIZE;
+    return ((size_t*)buf)[0];
+}
diff --git a/tests/common/malloc_wrappers.h b/tests/common/malloc_wrappers.h
index f1211a3..13dfb4f 100644
--- a/tests/common/malloc_wrappers.h
+++ b/tests/common/malloc_wrappers.h
@@ -4,3 +4,4 @@
 void free_with_check(void *mem);
 void* realloc_with_check(void *ptr, size_t size);
 size_t get_alloc_count();
+size_t get_allocation_size(const void *mem);
diff --git a/tests/fuzztest/validation.c b/tests/fuzztest/validation.c
index ec3486b..49aa82c 100644
--- a/tests/fuzztest/validation.c
+++ b/tests/fuzztest/validation.c
@@ -1,39 +1,147 @@
 #include "validation.h"
-#include "alltypes_static.pb.h"
+#include "malloc_wrappers.h"
+#include <pb_common.h>
 #include <assert.h>
 
-/* Check the invariants defined in security model on decoded structure */
-static void sanity_check_static(const alltypes_static_AllTypes *msg)
+void validate_static(pb_field_iter_t *iter)
 {
+    pb_size_t count = 1;
+    pb_size_t i;
     bool truebool = true;
     bool falsebool = false;
 
-    /* TODO: Add more checks, or rather, generate them automatically */
-    assert(strlen(msg->req_string) < sizeof(msg->req_string));
-    assert(strlen(msg->opt_string) < sizeof(msg->opt_string));
-    if (msg->rep_string_count > 0)
+    if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && iter->pSize)
     {
-        assert(strlen(msg->rep_string[0]) < sizeof(msg->rep_string[0]));
+        /* Array count must be between 0 and statically allocated size */
+        count = *(pb_size_t*)iter->pSize;
+        assert(count <= iter->array_size);
     }
-    assert(memcmp(&msg->req_bool, &truebool, sizeof(bool)) == 0 ||
-           memcmp(&msg->req_bool, &falsebool, sizeof(bool)) == 0);
-    assert(memcmp(&msg->has_opt_bool, &truebool, sizeof(bool)) == 0 ||
-           memcmp(&msg->has_opt_bool, &falsebool, sizeof(bool)) == 0);
-    assert(memcmp(&msg->opt_bool, &truebool, sizeof(bool)) == 0 ||
-           memcmp(&msg->opt_bool, &falsebool, sizeof(bool)) == 0);
-    assert(msg->rep_bool_count <= pb_arraysize(alltypes_static_AllTypes, rep_bool));
-    if (msg->rep_bool_count > 0)
+    else if (PB_HTYPE(iter->type) == PB_HTYPE_OPTIONAL && iter->pSize)
     {
-        assert(memcmp(&msg->rep_bool[0], &truebool, sizeof(bool)) == 0 ||
-               memcmp(&msg->rep_bool[0], &falsebool, sizeof(bool)) == 0);
+        /* Boolean has_ field must have a valid value */
+        assert(memcmp(iter->pSize, &truebool, sizeof(bool)) == 0 ||
+               memcmp(iter->pSize, &falsebool, sizeof(bool)) == 0);
+    }
+
+    for (i = 0; i < count; i++)
+    {
+        void *pData = (char*)iter->pData + iter->data_size * i;
+
+        if (PB_LTYPE(iter->type) == PB_LTYPE_STRING)
+        {
+            /* String length must be at most statically allocated size */
+            assert(strlen(pData) + 1 <= iter->data_size);
+        }
+        else if (PB_LTYPE(iter->type) == PB_LTYPE_BYTES)
+        {
+            /* Bytes length must be at most statically allocated size */
+            pb_bytes_array_t *bytes = pData;
+            assert(PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) <= iter->data_size);
+        }
+        else if (PB_LTYPE(iter->type) == PB_LTYPE_BOOL)
+        {
+            /* Bool fields must have valid value */
+            assert(memcmp(pData, &truebool, sizeof(bool)) == 0 ||
+                   memcmp(pData, &falsebool, sizeof(bool)) == 0);
+        }
+        else if (PB_LTYPE_IS_SUBMSG(iter->type))
+        {
+            validate_message(pData, 0, iter->submsg_desc);
+        }
+    }
+}
+
+void validate_pointer(pb_field_iter_t *iter)
+{
+    pb_size_t count = 1;
+    pb_size_t i;
+    bool truebool = true;
+    bool falsebool = false;
+
+    if (!iter->pData)
+    {
+        /* Nothing allocated */
+        if (iter->pSize)
+        {
+            assert(*(pb_size_t*)iter->pSize == 0);
+        }
+        return;
+    }
+
+    if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED)
+    {
+        /* Check that enough memory has been allocated for array */
+        size_t allocated_size = get_allocation_size(iter->pData);
+        count = *(pb_size_t*)iter->pSize;
+        assert(allocated_size >= count * iter->data_size);
+    }
+    else if (PB_LTYPE(iter->type) != PB_LTYPE_STRING && PB_LTYPE(iter->type) != PB_LTYPE_BYTES)
+    {
+        size_t allocated_size = get_allocation_size(iter->pData);
+        assert(allocated_size >= iter->data_size);
+    }
+
+    for (i = 0; i < count; i++)
+    {
+        void *pData = (char*)iter->pData + iter->data_size * i;
+
+        if (PB_LTYPE(iter->type) == PB_LTYPE_STRING)
+        {
+            /* Check that enough memory is allocated for string and that
+               the string is properly terminated. */
+            const char *str = pData;
+
+            if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED)
+            {
+                /* String arrays are stored as array of pointers */
+                str = ((const char**)iter->pData)[i];
+            }
+
+            assert(strlen(str) + 1 <= get_allocation_size(str));
+        }
+        else if (PB_LTYPE(iter->type) == PB_LTYPE_BYTES)
+        {
+            /* Bytes length must be at most statically allocated size */
+            const pb_bytes_array_t *bytes = pData;
+
+            if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED)
+            {
+                /* Bytes arrays are stored as array of pointers */
+                bytes = ((const pb_bytes_array_t**)iter->pData)[i];
+            }
+
+            assert(PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) <= get_allocation_size(bytes));
+        }
+        else if (PB_LTYPE(iter->type) == PB_LTYPE_BOOL)
+        {
+            /* Bool fields must have valid value */
+            assert(memcmp(pData, &truebool, sizeof(bool)) == 0 ||
+                   memcmp(pData, &falsebool, sizeof(bool)) == 0);
+        }
+        else if (PB_LTYPE_IS_SUBMSG(iter->type))
+        {
+            validate_message(pData, 0, iter->submsg_desc);
+        }
     }
 }
 
 void validate_message(const void *msg, size_t structsize, const pb_msgdesc_t *msgtype)
 {
-    if (msgtype == alltypes_static_AllTypes_fields)
+    pb_field_iter_t iter;
+
+    if (pb_field_iter_begin_const(&iter, msgtype, msg))
     {
-        sanity_check_static(msg);
+        do
+        {
+            if (PB_ATYPE(iter.type) == PB_ATYPE_STATIC)
+            {
+                validate_static(&iter);
+            }
+            else if (PB_ATYPE(iter.type) == PB_ATYPE_POINTER)
+            {
+                validate_pointer(&iter);
+            }
+        } while (pb_field_iter_next(&iter));
     }
 }