blob: 5bf3cfe2b818caf75aee3413b8cba1766830d89e [file] [log] [blame]
// 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.
#include "message_decoder.h"
#include <ostream>
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "src/lib/fidl_codec/library_loader.h"
#include "src/lib/fidl_codec/wire_object.h"
#include "src/lib/fidl_codec/wire_parser.h"
#include "src/lib/fidl_codec/wire_types.h"
#include "src/lib/fxl/logging.h"
namespace fidl_codec {
std::string DocumentToString(rapidjson::Document* document) {
rapidjson::StringBuffer output;
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
document->Accept(writer);
return output.GetString();
}
bool DecodedMessage::DecodeMessage(MessageDecoderDispatcher* dispatcher, uint64_t process_koid,
zx_handle_t handle, const uint8_t* bytes, uint32_t num_bytes,
const zx_handle_info_t* handles, uint32_t num_handles,
SyscallFidlType type, std::ostream& os,
std::string_view line_header, int tabs) {
if (dispatcher->loader() == nullptr) {
return false;
}
if ((bytes == nullptr) || (num_bytes < sizeof(fidl_message_header_t))) {
os << line_header << std::string(tabs * kTabSize, ' ') << "not enough data for message\n";
return false;
}
header_ = reinterpret_cast<const fidl_message_header_t*>(bytes);
const std::vector<const InterfaceMethod*>* methods =
dispatcher->loader()->GetByOrdinal(header_->ordinal);
if (methods == nullptr || methods->empty()) {
os << line_header << std::string(tabs * kTabSize, ' ') << "Protocol method with ordinal 0x"
<< std::hex << header_->ordinal << " not found\n";
return false;
}
method_ = (*methods)[0];
matched_request_ = DecodeRequest(method_, bytes, num_bytes, handles, num_handles,
&decoded_request_, request_error_stream_);
matched_response_ = DecodeResponse(method_, bytes, num_bytes, handles, num_handles,
&decoded_response_, response_error_stream_);
direction_ = dispatcher->ComputeDirection(process_koid, handle, type, method_,
matched_request_ != matched_response_);
switch (type) {
case SyscallFidlType::kOutputMessage:
if (direction_ == Direction::kClient) {
is_request_ = true;
}
message_direction_ = "sent ";
break;
case SyscallFidlType::kInputMessage:
if (direction_ == Direction::kServer) {
is_request_ = true;
}
message_direction_ = "received ";
break;
case SyscallFidlType::kOutputRequest:
is_request_ = true;
message_direction_ = "sent ";
break;
case SyscallFidlType::kInputResponse:
message_direction_ = "received ";
break;
}
if (direction_ != Direction::kUnknown) {
if ((is_request_ && !matched_request_) || (!is_request_ && !matched_response_)) {
if ((is_request_ && matched_response_) || (!is_request_ && matched_request_)) {
if ((type == SyscallFidlType::kOutputRequest) ||
(type == SyscallFidlType::kInputResponse)) {
// We know the direction: we can't be wrong => we haven't been able to decode the message.
// However, we can still display something.
return true;
}
// The first determination seems to be wrong. That is, we are expecting
// a request but only a response has been successfully decoded or we are
// expecting a response but only a request has been successfully
// decoded.
// Invert the deduction which should now be the right one.
dispatcher->UpdateDirection(
process_koid, handle,
(direction_ == Direction::kClient) ? Direction::kServer : Direction::kClient);
is_request_ = !is_request_;
}
}
}
return true;
}
bool DecodedMessage::Display(const Colors& colors, bool pretty_print, int columns, std::ostream& os,
std::string_view line_header, int tabs) {
if (direction_ == Direction::kUnknown) {
if (matched_request_ || matched_response_) {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.red
<< "Can't determine request/response." << colors.reset << " it can be:\n";
} else {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.red
<< "Can't decode message." << colors.reset << '\n';
}
++tabs;
}
if (matched_request_ && (is_request_ || (direction_ == Direction::kUnknown))) {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.white_on_magenta
<< message_direction_ << "request" << colors.reset << ' ' << colors.green
<< method_->enclosing_interface().name() << '.' << method_->name() << colors.reset << " = ";
if (pretty_print) {
decoded_request_->PrettyPrint(os, colors, header_, line_header, tabs, tabs * kTabSize,
columns);
} else {
rapidjson::Document actual_request;
if (decoded_request_ != nullptr) {
decoded_request_->ExtractJson(actual_request.GetAllocator(), actual_request);
}
os << DocumentToString(&actual_request);
}
os << '\n';
}
if (matched_response_ && (!is_request_ || (direction_ == Direction::kUnknown))) {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.white_on_magenta
<< message_direction_ << "response" << colors.reset << ' ' << colors.green
<< method_->enclosing_interface().name() << '.' << method_->name() << colors.reset << " = ";
if (pretty_print) {
decoded_response_->PrettyPrint(os, colors, header_, line_header, tabs, tabs * kTabSize,
columns);
} else {
rapidjson::Document actual_response;
if (decoded_response_ != nullptr) {
decoded_response_->ExtractJson(actual_response.GetAllocator(), actual_response);
}
os << DocumentToString(&actual_response);
}
os << '\n';
}
if (matched_request_ || matched_response_) {
return true;
}
std::string request_errors = request_error_stream_.str();
if (!request_errors.empty()) {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.red << message_direction_
<< "request errors" << colors.reset << ":\n"
<< request_errors;
if (decoded_request_ != nullptr) {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.white_on_magenta
<< message_direction_ << "request" << colors.reset << ' ' << colors.green
<< method_->enclosing_interface().name() << '.' << method_->name() << colors.reset
<< " = ";
decoded_request_->PrettyPrint(os, colors, header_, line_header, tabs, tabs * kTabSize,
columns);
os << '\n';
}
}
std::string response_errors = response_error_stream_.str();
if (!response_errors.empty()) {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.red << message_direction_
<< "response errors" << colors.reset << ":\n"
<< response_errors;
if (decoded_response_ != nullptr) {
os << line_header << std::string(tabs * kTabSize, ' ') << colors.white_on_magenta
<< message_direction_ << "request" << colors.reset << ' ' << colors.green
<< method_->enclosing_interface().name() << '.' << method_->name() << colors.reset
<< " = ";
decoded_response_->PrettyPrint(os, colors, header_, line_header, tabs, tabs * kTabSize,
columns);
os << '\n';
}
}
return false;
}
bool MessageDecoderDispatcher::DecodeMessage(uint64_t process_koid, zx_handle_t handle,
const uint8_t* bytes, uint32_t num_bytes,
const zx_handle_info_t* handles, uint32_t num_handles,
SyscallFidlType type, std::ostream& os,
std::string_view line_header, int tabs) {
DecodedMessage message;
if (!message.DecodeMessage(this, process_koid, handle, bytes, num_bytes, handles, num_handles,
type, os, line_header, tabs)) {
return false;
}
return message.Display(colors_, display_options_.pretty_print, display_options_.columns, os,
line_header, tabs);
}
Direction MessageDecoderDispatcher::ComputeDirection(uint64_t process_koid, zx_handle_t handle,
SyscallFidlType type,
const InterfaceMethod* method,
bool only_one_valid) {
auto handle_direction = handle_directions_.find(std::make_tuple(handle, process_koid));
if (handle_direction != handle_directions_.end()) {
return handle_direction->second;
}
// This is the first read or write we intercept for this handle/koid. If we
// launched the process, we suppose we intercepted the very first read or
// write.
// If this is not an event (which would mean method->request() is null), a
// write means that we are watching a client (a client starts by writing a
// request) and a read means that we are watching a server (a server starts
// by reading the first client request).
// If we attached to a running process, we can only determine correctly if
// we are watching a client or a server if we have only one matched_request
// or one matched_response.
if (IsLaunchedProcess(process_koid) || only_one_valid) {
// We launched the process or exactly one of request and response are
// valid => we can determine the direction.
switch (type) {
case SyscallFidlType::kOutputMessage:
handle_directions_[std::make_tuple(handle, process_koid)] =
(method->request() != nullptr) ? Direction::kClient : Direction::kServer;
break;
case SyscallFidlType::kInputMessage:
handle_directions_[std::make_tuple(handle, process_koid)] =
(method->request() != nullptr) ? Direction::kServer : Direction::kClient;
break;
case SyscallFidlType::kOutputRequest:
case SyscallFidlType::kInputResponse:
handle_directions_[std::make_tuple(handle, process_koid)] = Direction::kClient;
}
return handle_directions_[std::make_tuple(handle, process_koid)];
}
return Direction::kUnknown;
}
MessageDecoder::MessageDecoder(const uint8_t* bytes, uint32_t num_bytes,
const zx_handle_info_t* handles, uint32_t num_handles,
std::ostream& error_stream)
: num_bytes_(num_bytes),
start_byte_pos_(bytes),
end_handle_pos_(handles + num_handles),
handle_pos_(handles),
unions_are_xunions_(fidl_should_decode_union_from_xunion(
reinterpret_cast<const fidl_message_header_t*>(bytes))),
error_stream_(error_stream) {}
MessageDecoder::MessageDecoder(MessageDecoder* container, uint64_t offset, uint64_t num_bytes,
uint64_t num_handles)
: absolute_offset_(container->absolute_offset() + offset),
num_bytes_(num_bytes),
start_byte_pos_(container->start_byte_pos_ + offset),
end_handle_pos_(container->handle_pos_ + num_handles),
handle_pos_(container->handle_pos_),
unions_are_xunions_(container->unions_are_xunions_),
error_stream_(container->error_stream_) {
container->handle_pos_ += num_handles;
}
std::unique_ptr<Object> MessageDecoder::DecodeMessage(const Struct& message_format) {
// Set the offset for the next object (just after this one).
SkipObject(message_format.Size(this));
// Decode the object.
std::unique_ptr<Object> object = message_format.DecodeObject(this, /*type=*/nullptr,
/*offset=*/0, /*nullable=*/false);
// It's an error if we didn't use all the bytes in the buffer.
if (next_object_offset_ != num_bytes_) {
AddError() << "Message not fully decoded (decoded=" << next_object_offset_
<< ", size=" << num_bytes_ << ")\n";
}
// It's an error if we didn't use all the handles in the buffer.
if (GetRemainingHandles() != 0) {
AddError() << "Message not fully decoded (remain " << GetRemainingHandles() << " handles)\n";
}
return object;
}
std::unique_ptr<Value> MessageDecoder::DecodeValue(const Type* type) {
if (type == nullptr) {
return nullptr;
}
// Set the offset for the next object (just after this one).
SkipObject(type->InlineSize(this));
// Decode the envelope.
std::unique_ptr<Value> result = type->Decode(this, 0);
// It's an error if we didn't use all the bytes in the buffer.
if (next_object_offset_ != num_bytes_) {
AddError() << "Message envelope not fully decoded (decoded=" << next_object_offset_
<< ", size=" << num_bytes_ << ")\n";
}
// It's an error if we didn't use all the handles in the buffer.
if (GetRemainingHandles() != 0) {
AddError() << "Message envelope not fully decoded (remain " << GetRemainingHandles()
<< " handles)\n";
}
return result;
}
} // namespace fidl_codec