|  | // Copyright 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "quic/core/qpack/qpack_instruction_decoder.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <utility> | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "quic/platform/api/quic_bug_tracker.h" | 
|  | #include "quic/platform/api/quic_logging.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Maximum length of header name and header value.  This limits the amount of | 
|  | // memory the peer can make the decoder allocate when sending string literals. | 
|  | const size_t kStringLiteralLengthLimit = 1024 * 1024; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language, | 
|  | Delegate* delegate) | 
|  | : language_(language), | 
|  | delegate_(delegate), | 
|  | s_bit_(false), | 
|  | varint_(0), | 
|  | varint2_(0), | 
|  | is_huffman_encoded_(false), | 
|  | string_length_(0), | 
|  | error_detected_(false), | 
|  | state_(State::kStartInstruction) {} | 
|  |  | 
|  | bool QpackInstructionDecoder::Decode(absl::string_view data) { | 
|  | QUICHE_DCHECK(!data.empty()); | 
|  | QUICHE_DCHECK(!error_detected_); | 
|  |  | 
|  | while (true) { | 
|  | bool success = true; | 
|  | size_t bytes_consumed = 0; | 
|  |  | 
|  | switch (state_) { | 
|  | case State::kStartInstruction: | 
|  | success = DoStartInstruction(data); | 
|  | break; | 
|  | case State::kStartField: | 
|  | success = DoStartField(); | 
|  | break; | 
|  | case State::kReadBit: | 
|  | success = DoReadBit(data); | 
|  | break; | 
|  | case State::kVarintStart: | 
|  | success = DoVarintStart(data, &bytes_consumed); | 
|  | break; | 
|  | case State::kVarintResume: | 
|  | success = DoVarintResume(data, &bytes_consumed); | 
|  | break; | 
|  | case State::kVarintDone: | 
|  | success = DoVarintDone(); | 
|  | break; | 
|  | case State::kReadString: | 
|  | success = DoReadString(data, &bytes_consumed); | 
|  | break; | 
|  | case State::kReadStringDone: | 
|  | success = DoReadStringDone(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (!success) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // |success| must be false if an error is detected. | 
|  | QUICHE_DCHECK(!error_detected_); | 
|  |  | 
|  | QUICHE_DCHECK_LE(bytes_consumed, data.size()); | 
|  |  | 
|  | data = absl::string_view(data.data() + bytes_consumed, | 
|  | data.size() - bytes_consumed); | 
|  |  | 
|  | // Stop processing if no more data but next state would require it. | 
|  | if (data.empty() && (state_ != State::kStartField) && | 
|  | (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::AtInstructionBoundary() const { | 
|  | return state_ == State::kStartInstruction; | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoStartInstruction(absl::string_view data) { | 
|  | QUICHE_DCHECK(!data.empty()); | 
|  |  | 
|  | instruction_ = LookupOpcode(data[0]); | 
|  | field_ = instruction_->fields.begin(); | 
|  |  | 
|  | state_ = State::kStartField; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoStartField() { | 
|  | if (field_ == instruction_->fields.end()) { | 
|  | // Completed decoding this instruction. | 
|  |  | 
|  | if (!delegate_->OnInstructionDecoded(instruction_)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | state_ = State::kStartInstruction; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | switch (field_->type) { | 
|  | case QpackInstructionFieldType::kSbit: | 
|  | case QpackInstructionFieldType::kName: | 
|  | case QpackInstructionFieldType::kValue: | 
|  | state_ = State::kReadBit; | 
|  | return true; | 
|  | case QpackInstructionFieldType::kVarint: | 
|  | case QpackInstructionFieldType::kVarint2: | 
|  | state_ = State::kVarintStart; | 
|  | return true; | 
|  | default: | 
|  | QUIC_BUG(quic_bug_10767_1) << "Invalid field type."; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoReadBit(absl::string_view data) { | 
|  | QUICHE_DCHECK(!data.empty()); | 
|  |  | 
|  | switch (field_->type) { | 
|  | case QpackInstructionFieldType::kSbit: { | 
|  | const uint8_t bitmask = field_->param; | 
|  | s_bit_ = (data[0] & bitmask) == bitmask; | 
|  |  | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  |  | 
|  | return true; | 
|  | } | 
|  | case QpackInstructionFieldType::kName: | 
|  | case QpackInstructionFieldType::kValue: { | 
|  | const uint8_t prefix_length = field_->param; | 
|  | QUICHE_DCHECK_GE(7, prefix_length); | 
|  | const uint8_t bitmask = 1 << prefix_length; | 
|  | is_huffman_encoded_ = (data[0] & bitmask) == bitmask; | 
|  |  | 
|  | state_ = State::kVarintStart; | 
|  |  | 
|  | return true; | 
|  | } | 
|  | default: | 
|  | QUIC_BUG(quic_bug_10767_2) << "Invalid field type."; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoVarintStart(absl::string_view data, | 
|  | size_t* bytes_consumed) { | 
|  | QUICHE_DCHECK(!data.empty()); | 
|  | QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kVarint || | 
|  | field_->type == QpackInstructionFieldType::kVarint2 || | 
|  | field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  |  | 
|  | http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1); | 
|  | http2::DecodeStatus status = | 
|  | varint_decoder_.Start(data[0], field_->param, &buffer); | 
|  |  | 
|  | *bytes_consumed = 1 + buffer.Offset(); | 
|  | switch (status) { | 
|  | case http2::DecodeStatus::kDecodeDone: | 
|  | state_ = State::kVarintDone; | 
|  | return true; | 
|  | case http2::DecodeStatus::kDecodeInProgress: | 
|  | state_ = State::kVarintResume; | 
|  | return true; | 
|  | case http2::DecodeStatus::kDecodeError: | 
|  | OnError(ErrorCode::INTEGER_TOO_LARGE, "Encoded integer too large."); | 
|  | return false; | 
|  | default: | 
|  | QUIC_BUG(quic_bug_10767_3) << "Unknown decode status " << status; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoVarintResume(absl::string_view data, | 
|  | size_t* bytes_consumed) { | 
|  | QUICHE_DCHECK(!data.empty()); | 
|  | QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kVarint || | 
|  | field_->type == QpackInstructionFieldType::kVarint2 || | 
|  | field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  |  | 
|  | http2::DecodeBuffer buffer(data); | 
|  | http2::DecodeStatus status = varint_decoder_.Resume(&buffer); | 
|  |  | 
|  | *bytes_consumed = buffer.Offset(); | 
|  | switch (status) { | 
|  | case http2::DecodeStatus::kDecodeDone: | 
|  | state_ = State::kVarintDone; | 
|  | return true; | 
|  | case http2::DecodeStatus::kDecodeInProgress: | 
|  | QUICHE_DCHECK_EQ(*bytes_consumed, data.size()); | 
|  | QUICHE_DCHECK(buffer.Empty()); | 
|  | return true; | 
|  | case http2::DecodeStatus::kDecodeError: | 
|  | OnError(ErrorCode::INTEGER_TOO_LARGE, "Encoded integer too large."); | 
|  | return false; | 
|  | default: | 
|  | QUIC_BUG(quic_bug_10767_4) << "Unknown decode status " << status; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoVarintDone() { | 
|  | QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kVarint || | 
|  | field_->type == QpackInstructionFieldType::kVarint2 || | 
|  | field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  |  | 
|  | if (field_->type == QpackInstructionFieldType::kVarint) { | 
|  | varint_ = varint_decoder_.value(); | 
|  |  | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (field_->type == QpackInstructionFieldType::kVarint2) { | 
|  | varint2_ = varint_decoder_.value(); | 
|  |  | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | string_length_ = varint_decoder_.value(); | 
|  | if (string_length_ > kStringLiteralLengthLimit) { | 
|  | OnError(ErrorCode::STRING_LITERAL_TOO_LONG, "String literal too long."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::string* const string = | 
|  | (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; | 
|  | string->clear(); | 
|  |  | 
|  | if (string_length_ == 0) { | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | string->reserve(string_length_); | 
|  |  | 
|  | state_ = State::kReadString; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoReadString(absl::string_view data, | 
|  | size_t* bytes_consumed) { | 
|  | QUICHE_DCHECK(!data.empty()); | 
|  | QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  |  | 
|  | std::string* const string = | 
|  | (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; | 
|  | QUICHE_DCHECK_LT(string->size(), string_length_); | 
|  |  | 
|  | *bytes_consumed = std::min(string_length_ - string->size(), data.size()); | 
|  | string->append(data.data(), *bytes_consumed); | 
|  |  | 
|  | QUICHE_DCHECK_LE(string->size(), string_length_); | 
|  | if (string->size() == string_length_) { | 
|  | state_ = State::kReadStringDone; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool QpackInstructionDecoder::DoReadStringDone() { | 
|  | QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  |  | 
|  | std::string* const string = | 
|  | (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; | 
|  | QUICHE_DCHECK_EQ(string->size(), string_length_); | 
|  |  | 
|  | if (is_huffman_encoded_) { | 
|  | huffman_decoder_.Reset(); | 
|  | // HpackHuffmanDecoder::Decode() cannot perform in-place decoding. | 
|  | std::string decoded_value; | 
|  | huffman_decoder_.Decode(*string, &decoded_value); | 
|  | if (!huffman_decoder_.InputProperlyTerminated()) { | 
|  | OnError(ErrorCode::HUFFMAN_ENCODING_ERROR, | 
|  | "Error in Huffman-encoded string."); | 
|  | return false; | 
|  | } | 
|  | *string = std::move(decoded_value); | 
|  | } | 
|  |  | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* QpackInstructionDecoder::LookupOpcode( | 
|  | uint8_t byte) const { | 
|  | for (const auto* instruction : *language_) { | 
|  | if ((byte & instruction->opcode.mask) == instruction->opcode.value) { | 
|  | return instruction; | 
|  | } | 
|  | } | 
|  | // |language_| should be defined such that instruction opcodes cover every | 
|  | // possible input. | 
|  | QUICHE_DCHECK(false); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void QpackInstructionDecoder::OnError(ErrorCode error_code, | 
|  | absl::string_view error_message) { | 
|  | QUICHE_DCHECK(!error_detected_); | 
|  |  | 
|  | error_detected_ = true; | 
|  | delegate_->OnInstructionDecodingError(error_code, error_message); | 
|  | } | 
|  |  | 
|  | }  // namespace quic |