| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_FIDL_CODEC_MESSAGE_DECODER_H_ |
| #define SRC_LIB_FIDL_CODEC_MESSAGE_DECODER_H_ |
| |
| #include <lib/fidl/cpp/message.h> |
| #include <lib/fidl/txn_header.h> |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <cstdint> |
| #include <memory> |
| #include <ostream> |
| #include <sstream> |
| #include <string_view> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include "src/lib/fidl_codec/display_options.h" |
| #include "src/lib/fidl_codec/library_loader.h" |
| #include "src/lib/fidl_codec/memory_helpers.h" |
| #include "src/lib/fidl_codec/printer.h" |
| |
| namespace fidl_codec { |
| |
| struct DecodedMessageData; |
| class MessageDecoderDispatcher; |
| class Struct; |
| class StructValue; |
| class Type; |
| class Value; |
| |
| enum class Direction { kUnknown, kClient, kServer }; |
| |
| enum class SyscallFidlType { |
| kOutputMessage, // A message (request or response which is written). |
| kInputMessage, // A message (request or response which is read). |
| kOutputRequest, // A request which is written (case of zx_channel_call). |
| kInputResponse // A response which is read (case of zx_channel_call). |
| }; |
| |
| class DecodedMessage { |
| public: |
| DecodedMessage() = default; |
| |
| zx_txid_t txid() const { return txid_; } |
| uint64_t ordinal() const { return ordinal_; } |
| zx_status_t epitaph_error() const { return epitaph_error_; } |
| const InterfaceMethod* method() const { return method_; } |
| std::unique_ptr<StructValue>& decoded_request() { return decoded_request_; } |
| std::stringstream& request_error_stream() { return request_error_stream_; } |
| std::unique_ptr<StructValue>& decoded_response() { return decoded_response_; } |
| std::stringstream& response_error_stream() { return response_error_stream_; } |
| Direction direction() const { return direction_; } |
| bool is_request() const { return is_request_; } |
| bool received() const { return received_; } |
| |
| // Decodes a message and fill all the fields. Returns true if we can display something. |
| bool DecodeMessage(MessageDecoderDispatcher* dispatcher, uint64_t process_koid, |
| zx_handle_t handle, const uint8_t* bytes, uint32_t num_bytes, |
| const zx_handle_disposition_t* handles, uint32_t num_handles, |
| SyscallFidlType type, std::ostream& error_stream); |
| |
| private: |
| const fidl_message_header_t* header_ = nullptr; |
| zx_txid_t txid_ = 0; |
| uint64_t ordinal_ = 0; |
| zx_status_t epitaph_error_ = ZX_OK; |
| const InterfaceMethod* method_ = nullptr; |
| std::unique_ptr<StructValue> decoded_request_; |
| std::stringstream request_error_stream_; |
| bool matched_request_ = false; |
| std::unique_ptr<StructValue> decoded_response_; |
| std::stringstream response_error_stream_; |
| bool matched_response_ = false; |
| Direction direction_ = Direction::kUnknown; |
| bool is_request_ = false; |
| bool received_ = false; |
| }; |
| |
| // Class which is able to decode all the messages received/sent. |
| class MessageDecoderDispatcher { |
| public: |
| MessageDecoderDispatcher(LibraryLoader* loader, const DisplayOptions& display_options) |
| : loader_(loader), |
| display_options_(display_options), |
| colors_(display_options.needs_colors ? WithColors : WithoutColors) {} |
| |
| LibraryLoader* loader() const { return loader_; } |
| const DisplayOptions& display_options() const { return display_options_; } |
| const Colors& colors() const { return colors_; } |
| int columns() const { return display_options_.columns; } |
| bool with_process_info() const { return display_options_.with_process_info; } |
| std::map<std::tuple<zx_handle_t, uint64_t>, Direction>& handle_directions() { |
| return handle_directions_; |
| } |
| |
| void AddLaunchedProcess(uint64_t process_koid) { launched_processes_.insert(process_koid); } |
| |
| bool IsLaunchedProcess(uint64_t process_koid) { |
| return launched_processes_.find(process_koid) != launched_processes_.end(); |
| } |
| |
| // Heuristic which computes the direction of a message (outgoing request, incomming response, |
| // ...). |
| Direction ComputeDirection(uint64_t process_koid, zx_handle_t handle, SyscallFidlType type, |
| const InterfaceMethod* method, bool only_one_valid); |
| |
| // Update the direction. Used when the heuristic was wrong. |
| void UpdateDirection(uint64_t process_koid, zx_handle_t handle, Direction direction) { |
| handle_directions_[std::make_tuple(handle, process_koid)] = direction; |
| } |
| |
| private: |
| LibraryLoader* const loader_; |
| const DisplayOptions& display_options_; |
| const Colors& colors_; |
| std::unordered_set<uint64_t> launched_processes_; |
| std::map<std::tuple<zx_handle_t, uint64_t>, Direction> handle_directions_; |
| }; |
| |
| // Helper to decode a message (request or response). It generates a StructValue. |
| class MessageDecoder { |
| public: |
| MessageDecoder(const uint8_t* bytes, uint64_t num_bytes, const zx_handle_disposition_t* handles, |
| uint32_t num_handles, std::ostream& error_stream); |
| MessageDecoder(MessageDecoder* container, uint64_t offset, uint64_t num_bytes_remaining, |
| uint64_t num_handles_remaining); |
| |
| uint64_t absolute_offset() const { return absolute_offset_; } |
| |
| uint64_t num_bytes() const { return num_bytes_; } |
| |
| const zx_handle_disposition_t* handle_pos() const { return handle_pos_; } |
| |
| uint64_t next_object_offset() const { return next_object_offset_; } |
| |
| bool HasError() const { return error_count_ > 0; } |
| |
| // Add an error. |
| std::ostream& AddError() { |
| ++error_count_; |
| return error_stream_; |
| } |
| |
| size_t GetRemainingHandles() const { return end_handle_pos_ - handle_pos_; } |
| |
| // Used by numeric types to retrieve a numeric value. If there is not enough |
| // data, returns false and value is set to its zero-initialized value. |
| template <typename T> |
| bool GetValueAt(uint64_t offset, T* value); |
| |
| // Gets the address of some data of |size| at |offset|. If there is not enough |
| // data, returns null. |
| const uint8_t* GetAddress(uint64_t offset, uint64_t size) { |
| if ((offset > num_bytes_) || (size > num_bytes_ - offset)) { |
| AddError() << std::hex << (absolute_offset_ + offset) << std::dec |
| << ": Not enough data to decode (needs " << size << ", remains " |
| << (num_bytes_ - offset) << ")\n"; |
| return nullptr; |
| } |
| return start_byte_pos_ + offset; |
| } |
| |
| // Sets the next object offset. The current object (which is at the previous value of next object |
| // offset) is not decoded yet. It will be decoded just after this call. |
| // The new offset is 8 byte aligned. |
| void SkipObject(uint64_t size) { |
| uint64_t new_offset = (next_object_offset_ + size + 7) & ~7; |
| if (new_offset > num_bytes_) { |
| AddError() << std::hex << (absolute_offset_ + next_object_offset_) << std::dec |
| << ": Not enough data to decode (needs " << (new_offset - next_object_offset_) |
| << ", remains " << (num_bytes_ - next_object_offset_) << ")\n"; |
| new_offset = num_bytes_; |
| } |
| next_object_offset_ = new_offset; |
| } |
| |
| // Consumes a handle. Returns FIDL_HANDLE_ABSENT if there is no handle |
| // available. |
| zx_handle_disposition_t GetNextHandle() { |
| if (handle_pos_ == end_handle_pos_) { |
| AddError() << "Not enough handles\n"; |
| zx_handle_disposition_t result; |
| result.operation = kNoHandleDisposition; |
| result.handle = FIDL_HANDLE_ABSENT; |
| result.type = ZX_OBJ_TYPE_NONE; |
| result.rights = 0; |
| result.result = ZX_OK; |
| return result; |
| } |
| return *handle_pos_++; |
| } |
| |
| // Decodes a whole message (request or response) and return a StructValue. |
| std::unique_ptr<StructValue> DecodeMessage(const Struct& message_format); |
| |
| // Decodes a field. Used by envelopes. |
| std::unique_ptr<Value> DecodeValue(const Type* type); |
| |
| std::unique_ptr<StructValue> DecodeStruct(const Struct& struct_definition, uint64_t offset); |
| |
| // Decodes the header for a value which can be null. |
| bool DecodeNullableHeader(uint64_t offset, uint64_t size, bool* is_null, |
| uint64_t* nullable_offset); |
| |
| // Decodes a value in an envelope. |
| std::unique_ptr<Value> DecodeEnvelope(uint64_t offset, const Type* type); |
| |
| // Checks that we have a null envelope encoded. |
| bool CheckNullEnvelope(uint64_t offset); |
| |
| // Skips an unknown envelope content. |
| void SkipEnvelope(uint64_t offset); |
| |
| private: |
| // The absolute offset in the main buffer. |
| const uint64_t absolute_offset_ = 0; |
| |
| // The size of the message bytes. |
| const uint64_t num_bytes_; |
| |
| // The start of the message. |
| const uint8_t* const start_byte_pos_; |
| |
| // The end of the message. |
| const zx_handle_disposition_t* const end_handle_pos_; |
| |
| // The current handle decoding position in the message. |
| const zx_handle_disposition_t* handle_pos_; |
| |
| // Location of the next out of line object. |
| uint64_t next_object_offset_ = 0; |
| |
| // Errors found during the message decoding. |
| int error_count_ = 0; |
| |
| // Stream for the errors. |
| std::ostream& error_stream_; |
| }; |
| |
| // Used by numeric types to retrieve a numeric value. If there is not enough |
| // data, returns false and value is set to its zero-initialized-value. |
| template <typename T> |
| bool MessageDecoder::GetValueAt(uint64_t offset, T* value) { |
| if (offset + sizeof(T) > num_bytes_) { |
| if (offset <= num_bytes_) { |
| AddError() << std::hex << (absolute_offset_ + offset) << std::dec |
| << ": Not enough data to decode (needs " << sizeof(T) << ", remains " |
| << (num_bytes_ - offset) << ")\n"; |
| } |
| *value = {}; |
| return false; |
| } |
| *value = internal::MemoryFrom<T>(start_byte_pos_ + offset); |
| return true; |
| } |
| |
| } // namespace fidl_codec |
| |
| #endif // SRC_LIB_FIDL_CODEC_MESSAGE_DECODER_H_ |