source/CalleeFrames.h (147 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 <AbstractDomain.h> #include <PatriciaTreeMapAbstractPartition.h> #include <mariana-trench/CallPositionFrames.h> #include <mariana-trench/Position.h> namespace marianatrench { /** * Represents a set of frames with the same call position. * Based on its position in `Taint`, it is expected that all frames within * this class have the same callee and call position. */ class CalleeFrames final : public sparta::AbstractDomain<CalleeFrames> { private: using FramesByCallPosition = sparta::PatriciaTreeMapAbstractPartition< const Position * MT_NULLABLE, CallPositionFrames>; private: // Iterator based on `FlattenIterator`. struct CallPositionToFramesMapDereference { static CallPositionFrames::iterator begin( const std::pair<const Position*, CallPositionFrames>& pair) { return pair.second.begin(); } static CallPositionFrames::iterator end( const std::pair<const Position*, CallPositionFrames>& pair) { return pair.second.end(); } }; using ConstIterator = FlattenIterator< /* OuterIterator */ FramesByCallPosition::MapType::iterator, /* InnerIterator */ CallPositionFrames::iterator, CallPositionToFramesMapDereference>; public: // C++ container concept member types using iterator = ConstIterator; using const_iterator = ConstIterator; using value_type = Frame; using difference_type = std::ptrdiff_t; using size_type = std::size_t; using const_reference = const Frame&; using const_pointer = const Frame*; private: explicit CalleeFrames( const Method* MT_NULLABLE callee, FramesByCallPosition frames) : callee_(callee), frames_(std::move(frames)) {} public: /* Create the bottom (i.e, empty) frame set. */ CalleeFrames() : callee_(nullptr), frames_(FramesByCallPosition::bottom()) {} explicit CalleeFrames(std::initializer_list<Frame> frames); CalleeFrames(const CalleeFrames&) = default; CalleeFrames(CalleeFrames&&) = default; CalleeFrames& operator=(const CalleeFrames&) = default; CalleeFrames& operator=(CalleeFrames&&) = default; static CalleeFrames bottom() { return CalleeFrames( /* callee */ nullptr, FramesByCallPosition::bottom()); } static CalleeFrames top() { return CalleeFrames( /* callee */ nullptr, FramesByCallPosition::top()); } bool is_bottom() const override { return frames_.is_bottom(); } bool is_top() const override { return frames_.is_top(); } void set_to_bottom() override { callee_ = nullptr; frames_.set_to_bottom(); } void set_to_top() override { callee_ = nullptr; frames_.set_to_top(); } bool empty() const { return frames_.is_bottom(); } const Method* MT_NULLABLE callee() const { return callee_; } void add(const Frame& frame); bool leq(const CalleeFrames& other) const override; bool equals(const CalleeFrames& other) const override; void join_with(const CalleeFrames& other) override; void widen_with(const CalleeFrames& other) override; void meet_with(const CalleeFrames& other) override; void narrow_with(const CalleeFrames& other) override; void difference_with(const CalleeFrames& other); void map(const std::function<void(Frame&)>& f); ConstIterator begin() const { return ConstIterator(frames_.bindings().begin(), frames_.bindings().end()); } ConstIterator end() const { return ConstIterator(frames_.bindings().end(), frames_.bindings().end()); } void add_inferred_features(const FeatureMayAlwaysSet& features); LocalPositionSet local_positions() const; void add_local_position(const Position* position); void set_local_positions(const LocalPositionSet& positions); void add_inferred_features_and_local_position( const FeatureMayAlwaysSet& features, const Position* MT_NULLABLE position); /** * Propagate the taint from the callee to the caller. * * Return bottom if the taint should not be propagated. */ CalleeFrames propagate( const Method* callee, const AccessPath& callee_port, const Position* call_position, int maximum_source_sink_distance, Context& context, const std::vector<const DexType * MT_NULLABLE>& source_register_types, const std::vector<std::optional<std::string>>& source_constant_arguments) const; /* Return the set of leaf frames with the given position. */ CalleeFrames attach_position(const Position* position) const; CalleeFrames transform_kind_with_features( const std::function<std::vector<const Kind*>(const Kind*)>&, const std::function<FeatureMayAlwaysSet(const Kind*)>&) const; void append_callee_port( Path::Element path_element, const std::function<bool(const Kind*)>& filter); void filter_invalid_frames( const std::function< bool(const Method* MT_NULLABLE, const AccessPath&, const Kind*)>& is_valid); bool contains_kind(const Kind*) const; template <class T> std::unordered_map<T, CalleeFrames> partition_by_kind( const std::function<T(const Kind*)>& map_kind) const { std::unordered_map<T, CalleeFrames> result; for (const auto& [position, position_frames] : frames_.bindings()) { auto callee_frames_partitioned = position_frames.partition_by_kind(map_kind); for (const auto& [mapped_value, call_position_frames] : callee_frames_partitioned) { auto new_frames = CalleeFrames( callee_, FramesByCallPosition{std::pair(position, call_position_frames)}); auto existing = result.find(mapped_value); auto existing_or_bottom = existing == result.end() ? CalleeFrames::bottom() : existing->second; existing_or_bottom.join_with(new_frames); result[mapped_value] = existing_or_bottom; } } return result; } friend std::ostream& operator<<( std::ostream& out, const CalleeFrames& frames); private: const Method* MT_NULLABLE callee_; FramesByCallPosition frames_; }; } // namespace marianatrench