|  | /* | 
|  | * Copyright (C) 2018 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "src/tracing/service/packet_stream_validator.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <cinttypes> | 
|  |  | 
|  | #include "perfetto/base/logging.h" | 
|  | #include "perfetto/ext/base/utils.h" | 
|  | #include "perfetto/protozero/proto_utils.h" | 
|  |  | 
|  | #include "protos/perfetto/trace/trace_packet.pbzero.h" | 
|  |  | 
|  | namespace perfetto { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using protozero::proto_utils::ProtoWireType; | 
|  |  | 
|  | const uint32_t kReservedFieldIds[] = { | 
|  | protos::pbzero::TracePacket::kTrustedUidFieldNumber, | 
|  | protos::pbzero::TracePacket::kTrustedPacketSequenceIdFieldNumber, | 
|  | protos::pbzero::TracePacket::kTraceConfigFieldNumber, | 
|  | protos::pbzero::TracePacket::kTraceStatsFieldNumber, | 
|  | protos::pbzero::TracePacket::kCompressedPacketsFieldNumber, | 
|  | protos::pbzero::TracePacket::kSynchronizationMarkerFieldNumber, | 
|  | protos::pbzero::TracePacket::kTrustedPidFieldNumber, | 
|  | protos::pbzero::TracePacket::kMachineIdFieldNumber, | 
|  | }; | 
|  |  | 
|  | // This translation unit is quite subtle and perf-sensitive. Remember to check | 
|  | // BM_PacketStreamValidator in perfetto_benchmarks when making changes. | 
|  |  | 
|  | // Checks that a packet, spread over several slices, is well-formed and doesn't | 
|  | // contain reserved top-level fields. | 
|  | // The checking logic is based on a state-machine that skips the fields' payload | 
|  | // and operates as follows: | 
|  | //              +-------------------------------+ <-------------------------+ | 
|  | // +----------> | Read field preamble (varint)  | <----------------------+  | | 
|  | // |            +-------------------------------+                        |  | | 
|  | // |              |              |            |                          |  | | 
|  | // |       <Varint>        <Fixed 32/64>     <Length-delimited field>    |  | | 
|  | // |          V                  |                      V                |  | | 
|  | // |  +------------------+       |               +--------------+        |  | | 
|  | // |  | Read field value |       |               | Read length  |        |  | | 
|  | // |  | (another varint) |       |               |   (varint)   |        |  | | 
|  | // |  +------------------+       |               +--------------+        |  | | 
|  | // |           |                 V                      V                |  | | 
|  | // +-----------+        +----------------+     +-----------------+       |  | | 
|  | //                      | Skip 4/8 Bytes |     | Skip $len Bytes |-------+  | | 
|  | //                      +----------------+     +-----------------+          | | 
|  | //                               |                                          | | 
|  | //                               +------------------------------------------+ | 
|  | class ProtoFieldParserFSM { | 
|  | public: | 
|  | // This method effectively continuously parses varints (either for the field | 
|  | // preamble or the payload or the submessage length) and tells the caller | 
|  | // (the Validate() method) how many bytes to skip until the next field. | 
|  | size_t Push(uint8_t octet) { | 
|  | varint_ |= static_cast<uint64_t>(octet & 0x7F) << varint_shift_; | 
|  | if (octet & 0x80) { | 
|  | varint_shift_ += 7; | 
|  | if (varint_shift_ >= 64) { | 
|  | // Do not invoke UB on next call. | 
|  | varint_shift_ = 0; | 
|  | state_ = kInvalidVarInt; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | uint64_t varint = varint_; | 
|  | varint_ = 0; | 
|  | varint_shift_ = 0; | 
|  |  | 
|  | switch (state_) { | 
|  | case kFieldPreamble: { | 
|  | uint64_t field_type = varint & 7;  // 7 = 0..0111 | 
|  | auto field_id = static_cast<uint32_t>(varint >> 3); | 
|  | // Check if the field id is reserved, go into an error state if it is. | 
|  | for (size_t i = 0; i < base::ArraySize(kReservedFieldIds); ++i) { | 
|  | if (field_id == kReservedFieldIds[i]) { | 
|  | state_ = kWroteReservedField; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | // The field type is legit, now check it's well formed and within | 
|  | // boundaries. | 
|  | if (field_type == static_cast<uint64_t>(ProtoWireType::kVarInt)) { | 
|  | state_ = kVarIntValue; | 
|  | } else if (field_type == | 
|  | static_cast<uint64_t>(ProtoWireType::kFixed32)) { | 
|  | return 4; | 
|  | } else if (field_type == | 
|  | static_cast<uint64_t>(ProtoWireType::kFixed64)) { | 
|  | return 8; | 
|  | } else if (field_type == | 
|  | static_cast<uint64_t>(ProtoWireType::kLengthDelimited)) { | 
|  | state_ = kLenDelimitedLen; | 
|  | } else { | 
|  | state_ = kUnknownFieldType; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case kVarIntValue: { | 
|  | // Consume the int field payload and go back to the next field. | 
|  | state_ = kFieldPreamble; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case kLenDelimitedLen: { | 
|  | if (varint > protozero::proto_utils::kMaxMessageLength) { | 
|  | state_ = kMessageTooBig; | 
|  | return 0; | 
|  | } | 
|  | state_ = kFieldPreamble; | 
|  | return static_cast<size_t>(varint); | 
|  | } | 
|  |  | 
|  | case kWroteReservedField: | 
|  | case kUnknownFieldType: | 
|  | case kMessageTooBig: | 
|  | case kInvalidVarInt: | 
|  | // Persistent error states. | 
|  | return 0; | 
|  |  | 
|  | }          // switch(state_) | 
|  | return 0;  // To keep GCC happy. | 
|  | } | 
|  |  | 
|  | // Queried at the end of the all payload. A message is well-formed only | 
|  | // if the FSM is back to the state where it should parse the next field and | 
|  | // hasn't started parsing any preamble. | 
|  | bool valid() const { return state_ == kFieldPreamble && varint_shift_ == 0; } | 
|  | int state() const { return static_cast<int>(state_); } | 
|  |  | 
|  | private: | 
|  | enum State { | 
|  | kFieldPreamble = 0,  // Parsing the varint for the field preamble. | 
|  | kVarIntValue,        // Parsing the varint value for the field payload. | 
|  | kLenDelimitedLen,    // Parsing the length of the length-delimited field. | 
|  |  | 
|  | // Error states: | 
|  | kWroteReservedField,  // Tried to set a reserved field id. | 
|  | kUnknownFieldType,    // Encountered an invalid field type. | 
|  | kMessageTooBig,       // Size of the length delimited message was too big. | 
|  | kInvalidVarInt,       // VarInt larger than 64 bits. | 
|  | }; | 
|  |  | 
|  | State state_ = kFieldPreamble; | 
|  | uint64_t varint_ = 0; | 
|  | uint32_t varint_shift_ = 0; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | bool PacketStreamValidator::Validate(const Slices& slices) { | 
|  | ProtoFieldParserFSM parser; | 
|  | size_t skip_bytes = 0; | 
|  | for (const Slice& slice : slices) { | 
|  | for (size_t i = 0; i < slice.size;) { | 
|  | const size_t skip_bytes_cur_slice = std::min(skip_bytes, slice.size - i); | 
|  | if (skip_bytes_cur_slice > 0) { | 
|  | i += skip_bytes_cur_slice; | 
|  | skip_bytes -= skip_bytes_cur_slice; | 
|  | } else { | 
|  | uint8_t octet = *(reinterpret_cast<const uint8_t*>(slice.start) + i); | 
|  | skip_bytes = parser.Push(octet); | 
|  | i++; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (skip_bytes == 0 && parser.valid()) | 
|  | return true; | 
|  |  | 
|  | PERFETTO_DLOG("Packet validation error (state %d, skip = %zu)", | 
|  | parser.state(), skip_bytes); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace perfetto |