| // Copyright 2020 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_SEMANTIC_H_ |
| #define SRC_LIB_FIDL_CODEC_SEMANTIC_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <ostream> |
| #include <string> |
| #include <vector> |
| |
| #include "src/lib/fidl_codec/printer.h" |
| |
| namespace fidl_codec { |
| |
| class Value; |
| class StructValue; |
| class Type; |
| |
| namespace semantic { |
| |
| class ExpressionValue; |
| class HandleSemantic; |
| |
| // The context type (the kind of syscall). |
| enum class ContextType { kRead, kWrite, kCall }; |
| |
| // Context used during the execution of semantic rules. |
| class SemanticContext { |
| public: |
| SemanticContext(const HandleSemantic* handle_semantic, zx_koid_t pid, zx_handle_t handle, |
| const StructValue* request, const StructValue* response) |
| : handle_semantic_(handle_semantic), |
| pid_(pid), |
| handle_(handle), |
| request_(request), |
| response_(response) {} |
| |
| const HandleSemantic* handle_semantic() const { return handle_semantic_; } |
| zx_koid_t pid() const { return pid_; } |
| zx_handle_t handle() const { return handle_; } |
| const StructValue* request() const { return request_; } |
| const StructValue* response() const { return response_; } |
| |
| private: |
| // The semantic rules for the FIDL method. |
| const HandleSemantic* const handle_semantic_; |
| // The process id. |
| const zx_koid_t pid_; |
| // The handle we are reading/writing on. |
| const zx_handle_t handle_; |
| // The request (can be null). |
| const StructValue* const request_; |
| // The response (can be null). |
| const StructValue* const response_; |
| }; |
| |
| // Context used during the execution of semantic rules. |
| class AssignmentSemanticContext : public SemanticContext { |
| public: |
| AssignmentSemanticContext(HandleSemantic* handle_semantic, zx_koid_t pid, zx_koid_t tid, |
| zx_handle_t handle, ContextType type, const StructValue* request, |
| const StructValue* response) |
| : SemanticContext(handle_semantic, pid, handle, request, response), tid_(tid), type_(type) {} |
| |
| zx_koid_t tid() const { return tid_; } |
| ContextType type() const { return type_; } |
| HandleSemantic* handle_semantic() const { |
| return const_cast<HandleSemantic*>(SemanticContext::handle_semantic()); |
| } |
| |
| private: |
| // The thread id. |
| const zx_koid_t tid_; |
| // The context type. |
| const ContextType type_; |
| }; |
| |
| // Base class for all expressions (for semantic). |
| class Expression { |
| public: |
| Expression() = default; |
| virtual ~Expression() = default; |
| |
| // Dump the expression. |
| virtual void Dump(std::ostream& os) const = 0; |
| |
| // Execute the expression for a gien context. |
| virtual bool Execute(SemanticContext* context, ExpressionValue* result) const = 0; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& os, const Expression& expression) { |
| expression.Dump(os); |
| return os; |
| } |
| |
| // Defines a string literal. |
| class ExpressionStringLiteral : public Expression { |
| public: |
| ExpressionStringLiteral(std::string value) : value_(std::move(value)) {} |
| |
| void Dump(std::ostream& os) const override; |
| bool Execute(SemanticContext* context, ExpressionValue* result) const override; |
| |
| private: |
| const std::string value_; |
| }; |
| |
| // Defines an expression which accesses the request object. |
| class ExpressionRequest : public Expression { |
| public: |
| ExpressionRequest() = default; |
| |
| void Dump(std::ostream& os) const override; |
| bool Execute(SemanticContext* context, ExpressionValue* result) const override; |
| }; |
| |
| // Defines an expression which accesses the handle used to read/write the request. |
| class ExpressionHandle : public Expression { |
| public: |
| ExpressionHandle() = default; |
| |
| void Dump(std::ostream& os) const override; |
| bool Execute(SemanticContext* context, ExpressionValue* result) const override; |
| }; |
| |
| // Defines a handle description definition. |
| class ExpressionHandleDescription : public Expression { |
| public: |
| ExpressionHandleDescription(std::unique_ptr<Expression> type, std::unique_ptr<Expression> path) |
| : type_(std::move(type)), path_(std::move(path)) {} |
| |
| void Dump(std::ostream& os) const override; |
| bool Execute(SemanticContext* context, ExpressionValue* result) const override; |
| |
| private: |
| std::unique_ptr<Expression> type_; |
| std::unique_ptr<Expression> path_; |
| }; |
| |
| // Defines the access to an object field. |
| class ExpressionFieldAccess : public Expression { |
| public: |
| ExpressionFieldAccess(std::unique_ptr<Expression> expression, std::string_view field) |
| : expression_(std::move(expression)), field_(field) {} |
| |
| void Dump(std::ostream& os) const override; |
| bool Execute(SemanticContext* context, ExpressionValue* result) const override; |
| |
| private: |
| const std::unique_ptr<Expression> expression_; |
| const std::string field_; |
| }; |
| |
| // Defines the slash operator (used to concatenate two paths). |
| class ExpressionSlash : public Expression { |
| public: |
| ExpressionSlash(std::unique_ptr<Expression> left, std::unique_ptr<Expression> right) |
| : left_(std::move(left)), right_(std::move(right)) {} |
| |
| void Dump(std::ostream& os) const override; |
| bool Execute(SemanticContext* context, ExpressionValue* result) const override; |
| |
| private: |
| const std::unique_ptr<Expression> left_; |
| const std::unique_ptr<Expression> right_; |
| }; |
| |
| // Defines the colon operator (used to add attributes to a handle). |
| class ExpressionColon : public Expression { |
| public: |
| ExpressionColon(std::unique_ptr<Expression> left, std::unique_ptr<Expression> right) |
| : left_(std::move(left)), right_(std::move(right)) {} |
| |
| void Dump(std::ostream& os) const override; |
| bool Execute(SemanticContext* context, ExpressionValue* result) const override; |
| |
| private: |
| const std::unique_ptr<Expression> left_; |
| const std::unique_ptr<Expression> right_; |
| }; |
| |
| // Defines an assignment. An assignment is a rule which infers the semantic of one handle |
| // (destination) using the value of an expression (source). |
| class Assignment { |
| public: |
| Assignment(std::unique_ptr<Expression> destination, std::unique_ptr<Expression> source) |
| : destination_(std::move(destination)), source_(std::move(source)) {} |
| |
| void Dump(std::ostream& os) const; |
| void Execute(AssignmentSemanticContext* context) const; |
| |
| private: |
| const std::unique_ptr<Expression> destination_; |
| const std::unique_ptr<Expression> source_; |
| }; |
| |
| // Defines the semantic associated to a method. When a method is called, all the semantic rules |
| // (the assignments) are executed and add knowledge about the handles involved. |
| class MethodSemantic { |
| public: |
| MethodSemantic() = default; |
| |
| void AddAssignment(std::unique_ptr<Expression> destination, std::unique_ptr<Expression> source) { |
| assignments_.emplace_back( |
| std::make_unique<Assignment>(std::move(destination), std::move(source))); |
| } |
| |
| void Dump(std::ostream& os) const; |
| void ExecuteAssignments(AssignmentSemanticContext* context) const; |
| |
| private: |
| std::vector<std::unique_ptr<Assignment>> assignments_; |
| }; |
| |
| // Defines an expression to be displayed. |
| class DisplayExpression { |
| public: |
| DisplayExpression() = default; |
| |
| void set_header(std::string&& header) { header_ = std::move(header); } |
| |
| void set_expression(std::unique_ptr<Expression> expression) { |
| expression_ = std::move(expression); |
| } |
| |
| void set_footer(std::string&& footer) { footer_ = std::move(footer); } |
| |
| void Dump(std::ostream& os) const; |
| void PrettyPrint(PrettyPrinter& printer, SemanticContext* context); |
| |
| private: |
| std::string header_; |
| std::unique_ptr<Expression> expression_; |
| std::string footer_; |
| }; |
| |
| // Defines what needs to be display for a method. This is used to display short views of methods. |
| class MethodDisplay { |
| public: |
| MethodDisplay() = default; |
| |
| const std::vector<std::unique_ptr<DisplayExpression>>& inputs() const { return inputs_; } |
| const std::vector<std::unique_ptr<DisplayExpression>>& results() const { return results_; } |
| |
| void AddInput(std::unique_ptr<DisplayExpression> input) { |
| inputs_.emplace_back(std::move(input)); |
| } |
| |
| void AddResult(std::unique_ptr<DisplayExpression> result) { |
| results_.emplace_back(std::move(result)); |
| } |
| |
| void Dump(std::ostream& os) const; |
| |
| private: |
| std::vector<std::unique_ptr<DisplayExpression>> inputs_; |
| std::vector<std::unique_ptr<DisplayExpression>> results_; |
| }; |
| |
| // Holds the information we have inferred for a handle. |
| // Usually we can associate a type to a handle. |
| // Depending on the type, we can also associate: |
| // - a path (for example for directories and files). |
| // - a file descriptor (for example for sockets). |
| class InferredHandleInfo { |
| public: |
| InferredHandleInfo() = default; |
| |
| explicit InferredHandleInfo(std::string_view type) : type_(type) {} |
| |
| InferredHandleInfo(std::string_view type, int64_t fd, std::string_view attributes) |
| : type_(type), fd_(fd), attributes_(attributes) {} |
| |
| InferredHandleInfo(std::string_view type, std::string_view path, std::string_view attributes) |
| : type_(type), path_(path), attributes_(attributes) {} |
| |
| InferredHandleInfo(std::string_view type, int64_t fd, std::string_view path, |
| std::string_view attributes) |
| : type_(type), fd_(fd), path_(path), attributes_(attributes) {} |
| |
| const std::string& type() const { return type_; } |
| int64_t fd() const { return fd_; } |
| const std::string& path() const { return path_; } |
| const std::string& attributes() const { return attributes_; } |
| |
| // Convert a handle type (found in zircon/system/public/zircon/processargs.h) into a string. |
| static std::string_view Convert(uint32_t type); |
| |
| // Display the information we have about a handle. |
| void Display(PrettyPrinter& printer) const; |
| |
| private: |
| // Type of the handle. This can be a predefined type (when set by Convert) or |
| // any string when it is an applicative type. |
| const std::string type_; |
| // Numerical value associated with the handle. Mostly used by file descriptors. |
| const int64_t fd_ = -1; |
| // Path associated with the handle. We can have both fd and path defined at the |
| // same time. |
| const std::string path_; |
| // Applicative attributes associated with the handle. |
| const std::string attributes_; |
| }; |
| |
| // Holds the handle semantic for one process. That is all the meaningful information we have been |
| // able to infer for the handles owned by one process. |
| struct ProcessSemantic { |
| // All the handles for which we have some information. |
| std::map<zx_handle_t, std::unique_ptr<InferredHandleInfo>> handles; |
| // All the links between handle pairs. |
| std::map<zx_handle_t, zx_handle_t> linked_handles; |
| }; |
| |
| // Object which hold the information we have about handles for all the processes. |
| class HandleSemantic { |
| public: |
| HandleSemantic() = default; |
| virtual ~HandleSemantic() = default; |
| |
| const std::map<zx_koid_t, ProcessSemantic>& process_handles() const { return process_handles_; } |
| const std::map<zx_koid_t, zx_koid_t>& linked_koids() const { return linked_koids_; } |
| |
| size_t handle_size(zx_koid_t pid) const { |
| const auto& process_semantic = process_handles_.find(pid); |
| if (process_semantic == process_handles_.end()) { |
| return 0; |
| } |
| return process_semantic->second.handles.size(); |
| } |
| |
| const ProcessSemantic* GetProcessSemantic(zx_koid_t pid) const { |
| const auto& process_semantic = process_handles_.find(pid); |
| if (process_semantic == process_handles_.end()) { |
| return nullptr; |
| } |
| return &process_semantic->second; |
| } |
| |
| InferredHandleInfo* GetInferredHandleInfo(zx_koid_t pid, zx_handle_t handle) const { |
| const auto& process_semantic = process_handles_.find(pid); |
| if (process_semantic == process_handles_.end()) { |
| return nullptr; |
| } |
| const auto& result = process_semantic->second.handles.find(handle); |
| if (result == process_semantic->second.handles.end()) { |
| return nullptr; |
| } |
| return result->second.get(); |
| } |
| |
| virtual void CreateHandleInfo(zx_koid_t thread_koid, zx_handle_t handle) {} |
| |
| virtual bool NeedsToLoadHandleInfo(zx_koid_t tid, zx_handle_t handle) const { return false; } |
| |
| void AddInferredHandleInfo(zx_koid_t pid, zx_handle_t handle, |
| const InferredHandleInfo* inferred_handle_info) { |
| if (inferred_handle_info != nullptr) { |
| process_handles_[pid].handles[handle] = |
| std::make_unique<InferredHandleInfo>(*inferred_handle_info); |
| } |
| } |
| |
| void AddInferredHandleInfo(zx_koid_t pid, zx_handle_t handle, |
| std::unique_ptr<InferredHandleInfo> inferred_handle_info) { |
| process_handles_[pid].handles[handle] = std::move(inferred_handle_info); |
| } |
| |
| void AddInferredHandleInfo(zx_koid_t pid, zx_handle_t handle, std::string_view type) { |
| process_handles_[pid].handles[handle] = std::make_unique<InferredHandleInfo>(type); |
| } |
| |
| void AddInferredHandleInfo(zx_koid_t pid, zx_handle_t handle, std::string_view type, int64_t fd, |
| std::string_view attributes) { |
| process_handles_[pid].handles[handle] = |
| std::make_unique<InferredHandleInfo>(type, fd, attributes); |
| } |
| |
| void AddInferredHandleInfo(zx_koid_t pid, zx_handle_t handle, std::string_view type, |
| std::string_view path, std::string_view attributes) { |
| process_handles_[pid].handles[handle] = |
| std::make_unique<InferredHandleInfo>(type, path, attributes); |
| } |
| |
| void AddInferredHandleInfo(zx_koid_t pid, zx_handle_t handle, uint32_t type) { |
| process_handles_[pid].handles[handle] = |
| std::make_unique<InferredHandleInfo>(InferredHandleInfo::Convert(type)); |
| } |
| |
| // Returns the handle peer for a channel. |
| zx_handle_t GetLinkedHandle(zx_koid_t pid, zx_handle_t handle) const { |
| const auto& process_semantic = process_handles_.find(pid); |
| if (process_semantic == process_handles_.end()) { |
| return ZX_HANDLE_INVALID; |
| } |
| auto linked = process_semantic->second.linked_handles.find(handle); |
| if (linked == process_semantic->second.linked_handles.end()) { |
| return ZX_HANDLE_INVALID; |
| } |
| return linked->second; |
| } |
| |
| // Associates two channels which have been created by the same zx_channel_create. |
| void AddLinkedHandles(zx_koid_t pid, zx_handle_t handle0, zx_handle_t handle1) { |
| ProcessSemantic& process_semantic = process_handles_[pid]; |
| process_semantic.linked_handles.insert(std::make_pair(handle0, handle1)); |
| process_semantic.linked_handles.insert(std::make_pair(handle1, handle0)); |
| } |
| |
| // Returns the koid of a channel peer given the channel koid). |
| zx_koid_t GetLinkedKoid(zx_koid_t koid) const { |
| auto linked = linked_koids_.find(koid); |
| if (linked == linked_koids_.end()) { |
| return ZX_KOID_INVALID; |
| } |
| return linked->second; |
| } |
| |
| // Associates two channel koids. |
| void AddLinkedKoids(zx_koid_t koid0, zx_koid_t koid1) { |
| linked_koids_.insert(std::make_pair(koid0, koid1)); |
| linked_koids_.insert(std::make_pair(koid1, koid0)); |
| } |
| |
| private: |
| std::map<zx_koid_t, ProcessSemantic> process_handles_; |
| std::map<zx_koid_t, zx_koid_t> linked_koids_; |
| }; |
| |
| // Holds the evaluation of an expression. The value depends on kind_. |
| class ExpressionValue { |
| public: |
| ExpressionValue() = default; |
| |
| enum Kind { kUndefined, kValue, kHandle, kInferredHandleInfo, kString, kInteger }; |
| void set_value(const Type* value_type, const Value* value) { |
| FX_DCHECK(value != nullptr); |
| kind_ = kValue; |
| value_type_ = value_type; |
| value_ = value; |
| } |
| void set_handle(zx_handle_t handle) { |
| kind_ = kHandle; |
| handle_ = handle; |
| } |
| void set_inferred_handle_info(std::string_view type, int64_t fd, std::string_view path, |
| std::string_view attributes) { |
| kind_ = kInferredHandleInfo; |
| inferred_handle_info_ = std::make_unique<InferredHandleInfo>(type, fd, path, attributes); |
| } |
| void set_string(const std::string& string) { |
| kind_ = kString; |
| string_ = string; |
| } |
| void set_integer(int64_t integer) { |
| kind_ = kInteger; |
| integer_ = integer; |
| } |
| |
| Kind kind() const { return kind_; } |
| const Type* value_type() const { |
| FX_DCHECK(kind_ == kValue); |
| return value_type_; |
| } |
| const Value* value() const { |
| FX_DCHECK(kind_ == kValue); |
| return value_; |
| } |
| zx_handle_t handle() const { |
| FX_DCHECK(kind_ == kHandle); |
| return handle_; |
| } |
| const InferredHandleInfo* inferred_handle_info() const { |
| FX_DCHECK(kind_ == kInferredHandleInfo); |
| return inferred_handle_info_.get(); |
| } |
| const std::string& string() const { |
| FX_DCHECK(kind_ == kString); |
| return string_; |
| } |
| int64_t integer() const { |
| FX_DCHECK(kind_ == kInteger); |
| return integer_; |
| } |
| |
| // If the value is a handle and if the handle is linked then the value is assigned with the |
| // linked handle. |
| void UseLinkedHandle(const SemanticContext* context); |
| |
| void PrettyPrint(PrettyPrinter& printer); |
| |
| private: |
| Kind kind_ = kUndefined; |
| // If kind is kValue, value_ is a FIDL value with the type value_type_. |
| const Type* value_type_ = nullptr; |
| const Value* value_ = nullptr; |
| // If kind is kHandle, handle_ is a handle. |
| zx_handle_t handle_ = ZX_HANDLE_INVALID; |
| // If kind is kInferredHandleInfo, inferred_handle_info_ is a inferred handle info. |
| std::unique_ptr<InferredHandleInfo> inferred_handle_info_; |
| // If kind is kString, string_ is a string. |
| std::string string_; |
| // If kind is kInteger, integer_ is an integer. |
| int64_t integer_ = 0; |
| }; |
| |
| } // namespace semantic |
| } // namespace fidl_codec |
| |
| #endif // SRC_LIB_FIDL_CODEC_SEMANTIC_H_ |