协议缓冲区ParseFromString不检查消息结尾



我发现了一个有趣的协议缓冲区gotcha。如果您有两个类似的消息,则可以使用C++API或命令行将其中一个消息解析为另一个消息。

ParseFromString的有限文档没有提到它不需要消耗所有字符串,如果不消耗,也不会失败。

我曾期望ParseFromString在呈现类型为B的消息时无法解析类型为a的消息。毕竟,该消息包含额外的数据。然而,事实并非如此。一个示例脚本演示了这个问题:

#!/bin/sh
cat - >./foobar.proto <<EOF
syntax = "proto3";
package demo;
message A
{
uint64 foo = 1;
};
enum flagx { 
y = 0; 
z = 1; 
}
message B {
uint64 foolish = 1;
flagx bar = 2;
};
EOF
cat - >./mess.B.in.txtfmt <<EOF
foolish: 10
bar: y
EOF
cat - >./mess.in.txtfmt <<EOF
foo: 10
EOF
protoc --encode=demo.A foobar.proto <./mess.A.in.txtfmt >./mess.A.proto
protoc --encode=demo.B foobar.proto <./mess.B.in.txtfmt >./mess.B.proto
protoc --decode=demo.A foobar.proto >./mess.out.txtfmt <./mess.B.proto
echo "in: "
cat mess.B.in.txtfmt
echo "out: "
cat mess.out.txtfmt
echo "xxd mess.A.proto:"
xxd mess.A.proto
echo "xxd mess.B.proto:"
xxd mess.B.proto

输出为:

in: 
foolish: 10
bar: 20
out: 
foo: 10
xxd mess.A.proto:
00000000: 080a                                    
xxd mess.B.proto:
00000000: 080a

因此,对于消息A和B,二进制消息是相同的。

如果您更改协议,使您拥有另一个变量(uint64(而不是枚举,则您将获得不同的二进制消息,但是ParseFromString仍将成功地将较长的消息解析为较短的消息。

为了真正混淆事物,它似乎还能够将较短的消息解析为较长的消息。

这是bug还是功能?

我认为这是设计的,但文档可能会更好。

如果您尝试使用API而没有先阅读有关在线格式的内容,则可能会出现这种混乱。有线格式与API并非如您所期望的那样无关。

有线格式强调紧凑性而非正确性。如果你想检查消息的正确性,我们会邀请你使用其他方式。

你可能(可以说应该或必须(在你的信息中包括以下一个或多个:

  • 消息类型字段
  • 消息长度字段
  • 校验和

能够将较短的消息解析为较长的消息的第二点是因为在协议缓冲区3中,所有字段都是可选的。协议缓冲器2具有所需字段的概念。它的删除引起了一些争议(例如参见协议缓冲区3&https://capnproto.org/faq.html#how-do-i-make-a-field所需的类协议缓冲区(。消息中不包括具有默认值(通常为0(的字段。字段的名称也被数字所取代。因此,"不同"协议的两个消息可能很容易被两者解释。

最新更新