xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/quic_chaos_protector.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2021 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/quic_chaos_protector.h"
6 
7 #include <algorithm>
8 #include <cstddef>
9 #include <cstdint>
10 #include <limits>
11 #include <memory>
12 #include <optional>
13 
14 #include "absl/strings/string_view.h"
15 #include "quiche/quic/core/crypto/quic_random.h"
16 #include "quiche/quic/core/frames/quic_crypto_frame.h"
17 #include "quiche/quic/core/frames/quic_frame.h"
18 #include "quiche/quic/core/frames/quic_padding_frame.h"
19 #include "quiche/quic/core/frames/quic_ping_frame.h"
20 #include "quiche/quic/core/quic_data_reader.h"
21 #include "quiche/quic/core/quic_data_writer.h"
22 #include "quiche/quic/core/quic_framer.h"
23 #include "quiche/quic/core/quic_packets.h"
24 #include "quiche/quic/core/quic_stream_frame_data_producer.h"
25 #include "quiche/quic/platform/api/quic_bug_tracker.h"
26 #include "quiche/common/platform/api/quiche_logging.h"
27 
28 namespace quic {
29 
QuicChaosProtector(const QuicCryptoFrame & crypto_frame,int num_padding_bytes,size_t packet_size,QuicFramer * framer,QuicRandom * random)30 QuicChaosProtector::QuicChaosProtector(const QuicCryptoFrame& crypto_frame,
31                                        int num_padding_bytes,
32                                        size_t packet_size, QuicFramer* framer,
33                                        QuicRandom* random)
34     : packet_size_(packet_size),
35       crypto_data_length_(crypto_frame.data_length),
36       crypto_buffer_offset_(crypto_frame.offset),
37       level_(crypto_frame.level),
38       remaining_padding_bytes_(num_padding_bytes),
39       framer_(framer),
40       random_(random) {
41   QUICHE_DCHECK_NE(framer_, nullptr);
42   QUICHE_DCHECK_NE(framer_->data_producer(), nullptr);
43   QUICHE_DCHECK_NE(random_, nullptr);
44 }
45 
~QuicChaosProtector()46 QuicChaosProtector::~QuicChaosProtector() { DeleteFrames(&frames_); }
47 
BuildDataPacket(const QuicPacketHeader & header,char * buffer)48 std::optional<size_t> QuicChaosProtector::BuildDataPacket(
49     const QuicPacketHeader& header, char* buffer) {
50   if (!CopyCryptoDataToLocalBuffer()) {
51     return std::nullopt;
52   }
53   SplitCryptoFrame();
54   AddPingFrames();
55   SpreadPadding();
56   ReorderFrames();
57   return BuildPacket(header, buffer);
58 }
59 
WriteStreamData(QuicStreamId id,QuicStreamOffset offset,QuicByteCount data_length,QuicDataWriter *)60 WriteStreamDataResult QuicChaosProtector::WriteStreamData(
61     QuicStreamId id, QuicStreamOffset offset, QuicByteCount data_length,
62     QuicDataWriter* /*writer*/) {
63   QUIC_BUG(chaos stream) << "This should never be called; id " << id
64                          << " offset " << offset << " data_length "
65                          << data_length;
66   return STREAM_MISSING;
67 }
68 
WriteCryptoData(EncryptionLevel level,QuicStreamOffset offset,QuicByteCount data_length,QuicDataWriter * writer)69 bool QuicChaosProtector::WriteCryptoData(EncryptionLevel level,
70                                          QuicStreamOffset offset,
71                                          QuicByteCount data_length,
72                                          QuicDataWriter* writer) {
73   if (level != level_) {
74     QUIC_BUG(chaos bad level) << "Unexpected " << level << " != " << level_;
75     return false;
76   }
77   // This is `offset + data_length > buffer_offset_ + buffer_length_`
78   // but with integer overflow protection.
79   if (offset < crypto_buffer_offset_ || data_length > crypto_data_length_ ||
80       offset - crypto_buffer_offset_ > crypto_data_length_ - data_length) {
81     QUIC_BUG(chaos bad lengths)
82         << "Unexpected buffer_offset_ " << crypto_buffer_offset_ << " offset "
83         << offset << " buffer_length_ " << crypto_data_length_
84         << " data_length " << data_length;
85     return false;
86   }
87   writer->WriteBytes(&crypto_data_buffer_[offset - crypto_buffer_offset_],
88                      data_length);
89   return true;
90 }
91 
CopyCryptoDataToLocalBuffer()92 bool QuicChaosProtector::CopyCryptoDataToLocalBuffer() {
93   crypto_frame_buffer_ = std::make_unique<char[]>(packet_size_);
94   frames_.push_back(QuicFrame(
95       new QuicCryptoFrame(level_, crypto_buffer_offset_, crypto_data_length_)));
96   // We use |framer_| to serialize the CRYPTO frame in order to extract its
97   // data from the crypto data producer. This ensures that we reuse the
98   // usual serialization code path, but has the downside that we then need to
99   // parse the offset and length in order to skip over those fields.
100   QuicDataWriter writer(packet_size_, crypto_frame_buffer_.get());
101   if (!framer_->AppendCryptoFrame(*frames_.front().crypto_frame, &writer)) {
102     QUIC_BUG(chaos write crypto data);
103     return false;
104   }
105   QuicDataReader reader(crypto_frame_buffer_.get(), writer.length());
106   uint64_t parsed_offset, parsed_length;
107   if (!reader.ReadVarInt62(&parsed_offset) ||
108       !reader.ReadVarInt62(&parsed_length)) {
109     QUIC_BUG(chaos parse crypto frame);
110     return false;
111   }
112 
113   absl::string_view crypto_data = reader.ReadRemainingPayload();
114   crypto_data_buffer_ = crypto_data.data();
115 
116   QUICHE_DCHECK_EQ(parsed_offset, crypto_buffer_offset_);
117   QUICHE_DCHECK_EQ(parsed_length, crypto_data_length_);
118   QUICHE_DCHECK_EQ(parsed_length, crypto_data.length());
119 
120   return true;
121 }
122 
SplitCryptoFrame()123 void QuicChaosProtector::SplitCryptoFrame() {
124   const int max_overhead_of_adding_a_crypto_frame =
125       static_cast<int>(QuicFramer::GetMinCryptoFrameSize(
126           crypto_buffer_offset_ + crypto_data_length_, crypto_data_length_));
127   // Pick a random number of CRYPTO frames to add.
128   constexpr uint64_t kMaxAddedCryptoFrames = 10;
129   const uint64_t num_added_crypto_frames =
130       random_->InsecureRandUint64() % (kMaxAddedCryptoFrames + 1);
131   for (uint64_t i = 0; i < num_added_crypto_frames; i++) {
132     if (remaining_padding_bytes_ < max_overhead_of_adding_a_crypto_frame) {
133       break;
134     }
135     // Pick a random frame and split it by shrinking the picked frame and
136     // moving the second half of its data to a new frame that is then appended
137     // to |frames|.
138     size_t frame_to_split_index =
139         random_->InsecureRandUint64() % frames_.size();
140     QuicCryptoFrame* frame_to_split =
141         frames_[frame_to_split_index].crypto_frame;
142     if (frame_to_split->data_length <= 1) {
143       continue;
144     }
145     const int frame_to_split_old_overhead =
146         static_cast<int>(QuicFramer::GetMinCryptoFrameSize(
147             frame_to_split->offset, frame_to_split->data_length));
148     const QuicPacketLength frame_to_split_new_data_length =
149         1 + (random_->InsecureRandUint64() % (frame_to_split->data_length - 1));
150     const QuicPacketLength new_frame_data_length =
151         frame_to_split->data_length - frame_to_split_new_data_length;
152     const QuicStreamOffset new_frame_offset =
153         frame_to_split->offset + frame_to_split_new_data_length;
154     frame_to_split->data_length -= new_frame_data_length;
155     frames_.push_back(QuicFrame(
156         new QuicCryptoFrame(level_, new_frame_offset, new_frame_data_length)));
157     const int frame_to_split_new_overhead =
158         static_cast<int>(QuicFramer::GetMinCryptoFrameSize(
159             frame_to_split->offset, frame_to_split->data_length));
160     const int new_frame_overhead =
161         static_cast<int>(QuicFramer::GetMinCryptoFrameSize(
162             new_frame_offset, new_frame_data_length));
163     QUICHE_DCHECK_LE(frame_to_split_new_overhead, frame_to_split_old_overhead);
164     // Readjust padding based on increased overhead.
165     remaining_padding_bytes_ -= new_frame_overhead;
166     remaining_padding_bytes_ -= frame_to_split_new_overhead;
167     remaining_padding_bytes_ += frame_to_split_old_overhead;
168   }
169 }
170 
AddPingFrames()171 void QuicChaosProtector::AddPingFrames() {
172   if (remaining_padding_bytes_ == 0) {
173     return;
174   }
175   constexpr uint64_t kMaxAddedPingFrames = 10;
176   const uint64_t num_ping_frames =
177       random_->InsecureRandUint64() %
178       std::min<uint64_t>(kMaxAddedPingFrames, remaining_padding_bytes_);
179   for (uint64_t i = 0; i < num_ping_frames; i++) {
180     frames_.push_back(QuicFrame(QuicPingFrame()));
181   }
182   remaining_padding_bytes_ -= static_cast<int>(num_ping_frames);
183 }
184 
ReorderFrames()185 void QuicChaosProtector::ReorderFrames() {
186   // Walk the array backwards and swap each frame with a random earlier one.
187   for (size_t i = frames_.size() - 1; i > 0; i--) {
188     std::swap(frames_[i], frames_[random_->InsecureRandUint64() % (i + 1)]);
189   }
190 }
191 
SpreadPadding()192 void QuicChaosProtector::SpreadPadding() {
193   for (auto it = frames_.begin(); it != frames_.end(); ++it) {
194     const int padding_bytes_in_this_frame =
195         random_->InsecureRandUint64() % (remaining_padding_bytes_ + 1);
196     if (padding_bytes_in_this_frame <= 0) {
197       continue;
198     }
199     it = frames_.insert(
200         it, QuicFrame(QuicPaddingFrame(padding_bytes_in_this_frame)));
201     ++it;  // Skip over the padding frame we just added.
202     remaining_padding_bytes_ -= padding_bytes_in_this_frame;
203   }
204   if (remaining_padding_bytes_ > 0) {
205     frames_.push_back(QuicFrame(QuicPaddingFrame(remaining_padding_bytes_)));
206   }
207 }
208 
BuildPacket(const QuicPacketHeader & header,char * buffer)209 std::optional<size_t> QuicChaosProtector::BuildPacket(
210     const QuicPacketHeader& header, char* buffer) {
211   QuicStreamFrameDataProducer* original_data_producer =
212       framer_->data_producer();
213   framer_->set_data_producer(this);
214 
215   size_t length =
216       framer_->BuildDataPacket(header, frames_, buffer, packet_size_, level_);
217 
218   framer_->set_data_producer(original_data_producer);
219   if (length == 0) {
220     return std::nullopt;
221   }
222   return length;
223 }
224 
225 }  // namespace quic
226