Fix segfault with pointer fields and proto3_singular_msgs = true. (#504,#505)
If option proto3_singular_msgs was given and a proto3 submessage
contained a pointer field set to NULL, the encoder would crash with
null pointer dereference.
diff --git a/pb_encode.c b/pb_encode.c
index 409fec3..1a94202 100644
--- a/pb_encode.c
+++ b/pb_encode.c
@@ -264,7 +264,22 @@
}
/* Rest is proto3 singular fields */
- if (PB_LTYPE(type) == PB_LTYPE_BYTES)
+ if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
+ {
+ /* Simple integer / float fields */
+ pb_size_t i;
+ const char *p = (const char*)field->pData;
+ for (i = 0; i < field->data_size; i++)
+ {
+ if (p[i] != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ else if (PB_LTYPE(type) == PB_LTYPE_BYTES)
{
const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData;
return bytes->size == 0;
@@ -302,27 +317,29 @@
return true;
}
}
-
+ else if (PB_ATYPE(type) == PB_ATYPE_POINTER)
{
- /* Catch-all branch that does byte-per-byte comparison for zero value.
- *
- * This is for all pointer fields, and for static PB_LTYPE_VARINT,
- * UVARINT, SVARINT, FIXED32, FIXED64, EXTENSION fields, and also
- * callback fields. These all have integer or pointer value which
- * can be compared with 0.
- */
- pb_size_t i;
- const char *p = (const char*)field->pData;
- for (i = 0; i < field->data_size; i++)
- {
- if (p[i] != 0)
- {
- return false;
- }
- }
-
- return true;
+ return field->pData == NULL;
}
+ else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
+ {
+ if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
+ {
+ const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData;
+ return extension == NULL;
+ }
+ else if (field->descriptor->field_callback == pb_default_field_callback)
+ {
+ pb_callback_t *pCallback = (pb_callback_t*)field->pData;
+ return pCallback->funcs.encode == NULL;
+ }
+ else
+ {
+ return field->descriptor->field_callback == NULL;
+ }
+ }
+
+ return false; /* Not typically reached, safe default for weird special cases. */
}
/* Encode a field with static or pointer allocation, i.e. one whose data
diff --git a/tests/regression/issue_504/test.c b/tests/regression/issue_504/test.c
index 1ca86db..6c6693a 100644
--- a/tests/regression/issue_504/test.c
+++ b/tests/regression/issue_504/test.c
@@ -13,34 +13,65 @@
int status = 0;
uint8_t buffer[512] = {0};
int i;
- pb_ostream_t ostream;
- MyMessage msg = MyMessage_init_zero;
- char *pStr, *pStrAligned;
- ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
- /* copy STR to a malloced 0x100 aligned address */
- pStr = malloc(sizeof(STR) + ALIGN);
- pStrAligned = (char*)((uintptr_t)(pStr + ALIGN) & ~(ALIGN - 1));
- memcpy(pStrAligned, STR, sizeof(STR));
+ {
+ pb_ostream_t ostream;
+ MyMessage msg = MyMessage_init_zero;
+ char *pStr, *pStrAligned;
- msg.submessage.somestring = pStrAligned;
- printf("%p: '%s'\n", msg.submessage.somestring, msg.submessage.somestring);
+ COMMENT("Test for false negatives with pointer value low byte 0x00")
+ ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
- if (!pb_encode(&ostream, MyMessage_fields, &msg)) {
- fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&ostream));
- return 1;
+ /* copy STR to a malloced 0x100 aligned address */
+ pStr = malloc(sizeof(STR) + ALIGN);
+ pStrAligned = (char*)((uintptr_t)(pStr + ALIGN) & ~(ALIGN - 1));
+ memcpy(pStrAligned, STR, sizeof(STR));
+
+ msg.submessage.somestring = pStrAligned;
+ printf("%p: '%s'\n", msg.submessage.somestring, msg.submessage.somestring);
+
+ if (!pb_encode(&ostream, MyMessage_fields, &msg)) {
+ fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&ostream));
+ return 1;
+ }
+
+ free(pStr);
+ msg.submessage.somestring = NULL;
+
+ printf("response payload (%d):", (int)ostream.bytes_written);
+ for (i = 0; i < ostream.bytes_written; i++) {
+ printf("%02X", buffer[i]);
+ }
+ printf("\n");
+
+ TEST(ostream.bytes_written != 0);
}
- free(pStr);
- msg.submessage.somestring = NULL;
+ {
+ pb_ostream_t ostream;
+ struct {
+ MyMessage msg;
+ uint32_t bar;
+ } msg = {MyMessage_init_zero, 0};
- printf("response payload (%d):", (int)ostream.bytes_written);
- for (i = 0; i < ostream.bytes_written; i++) {
- printf("%02X", buffer[i]);
+ COMMENT("Test for false positives with data after end of struct")
+ ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+ msg.bar = 0xFFFFFFFF;
+
+ if (!pb_encode(&ostream, MyMessage_fields, &msg)) {
+ fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&ostream));
+ return 1;
+ }
+
+ printf("response payload (%d):", (int)ostream.bytes_written);
+ for (i = 0; i < ostream.bytes_written; i++) {
+ printf("%02X", buffer[i]);
+ }
+ printf("\n");
+
+ TEST(ostream.bytes_written == 0);
}
- printf("\n");
-
- TEST(ostream.bytes_written != 0);
return status;
}
diff --git a/tests/regression/issue_504/test.proto b/tests/regression/issue_504/test.proto
index bdafd3c..7a6f0d4 100644
--- a/tests/regression/issue_504/test.proto
+++ b/tests/regression/issue_504/test.proto
@@ -10,4 +10,10 @@
message SubMessage
{
string somestring = 1 [(nanopb).type = FT_POINTER];
+ SubMessage2 submsg2 = 2 [(nanopb).type = FT_POINTER];
}
+
+message SubMessage2
+{
+ uint32 foo = 1;
+}
\ No newline at end of file