source/Model.h (201 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <optional> #include <ostream> #include <string> #include <vector> #include <json/json.h> #include <mariana-trench/Access.h> #include <mariana-trench/Context.h> #include <mariana-trench/FeatureSet.h> #include <mariana-trench/Flags.h> #include <mariana-trench/Issue.h> #include <mariana-trench/IssueSet.h> #include <mariana-trench/Method.h> #include <mariana-trench/Position.h> #include <mariana-trench/Propagation.h> #include <mariana-trench/PropagationSet.h> #include <mariana-trench/PropagationTree.h> #include <mariana-trench/RootPatriciaTreeAbstractPartition.h> #include <mariana-trench/Sanitizer.h> #include <mariana-trench/Taint.h> #include <mariana-trench/TaintTree.h> namespace marianatrench { using SanitizerSet = GroupHashedSetAbstractDomain< Sanitizer, Sanitizer::GroupHash, Sanitizer::GroupEqual>; /** * A `Model` is a summary of what we know about a method. A `Model` should * contain the properties we are interested in, such as *generations*, * *propagations* and *sinks*. * * A *mode* describes a specific behavior of a model. See `Model::Mode`. * * A *generation* describes the property that the method either * returns a tainted value or mutates (and hence taints) a reference type * argument, regardless of whether parameters are tainted. * * A *parameter source* of a method describes the property that the method * receives a tainted value on a given parameter. * * A *propagation* describes how taint may flow through a method. More * specifically, how taint may flow from a parameter to the method's return * value or another parameters. A *propagation* will only propagate the taint * if the parameter is tainted. See `Propagation`. * * A *global sanitizer* sanitizes all sources, sinks or propagations flowing * through the method that have a kind dictated by its kinds field * * *Attach to sources* automatically adds features to all sources flowing out of * the method. * * *Attach to sinks* automatically adds features to all sources flowing in * the method. * * *Attach to propagations* automatically adds features to all propagations from * or to a given argument or return value. * * *Add features to arguments* automatically adds features to all taint that * might flow in or out of a given argument. This is equivalent to *attach to * sources/sinks/propagations*, but also adds features even when there is no * inferred propagation. E.g, * ``` * List<String> x; * f(x); * // Here x has the feature, regardless of the propagations of f. * ``` * * *inline as* is either top, bottom or an argument access path that will be * used to inline the method at call sites. */ class Model final { public: /** * A `Mode` describes a specific behavior of a model. * * Note that enumeration values must be a power of 2, see `Flags`. */ enum class Mode : unsigned { // Do not infer modes using heuristics. OverrideDefault = 0x1, // Skip the analysis of this method. SkipAnalysis = 0x2, // Add the 'via-obscure' feature to sources flowing through this method. AddViaObscureFeature = 0x4, // Taint-in-taint-out (taint on arguments flow into the return value). TaintInTaintOut = 0x8, // Taint-in-taint-this (taint on arguments flow into `this`). TaintInTaintThis = 0x10, // Do not join all overrides at virtual call sites. NoJoinVirtualOverrides = 0x20, // Do not collapse input paths when applying propagations. NoCollapseOnPropagation = 0x40, // Alias existing memory location on method invokes. AliasMemoryLocationOnInvoke = 0x80, Normal = 0, }; using Modes = Flags<Mode>; public: /* Create an empty model. */ explicit Model(); /** * Create a model for the given method. * * By default, it uses a set of heuristics to infer the modes. Use * `Mode::OverrideDefault` to disable it. */ explicit Model( const Method* MT_NULLABLE method, Context& context, Modes modes = Mode::Normal, const std::vector<std::pair<AccessPath, Frame>>& generations = {}, const std::vector<std::pair<AccessPath, Frame>>& parameter_sources = {}, const std::vector<std::pair<AccessPath, Frame>>& sinks = {}, const std::vector<std::pair<Propagation, AccessPath>>& propagations = {}, const std::vector<Sanitizer>& global_sanitizers = {}, const std::vector<std::pair<Root, SanitizerSet>>& port_sanitizers = {}, const std::vector<std::pair<Root, FeatureSet>>& attach_to_sources = {}, const std::vector<std::pair<Root, FeatureSet>>& attach_to_sinks = {}, const std::vector<std::pair<Root, FeatureSet>>& attach_to_propagations = {}, const std::vector<std::pair<Root, FeatureSet>>& add_features_to_arguments = {}, const AccessPathConstantDomain& inline_as = AccessPathConstantDomain::bottom(), const IssueSet& issues = {}); Model(const Model& other) = default; Model(Model&&) = default; Model& operator=(const Model& other) = default; Model& operator=(Model&&) = default; ~Model() = default; bool operator==(const Model& other) const; bool operator!=(const Model& other) const; const Method* MT_NULLABLE method() const { return method_; } /* Copy the model and attach it to the given method. */ Model instantiate(const Method* method, Context& context) const; /* Return the callee model for the given callsite. */ Model at_callsite( const Method* caller, const Position* position, Context& context, const std::vector<const DexType * MT_NULLABLE>& source_register_types, const std::vector<std::optional<std::string>>& source_constant_arguments) const; void collapse_invalid_paths(Context& context); void approximate(); bool empty() const; bool check_root_consistency(Root root) const; bool check_port_consistency(const AccessPath& access_path) const; bool check_parameter_source_port_consistency( const AccessPath& access_path) const; bool check_frame_consistency(const Frame& frame, std::string_view kind) const; bool check_propagation_consistency(const Propagation& propagation) const; bool check_inline_as_consistency( const AccessPathConstantDomain& inline_as) const; void add_mode(Model::Mode mode, Context& context); void add_taint_in_taint_out(Context& context); void add_taint_in_taint_this(Context& context); void add_generation(AccessPath port, Frame generation); void add_generations(AccessPath port, Taint generations); /* Add generations after applying sanitizers */ void add_inferred_generations(AccessPath port, Taint generations); const TaintAccessPathTree& generations() const { return generations_; } void set_generations(TaintAccessPathTree generations) { generations_ = std::move(generations); } void add_parameter_source(AccessPath port, Frame source); const TaintAccessPathTree& parameter_sources() const { return parameter_sources_; } void set_parameter_sources(TaintAccessPathTree parameter_sources) { parameter_sources_ = std::move(parameter_sources); } void add_sink(AccessPath port, Frame sink); void add_sinks(AccessPath port, Taint sinks); /* Add sinks after applying sanitizers */ void add_inferred_sinks(AccessPath port, Taint sinks); const TaintAccessPathTree& sinks() const { return sinks_; } void set_sinks(TaintAccessPathTree sinks) { sinks_ = std::move(sinks); } void add_propagation(Propagation propagation, AccessPath output); /* Add a propagation after applying sanitizers */ void add_inferred_propagation(Propagation propagation, AccessPath output); const PropagationAccessPathTree& propagations() const { return propagations_; } void add_global_sanitizer(Sanitizer sanitizer); const SanitizerSet& global_sanitizers() const { return global_sanitizers_; } Taint apply_source_sink_sanitizers(SanitizerKind kind, Taint taint, Root root); bool has_global_propagation_sanitizer(); void add_port_sanitizers(SanitizerSet sanitizers, Root root); void add_attach_to_sources(Root root, FeatureSet features); FeatureSet attach_to_sources(Root root) const; void add_attach_to_sinks(Root root, FeatureSet features); FeatureSet attach_to_sinks(Root root) const; void add_attach_to_propagations(Root root, FeatureSet features); FeatureSet attach_to_propagations(Root root) const; void add_add_features_to_arguments(Root root, FeatureSet features); bool has_add_features_to_arguments() const; FeatureSet add_features_to_arguments(Root root) const; const AccessPathConstantDomain& inline_as() const; void set_inline_as(AccessPathConstantDomain inline_as); void add_issue(Issue issue); const IssueSet& issues() const { return issues_; } void set_issues(IssueSet issues) { issues_ = std::move(issues); } void remove_kinds(const std::unordered_set<const Kind*>& to_remove); bool override_default() const; bool skip_analysis() const; bool add_via_obscure_feature() const; bool is_taint_in_taint_out() const; bool is_taint_in_taint_this() const; bool no_join_virtual_overrides() const; bool no_collapse_on_propagation() const; bool alias_memory_location_on_invoke() const; Modes modes() const { return modes_; } bool leq(const Model& other) const; void join_with(const Model& other); static Model from_json( const Method* MT_NULLABLE method, const Json::Value& value, Context& context); Json::Value to_json() const; /* Export the model to json and include the method position. */ Json::Value to_json(Context& context) const; friend std::ostream& operator<<(std::ostream& out, const Model& model); private: const Method* MT_NULLABLE method_; Modes modes_; TaintAccessPathTree generations_; TaintAccessPathTree parameter_sources_; TaintAccessPathTree sinks_; PropagationAccessPathTree propagations_; SanitizerSet global_sanitizers_; RootPatriciaTreeAbstractPartition<SanitizerSet> port_sanitizers_; RootPatriciaTreeAbstractPartition<FeatureSet> attach_to_sources_; RootPatriciaTreeAbstractPartition<FeatureSet> attach_to_sinks_; RootPatriciaTreeAbstractPartition<FeatureSet> attach_to_propagations_; RootPatriciaTreeAbstractPartition<FeatureSet> add_features_to_arguments_; AccessPathConstantDomain inline_as_; IssueSet issues_; }; inline Model::Modes operator|(Model::Mode left, Model::Mode right) { return Model::Modes(left) | right; } std::string model_mode_to_string(Model::Mode mode); std::optional<Model::Mode> string_to_model_mode(const std::string& mode); constexpr std::array<Model::Mode, 8> k_all_modes = { Model::Mode::OverrideDefault, Model::Mode::SkipAnalysis, Model::Mode::AddViaObscureFeature, Model::Mode::TaintInTaintOut, Model::Mode::TaintInTaintThis, Model::Mode::NoJoinVirtualOverrides, Model::Mode::NoCollapseOnPropagation, Model::Mode::AliasMemoryLocationOnInvoke}; } // namespace marianatrench