From 4f5ff39aedf96983ee39178bbf230433efce6199 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Mon, 9 Apr 2018 19:06:56 +0300 Subject: Set version to 0.3.9.1-dev --- CMakeLists.txt | 2 +- generator/nanopb_generator.py | 2 +- pb.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index deb9eec..ce445cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.4.0-dev) +set(nanopb_VERSION_STRING nanopb-0.3.9.1-dev) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index f77f8c4..e80266d 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.4.0-dev" +nanopb_version = "nanopb-0.3.9.1-dev" import sys import re diff --git a/pb.h b/pb.h index a0dd323..55a6c73 100644 --- a/pb.h +++ b/pb.h @@ -46,7 +46,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.4.0-dev +#define NANOPB_VERSION nanopb-0.3.9.1-dev /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3 From 04cd1f94cc513b4cb3eac924c74039ccc05a944b Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 14 Apr 2018 20:51:13 +0300 Subject: Publishing nanopb-0.3.9.1 --- CMakeLists.txt | 2 +- generator/nanopb_generator.py | 2 +- library.json | 2 +- pb.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ce445cf..2625a66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.3.9.1-dev) +set(nanopb_VERSION_STRING nanopb-0.3.9.1) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index e80266d..5b74ca1 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.9.1-dev" +nanopb_version = "nanopb-0.3.9.1" import sys import re diff --git a/library.json b/library.json index 0a3e17c..b070e54 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Nanopb", - "version": "0.3.9", + "version": "0.3.9.1", "keywords": "protocol buffers, protobuf, google", "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.", "repository": { diff --git a/pb.h b/pb.h index 55a6c73..174a84b 100644 --- a/pb.h +++ b/pb.h @@ -46,7 +46,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.9.1-dev +#define NANOPB_VERSION nanopb-0.3.9.1 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3 From e02f9b185276d5a0770856c11e0b965fcb782e13 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 30 Jan 2019 14:26:23 +0200 Subject: Add regression test for issue #376 --- tests/regression/issue_376/SConscript | 14 +++++++++ tests/regression/issue_376/fixed_array.options | 1 + tests/regression/issue_376/fixed_array.proto | 11 +++++++ tests/regression/issue_376/test_fixarray.c | 40 ++++++++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 tests/regression/issue_376/SConscript create mode 100644 tests/regression/issue_376/fixed_array.options create mode 100644 tests/regression/issue_376/fixed_array.proto create mode 100644 tests/regression/issue_376/test_fixarray.c diff --git a/tests/regression/issue_376/SConscript b/tests/regression/issue_376/SConscript new file mode 100644 index 0000000..017a3ec --- /dev/null +++ b/tests/regression/issue_376/SConscript @@ -0,0 +1,14 @@ +# Regression test for #376: +# Problem with fixed array inside proto3 submessage + +Import("env") + +env.NanopbProto(["fixed_array.proto", "fixed_array.options"]) +testprog = env.Program(["test_fixarray.c", + "fixed_array.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(testprog) + diff --git a/tests/regression/issue_376/fixed_array.options b/tests/regression/issue_376/fixed_array.options new file mode 100644 index 0000000..60fd687 --- /dev/null +++ b/tests/regression/issue_376/fixed_array.options @@ -0,0 +1 @@ +SubMessage.data fixed_count:true,max_count:8 diff --git a/tests/regression/issue_376/fixed_array.proto b/tests/regression/issue_376/fixed_array.proto new file mode 100644 index 0000000..6d86f80 --- /dev/null +++ b/tests/regression/issue_376/fixed_array.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +message MainMessage { + SubMessage submsg = 1; +} + +message SubMessage { + repeated int32 data = 1; +} + + diff --git a/tests/regression/issue_376/test_fixarray.c b/tests/regression/issue_376/test_fixarray.c new file mode 100644 index 0000000..f321b6d --- /dev/null +++ b/tests/regression/issue_376/test_fixarray.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include "fixed_array.pb.h" +#include "unittests.h" + +int main(int argc, char **argv) +{ + int status = 0; + uint8_t buffer[64]; + pb_size_t msglen = 0; + + { + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + MainMessage msg = MainMessage_init_zero; + + msg.submsg.data[0] = 0; + msg.submsg.data[4] = 5; + + TEST(pb_encode(&stream, MainMessage_fields, &msg)); + + msglen = stream.bytes_written; + TEST(msglen > 5); + } + + { + pb_istream_t stream = pb_istream_from_buffer(buffer, msglen); + MainMessage msg = MainMessage_init_zero; + + TEST(pb_decode(&stream, MainMessage_fields, &msg)); + + TEST(msg.submsg.data[0] == 0); + TEST(msg.submsg.data[4] == 5); + } + + return status; +} + -- cgit v1.2.3 From 6c772817cafb6f45f446fef70746a89c8dfdfd47 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 30 Jan 2019 14:26:31 +0200 Subject: Fix encoding of fixed size arrays inside proto3 submessages (#376) --- pb_encode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pb_encode.c b/pb_encode.c index d0129e6..78a3fdb 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -225,7 +225,10 @@ static bool pb_check_proto3_default_value(const pb_field_t *field, const void *p else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { /* Repeated fields inside proto3 submessage: present if count != 0 */ - return *(const pb_size_t*)pSize == 0; + if (field->size_offset != 0) + return *(const pb_size_t*)pSize == 0; + else + return false; /* Fixed length array */ } else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) { -- cgit v1.2.3 From b10e962cea0c950126e03d2be81984fc925fec40 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 10 Nov 2018 15:06:53 +0200 Subject: Generator: Allow comma separated options in plugin mode (#343) --- generator/nanopb_generator.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 906a1ac..4778e0e 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -347,7 +347,7 @@ class Field: if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static: raise Exception("Field '%s' is defined as static, but max_size or " "max_count is not given." % self.name) - + if field_options.fixed_count and self.max_count is None: raise Exception("Field '%s' is defined as fixed count, " "but max_count is not given." % self.name) @@ -1746,6 +1746,24 @@ def main_plugin(): import shlex args = shlex.split(params) + + if len(args) == 1 and ',' in args[0]: + # For compatibility with other protoc plugins, support options + # separated by comma. + lex = shlex.shlex(params) + lex.whitespace_split = True + lex.whitespace = ',' + args = list(lex) + + optparser.usage = "Usage: protoc --nanopb_out=[options][,more_options]:outdir file.proto" + optparser.epilog = "Output will be written to file.pb.h and file.pb.c." + + if '-h' in args or '--help' in args: + # By default optparser prints help to stdout, which doesn't work for + # protoc plugins. + optparser.print_help(sys.stderr) + sys.exit(1) + options, dummy = optparser.parse_args(args) Globals.verbose_options = options.verbose -- cgit v1.2.3 From 74c5cafe2b9052a9f851988f6fafb4fc89ceb276 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 7 Mar 2019 14:57:47 +0200 Subject: Add testcase for #363 --- tests/regression/issue_363/SConscript | 14 ++++++++++ tests/regression/issue_363/oneofmsg.proto | 10 +++++++ tests/regression/issue_363/test_oneofmsg.c | 42 ++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 tests/regression/issue_363/SConscript create mode 100644 tests/regression/issue_363/oneofmsg.proto create mode 100644 tests/regression/issue_363/test_oneofmsg.c diff --git a/tests/regression/issue_363/SConscript b/tests/regression/issue_363/SConscript new file mode 100644 index 0000000..4ce3c6d --- /dev/null +++ b/tests/regression/issue_363/SConscript @@ -0,0 +1,14 @@ +# Regression test for #363: +# Incorrect PB_STATIC_ASSERT for bytes inside oneof + +Import("env") + +env.NanopbProto("oneofmsg.proto") +testprog = env.Program(["test_oneofmsg.c", + "oneofmsg.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(testprog) + diff --git a/tests/regression/issue_363/oneofmsg.proto b/tests/regression/issue_363/oneofmsg.proto new file mode 100644 index 0000000..735bed8 --- /dev/null +++ b/tests/regression/issue_363/oneofmsg.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "nanopb.proto"; + +message BodyMessage { + oneof body_type { + bytes device_data_crypted = 1 [(nanopb).max_size = 252]; + bytes device_config_crypted = 2 [(nanopb).max_size = 252]; + } +} diff --git a/tests/regression/issue_363/test_oneofmsg.c b/tests/regression/issue_363/test_oneofmsg.c new file mode 100644 index 0000000..c218192 --- /dev/null +++ b/tests/regression/issue_363/test_oneofmsg.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include "oneofmsg.pb.h" +#include "unittests.h" + +int main(int argc, char **argv) +{ + int status = 0; + uint8_t buffer[512]; + pb_size_t msglen = 0; + + { + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + BodyMessage msg = BodyMessage_init_zero; + + msg.which_body_type = BodyMessage_device_data_crypted_tag; + msg.body_type.device_data_crypted.size = 252; + memset(msg.body_type.device_data_crypted.bytes, 0xAA, 252); + + TEST(pb_encode(&stream, BodyMessage_fields, &msg)); + + msglen = stream.bytes_written; + TEST(msglen > 252); + } + + { + pb_istream_t stream = pb_istream_from_buffer(buffer, msglen); + BodyMessage msg = BodyMessage_init_zero; + + TEST(pb_decode(&stream, BodyMessage_fields, &msg)); + + TEST(msg.which_body_type == BodyMessage_device_data_crypted_tag); + TEST(msg.body_type.device_data_crypted.size == 252); + TEST(msg.body_type.device_data_crypted.bytes[251] == 0xAA); + } + + return status; +} + -- cgit v1.2.3 From 92afed1488f509c34cb861baebf9a1aef1de09d4 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 7 Mar 2019 14:58:11 +0200 Subject: Fix incorrect PB_STATIC_ASSERT for bytes inside oneof (#363) --- generator/nanopb_generator.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 4778e0e..3bdc7ac 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -617,7 +617,15 @@ class Field: '''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly. Returns numeric value or a C-expression for assert.''' check = [] - if self.pbtype == 'MESSAGE' and self.allocation == 'STATIC': + + need_check = False + + if self.pbtype == 'BYTES' and self.allocation == 'STATIC' and self.max_size > 251: + need_check = True + elif self.pbtype == 'MESSAGE' and self.allocation == 'STATIC': + need_check = True + + if need_check: if self.rules == 'REPEATED': check.append('pb_membersize(%s, %s[0])' % (self.struct_name, self.name)) elif self.rules == 'ONEOF': @@ -627,9 +635,6 @@ class Field: check.append('pb_membersize(%s, %s.%s)' % (self.struct_name, self.union_name, self.name)) else: check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name)) - elif self.pbtype == 'BYTES' and self.allocation == 'STATIC': - if self.max_size > 251: - check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name)) return FieldMaxSize([self.tag, self.max_size, self.max_count], check, -- cgit v1.2.3 From 45d3bbcd67e8f7c4239bfa2d245b4269ebdbd20a Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 7 Mar 2019 15:53:18 +0200 Subject: Add testcase for #380 --- tests/regression/issue_380/SConscript | 9 +++++++++ tests/regression/issue_380/manglenames.options | 1 + tests/regression/issue_380/manglenames.proto | 16 ++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/regression/issue_380/SConscript create mode 100644 tests/regression/issue_380/manglenames.options create mode 100644 tests/regression/issue_380/manglenames.proto diff --git a/tests/regression/issue_380/SConscript b/tests/regression/issue_380/SConscript new file mode 100644 index 0000000..4606d9b --- /dev/null +++ b/tests/regression/issue_380/SConscript @@ -0,0 +1,9 @@ +# Regression test for #380: +# mangle_names:M_STRIP_PACKAGE is broken when message name (partially) +# matches package name + +Import("env") + +env.NanopbProto(["manglenames.proto", "manglenames.options"]) +env.Object("manglenames.pb.o", "manglenames.pb.c") + diff --git a/tests/regression/issue_380/manglenames.options b/tests/regression/issue_380/manglenames.options new file mode 100644 index 0000000..d9b4e41 --- /dev/null +++ b/tests/regression/issue_380/manglenames.options @@ -0,0 +1 @@ +* mangle_names:M_STRIP_PACKAGE diff --git a/tests/regression/issue_380/manglenames.proto b/tests/regression/issue_380/manglenames.proto new file mode 100644 index 0000000..186161d --- /dev/null +++ b/tests/regression/issue_380/manglenames.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; +package A; + +message A { + message B { + optional uint32 val = 1; + } + optional B b = 1; +} + +message AP { + message B { + optional uint32 val = 1; + } + optional B m = 1; +} -- cgit v1.2.3 From fed8c4abe3a900521f8d6bcd7f17e7dfea79d0a9 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 7 Mar 2019 15:53:33 +0200 Subject: Fix generator error with mangle_names option (#380) --- generator/nanopb_generator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 3bdc7ac..c383ff5 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -8,6 +8,7 @@ nanopb_version = "nanopb-0.3.9.2" import sys import re import codecs +import copy from functools import reduce try: @@ -1181,11 +1182,11 @@ class ProtoFile: if message_options.skip_message: continue + message = copy.deepcopy(message) for field in message.field: if field.type in (FieldD.TYPE_MESSAGE, FieldD.TYPE_ENUM): field.type_name = mangle_field_typename(field.type_name) - self.messages.append(Message(name, message, message_options)) for enum in message.enum_type: name = create_name(names + enum.name) -- cgit v1.2.3 From 3c69a905b16df149e1bda12f25e0522073a24678 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Fri, 8 Mar 2019 15:55:06 +0200 Subject: Publishing nanopb-0.3.9.3 --- CHANGELOG.txt | 6 ++++++ CMakeLists.txt | 2 +- generator/nanopb_generator.py | 2 +- library.json | 2 +- pb.h | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4c6ab7f..69f0bc7 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,9 @@ +nanopb-0.3.9.3 (2019-03-08) + Fix fixed size and callback repeated fields inside proto3 submessages (#376, #382, #386) + Fix incorrect PB_STATIC_ASSERT for bytes inside oneof (#363) + Fix generator error with mangle_names option (#380) + Generator: Allow comma separated options in plugin mode (#343) + nanopb-0.3.9.2 (2018-11-10) Erroneous free() when using callbacks combined with PB_ENABLE_MALLOC (#346) Fix possible null-pointer dereference in decode_callback_field (#342) diff --git a/CMakeLists.txt b/CMakeLists.txt index 642c68d..8eccb62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.3.9.2) +set(nanopb_VERSION_STRING nanopb-0.3.9.3) set(nanopb_SOVERSION 0) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index c383ff5..03f41a0 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.9.2" +nanopb_version = "nanopb-0.3.9.3" import sys import re diff --git a/library.json b/library.json index 0a6e5ba..a41055c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Nanopb", - "version": "0.3.9.2", + "version": "0.3.9.3", "keywords": "protocol buffers, protobuf, google", "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.", "repository": { diff --git a/pb.h b/pb.h index a10f012..ebc2a47 100644 --- a/pb.h +++ b/pb.h @@ -46,7 +46,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.9.2 +#define NANOPB_VERSION nanopb-0.3.9.3 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3 From 0381b5f0abaa0c627a8ee8291f1281f97aec5d6c Mon Sep 17 00:00:00 2001 From: Noah Pendleton <2538614+noahp@users.noreply.github.com> Date: Fri, 31 May 2019 21:09:57 -0400 Subject: =?UTF-8?q?=F0=9F=94=80=20use=20min/max=20enum=20value=20for=20min?= =?UTF-8?q?/max?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unsorted enums in message specifications do not result in correct min/max values per the protobuf spec for cpp code: https://developers.google.com/protocol-buffers/docs/reference/cpp-generated#enum The google cpp compiler uses the min/max _value_ instead of the position of the enum entry in the message spec, so this patch updates nanopb to do the same. Example input: ```protobuf syntax = "proto3"; enum Language { UNKNOWN = 0; ENGLISH_EN_GB = 12; ENGLISH_EN_US = 1; FRENCH_FR_FR = 2; ITALIAN_IT_IT = 3; GERMAN_DE_DE = 4; SPANISH_ES_AR = 13; SPANISH_ES_ES = 5; SPANISH_ES_MX = 14; SWEDISH_SV_SE = 6; DUTCH_NL_NL = 7; KOREAN_KO_KR = 8; JAPANESE_JA_JP = 9; CHINESE_SIMPLIFIED_ZH_CN = 10; CHINESE_TRADITIONAL_ZH_TW = 11; } ``` This would previously result in: ```c #define _Language_MIN Language_UNKNOWN #define _Language_MAX Language_CHINESE_TRADITIONAL_ZH_TW #define _Language_ARRAYSIZE ((Language)(Language_CHINESE_TRADITIONAL_ZH_TW+1)) ``` After this patch, ```c #define _Language_MIN Language_UNKNOWN #define _Language_MAX Language_SPANISH_ES_MX #define _Language_ARRAYSIZE ((Language)(Language_SPANISH_ES_MX+1)) ``` Add a test `enum_minmax` to cover this case. --- generator/nanopb_generator.py | 9 ++++++--- tests/enum_minmax/SConscript | 8 ++++++++ tests/enum_minmax/enumminmax.proto | 22 ++++++++++++++++++++++ tests/enum_minmax/enumminmax_unittests.c | 16 ++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/enum_minmax/SConscript create mode 100644 tests/enum_minmax/enumminmax.proto create mode 100644 tests/enum_minmax/enumminmax_unittests.c diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 03f41a0..63983e5 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -224,9 +224,12 @@ class Enum: result += ' %s;' % self.names - result += '\n#define _%s_MIN %s' % (self.names, self.values[0][0]) - result += '\n#define _%s_MAX %s' % (self.names, self.values[-1][0]) - result += '\n#define _%s_ARRAYSIZE ((%s)(%s+1))' % (self.names, self.names, self.values[-1][0]) + # sort the enum by value + sorted_values = sorted(self.values, key = lambda x: (x[1], x[0])) + + result += '\n#define _%s_MIN %s' % (self.names, sorted_values[0][0]) + result += '\n#define _%s_MAX %s' % (self.names, sorted_values[-1][0]) + result += '\n#define _%s_ARRAYSIZE ((%s)(%s+1))' % (self.names, self.names, sorted_values[-1][0]) if not self.options.long_names: # Define the long names always so that enum value references diff --git a/tests/enum_minmax/SConscript b/tests/enum_minmax/SConscript new file mode 100644 index 0000000..6a66cbd --- /dev/null +++ b/tests/enum_minmax/SConscript @@ -0,0 +1,8 @@ +# Test that different sizes of enum fields are properly encoded and decoded. + +Import('env') + +env.NanopbProto('enumminmax') + +p = env.Program(["enumminmax_unittests.c",]) +env.RunTest(p) diff --git a/tests/enum_minmax/enumminmax.proto b/tests/enum_minmax/enumminmax.proto new file mode 100644 index 0000000..482a8db --- /dev/null +++ b/tests/enum_minmax/enumminmax.proto @@ -0,0 +1,22 @@ +/* Test out-of-order enum values. + */ + +syntax = "proto3"; + +enum Language { + UNKNOWN = 0; + ENGLISH_EN_GB = 12; + ENGLISH_EN_US = 1; + FRENCH_FR_FR = 2; + ITALIAN_IT_IT = 3; + GERMAN_DE_DE = 4; + SPANISH_ES_AR = 13; + SPANISH_ES_ES = 5; + SPANISH_ES_MX = 14; + SWEDISH_SV_SE = 6; + DUTCH_NL_NL = 7; + KOREAN_KO_KR = 8; + JAPANESE_JA_JP = 9; + CHINESE_SIMPLIFIED_ZH_CN = 10; + CHINESE_TRADITIONAL_ZH_TW = 11; +} diff --git a/tests/enum_minmax/enumminmax_unittests.c b/tests/enum_minmax/enumminmax_unittests.c new file mode 100644 index 0000000..ccb10a4 --- /dev/null +++ b/tests/enum_minmax/enumminmax_unittests.c @@ -0,0 +1,16 @@ +#include "unittests.h" +#include "enumminmax.pb.h" + +int main() +{ + int status = 0; + + COMMENT("Verify min/max on unsorted enum"); + { + TEST(_Language_MIN == Language_UNKNOWN); + TEST(_Language_MAX == Language_SPANISH_ES_MX); + TEST(_Language_ARRAYSIZE == (Language_SPANISH_ES_MX+1)); + } + + return status; +} -- cgit v1.2.3 From 92033ba4f629ad859683f27fd18dcffff39be88c Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 1 Jun 2019 09:45:21 +0300 Subject: Add migration note about enum min/max --- docs/migration.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/migration.rst b/docs/migration.rst index fdc1d8c..a1843e3 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -11,7 +11,24 @@ are included, in order to make it easier to find this document. .. contents :: -Nanopb-0.3.9.1, 0.4.0 (2018-xx-xx) +Nanopb-0.3.9.4, 0.4.0 (2019-xx-xx) +================================== + +Fix generation of min/max defines for enum types +------------------------------------------------ + +**Rationale:** Nanopb generator makes #defines for enum minimum and maximum +value. Previously these defines incorrectly had the first and last enum value, +instead of the actual minimum and maximum. (issue #405) + +**Changes:** Minimum define now always has the smallest value, and maximum +define always has the largest value. + +**Required actions:** If these defines are used and enum values in .proto file +are not defined in ascending order, user code behaviour may change. Check that +user code doesn't expect the old, incorrect first/last behaviour. + +Nanopb-0.3.9.1, 0.4.0 (2018-04-14) ================================== Fix handling of string and bytes default values -- cgit v1.2.3 From 6d4bd5c79ee898faca0e19ec7aedf8e94c2c75f3 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 27 Aug 2019 13:01:03 +0200 Subject: Introduce new compile time flag: PB_ENCODE_ARRAYS_UNPACKED Don't encode scalar arrays as packed. This is only to be used when the decoder on the receiving side cannot process packed scalar arrays. Such example is older protobuf.js. --- docs/reference.rst | 4 ++++ pb.h | 5 +++++ pb_encode.c | 2 ++ 3 files changed, 11 insertions(+) diff --git a/docs/reference.rst b/docs/reference.rst index 40a69ad..89d1f0f 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -55,6 +55,10 @@ PB_SYSTEM_HEADER Replace the standard header files with a single for example *#define PB_SYSTEM_HEADER "foo.h"*. PB_WITHOUT_64BIT Disable 64-bit support, for old compilers or for a slight speedup on 8-bit platforms. +PB_ENCODE_ARRAYS_UNPACKED Don't encode scalar arrays as packed. + This is only to be used when the decoder on the + receiving side cannot process packed scalar + arrays. Such example is older protobuf.js. ============================ ================================================ The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow diff --git a/pb.h b/pb.h index ebc2a47..5a3ebb8 100644 --- a/pb.h +++ b/pb.h @@ -38,6 +38,11 @@ /* #define PB_OLD_CALLBACK_STYLE */ +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + /****************************************************************** * You usually don't need to change anything below this line. * * Feel free to look around and use the defined macros, though. * diff --git a/pb_encode.c b/pb_encode.c index 78a3fdb..221c0ea 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -136,6 +136,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) PB_RETURN_ERROR(stream, "array max size exceeded"); +#ifndef PB_ENCODE_ARRAYS_UNPACKED /* We always pack arrays if the datatype allows it. */ if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) { @@ -180,6 +181,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie } } else +#endif { p = pData; for (i = 0; i < count; i++) -- cgit v1.2.3 From 1effc72370a4331bed0c08a4308e3f1cf38dc86b Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 17 Aug 2019 16:14:00 +0300 Subject: Add scons package to travis rules, for build automation --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e8eca0f..20688b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,7 @@ addons: - g++-4.9 - gcc-5 - g++-5 + - scons before_install: -- cgit v1.2.3 From 0101f7cdaa7be255875de06b9313c1a6e03fcb4d Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 22 Aug 2019 11:25:41 +0300 Subject: network_server example: Detect count=0 in read_callback() (#421) Previously the example code would falsely detect EOF condition if called with count = 0. --- examples/network_server/common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/network_server/common.c b/examples/network_server/common.c index 04a5aa8..05048f6 100644 --- a/examples/network_server/common.c +++ b/examples/network_server/common.c @@ -19,6 +19,9 @@ static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count) int fd = (intptr_t)stream->state; int result; + if (count == 0) + return true; + result = recv(fd, buf, count, MSG_WAITALL); if (result == 0) -- cgit v1.2.3 From e29e6f4ebadef5e1ed9be1799e2dfbb12a26af67 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 22 Aug 2019 11:31:24 +0300 Subject: Don't call stream callback with count=0 (#421) This could happen with zero-length submessages and strings. While technically valid, it is an useless call and a rare corner case that can easily lead to bugs in the callback implementation. --- pb_decode.c | 3 +++ pb_encode.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pb_decode.c b/pb_decode.c index 2c652f1..a7caeff 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -99,6 +99,9 @@ static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t co bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { + if (count == 0) + return true; + #ifndef PB_BUFFER_ONLY if (buf == NULL && stream->callback != buf_read) { diff --git a/pb_encode.c b/pb_encode.c index 221c0ea..64b62b4 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -100,7 +100,7 @@ pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { - if (stream->callback != NULL) + if (count > 0 && stream->callback != NULL) { if (stream->bytes_written + count > stream->max_size) PB_RETURN_ERROR(stream, "stream full"); -- cgit v1.2.3 From 6f6a8e45957e033b920d40cc7ea85d13506e78c6 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 17 Aug 2019 16:08:08 +0300 Subject: .gitignore: don't ignore generator-bin files (#419) --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3bb09db..e5d09f6 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ examples/using_double_on_avr/test_conversions examples/using_union_messages/decode examples/using_union_messages/encode generator/nanopb_pb2.pyc +!generator-bin/**/* + -- cgit v1.2.3 From 7b0a42f10ce3676ae629aedca96f465e8d1edc80 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 2 Oct 2019 10:51:39 +0300 Subject: Make fuzztest detect invalid bool values (#434) --- tests/SConstruct | 2 +- tests/fuzztest/fuzztest.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/SConstruct b/tests/SConstruct index d2dfeec..504f1b8 100644 --- a/tests/SConstruct +++ b/tests/SConstruct @@ -103,7 +103,7 @@ if not env.GetOption('clean'): # Check if we can use undefined behaviour sanitizer (only with clang) # TODO: Fuzz test triggers the bool sanitizer, figure out whether to # modify the fuzz test or to keep ignoring the check. - extra = '-fsanitize=undefined,integer -fno-sanitize-recover=undefined,integer -fsanitize-recover=bool ' + extra = '-fsanitize=undefined,integer -fno-sanitize-recover=undefined,integer ' if 'clang' in env['CC']: if conf.CheckCCFLAGS(extra, linkflags = extra): conf.env.Append(CORECFLAGS = extra) diff --git a/tests/fuzztest/fuzztest.c b/tests/fuzztest/fuzztest.c index ee851ec..0dc2382 100644 --- a/tests/fuzztest/fuzztest.c +++ b/tests/fuzztest/fuzztest.c @@ -171,6 +171,33 @@ static void rand_mess(uint8_t *buf, size_t count) /* Some default data to put in the message */ static const alltypes_static_AllTypes initval = alltypes_static_AllTypes_init_default; +/* Check the invariants defined in security model on decoded structure */ +static void sanity_check_static(alltypes_static_AllTypes *msg) +{ + 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) + { + assert(strlen(msg->rep_string[0]) < sizeof(msg->rep_string[0])); + } + 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) + { + assert(memcmp(&msg->rep_bool[0], &truebool, sizeof(bool)) == 0 || + memcmp(&msg->rep_bool[0], &falsebool, sizeof(bool)) == 0); + } +} + #define BUFSIZE 4096 static bool do_static_encode(uint8_t *buffer, size_t *msglen) @@ -230,6 +257,11 @@ static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success rand_fill((uint8_t*)msg, sizeof(alltypes_static_AllTypes)); stream = pb_istream_from_buffer(buffer, msglen); status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg); + + if (status) + { + sanity_check_static(msg); + } if (!status && assert_success) { @@ -285,6 +317,7 @@ static void do_static_roundtrip(uint8_t *buffer, size_t msglen) pb_istream_t stream = pb_istream_from_buffer(buffer, msglen); status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1); assert(status); + sanity_check_static(msg1); } { @@ -298,6 +331,7 @@ static void do_static_roundtrip(uint8_t *buffer, size_t msglen) pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2); status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2); assert(status); + sanity_check_static(msg2); } { -- cgit v1.2.3 From 252f4199540a113a37d19de5ba051922909c810d Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 2 Oct 2019 10:56:49 +0300 Subject: Fix undefined behavior with bool fields (#434) Previously nanopb didn't enforce that decoded bool fields had valid true/false values. This could lead to undefined behavior in user code. This has potential security implications when 1) message contains bool field (has_ fields are safe) and 2) user code uses ternary operator dependent on the field value, such as: int value = msg.my_bool ? 1234 : 0 and 3) the value returned from ternary operator affects a memory access, such as: data_array[value] = 9999 --- docs/migration.rst | 16 ++++++++++++++++ docs/security.rst | 1 + pb.h | 27 ++++++++++++++------------- pb_decode.c | 18 ++++++++++++++++++ pb_decode.h | 7 +++++-- pb_encode.c | 30 ++++++++++++++++++++++++++++-- 6 files changed, 82 insertions(+), 17 deletions(-) diff --git a/docs/migration.rst b/docs/migration.rst index a1843e3..d358600 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -28,6 +28,22 @@ define always has the largest value. are not defined in ascending order, user code behaviour may change. Check that user code doesn't expect the old, incorrect first/last behaviour. +Fix undefined behavior related to bool fields +--------------------------------------------- + +**Rationale:** In C99, `bool` variables are not allowed to have other values +than `true` and `false`. Compilers use this fact in optimization, and constructs +like `int foo = msg.has_field ? 100 : 0` will give unexpected results otherwise. +Previously nanopb didn't enforce that decoded bool fields had valid values. + +**Changes:** Bool fields are now handled separately as `PB_LTYPE_BOOL`. The +`LTYPE` descriptor numbers for other field types were renumbered. + +**Required actions:** Source code files must be recompiled, but regenerating +`.pb.h`/`.pb.c` files from `.proto` is not required. If user code directly uses +the nanopb internal field representation (search for `PB_LTYPE_VARINT` in source), +it may need updating. + Nanopb-0.3.9.1, 0.4.0 (2018-04-14) ================================== diff --git a/docs/security.rst b/docs/security.rst index d854612..6f7152e 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -58,6 +58,7 @@ untrusted data has been maliciously crafted: - The *count* fields of arrays will not exceed the array size. - The *size* field of bytes will not exceed the allocated size. - All string fields will have null terminator. + - bool fields will have valid true/false values (since nanopb-0.3.9.4) 5. After pb_encode() returns successfully, the resulting message is a valid protocol buffers message. (Except if user-defined callbacks write incorrect diff --git a/pb.h b/pb.h index 5a3ebb8..aaf3f58 100644 --- a/pb.h +++ b/pb.h @@ -150,39 +150,40 @@ typedef uint_least8_t pb_type_t; /**** Field data types ****/ /* Numeric types */ -#define PB_LTYPE_VARINT 0x00 /* int32, int64, enum, bool */ -#define PB_LTYPE_UVARINT 0x01 /* uint32, uint64 */ -#define PB_LTYPE_SVARINT 0x02 /* sint32, sint64 */ -#define PB_LTYPE_FIXED32 0x03 /* fixed32, sfixed32, float */ -#define PB_LTYPE_FIXED64 0x04 /* fixed64, sfixed64, double */ +#define PB_LTYPE_BOOL 0x00 /* bool */ +#define PB_LTYPE_VARINT 0x01 /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02 /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03 /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04 /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05 /* fixed64, sfixed64, double */ /* Marker for last packable field type. */ -#define PB_LTYPE_LAST_PACKABLE 0x04 +#define PB_LTYPE_LAST_PACKABLE 0x05 /* Byte array with pre-allocated buffer. * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ -#define PB_LTYPE_BYTES 0x05 +#define PB_LTYPE_BYTES 0x06 /* String with pre-allocated buffer. * data_size is the maximum length. */ -#define PB_LTYPE_STRING 0x06 +#define PB_LTYPE_STRING 0x07 /* Submessage * submsg_fields is pointer to field descriptions */ -#define PB_LTYPE_SUBMESSAGE 0x07 +#define PB_LTYPE_SUBMESSAGE 0x08 /* Extension pseudo-field * The field contains a pointer to pb_extension_t */ -#define PB_LTYPE_EXTENSION 0x08 +#define PB_LTYPE_EXTENSION 0x09 /* Byte array with inline, pre-allocated byffer. * data_size is the length of the inline, allocated buffer. * This differs from PB_LTYPE_BYTES by defining the element as * pb_byte_t[data_size] rather than pb_bytes_array_t. */ -#define PB_LTYPE_FIXED_LENGTH_BYTES 0x09 +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0A /* Number of declared LTYPES */ -#define PB_LTYPES_COUNT 0x0A +#define PB_LTYPES_COUNT 0x0B #define PB_LTYPE_MASK 0x0F /**** Field repetition rules ****/ @@ -491,7 +492,7 @@ struct pb_extension_s { PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) /* The mapping from protobuf types to LTYPEs is done using these macros. */ -#define PB_LTYPE_MAP_BOOL PB_LTYPE_VARINT +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL #define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES #define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 #define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT diff --git a/pb_decode.c b/pb_decode.c index a7caeff..1779503 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -34,6 +34,7 @@ static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_ static bool checkreturn find_extension_field(pb_field_iter_t *iter); static void pb_field_set_to_default(pb_field_iter_t *iter); static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest); static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest); static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest); @@ -65,6 +66,7 @@ static void pb_release_single_field(const pb_field_iter_t *iter); * Order in the array must match pb_action_t LTYPE numbering. */ static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { + &pb_dec_bool, &pb_dec_varint, &pb_dec_uvarint, &pb_dec_svarint, @@ -1245,6 +1247,11 @@ void pb_release(const pb_field_t fields[], void *dest_struct) /* Field decoders */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + return pb_dec_bool(stream, NULL, (void*)dest); +} + bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) { pb_uint64_t value; @@ -1294,6 +1301,17 @@ bool pb_decode_fixed64(pb_istream_t *stream, void *dest) } #endif +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint32_t value; + PB_UNUSED(field); + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) { pb_uint64_t value; diff --git a/pb_decode.h b/pb_decode.h index 398b24a..3577c20 100644 --- a/pb_decode.h +++ b/pb_decode.h @@ -134,7 +134,7 @@ bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *ta /* Skip the field payload data, given the wire type. */ bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); -/* Decode an integer in the varint format. This works for bool, enum, int32, +/* Decode an integer in the varint format. This works for enum, int32, * int64, uint32 and uint64 field types. */ #ifndef PB_WITHOUT_64BIT bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); @@ -142,10 +142,13 @@ bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); #define pb_decode_varint pb_decode_varint32 #endif -/* Decode an integer in the varint format. This works for bool, enum, int32, +/* Decode an integer in the varint format. This works for enum, int32, * and uint32 field types. */ bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + /* Decode an integer in the zig-zagged svarint format. This works for sint32 * and sint64. */ #ifndef PB_WITHOUT_64BIT diff --git a/pb_encode.c b/pb_encode.c index 64b62b4..a8cd14e 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -28,6 +28,7 @@ static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *fie static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData); static void *pb_const_cast(const void *p); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_t *field, const void *src); static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src); static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); @@ -52,6 +53,7 @@ static bool checkreturn pb_encode_negative_varint(pb_ostream_t *stream, pb_uint6 * Order in the array must match pb_action_t LTYPE numbering. */ static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = { + &pb_enc_bool, &pb_enc_varint, &pb_enc_uvarint, &pb_enc_svarint, @@ -122,6 +124,22 @@ bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t cou * Encode a single field * *************************/ +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + /* Encode a static array. Handles the size calculations and possible packing. */ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func) @@ -240,7 +258,7 @@ static bool pb_check_proto3_default_value(const pb_field_t *field, const void *p else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->size_offset != 0) { /* Proto2 optional fields inside proto3 submessage */ - return *(const bool*)pSize == false; + return safe_read_bool(pSize) == false; } /* Rest is proto3 singular fields */ @@ -356,7 +374,7 @@ static bool checkreturn encode_basic_field(pb_ostream_t *stream, break; case PB_HTYPE_OPTIONAL: - if (*(const bool*)pSize) + if (safe_read_bool(pSize)) { if (!pb_encode_tag_for_field(stream, field)) return false; @@ -648,6 +666,7 @@ bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t pb_wire_type_t wiretype; switch (PB_LTYPE(field->type)) { + case PB_LTYPE_BOOL: case PB_LTYPE_VARINT: case PB_LTYPE_UVARINT: case PB_LTYPE_SVARINT: @@ -736,6 +755,13 @@ bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fie /* Field encoders */ +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + uint32_t value = (uint32_t)safe_read_bool(src); + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src) { pb_int64_t value = 0; -- cgit v1.2.3 From 9bb001e88ad1cd7c36f090eee828d435ae63a294 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 10 Oct 2019 19:48:45 +0200 Subject: Fix "unused variable" warning when using PB_ENCODE_ARRAYS_UNPACKED --- pb_encode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pb_encode.c b/pb_encode.c index a8cd14e..7a4ba31 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -146,8 +146,10 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie { size_t i; const void *p; +#ifndef PB_ENCODE_ARRAYS_UNPACKED size_t size; - +#endif + if (count == 0) return true; -- cgit v1.2.3 From 536fe50629c3e9c88a0a4c367929ea0f10e53ddf Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 11 Oct 2019 09:27:29 +0200 Subject: Add test for PB_ENCODE_ARRAYS_UNPACKED --- tests/encode_arrays_unpacked/SConscript | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/encode_arrays_unpacked/SConscript diff --git a/tests/encode_arrays_unpacked/SConscript b/tests/encode_arrays_unpacked/SConscript new file mode 100644 index 0000000..bd36d9d --- /dev/null +++ b/tests/encode_arrays_unpacked/SConscript @@ -0,0 +1,28 @@ +# Run the alltypes test case, but compile with PB_ENCODE_ARRAYS_UNPACKED=1 + +Import("env") + +# Take copy of the files for custom build. +c = Copy("$TARGET", "$SOURCE") +env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c) +env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c) +env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c) +env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c) + +# Define the compilation options +opts = env.Clone() +opts.Append(CPPDEFINES = {'PB_ENCODE_ARRAYS_UNPACKED': 1}) + +# Build new version of core +strict = opts.Clone() +strict.Append(CFLAGS = strict['CORECFLAGS']) +strict.Object("pb_decode_unpacked.o", "$NANOPB/pb_decode.c") +strict.Object("pb_encode_unpacked.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common_unpacked.o", "$NANOPB/pb_common.c") + +# Now build and run the test normally. +enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_unpacked.o", "pb_common_unpacked.o"]) +dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_unpacked.o", "pb_common_unpacked.o"]) + +env.RunTest(enc) +env.RunTest([dec, "encode_alltypes.output"]) -- cgit v1.2.3 From 1d6f3f4d1399b11f65008ba09826fa904a4d1739 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sun, 13 Oct 2019 15:05:30 +0300 Subject: Update changelog --- AUTHORS.txt | 3 +++ CHANGELOG.txt | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 2d63fed..4fd600b 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -50,3 +50,6 @@ Gabriel Staples Amarnath Michal Rostecki Pei Wang +Noah Pendleton <2538614+noahp@users.noreply.github.com> +Pavol Rusnak + diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 69f0bc7..bf1c8fd 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,10 @@ +nanopb-0.3.9.4 (2019-10-13) + Fix undefined behavior with bool fields (#434) + Fix enum min/max defines when values are not in order (#405) + Fix network_server socket example with zero-length strings (#421) + Don't call stream read callback with count=0 (#421) + Add compile time flag PB_ENCODE_ARRAYS_UNPACKED (#427) + nanopb-0.3.9.3 (2019-03-08) Fix fixed size and callback repeated fields inside proto3 submessages (#376, #382, #386) Fix incorrect PB_STATIC_ASSERT for bytes inside oneof (#363) -- cgit v1.2.3 From 493adf3616bee052649c63c473f8355630c2797f Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sun, 13 Oct 2019 15:06:05 +0300 Subject: Publishing nanopb-0.3.9.4 --- CMakeLists.txt | 2 +- generator/nanopb_generator.py | 2 +- library.json | 2 +- pb.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eccb62..9704d35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.3.9.3) +set(nanopb_VERSION_STRING nanopb-0.3.9.4) set(nanopb_SOVERSION 0) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 63983e5..12af67e 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.9.3" +nanopb_version = "nanopb-0.3.9.4" import sys import re diff --git a/library.json b/library.json index a41055c..7bed92d 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Nanopb", - "version": "0.3.9.3", + "version": "0.3.9.4", "keywords": "protocol buffers, protobuf, google", "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.", "repository": { diff --git a/pb.h b/pb.h index aaf3f58..fc0fe7a 100644 --- a/pb.h +++ b/pb.h @@ -51,7 +51,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.9.3 +#define NANOPB_VERSION nanopb-0.3.9.4 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3 From ba4e832488fbb7165b908a17225c367c13cb72f3 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 16 Oct 2019 21:02:59 +0300 Subject: Avoid compiler warning in bool cast (#440) --- pb_encode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pb_encode.c b/pb_encode.c index 7a4ba31..4dd53a9 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -759,7 +759,7 @@ bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fie static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_t *field, const void *src) { - uint32_t value = (uint32_t)safe_read_bool(src); + uint32_t value = safe_read_bool(src) ? 1 : 0; PB_UNUSED(field); return pb_encode_varint(stream, value); } -- cgit v1.2.3 From 92de18fd4eebba7056b1376241b7fb62bd9e7246 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 11 Dec 2019 19:30:49 -0800 Subject: Backported Python pip packaging rules. --- extra/poetry/poetry_build.sh | 9 +++++++++ extra/poetry/pyproject.toml | 26 ++++++++++++++++++++++++++ generator/nanopb_generator.py | 9 +++++++-- tools/set_version.sh | 2 ++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100755 extra/poetry/poetry_build.sh create mode 100644 extra/poetry/pyproject.toml diff --git a/extra/poetry/poetry_build.sh b/extra/poetry/poetry_build.sh new file mode 100755 index 0000000..d428d39 --- /dev/null +++ b/extra/poetry/poetry_build.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +rm -rf nanopb +mkdir nanopb +cp -pr ../../generator nanopb/ +touch nanopb/__init__.py nanopb/generator/__init__.py +cp -pr ../../README.md . +sed -i -e 's/\(version =.*\)-dev.*/\1-dev'$(git rev-list HEAD --count)'"/' pyproject.toml +poetry build diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml new file mode 100644 index 0000000..119970b --- /dev/null +++ b/extra/poetry/pyproject.toml @@ -0,0 +1,26 @@ +[tool.poetry] +name = "nanopb" +version = "0.3.9.4" +description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." +authors = ["Petteri Aimonen "] +license = "Zlib" +repository = "https://github.com/nanopb/nanopb/" +readme = "README.md" +homepage = "https://jpa.kapsi.fi/nanopb/" +documentation = "https://jpa.kapsi.fi/nanopb/docs/index.html" +keywords = ["protobuf", "protoc"] +classifiers = ["Topic :: Software Development :: Build Tools"] + +[tool.poetry.scripts] +nanopb_generator = "nanopb.generator.nanopb_generator:main_cli" +protoc-gen-nanopb = "nanopb.generator.nanopb_generator:main_plugin" + +[tool.poetry.dependencies] +python = ">=2.7" +protobuf = ">=3.6" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 12af67e..526a74a 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -15,6 +15,8 @@ try: # Add some dummy imports to keep packaging tools happy. import google, distutils.util # bbfreeze seems to need these import pkg_resources # pyinstaller / protobuf 2.5 seem to need these + import proto.nanopb_pb2 as nanopb_pb2 # pyinstaller seems to need this + import proto.plugin_pb2 as plugin_pb2 except: # Don't care, we will error out later if it is actually important. pass @@ -32,8 +34,7 @@ except: raise try: - import proto.nanopb_pb2 as nanopb_pb2 - import proto.plugin_pb2 as plugin_pb2 + from .proto import nanopb_pb2, plugin_pb2 except TypeError: sys.stderr.write(''' **************************************************************************** @@ -51,6 +52,10 @@ except TypeError: **************************************************************************** ''' + '\n') raise +except (ValueError, SystemError): + # Probably invoked directly instead of via installed scripts. + import proto.nanopb_pb2 as nanopb_pb2 + import proto.plugin_pb2 as plugin_pb2 except: sys.stderr.write(''' ******************************************************************** diff --git a/tools/set_version.sh b/tools/set_version.sh index 0adb597..6aa25eb 100755 --- a/tools/set_version.sh +++ b/tools/set_version.sh @@ -12,3 +12,5 @@ VERSION_ONLY=$(echo $1 | sed 's/nanopb-//') if [[ $1 != *dev ]] then sed -i -e 's/"version":\s*"[^"]*"/"version": "'$VERSION_ONLY'"/' library.json fi + +sed -i -e 's/version =.*/version = "'$VERSION_ONLY'"/' extra/poetry/pyproject.toml -- cgit v1.2.3 From 597c747e5365fd5599d8aa0e6bb8c9bb882a95ee Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 14 Dec 2019 09:42:46 +0200 Subject: Generate python-protobuf files for PyPI package (#461) --- extra/poetry/poetry_build.sh | 1 + extra/poetry/pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extra/poetry/poetry_build.sh b/extra/poetry/poetry_build.sh index d428d39..7ed20f3 100755 --- a/extra/poetry/poetry_build.sh +++ b/extra/poetry/poetry_build.sh @@ -5,5 +5,6 @@ mkdir nanopb cp -pr ../../generator nanopb/ touch nanopb/__init__.py nanopb/generator/__init__.py cp -pr ../../README.md . +make -C nanopb/generator/proto sed -i -e 's/\(version =.*\)-dev.*/\1-dev'$(git rev-list HEAD --count)'"/' pyproject.toml poetry build diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index 119970b..edfbc0c 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.4" +version = "0.3.9.4.post1" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" -- cgit v1.2.3 From e32e7c7ee2a472a50bc17e25bb13472fb1ddf53c Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 14 Dec 2019 09:46:24 +0200 Subject: Revert "Generate python-protobuf files for PyPI package (#461)" This reverts commit 597c747e5365fd5599d8aa0e6bb8c9bb882a95ee. --- extra/poetry/poetry_build.sh | 1 - extra/poetry/pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/extra/poetry/poetry_build.sh b/extra/poetry/poetry_build.sh index 7ed20f3..d428d39 100755 --- a/extra/poetry/poetry_build.sh +++ b/extra/poetry/poetry_build.sh @@ -5,6 +5,5 @@ mkdir nanopb cp -pr ../../generator nanopb/ touch nanopb/__init__.py nanopb/generator/__init__.py cp -pr ../../README.md . -make -C nanopb/generator/proto sed -i -e 's/\(version =.*\)-dev.*/\1-dev'$(git rev-list HEAD --count)'"/' pyproject.toml poetry build diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index edfbc0c..119970b 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.4.post1" +version = "0.3.9.4" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" -- cgit v1.2.3 From 29440cd9b9000a38ad6f6d91d1b9c7be1f7cd50c Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 10 Nov 2018 14:45:20 +0200 Subject: Automatically rebuild nanopb_pb2.py --- generator/proto/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/generator/proto/__init__.py b/generator/proto/__init__.py index e69de29..96b2ee0 100644 --- a/generator/proto/__init__.py +++ b/generator/proto/__init__.py @@ -0,0 +1,19 @@ +'''This file automatically rebuilds the proto definitions for Python.''' + +import os +import os.path +import sys +import subprocess + +dirname = os.path.dirname(__file__) +protosrc = os.path.join(dirname, "nanopb.proto") +protodst = os.path.join(dirname, "nanopb_pb2.py") + +if os.path.isfile(protosrc): + src_date = os.path.getmtime(protosrc) + if not os.path.isfile(protodst) or os.path.getmtime(protodst) < src_date: + cmd = ["protoc", "--python_out=.", "nanopb.proto", "plugin.proto"] + status = subprocess.call(cmd, cwd = dirname) + if status != 0: + sys.stderr.write("Failed to build nanopb_pb2.py: " + ' '.join(cmd) + "\n") + -- cgit v1.2.3 From 1c642695e95acd1371efadae7f5728eab006b430 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 14 Dec 2019 09:48:41 +0200 Subject: Further fix attempt for #461 --- extra/poetry/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index 119970b..42fdfea 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.4" +version = "0.3.9.4.post2" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" -- cgit v1.2.3 From a2738568dee7c192cc2836bbd27cd7721e39230a Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 14 Dec 2019 09:51:26 +0200 Subject: Eh, removed wrong release on PyPI.. --- extra/poetry/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index 42fdfea..31b0720 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.4.post2" +version = "0.3.9.4.post3" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" -- cgit v1.2.3 From ddaa999787108c12db76e7525f7340f9066ef547 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 19 Dec 2019 10:07:49 +0200 Subject: Fix empty submessages getting encoded in proto3 mode (#395) Repeated callback fields were being treated as "always present", even if the callback pointer wasn't set. --- pb_encode.c | 2 +- tests/regression/issue_395/SConscript | 14 ++++++++++++ tests/regression/issue_395/test.c | 38 +++++++++++++++++++++++++++++++++ tests/regression/issue_395/test.options | 2 ++ tests/regression/issue_395/test.proto | 37 ++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/regression/issue_395/SConscript create mode 100644 tests/regression/issue_395/test.c create mode 100644 tests/regression/issue_395/test.options create mode 100644 tests/regression/issue_395/test.proto diff --git a/pb_encode.c b/pb_encode.c index 4dd53a9..0f89d98 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -249,7 +249,7 @@ static bool pb_check_proto3_default_value(const pb_field_t *field, const void *p /* Repeated fields inside proto3 submessage: present if count != 0 */ if (field->size_offset != 0) return *(const pb_size_t*)pSize == 0; - else + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) return false; /* Fixed length array */ } else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) diff --git a/tests/regression/issue_395/SConscript b/tests/regression/issue_395/SConscript new file mode 100644 index 0000000..8bc1030 --- /dev/null +++ b/tests/regression/issue_395/SConscript @@ -0,0 +1,14 @@ +# Regression test for #395: +# Unexpected empty submessage in proto3 mode + +Import("env") + +env.NanopbProto(["test.proto", "test.options"]) +testprog = env.Program(["test.c", + "test.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(testprog) + diff --git a/tests/regression/issue_395/test.c b/tests/regression/issue_395/test.c new file mode 100644 index 0000000..9578caf --- /dev/null +++ b/tests/regression/issue_395/test.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include "test.pb.h" +#include "unittests.h" + +int main(int argc, char **argv) +{ + int status = 0; + uint8_t buffer[512] = {0}; + int i; + pb_ostream_t ostream; + + Reply reply = Reply_init_zero; + Reply_Result request_result = Reply_Result_OK; + + ostream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + reply.result = request_result; + if (!pb_encode(&ostream, Reply_fields, &reply)) { + 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 == 2); + TEST(buffer[0] == 0x08); + TEST(buffer[1] == 0x01); + + return status; +} + diff --git a/tests/regression/issue_395/test.options b/tests/regression/issue_395/test.options new file mode 100644 index 0000000..5594796 --- /dev/null +++ b/tests/regression/issue_395/test.options @@ -0,0 +1,2 @@ +SubSubAMessage.somestring max_size:64 +SubSubBMessage.somestring max_size:64 diff --git a/tests/regression/issue_395/test.proto b/tests/regression/issue_395/test.proto new file mode 100644 index 0000000..91bf371 --- /dev/null +++ b/tests/regression/issue_395/test.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +message Error +{ + int32 code = 1; + string message = 2; +} + +message SubSubAMessage +{ + string somestring = 1; +} + +message SubSubBMessage +{ + string somestring = 1; +} + +message SubMessage +{ + SubSubAMessage subsubmessageA = 1; + repeated SubSubBMessage subsubmessageB = 2; +} + +message Reply +{ + enum Result + { + ERROR = 0; + OK = 1; + SOME_A = 2; + } + + Result result = 1; + Error error = 2; + SubMessage submessage = 3; +} -- cgit v1.2.3 From ced3bb2478b018ad463143c272a4e6315e265fef Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Tue, 31 Dec 2019 13:07:17 +0200 Subject: Add workaround for avr-libc realloc() bug (#475) --- pb_decode.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pb_decode.c b/pb_decode.c index 1779503..5195f88 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -491,6 +491,16 @@ static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t if (data_size == 0 || array_size == 0) PB_RETURN_ERROR(stream, "invalid size"); +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + /* Check for multiplication overflows. * This code avoids the costly division if the sizes are small enough. * Multiplication is safe as long as only half of bits are set -- cgit v1.2.3 From aa9d0d1ca78d6adec3adfeecf3a706c7f9df81f2 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 1 Feb 2020 18:40:45 +0200 Subject: Fix invalid free() after failed realloc() (GHSA-gcx3-7m76-287p) --- pb_decode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pb_decode.c b/pb_decode.c index 5195f88..df8873e 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -655,11 +655,11 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ if (*size == PB_SIZE_MAX) PB_RETURN_ERROR(stream, "too many array entries"); - (*size)++; - if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size)) + if (!allocate_field(stream, iter->pData, iter->pos->data_size, (size_t)(*size + 1))) return false; - pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1); + pItem = *(char**)iter->pData + iter->pos->data_size * (*size); + (*size)++; initialize_pointer_field(pItem, iter); return func(stream, iter->pos, pItem); } -- cgit v1.2.3 From c046e36149ee09d9a2c5b8f020a6a2befd5e85bc Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 1 Feb 2020 20:10:30 +0200 Subject: Publishing nanopb-0.3.9.5 --- CHANGELOG.txt | 5 +++++ CMakeLists.txt | 2 +- extra/poetry/pyproject.toml | 2 +- generator/nanopb_generator.py | 2 +- library.json | 2 +- pb.h | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index bf1c8fd..f8aedaa 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,8 @@ +nanopb-0.3.9.5 (2020-02-02) + Fix invalid free() after failed realloc() (GHSA-gcx3-7m76-287p) + Add workaround for avr-libc realloc() bug (#475) + Fix empty submessages getting encoded in proto3 mode (#395) + nanopb-0.3.9.4 (2019-10-13) Fix undefined behavior with bool fields (#434) Fix enum min/max defines when values are not in order (#405) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9704d35..fbe499e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.3.9.4) +set(nanopb_VERSION_STRING nanopb-0.3.9.5) set(nanopb_SOVERSION 0) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index 31b0720..56083ef 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.4.post3" +version = "0.3.9.5" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 526a74a..f236839 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.9.4" +nanopb_version = "nanopb-0.3.9.5" import sys import re diff --git a/library.json b/library.json index 7bed92d..9d529e2 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Nanopb", - "version": "0.3.9.4", + "version": "0.3.9.5", "keywords": "protocol buffers, protobuf, google", "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.", "repository": { diff --git a/pb.h b/pb.h index fc0fe7a..2c5868b 100644 --- a/pb.h +++ b/pb.h @@ -51,7 +51,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.9.4 +#define NANOPB_VERSION nanopb-0.3.9.5 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3 From accfbbbd6840dd796efe835a0bf4f89a0835c238 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sun, 2 Feb 2020 10:57:12 +0200 Subject: Avoid overflows in allocation for packed fields. This will only occur with unlimited length streams, so it's kind-of low impact. Such streams allow denial of service anyway. --- CHANGELOG.txt | 1 + pb_decode.c | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index f8aedaa..3ce6e98 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,7 @@ nanopb-0.3.9.5 (2020-02-02) Fix invalid free() after failed realloc() (GHSA-gcx3-7m76-287p) Add workaround for avr-libc realloc() bug (#475) Fix empty submessages getting encoded in proto3 mode (#395) + Avoid overflows in allocation for packed fields. nanopb-0.3.9.4 (2019-10-13) Fix undefined behavior with bool fields (#434) diff --git a/pb_decode.c b/pb_decode.c index df8873e..ce5c4ae 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -607,12 +607,25 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ while (substream.bytes_left) { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + if ((size_t)*size + 1 > allocated_size) { /* Allocate more storage. This tries to guess the * number of remaining entries. Round the division * upwards. */ - allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1; + size_t remain = (substream.bytes_left - 1) / iter->pos->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size)) { @@ -630,15 +643,6 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ break; } - if (*size == PB_SIZE_MAX) - { -#ifndef PB_NO_ERRMSG - stream->errmsg = "too many array entries"; -#endif - status = false; - break; - } - (*size)++; } if (!pb_close_string_substream(stream, &substream)) -- cgit v1.2.3 From b233555f55628d0097266ea7ae653f5ec52f3732 Mon Sep 17 00:00:00 2001 From: Franck Date: Thu, 26 Mar 2020 11:21:58 +0100 Subject: Regression test + fix for proto3 submessage improperly considered empty --- pb_encode.c | 6 +++++ tests/regression/issue_504/SConscript | 12 +++++++++ tests/regression/issue_504/test.c | 47 +++++++++++++++++++++++++++++++++++ tests/regression/issue_504/test.proto | 13 ++++++++++ 4 files changed, 78 insertions(+) create mode 100644 tests/regression/issue_504/SConscript create mode 100644 tests/regression/issue_504/test.c create mode 100644 tests/regression/issue_504/test.proto diff --git a/pb_encode.c b/pb_encode.c index 0f89d98..be82510 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -304,6 +304,12 @@ static bool pb_check_proto3_default_value(const pb_field_t *field, const void *p return true; } } + + /* Compares pointers to NULL in case of FT_POINTER */ + if (PB_ATYPE(type) == PB_ATYPE_POINTER && PB_LTYPE(type) > PB_LTYPE_LAST_PACKABLE) + { + return !*(const void**)((uintptr_t)pData); + } { /* Catch-all branch that does byte-per-byte comparison for zero value. diff --git a/tests/regression/issue_504/SConscript b/tests/regression/issue_504/SConscript new file mode 100644 index 0000000..c303171 --- /dev/null +++ b/tests/regression/issue_504/SConscript @@ -0,0 +1,12 @@ +# Regression test for #504: +# Non empty submessage considered empty on FT_POINTER fields with address aligned on 0x100 + +Import('env', 'malloc_env') + +env.NanopbProto(["test.proto"]) +test = malloc_env.Program(["test.c", + "test.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(test) diff --git a/tests/regression/issue_504/test.c b/tests/regression/issue_504/test.c new file mode 100644 index 0000000..1ca86db --- /dev/null +++ b/tests/regression/issue_504/test.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include "test.pb.h" +#include "unittests.h" + +const char STR[] = "test str"; +#define ALIGN 0x100 + +int main(int argc, char **argv) +{ + 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)); + + 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); + + return status; +} + diff --git a/tests/regression/issue_504/test.proto b/tests/regression/issue_504/test.proto new file mode 100644 index 0000000..a5b0d9c --- /dev/null +++ b/tests/regression/issue_504/test.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +import "nanopb.proto"; + +message MyMessage +{ + SubMessage submessage = 1; +} + +message SubMessage +{ + string somestring = 1 [(nanopb).type = FT_POINTER]; +} -- cgit v1.2.3 From 7b442354d0345e7c19b018800f7ead497c1a61ac Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Mon, 22 Jun 2020 14:57:26 +0300 Subject: Fix buffer overflow when encoding bytes with size set to 65535 (#547) On platforms where size_t equals pb_size_t, for example AVR where both are 16-bit, or x86 and ARM when PB_FIELD_32BIT is defined, the buffer size checks in pb_write() and pb_enc_submessage can overflow if a bytes field has size close to maximum size value. This causes read and write out of bounds. This issue can cause a security vulnerability if the size of a bytes field in the structure given to pb_encode() is untrusted. Note that pb_decode() has correct bounds checking and will reject too large values. --- pb_encode.c | 11 ++++++++--- tests/regression/issue_547/SConscript | 21 +++++++++++++++++++++ tests/regression/issue_547/test.c | 28 ++++++++++++++++++++++++++++ tests/regression/issue_547/test.proto | 7 +++++++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 tests/regression/issue_547/SConscript create mode 100644 tests/regression/issue_547/test.c create mode 100644 tests/regression/issue_547/test.proto diff --git a/pb_encode.c b/pb_encode.c index be82510..371d256 100644 --- a/pb_encode.c +++ b/pb_encode.c @@ -104,8 +104,11 @@ bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t cou { if (count > 0 && stream->callback != NULL) { - if (stream->bytes_written + count > stream->max_size) + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { PB_RETURN_ERROR(stream, "stream full"); + } #ifdef PB_BUFFER_ONLY if (!buf_write(stream, buf, count)) @@ -849,6 +852,7 @@ static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *f static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src) { const pb_bytes_array_t *bytes = NULL; + size_t allocsize; bytes = (const pb_bytes_array_t*)src; @@ -858,8 +862,9 @@ static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *fie return pb_encode_string(stream, NULL, 0); } - if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && - PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size) + allocsize = PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size); + if (allocsize < bytes->size || + (PB_ATYPE(field->type) == PB_ATYPE_STATIC && allocsize > field->data_size)) { PB_RETURN_ERROR(stream, "bytes size exceeded"); } diff --git a/tests/regression/issue_547/SConscript b/tests/regression/issue_547/SConscript new file mode 100644 index 0000000..c0a19fa --- /dev/null +++ b/tests/regression/issue_547/SConscript @@ -0,0 +1,21 @@ +# Regression test for issue #547: +# Buffer overflow when encoding bytes with size set to 65535 + +Import("env") + +env.NanopbProto("test.proto") + +# Define the compilation options +opts = env.Clone() +opts.Append(CPPDEFINES = {'PB_FIELD_32BIT': 1}) + +# Build new version of core +strict = opts.Clone() +strict.Append(CFLAGS = strict['CORECFLAGS']) +strict.Object("pb_encode_fields32.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common_fields32.o", "$NANOPB/pb_common.c") + +# Build and run test +test = opts.Program(["test.c", "test.pb.c", "pb_encode_fields32.o", "pb_common_fields32.o"]) + +env.RunTest(test) diff --git a/tests/regression/issue_547/test.c b/tests/regression/issue_547/test.c new file mode 100644 index 0000000..9530302 --- /dev/null +++ b/tests/regression/issue_547/test.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include "test.pb.h" + +int main() +{ + pb_byte_t buf[512]; + MyMessage msg = MyMessage_init_zero; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + + msg.mybytes.size = 0xFFFFFFFF; + + if (pb_encode(&stream, MyMessage_fields, &msg)) + { + fprintf(stderr, "Failure: expected pb_encode() to fail.\n"); + return 1; + } + else if (strcmp(PB_GET_ERROR(&stream), "bytes size exceeded") != 0) + { + fprintf(stderr, "Unexpected encoding error: %s\n", PB_GET_ERROR(&stream)); + return 2; + } + else + { + return 0; + } +} diff --git a/tests/regression/issue_547/test.proto b/tests/regression/issue_547/test.proto new file mode 100644 index 0000000..56daa41 --- /dev/null +++ b/tests/regression/issue_547/test.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import "nanopb.proto"; + +message MyMessage { + required bytes mybytes = 1 [(nanopb).max_size = 512]; +} -- cgit v1.2.3 From 63f8e5e4946a5f8f0211b454bd37187871c3d023 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Thu, 26 Dec 2019 22:47:20 +0200 Subject: Fix ImportError when using generator/protoc with Python 3 --- generator/nanopb_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index f236839..2d22c33 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -52,7 +52,7 @@ except TypeError: **************************************************************************** ''' + '\n') raise -except (ValueError, SystemError): +except (ValueError, SystemError, ImportError): # Probably invoked directly instead of via installed scripts. import proto.nanopb_pb2 as nanopb_pb2 import proto.plugin_pb2 as plugin_pb2 -- cgit v1.2.3 From 84265e68619921b6df9c9e9a75a45bd50718c9cf Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 22 Jun 2020 08:55:49 -0700 Subject: Swift package manager (#549) Add build rules for Swift package manager --- .github/workflows/spm.yml | 15 +++++++++++ Package.swift | 63 +++++++++++++++++++++++++++++++++++++++++++ SwiftPackage/module.modulemap | 5 ++++ SwiftPackage/nanopb.h | 4 +++ spm-test/objc/c-header.c | 1 + spm-test/objc/objc-header.m | 1 + spm-test/objc/objc-module.m | 1 + spm-test/swift/main.swift | 1 + 8 files changed, 91 insertions(+) create mode 100644 .github/workflows/spm.yml create mode 100644 Package.swift create mode 100644 SwiftPackage/module.modulemap create mode 100644 SwiftPackage/nanopb.h create mode 100644 spm-test/objc/c-header.c create mode 100644 spm-test/objc/objc-header.m create mode 100644 spm-test/objc/objc-module.m create mode 100644 spm-test/swift/main.swift diff --git a/.github/workflows/spm.yml b/.github/workflows/spm.yml new file mode 100644 index 0000000..999851a --- /dev/null +++ b/.github/workflows/spm.yml @@ -0,0 +1,15 @@ +name: spm + +on: + push: + pull_request: + +jobs: + swift-build-run: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v2 + - name: Build + run: swift build + - name: Run + run: swift test diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..828d5ed --- /dev/null +++ b/Package.swift @@ -0,0 +1,63 @@ +// swift-tools-version:5.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "nanopb", + platforms: [ + .macOS(.v10_10), + .iOS(.v8), + .tvOS(.v9), + .watchOS(.v2) + ], + + targets: [ + .target( + name: "nanopb", + path: ".", + sources: [ + "pb.h", + "pb_common.h", + "pb_common.c", + "pb_decode.h", + "pb_decode.c", + "pb_encode.h", + "pb_encode.c" + ], + publicHeadersPath: "SwiftPackage", + cSettings: [ + .define("PB_FIELD_32BIT", to: "1"), + .define("PB_NO_PACKED_STRUCTS", to: "1"), + .define("PB_ENABLE_MALLOC", to: "1"), + ] + ), + .testTarget( + name: "swift-test", + dependencies: [ + "nanopb", + ], + path: "spm-test/swift", + cSettings: [ + .headerSearchPath("../"), + .define("PB_FIELD_32BIT", to: "1"), + .define("PB_NO_PACKED_STRUCTS", to: "1"), + .define("PB_ENABLE_MALLOC", to: "1"), + ] + ), + .testTarget( + name: "objc-test", + dependencies: [ + "nanopb", + ], + path: "spm-test/objc", + cSettings: [ + .headerSearchPath("../"), + .define("PB_FIELD_32BIT", to: "1"), + .define("PB_NO_PACKED_STRUCTS", to: "1"), + .define("PB_ENABLE_MALLOC", to: "1"), + ] + ) + ] +) + diff --git a/SwiftPackage/module.modulemap b/SwiftPackage/module.modulemap new file mode 100644 index 0000000..0888ab1 --- /dev/null +++ b/SwiftPackage/module.modulemap @@ -0,0 +1,5 @@ +module nanopb { + umbrella header "nanopb.h" + + export * +} \ No newline at end of file diff --git a/SwiftPackage/nanopb.h b/SwiftPackage/nanopb.h new file mode 100644 index 0000000..d0666d7 --- /dev/null +++ b/SwiftPackage/nanopb.h @@ -0,0 +1,4 @@ +#import "../pb.h" +#import "../pb_common.h" +#import "../pb_decode.h" +#import "../pb_encode.h" diff --git a/spm-test/objc/c-header.c b/spm-test/objc/c-header.c new file mode 100644 index 0000000..2995860 --- /dev/null +++ b/spm-test/objc/c-header.c @@ -0,0 +1 @@ +#include "nanopb.h" diff --git a/spm-test/objc/objc-header.m b/spm-test/objc/objc-header.m new file mode 100644 index 0000000..d8f79cf --- /dev/null +++ b/spm-test/objc/objc-header.m @@ -0,0 +1 @@ +#import "nanopb.h" diff --git a/spm-test/objc/objc-module.m b/spm-test/objc/objc-module.m new file mode 100644 index 0000000..18b2833 --- /dev/null +++ b/spm-test/objc/objc-module.m @@ -0,0 +1 @@ +@import nanopb; diff --git a/spm-test/swift/main.swift b/spm-test/swift/main.swift new file mode 100644 index 0000000..97cf622 --- /dev/null +++ b/spm-test/swift/main.swift @@ -0,0 +1 @@ +import nanopb -- cgit v1.2.3 From 4502555fd3a11d2618e58c2053413b2ffcd9e59a Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Tue, 23 Jun 2020 10:30:00 +0300 Subject: Publishing nanopb-0.3.9.6 --- AUTHORS.txt | 3 ++- CHANGELOG.txt | 6 ++++++ CMakeLists.txt | 2 +- extra/poetry/pyproject.toml | 2 +- generator/nanopb_generator.py | 2 +- library.json | 2 +- pb.h | 2 +- 7 files changed, 13 insertions(+), 6 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index 4fd600b..707faaa 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -52,4 +52,5 @@ Michal Rostecki Pei Wang Noah Pendleton <2538614+noahp@users.noreply.github.com> Pavol Rusnak - +Franck +Paul Beusterien diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 3ce6e98..cea2be6 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,9 @@ +nanopb-0.3.9.6 (2020-06-xx) + Fix buffer overflow when encoding bytes with size set to 65535 (#547, GHSA-3p39-mfxg-hrq4) + Fix proto3 submessage improperly considered empty (#504) + Fix ImportError when using generator/protoc with Python 3 + Add build rules for Swift package manager (#549) + nanopb-0.3.9.5 (2020-02-02) Fix invalid free() after failed realloc() (GHSA-gcx3-7m76-287p) Add workaround for avr-libc realloc() bug (#475) diff --git a/CMakeLists.txt b/CMakeLists.txt index fbe499e..3bad8cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.3.9.5) +set(nanopb_VERSION_STRING nanopb-0.3.9.6) set(nanopb_SOVERSION 0) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index 56083ef..b5357b0 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.5" +version = "0.3.9.6" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 2d22c33..7cc51ea 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.9.5" +nanopb_version = "nanopb-0.3.9.6" import sys import re diff --git a/library.json b/library.json index 9d529e2..ca7324e 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Nanopb", - "version": "0.3.9.5", + "version": "0.3.9.6", "keywords": "protocol buffers, protobuf, google", "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.", "repository": { diff --git a/pb.h b/pb.h index 2c5868b..2ba6c63 100644 --- a/pb.h +++ b/pb.h @@ -51,7 +51,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.9.5 +#define NANOPB_VERSION nanopb-0.3.9.6 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3 From 3cfa21200eea012d8765239ad4c50d8a36c283f1 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 7 Aug 2020 02:38:52 -0700 Subject: More seamless SwiftPM implementation (#567) This update allows clients to share common syntax for nanopb imports/includes between CocoaPods and Swift Package Manager. See example at firebase/firebase-ios-sdk#6165 --- Package.swift | 9 ++++++++- SwiftPackage/module.modulemap | 5 ----- SwiftPackage/nanopb.h | 4 ---- spm-test/objc/c-header.c | 4 +++- spm-test/objc/objc-header.m | 4 +++- spm-test/objc/objc-qualified.m | 3 +++ spm_headers/nanopb/pb.h | 1 + spm_headers/nanopb/pb_common.h | 1 + spm_headers/nanopb/pb_decode.h | 1 + spm_headers/nanopb/pb_encode.h | 1 + spm_headers/pb.h | 1 + spm_headers/pb_common.h | 1 + spm_headers/pb_decode.h | 1 + spm_headers/pb_encode.h | 1 + 14 files changed, 25 insertions(+), 12 deletions(-) delete mode 100644 SwiftPackage/module.modulemap delete mode 100644 SwiftPackage/nanopb.h create mode 100644 spm-test/objc/objc-qualified.m create mode 120000 spm_headers/nanopb/pb.h create mode 120000 spm_headers/nanopb/pb_common.h create mode 120000 spm_headers/nanopb/pb_decode.h create mode 120000 spm_headers/nanopb/pb_encode.h create mode 100644 spm_headers/pb.h create mode 100644 spm_headers/pb_common.h create mode 100644 spm_headers/pb_decode.h create mode 100644 spm_headers/pb_encode.h diff --git a/Package.swift b/Package.swift index 828d5ed..2b8c581 100644 --- a/Package.swift +++ b/Package.swift @@ -12,6 +12,13 @@ let package = Package( .watchOS(.v2) ], + products: [ + .library( + name: "nanopb", + targets: ["nanopb"] + ) + ], + targets: [ .target( name: "nanopb", @@ -25,7 +32,7 @@ let package = Package( "pb_encode.h", "pb_encode.c" ], - publicHeadersPath: "SwiftPackage", + publicHeadersPath: "spm_headers", cSettings: [ .define("PB_FIELD_32BIT", to: "1"), .define("PB_NO_PACKED_STRUCTS", to: "1"), diff --git a/SwiftPackage/module.modulemap b/SwiftPackage/module.modulemap deleted file mode 100644 index 0888ab1..0000000 --- a/SwiftPackage/module.modulemap +++ /dev/null @@ -1,5 +0,0 @@ -module nanopb { - umbrella header "nanopb.h" - - export * -} \ No newline at end of file diff --git a/SwiftPackage/nanopb.h b/SwiftPackage/nanopb.h deleted file mode 100644 index d0666d7..0000000 --- a/SwiftPackage/nanopb.h +++ /dev/null @@ -1,4 +0,0 @@ -#import "../pb.h" -#import "../pb_common.h" -#import "../pb_decode.h" -#import "../pb_encode.h" diff --git a/spm-test/objc/c-header.c b/spm-test/objc/c-header.c index 2995860..17d6795 100644 --- a/spm-test/objc/c-header.c +++ b/spm-test/objc/c-header.c @@ -1 +1,3 @@ -#include "nanopb.h" +#include "pb.h" +#include "pb_common.h" +#include "pb_decode.h" diff --git a/spm-test/objc/objc-header.m b/spm-test/objc/objc-header.m index d8f79cf..860f42b 100644 --- a/spm-test/objc/objc-header.m +++ b/spm-test/objc/objc-header.m @@ -1 +1,3 @@ -#import "nanopb.h" +#import "pb.h" +#import +#include "pb_decode.h" diff --git a/spm-test/objc/objc-qualified.m b/spm-test/objc/objc-qualified.m new file mode 100644 index 0000000..ada0ba1 --- /dev/null +++ b/spm-test/objc/objc-qualified.m @@ -0,0 +1,3 @@ +#import "nanopb/pb.h" +#import +#include "nanopb/pb_decode.h" diff --git a/spm_headers/nanopb/pb.h b/spm_headers/nanopb/pb.h new file mode 120000 index 0000000..e2be14d --- /dev/null +++ b/spm_headers/nanopb/pb.h @@ -0,0 +1 @@ +../../pb.h \ No newline at end of file diff --git a/spm_headers/nanopb/pb_common.h b/spm_headers/nanopb/pb_common.h new file mode 120000 index 0000000..9449ad8 --- /dev/null +++ b/spm_headers/nanopb/pb_common.h @@ -0,0 +1 @@ +../../pb_common.h \ No newline at end of file diff --git a/spm_headers/nanopb/pb_decode.h b/spm_headers/nanopb/pb_decode.h new file mode 120000 index 0000000..d8288c7 --- /dev/null +++ b/spm_headers/nanopb/pb_decode.h @@ -0,0 +1 @@ +../../pb_decode.h \ No newline at end of file diff --git a/spm_headers/nanopb/pb_encode.h b/spm_headers/nanopb/pb_encode.h new file mode 120000 index 0000000..3236a5f --- /dev/null +++ b/spm_headers/nanopb/pb_encode.h @@ -0,0 +1 @@ +../../pb_encode.h \ No newline at end of file diff --git a/spm_headers/pb.h b/spm_headers/pb.h new file mode 100644 index 0000000..ee67ff0 --- /dev/null +++ b/spm_headers/pb.h @@ -0,0 +1 @@ +#include "nanopb/pb.h" diff --git a/spm_headers/pb_common.h b/spm_headers/pb_common.h new file mode 100644 index 0000000..656424e --- /dev/null +++ b/spm_headers/pb_common.h @@ -0,0 +1 @@ +#include "nanopb/pb_common.h" diff --git a/spm_headers/pb_decode.h b/spm_headers/pb_decode.h new file mode 100644 index 0000000..dee5064 --- /dev/null +++ b/spm_headers/pb_decode.h @@ -0,0 +1 @@ +#include "nanopb/pb_decode.h" diff --git a/spm_headers/pb_encode.h b/spm_headers/pb_encode.h new file mode 100644 index 0000000..3e8b73b --- /dev/null +++ b/spm_headers/pb_encode.h @@ -0,0 +1 @@ +#include "nanopb/pb_encode.h" -- cgit v1.2.3 From 8119dfe5631f2616d11e50ead95448d12e816062 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Sun, 13 Sep 2020 08:17:09 -0700 Subject: Fix iOS Build warning (#585) --- Package.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Package.swift b/Package.swift index 2b8c581..1c62f98 100644 --- a/Package.swift +++ b/Package.swift @@ -5,13 +5,6 @@ import PackageDescription let package = Package( name: "nanopb", - platforms: [ - .macOS(.v10_10), - .iOS(.v8), - .tvOS(.v9), - .watchOS(.v2) - ], - products: [ .library( name: "nanopb", -- cgit v1.2.3 From d9d5dfd869aca4d00a81f671b2445fb4cea0352f Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 25 Nov 2020 08:52:53 +0200 Subject: Expand mem_release testcase to cover submessage merge (#615) This also covers the fairly rarely used behavior of protobuf C++ library regarding oneof merges: if an oneof submessage occurs multiple times in a message, their contents are merged together. This behavior was also previously broken in nanopb. --- tests/mem_release/mem_release.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tests/mem_release/mem_release.c b/tests/mem_release/mem_release.c index 6e06da5..fae4516 100644 --- a/tests/mem_release/mem_release.c +++ b/tests/mem_release/mem_release.c @@ -133,13 +133,16 @@ static bool test_OneofMessage() } } - /* Encode second with SubMessage, invoking 'merge' behaviour */ + /* Encode second with SubMessage, replacing the oneof item */ { OneofMessage msg = OneofMessage_init_zero; + char *teststr = "1"; msg.which_msgs = OneofMessage_msg2_tag; msg.first = 999; msg.msgs.msg2.dynamic_str = "ABCD"; + msg.msgs.msg2.dynamic_str_arr_count = 1; + msg.msgs.msg2.dynamic_str_arr = &teststr; msg.last = 888; if (!pb_encode(&stream, OneofMessage_fields, &msg)) @@ -148,6 +151,25 @@ static bool test_OneofMessage() return false; } } + + /* Encode second SubMessage, invoking submessage merge behavior */ + { + OneofMessage msg = OneofMessage_init_zero; + char *teststr = "2"; + msg.which_msgs = OneofMessage_msg2_tag; + + msg.first = 99; + msg.msgs.msg2.dynamic_str = "EFGH"; + msg.msgs.msg2.dynamic_str_arr_count = 1; + msg.msgs.msg2.dynamic_str_arr = &teststr; + msg.last = 88; + + if (!pb_encode(&stream, OneofMessage_fields, &msg)) + { + fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream)); + return false; + } + } msgsize = stream.bytes_written; } @@ -160,13 +182,16 @@ static bool test_OneofMessage() return false; } - TEST(msg.first == 999); + TEST(msg.first == 99); TEST(msg.which_msgs == OneofMessage_msg2_tag); TEST(msg.msgs.msg2.dynamic_str); - TEST(strcmp(msg.msgs.msg2.dynamic_str, "ABCD") == 0); - TEST(msg.msgs.msg2.dynamic_str_arr == NULL); + TEST(strcmp(msg.msgs.msg2.dynamic_str, "EFGH") == 0); + TEST(msg.msgs.msg2.dynamic_str_arr != NULL); + TEST(msg.msgs.msg2.dynamic_str_arr_count == 2); + TEST(strcmp(msg.msgs.msg2.dynamic_str_arr[0], "1") == 0); + TEST(strcmp(msg.msgs.msg2.dynamic_str_arr[1], "2") == 0); TEST(msg.msgs.msg2.dynamic_submsg == NULL); - TEST(msg.last == 888); + TEST(msg.last == 88); pb_release(OneofMessage_fields, &msg); TEST(get_alloc_count() == 0); -- cgit v1.2.3 From 4fe23595732b6f1254cfc11a9b8d6da900b55b0c Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 25 Nov 2020 11:38:40 +0200 Subject: Fix memory leak with oneofs and PB_ENABLE_MALLOC (#615) Nanopb would leak memory when all of the following conditions were true: - PB_ENABLE_MALLOC is defined at the compile time - Message definitions contains an oneof field, the oneof contains a static submessage, and the static submessage contains a pointer field. - Data being decoded contains two values for the submessage. The logic in pb_release_union_field would detect that the same submessage occurs twice, and wouldn't release it because keeping the old values is necessary to match the C++ library behavior regarding message merges. But then decode_static_field() would go to memset() the whole submessage to zero, because it unconditionally assumed it to be uninitialized memory. This would normally happen when the contents of the union field is switched to a different oneof item, instead of merging with the same one. This commit changes it so that the field is memset() only when `which_field` contains a different tag. --- pb_decode.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pb_decode.c b/pb_decode.c index ce5c4ae..3f14a39 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -464,14 +464,17 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t } case PB_HTYPE_ONEOF: - *(pb_size_t*)iter->pSize = iter->pos->tag; - if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && + *(pb_size_t*)iter->pSize != iter->pos->tag) { /* We memset to zero so that any callbacks are set to NULL. - * Then set any default values. */ + * This is because the callbacks might otherwise have values + * from some other union field. */ memset(iter->pData, 0, iter->pos->data_size); pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData); } + *(pb_size_t*)iter->pSize = iter->pos->tag; + return func(stream, iter->pos, iter->pData); default: -- cgit v1.2.3 From 4a979c0f59c4deca9501909a582ee2437808ad14 Mon Sep 17 00:00:00 2001 From: L <46594312+WakandaO2@users.noreply.github.com> Date: Fri, 16 Oct 2020 21:40:16 +0300 Subject: Add '--version' option to nanopb_generator.py (#607) --- generator/nanopb_generator.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 7cc51ea..0d0a112 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -1570,6 +1570,8 @@ optparser = OptionParser( usage = "Usage: nanopb_generator.py [options] file.pb ...", epilog = "Compile file.pb from file.proto by: 'protoc -ofile.pb file.proto'. " + "Output will be written to file.pb.h and file.pb.c.") +optparser.add_option("--version", dest="version", action="store_true", + help="Show version info and exit") optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[], help="Exclude file from generated #include list.") optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default=".pb", @@ -1703,6 +1705,10 @@ def main_cli(): options, filenames = optparser.parse_args() + if options.version: + print(nanopb_version) + sys.exit(0) + if not filenames: optparser.print_help() sys.exit(1) @@ -1780,6 +1786,10 @@ def main_plugin(): options, dummy = optparser.parse_args(args) + if options.version: + sys.stderr.write('%s\n' % (nanopb_version)) + sys.exit(0) + Globals.verbose_options = options.verbose if options.verbose: -- cgit v1.2.3 From f96d99fe55d60a0601c949f78a306de68a28f33b Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Tue, 24 Nov 2020 16:17:48 +1100 Subject: docs: fix simple typo, specifing -> specifying (#614) There is a small typo in pb_encode.h, spm_headers/nanopb/pb_encode.h. Should read `specifying` rather than `specifing`. --- pb_encode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pb_encode.h b/pb_encode.h index 8bf78dd..b1d822f 100644 --- a/pb_encode.h +++ b/pb_encode.h @@ -123,7 +123,7 @@ bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); * structure. Call this from the callback before writing out field contents. */ bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field); -/* Encode field header by manually specifing wire type. You need to use this +/* Encode field header by manually specifying wire type. You need to use this * if you want to write out packed arrays from a callback field. */ bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); -- cgit v1.2.3 From 2db67de2e8e0385ed3c2c838b9ec156b3c42c4e2 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 25 Nov 2020 09:36:01 +0200 Subject: Fix unsigned enums not working correctly inside OneOf (#611) Fixed also a few smaller issues where generator didn't consider fields inside oneof for field name mangling. --- generator/nanopb_generator.py | 15 ++++++++++++--- tests/regression/issue_611/SConscript | 8 ++++++++ tests/regression/issue_611/uenum.expected | 3 +++ tests/regression/issue_611/uenum.proto | 28 ++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tests/regression/issue_611/SConscript create mode 100644 tests/regression/issue_611/uenum.expected create mode 100644 tests/regression/issue_611/uenum.proto diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 0d0a112..e1add38 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -1010,6 +1010,15 @@ class Message: result += default + '\n' return result + def all_fields(self): + '''Iterate over all fields in this message, including nested OneOfs.''' + for f in self.fields: + if isinstance(f, OneOf): + for f2 in f.fields: + yield f2 + else: + yield f + def count_required_fields(self): '''Returns number of required fields inside this message''' count = 0 @@ -1220,7 +1229,7 @@ class ProtoFile: for enum in other.enums: if not enum.options.long_names: for message in self.messages: - for field in message.fields: + for field in message.all_fields(): if field.default in enum.value_longnames: idx = enum.value_longnames.index(field.default) field.default = enum.values[idx][0] @@ -1229,7 +1238,7 @@ class ProtoFile: for enum in other.enums: if not enum.has_negative(): for message in self.messages: - for field in message.fields: + for field in message.all_fields(): if field.pbtype == 'ENUM' and field.ctype == enum.names: field.pbtype = 'UENUM' @@ -1458,7 +1467,7 @@ class ProtoFile: # Add check for sizeof(double) has_double = False for msg in self.messages: - for field in msg.fields: + for field in msg.all_fields(): if field.ctype == 'double': has_double = True diff --git a/tests/regression/issue_611/SConscript b/tests/regression/issue_611/SConscript new file mode 100644 index 0000000..6295107 --- /dev/null +++ b/tests/regression/issue_611/SConscript @@ -0,0 +1,8 @@ +# Regression test for #611: +# Enum messages inside OneOf generated as ENUM instead of UENUM + +Import("env") + +env.NanopbProto("uenum.proto") +env.Match(["uenum.pb.c", "uenum.expected"]) +env.Object("uenum.pb.c") diff --git a/tests/regression/issue_611/uenum.expected b/tests/regression/issue_611/uenum.expected new file mode 100644 index 0000000..0806f24 --- /dev/null +++ b/tests/regression/issue_611/uenum.expected @@ -0,0 +1,3 @@ +[^U]ENUM.*ONEOF.*MyEnum +UENUM.*ONEOF.*MyUEnum + diff --git a/tests/regression/issue_611/uenum.proto b/tests/regression/issue_611/uenum.proto new file mode 100644 index 0000000..f6df218 --- /dev/null +++ b/tests/regression/issue_611/uenum.proto @@ -0,0 +1,28 @@ +syntax = "proto2"; + +enum my_enum { + E0 = 0; + E240 = 240; + E1 = -1; +} + +enum my_uenum { + U0 = 0; + U240 = 240; +} + +message my_message_regular { + required my_enum MyEnum = 1; + required my_uenum MyUEnum = 2; +} + +message my_message_oneof { + required uint32 Data1 = 1; + oneof Data { + bool null = 2; + my_enum MyEnum = 3; + my_uenum MyUEnum = 4; + } +} + + -- cgit v1.2.3 From d6c69808d2cd0243dfc2f742b9b0c82016baae24 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 25 Nov 2020 10:35:08 +0200 Subject: Include version number in generator verbose output --- generator/nanopb_generator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index e1add38..c87ba48 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -1731,6 +1731,7 @@ def main_cli(): sys.exit(1) if options.verbose: + sys.stderr.write("Nanopb version %s\n" % nanopb_version) sys.stderr.write('Google Python protobuf library imported from %s, version %s\n' % (google.protobuf.__file__, google.protobuf.__version__)) @@ -1802,6 +1803,7 @@ def main_plugin(): Globals.verbose_options = options.verbose if options.verbose: + sys.stderr.write("Nanopb version %s\n" % nanopb_version) sys.stderr.write('Google Python protobuf library imported from %s, version %s\n' % (google.protobuf.__file__, google.protobuf.__version__)) -- cgit v1.2.3 From 3302311832383374d81ebb55f89ad5c5bcda4c17 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 25 Nov 2020 13:10:57 +0200 Subject: Update changelog --- CHANGELOG.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index cea2be6..6456812 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,10 @@ -nanopb-0.3.9.6 (2020-06-xx) +nanopb-0.3.9.7 (2020-11-25) + Fix memory leak with oneofs and PB_ENABLE_MALLOC (#615, GHSA-85rr-4rh9-hhwh) + Fix unsigned enums not working correctly inside OneOf (#611) + Add '--version' option to nanopb_generator.py (#607) + SwiftPM rule updates (#567, #585) + +nanopb-0.3.9.6 (2020-06-23) Fix buffer overflow when encoding bytes with size set to 65535 (#547, GHSA-3p39-mfxg-hrq4) Fix proto3 submessage improperly considered empty (#504) Fix ImportError when using generator/protoc with Python 3 -- cgit v1.2.3 From c2d43e59d8ec880ed261366818f0cacc5c8cc2e6 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 25 Nov 2020 13:44:58 +0200 Subject: Publishing nanopb-0.3.9.7 --- CMakeLists.txt | 2 +- extra/poetry/pyproject.toml | 2 +- generator/nanopb_generator.py | 2 +- library.json | 2 +- pb.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bad8cb..4f1853d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.3.9.6) +set(nanopb_VERSION_STRING nanopb-0.3.9.7) set(nanopb_SOVERSION 0) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index b5357b0..9831fd0 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.6" +version = "0.3.9.7" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index c87ba48..b58355c 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.9.6" +nanopb_version = "nanopb-0.3.9.7" import sys import re diff --git a/library.json b/library.json index ca7324e..58db1b6 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Nanopb", - "version": "0.3.9.6", + "version": "0.3.9.7", "keywords": "protocol buffers, protobuf, google", "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.", "repository": { diff --git a/pb.h b/pb.h index 2ba6c63..929bfa1 100644 --- a/pb.h +++ b/pb.h @@ -51,7 +51,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.9.6 +#define NANOPB_VERSION nanopb-0.3.9.7 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3 From ba2e8c782c9753d88a69487186aea84da9620c7b Mon Sep 17 00:00:00 2001 From: niooss-ledger <64031241+niooss-ledger@users.noreply.github.com> Date: Thu, 3 Dec 2020 17:02:43 +0100 Subject: Backport "Verify stream size before allocating string / bytes." (#620) The 0.4 branch contains commit 2519119babea ("Verify stream size before allocating string / bytes."): This stops ridicuously large mallocs from getting through on length-limited streams or buffers. Typically you should also override realloc() to limit allocation size yourself if dealing with untrusted data in pointer mode, but this at least limits the potential denial-of-service attacks. Backport this patch in 0.3 maintenance branch in order to make fuzzing not crash as soon as the decoder tries to allocate a large buffer. --- pb_decode.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pb_decode.c b/pb_decode.c index 3f14a39..6469afb 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -1452,6 +1452,9 @@ static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *fie #ifndef PB_ENABLE_MALLOC PB_RETURN_ERROR(stream, "no malloc support"); #else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + if (!allocate_field(stream, dest, alloc_size, 1)) return false; bdest = *(pb_bytes_array_t**)dest; @@ -1487,6 +1490,9 @@ static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *fi #ifndef PB_ENABLE_MALLOC PB_RETURN_ERROR(stream, "no malloc support"); #else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + if (!allocate_field(stream, dest, alloc_size, 1)) return false; dest = *(void**)dest; -- cgit v1.2.3 From 57674cbc6244842038486409a13e790a23a5af8d Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 4 Dec 2020 22:16:57 -0800 Subject: Don't generate lines with trailing spaces (#622) --- generator/nanopb_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index b58355c..b9743a4 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -1439,7 +1439,7 @@ class ProtoFile: msgs = '_'.join(str(n) for n in checks_msgnames) yield '/* If you get an error here, it means that you need to define PB_FIELD_32BIT\n' yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n' - yield ' * \n' + yield ' *\n' yield ' * The reason you need to do this is that some of your messages contain tag\n' yield ' * numbers or field sizes that are larger than what can fit in 8 or 16 bit\n' yield ' * field descriptors.\n' @@ -1456,7 +1456,7 @@ class ProtoFile: msgs = '_'.join(str(n) for n in checks_msgnames) yield '/* If you get an error here, it means that you need to define PB_FIELD_16BIT\n' yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n' - yield ' * \n' + yield ' *\n' yield ' * The reason you need to do this is that some of your messages contain tag\n' yield ' * numbers or field sizes that are larger than what can fit in the default\n' yield ' * 8 bit descriptors.\n' -- cgit v1.2.3 From afd9e83039afa9e3f18eb53ad4d9c80d860c9361 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 3 Feb 2021 17:32:46 +0200 Subject: Expand alltypes test case for default values inside oneof (#635) The 0.3 branch was not affected by this bug, but expanding the test case to cover it. --- tests/alltypes/decode_alltypes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/alltypes/decode_alltypes.c b/tests/alltypes/decode_alltypes.c index b74121f..3b0f524 100644 --- a/tests/alltypes/decode_alltypes.c +++ b/tests/alltypes/decode_alltypes.c @@ -198,6 +198,7 @@ bool check_alltypes(pb_istream_t *stream, int mode) TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag); TEST(strcmp(alltypes.oneof.oneof_msg1.substuff1, "4059") == 0); TEST(alltypes.oneof.oneof_msg1.substuff2 == 4059); + TEST(alltypes.oneof.oneof_msg1.substuff3 == 3); } else if (mode == 2) { -- cgit v1.2.3 From 0aa1dab94a4deff4940a39eafeefdc5d8f3a107e Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 20 Mar 2021 09:44:31 +0200 Subject: Add testcase for #647: invalid free with oneof --- tests/regression/issue_647/SConscript | 12 ++++++++++++ tests/regression/issue_647/repro.c | 16 ++++++++++++++++ tests/regression/issue_647/repro.proto | 10 ++++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/regression/issue_647/SConscript create mode 100644 tests/regression/issue_647/repro.c create mode 100644 tests/regression/issue_647/repro.proto diff --git a/tests/regression/issue_647/SConscript b/tests/regression/issue_647/SConscript new file mode 100644 index 0000000..70eedf8 --- /dev/null +++ b/tests/regression/issue_647/SConscript @@ -0,0 +1,12 @@ +# Regression test for #647: +# Ill-formed oneof message leads to calling free on an arbitrary pointer + +Import("env") + +env.NanopbProto("repro.proto") + +test = env.Program(["repro.c", "repro.pb.c", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) +env.RunTest(test) diff --git a/tests/regression/issue_647/repro.c b/tests/regression/issue_647/repro.c new file mode 100644 index 0000000..48d9570 --- /dev/null +++ b/tests/regression/issue_647/repro.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include "repro.pb.h" + +int main() { + const uint8_t data[] = {0x08, 0x08, 0x2d}; + int status = 0; + Repro repro = Repro_init_zero; + + pb_istream_t stream = pb_istream_from_buffer(data, sizeof(data)); + TEST(!pb_decode(&stream, Repro_fields, &repro)); + TEST(get_alloc_count() == 0); + + return status; +} diff --git a/tests/regression/issue_647/repro.proto b/tests/regression/issue_647/repro.proto new file mode 100644 index 0000000..1fe7777 --- /dev/null +++ b/tests/regression/issue_647/repro.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +import "nanopb.proto"; + +message Repro { + oneof value_type { + bool boolean_value = 1; + bytes bytes_value = 5 [(nanopb).type = FT_POINTER]; + } +} -- cgit v1.2.3 From 4a375a560651a86726e5283be85a9231fd0efe9c Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 20 Mar 2021 09:45:04 +0200 Subject: Fix invalid free() with oneof (#647) Nanopb would call free() or realloc() on an invalid (attacker controlled) pointer value when all the following conditions are true: - PB_ENABLE_MALLOC is defined at the compile time - Message definition contains an oneof field, and the oneof contains at least one pointer type field and at least one non-pointer type field. - Data being decoded first contains a non-pointer value for the oneof field, and later contains an overwriting pointer value. Depending on message layout, the bug may not be exploitable in all cases, but it is known to be exploitable at least with string and bytes fields. Actual security impact will also depend on the heap implementation used. --- pb_decode.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pb_decode.c b/pb_decode.c index 6469afb..4efe8a3 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -1150,7 +1150,14 @@ static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter) * This shouldn't fail unless the pb_field_t structure is corrupted. */ if (!pb_field_iter_find(iter, new_tag)) PB_RETURN_ERROR(stream, "iterator error"); - + + if (PB_ATYPE(iter->pos->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)iter->pData = NULL; + } + return true; } -- cgit v1.2.3 From 7ee9ef9f627d85cbe1b8c4f49a3ed26eed216c77 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Mon, 22 Mar 2021 14:50:07 +0200 Subject: Publishing nanopb-0.3.9.8 --- CHANGELOG.txt | 5 +++++ CMakeLists.txt | 2 +- extra/poetry/pyproject.toml | 2 +- generator/nanopb_generator.py | 2 +- library.json | 2 +- pb.h | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 6456812..44e952b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,8 @@ +nanopb-0.3.9.8 (2021-03-22) + Fix invalid free() with oneof (#647, GHSA-7mv5-5mxh-qg88) + Don't generate lines with trailing spaces (#622) + Verify stream size before allocating string / bytes (#620) + nanopb-0.3.9.7 (2020-11-25) Fix memory leak with oneofs and PB_ENABLE_MALLOC (#615, GHSA-85rr-4rh9-hhwh) Fix unsigned enums not working correctly inside OneOf (#611) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f1853d..b16fbbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(nanopb C) -set(nanopb_VERSION_STRING nanopb-0.3.9.7) +set(nanopb_VERSION_STRING nanopb-0.3.9.8) set(nanopb_SOVERSION 0) string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING}) diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml index 9831fd0..e3a5586 100644 --- a/extra/poetry/pyproject.toml +++ b/extra/poetry/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nanopb" -version = "0.3.9.7" +version = "0.3.9.8" description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system." authors = ["Petteri Aimonen "] license = "Zlib" diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index b9743a4..b4f1d83 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.9.7" +nanopb_version = "nanopb-0.3.9.8" import sys import re diff --git a/library.json b/library.json index 58db1b6..cbd1528 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Nanopb", - "version": "0.3.9.7", + "version": "0.3.9.8", "keywords": "protocol buffers, protobuf, google", "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.", "repository": { diff --git a/pb.h b/pb.h index 929bfa1..236e894 100644 --- a/pb.h +++ b/pb.h @@ -51,7 +51,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.9.7 +#define NANOPB_VERSION nanopb-0.3.9.8 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: -- cgit v1.2.3