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));
}
}