1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_rpc/internal/packet.h"
16
17 #include "pw_bytes/array.h"
18 #include "pw_fuzzer/fuzztest.h"
19 #include "pw_protobuf/wire_format.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace pw::rpc::internal {
23 namespace {
24
25 using protobuf::FieldKey;
26 using ::pw::rpc::internal::pwpb::PacketType;
27 using std::byte;
28 using namespace fuzzer;
29
30 constexpr auto kPayload = bytes::Array<0x82, 0x02, 0xff, 0xff>();
31
32 constexpr auto kEncoded = bytes::Array<
33 // Payload
34 uint32_t(FieldKey(5, protobuf::WireType::kDelimited)),
35 0x04,
36 0x82,
37 0x02,
38 0xff,
39 0xff,
40
41 // Packet type
42 uint32_t(FieldKey(1, protobuf::WireType::kVarint)),
43 1, // RESPONSE
44
45 // Channel ID
46 uint32_t(FieldKey(2, protobuf::WireType::kVarint)),
47 1,
48
49 // Service ID
50 uint32_t(FieldKey(3, protobuf::WireType::kFixed32)),
51 42,
52 0,
53 0,
54 0,
55
56 // Method ID
57 uint32_t(FieldKey(4, protobuf::WireType::kFixed32)),
58 100,
59 0,
60 0,
61 0,
62
63 // Status (not encoded if it is zero)
64 // FieldKey(6, protobuf::WireType::kVarint),
65 // 0x00
66
67 // Call ID
68 uint32_t(FieldKey(7, protobuf::WireType::kVarint)),
69 7>();
70
71 // Test that a default-constructed packet sets its members to the default
72 // protobuf values.
73 static_assert(Packet().type() == PacketType{});
74 static_assert(Packet().channel_id() == 0);
75 static_assert(Packet().service_id() == 0);
76 static_assert(Packet().method_id() == 0);
77 static_assert(Packet().status() == static_cast<Status::Code>(0));
78 static_assert(Packet().payload().empty());
79
TEST(Packet,Encode)80 TEST(Packet, Encode) {
81 byte buffer[64];
82
83 Packet packet(PacketType::RESPONSE, 1, 42, 100, 7, kPayload);
84
85 auto result = packet.Encode(buffer);
86 ASSERT_EQ(OkStatus(), result.status());
87 ASSERT_EQ(kEncoded.size(), result.value().size());
88 EXPECT_EQ(std::memcmp(kEncoded.data(), buffer, kEncoded.size()), 0);
89 }
90
TEST(Packet,Encode_BufferTooSmall)91 TEST(Packet, Encode_BufferTooSmall) {
92 byte buffer[2];
93
94 Packet packet(PacketType::RESPONSE, 1, 42, 100, 12, kPayload);
95
96 auto result = packet.Encode(buffer);
97 EXPECT_EQ(Status::ResourceExhausted(), result.status());
98 }
99
TEST(Packet,Decode_ValidPacket)100 TEST(Packet, Decode_ValidPacket) {
101 auto result = Packet::FromBuffer(kEncoded);
102 ASSERT_TRUE(result.ok());
103
104 auto& packet = result.value();
105 EXPECT_EQ(PacketType::RESPONSE, packet.type());
106 EXPECT_EQ(1u, packet.channel_id());
107 EXPECT_EQ(42u, packet.service_id());
108 EXPECT_EQ(100u, packet.method_id());
109 EXPECT_EQ(7u, packet.call_id());
110 ASSERT_EQ(sizeof(kPayload), packet.payload().size());
111 EXPECT_EQ(
112 0,
113 std::memcmp(packet.payload().data(), kPayload.data(), kPayload.size()));
114 }
115
TEST(Packet,Decode_InvalidPacket)116 TEST(Packet, Decode_InvalidPacket) {
117 byte bad_data[] = {byte{0xFF}, byte{0x00}, byte{0x00}, byte{0xFF}};
118 EXPECT_EQ(Status::DataLoss(), Packet::FromBuffer(bad_data).status());
119 }
120
EncodeDecode(uint32_t channel_id,uint32_t service_id,uint32_t method_id,uint32_t call_id,ConstByteSpan payload,Status status)121 void EncodeDecode(uint32_t channel_id,
122 uint32_t service_id,
123 uint32_t method_id,
124 uint32_t call_id,
125 ConstByteSpan payload,
126 Status status) {
127 Packet packet;
128 packet.set_channel_id(channel_id);
129 packet.set_service_id(service_id);
130 packet.set_method_id(method_id);
131 packet.set_call_id(call_id);
132 packet.set_payload(payload);
133 packet.set_status(status);
134
135 byte buffer[128];
136 Result result = packet.Encode(buffer);
137 ASSERT_EQ(result.status(), OkStatus());
138
139 span<byte> packet_data(buffer, result.value().size());
140 auto decode_result = Packet::FromBuffer(packet_data);
141 ASSERT_TRUE(decode_result.ok());
142
143 auto& decoded = decode_result.value();
144 EXPECT_EQ(decoded.type(), packet.type());
145 EXPECT_EQ(decoded.channel_id(), packet.channel_id());
146 EXPECT_EQ(decoded.service_id(), packet.service_id());
147 EXPECT_EQ(decoded.method_id(), packet.method_id());
148 EXPECT_EQ(decoded.call_id(), packet.call_id());
149 ASSERT_EQ(decoded.payload().size(), packet.payload().size());
150 EXPECT_EQ(std::memcmp(decoded.payload().data(),
151 packet.payload().data(),
152 packet.payload().size()),
153 0);
154 EXPECT_EQ(decoded.status(), status);
155 }
156
TEST(Packet,EncodeDecodeFixed)157 TEST(Packet, EncodeDecodeFixed) {
158 constexpr byte payload[]{byte(0x00), byte(0x01), byte(0x02), byte(0x03)};
159 EncodeDecode(12, 0xdeadbeef, 0x03a82921, 33, payload, Status::Unavailable());
160 }
161
162 FUZZ_TEST(Packet, EncodeDecode)
163 .WithDomains(NonZero<uint32_t>(),
164 NonZero<uint32_t>(),
165 NonZero<uint32_t>(),
166 NonZero<uint32_t>(),
167 VectorOf<100>(Arbitrary<byte>()),
168 Arbitrary<Status>());
169
170 constexpr size_t kReservedSize = 2 /* type */ + 2 /* channel */ +
171 5 /* service */ + 5 /* method */ +
172 2 /* payload key */ + 2 /* status */;
173
TEST(Packet,PayloadUsableSpace_ExactFit)174 TEST(Packet, PayloadUsableSpace_ExactFit) {
175 EXPECT_EQ(
176 kReservedSize,
177 Packet(PacketType::RESPONSE, 1, 42, 100, 28282).MinEncodedSizeBytes());
178 }
179
TEST(Packet,PayloadUsableSpace_LargerVarints)180 TEST(Packet, PayloadUsableSpace_LargerVarints) {
181 EXPECT_EQ(kReservedSize + 2 /* channel */, // service and method are Fixed32
182 Packet(PacketType::RESPONSE, 17000, 200, 200, 28282)
183 .MinEncodedSizeBytes());
184 }
185
186 } // namespace
187 } // namespace pw::rpc::internal
188