source/AnalysisEnvironment.cpp (300 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. */ #include <mariana-trench/AnalysisEnvironment.h> #include <Show.h> #include <mariana-trench/Assert.h> namespace marianatrench { namespace { Taint propagate_artificial_sources(Taint taint, Path::Element path_element) { // This is called when propagating taint down in an abstract tree. taint.append_callee_port(path_element, [](const Kind* kind) { return kind == Kinds::artificial_source(); }); return taint; } } // namespace AnalysisEnvironment::AnalysisEnvironment() : memory_locations_(MemoryLocationsPartition::bottom()), taint_(TaintAbstractPartition::bottom()), position_(DexPositionDomain::bottom()), last_parameter_load_(LastParameterLoadDomain::bottom()) {} AnalysisEnvironment::AnalysisEnvironment( MemoryLocationsPartition memory_locations, TaintAbstractPartition taint, DexPositionDomain position, LastParameterLoadDomain last_parameter_load) : memory_locations_(std::move(memory_locations)), taint_(std::move(taint)), position_(std::move(position)), last_parameter_load_(std::move(last_parameter_load)) {} AnalysisEnvironment AnalysisEnvironment::initial() { return AnalysisEnvironment( MemoryLocationsPartition::bottom(), TaintAbstractPartition::bottom(), DexPositionDomain::top(), LastParameterLoadDomain(0)); } bool AnalysisEnvironment::is_bottom() const { return memory_locations_.is_bottom() && taint_.is_bottom() && position_.is_bottom() && last_parameter_load_.is_bottom(); } bool AnalysisEnvironment::is_top() const { return memory_locations_.is_top() && taint_.is_top() && position_.is_top() && last_parameter_load_.is_top(); } bool AnalysisEnvironment::leq(const AnalysisEnvironment& other) const { return memory_locations_.leq(other.memory_locations_) && taint_.leq(other.taint_) && position_.leq(other.position_) && last_parameter_load_.leq(other.last_parameter_load_); } bool AnalysisEnvironment::equals(const AnalysisEnvironment& other) const { return memory_locations_.equals(other.memory_locations_) && taint_.equals(other.taint_) && position_.equals(other.position_) && last_parameter_load_.equals(other.last_parameter_load_); } void AnalysisEnvironment::set_to_bottom() { memory_locations_.set_to_bottom(); taint_.set_to_bottom(); position_.set_to_bottom(); last_parameter_load_.set_to_bottom(); } void AnalysisEnvironment::set_to_top() { memory_locations_.set_to_top(); taint_.set_to_top(); position_.set_to_top(); last_parameter_load_.set_to_top(); } void AnalysisEnvironment::join_with(const AnalysisEnvironment& other) { mt_if_expensive_assert(auto previous = *this); memory_locations_.join_with(other.memory_locations_); taint_.join_with(other.taint_); position_.join_with(other.position_); last_parameter_load_.join_with(other.last_parameter_load_); mt_expensive_assert(previous.leq(*this) && other.leq(*this)); } void AnalysisEnvironment::widen_with(const AnalysisEnvironment& other) { mt_if_expensive_assert(auto previous = *this); memory_locations_.widen_with(other.memory_locations_); taint_.widen_with(other.taint_); position_.widen_with(other.position_); last_parameter_load_.widen_with(other.last_parameter_load_); mt_expensive_assert(previous.leq(*this) && other.leq(*this)); } void AnalysisEnvironment::meet_with(const AnalysisEnvironment& other) { memory_locations_.meet_with(other.memory_locations_); taint_.meet_with(other.taint_); position_.meet_with(other.position_); last_parameter_load_.meet_with(other.last_parameter_load_); } void AnalysisEnvironment::narrow_with(const AnalysisEnvironment& other) { memory_locations_.narrow_with(other.memory_locations_); taint_.narrow_with(other.taint_); position_.narrow_with(other.position_); last_parameter_load_.narrow_with(other.last_parameter_load_); } void AnalysisEnvironment::assign( Register register_id, MemoryLocation* memory_location) { memory_locations_.set(register_id, MemoryLocationsDomain{memory_location}); } void AnalysisEnvironment::assign( Register register_id, MemoryLocationsDomain memory_locations) { mt_assert(!memory_locations.is_top()); memory_locations_.set(register_id, memory_locations); } MemoryLocationsDomain AnalysisEnvironment::memory_locations( Register register_id) const { MemoryLocationsDomain memory_locations = memory_locations_.get(register_id); if (!memory_locations.is_value()) { // Return an empty set instead of top or bottom. memory_locations = {}; } return memory_locations; } MemoryLocationsDomain AnalysisEnvironment::memory_locations( Register register_id, const DexString* field) const { MemoryLocationsDomain memory_locations = memory_locations_.get(register_id); if (!memory_locations.is_value()) { memory_locations = {}; } MemoryLocationsDomain fields; for (auto* memory_location : memory_locations.elements()) { fields.add(memory_location->make_field(field)); } return fields; } TaintTree AnalysisEnvironment::read(MemoryLocation* memory_location) const { return taint_.get(memory_location->root()) .read(memory_location->path(), propagate_artificial_sources); } TaintTree AnalysisEnvironment::read( MemoryLocation* memory_location, const Path& path) const { Path full_path = memory_location->path(); full_path.extend(path); return taint_.get(memory_location->root()) .read(full_path, propagate_artificial_sources); } TaintTree AnalysisEnvironment::read( const MemoryLocationsDomain& memory_locations) const { if (!memory_locations.is_value()) { return TaintTree::bottom(); } TaintTree taint; for (auto* memory_location : memory_locations.elements()) { taint.join_with(read(memory_location)); } return taint; } TaintTree AnalysisEnvironment::read(Register register_id) const { return read(memory_locations_.get(register_id)); } TaintTree AnalysisEnvironment::read(Register register_id, const Path& path) const { MemoryLocationsDomain memory_locations = memory_locations_.get(register_id); if (!memory_locations.is_value()) { return TaintTree::bottom(); } TaintTree taint; for (auto* memory_location : memory_locations.elements()) { taint.join_with(read(memory_location, path)); } return taint; } void AnalysisEnvironment::write( MemoryLocation* memory_location, TaintTree taint, UpdateKind kind) { taint_.update(memory_location->root(), [&](const TaintTree& tree) { auto copy = tree; copy.write(memory_location->path(), std::move(taint), kind); return copy; }); } void AnalysisEnvironment::write( MemoryLocation* memory_location, const Path& path, TaintTree taint, UpdateKind kind) { Path full_path = memory_location->path(); full_path.extend(path); taint_.update(memory_location->root(), [&](const TaintTree& tree) { auto copy = tree; copy.write(full_path, std::move(taint), kind); return copy; }); } void AnalysisEnvironment::write( MemoryLocation* memory_location, const Path& path, Taint taint, UpdateKind kind) { Path full_path = memory_location->path(); full_path.extend(path); taint_.update(memory_location->root(), [&](const TaintTree& tree) { auto copy = tree; copy.write(full_path, std::move(taint), kind); return copy; }); } void AnalysisEnvironment::write( Register register_id, TaintTree taint, UpdateKind kind) { MemoryLocationsDomain memory_locations = memory_locations_.get(register_id); if (!memory_locations.is_value()) { return; } if (memory_locations.size() > 1) { // In practice, only one of the memory location is affected, so we must // treat this as a weak update, even if a strong update was requested. kind = UpdateKind::Weak; } for (auto* memory_location : memory_locations.elements()) { write(memory_location, taint, kind); } } void AnalysisEnvironment::write( Register register_id, const Path& path, TaintTree taint, UpdateKind kind) { MemoryLocationsDomain memory_locations = memory_locations_.get(register_id); if (!memory_locations.is_value()) { return; } if (memory_locations.size() > 1) { // In practice, only one of the memory location is affected, so we must // treat this as a weak update, even if a strong update was requested. kind = UpdateKind::Weak; } for (auto* memory_location : memory_locations.elements()) { write(memory_location, path, taint, kind); } } void AnalysisEnvironment::write( Register register_id, const Path& path, Taint taint, UpdateKind kind) { MemoryLocationsDomain memory_locations = memory_locations_.get(register_id); if (!memory_locations.is_value()) { return; } if (memory_locations.size() > 1) { // In practice, only one of the memory location is affected, so we must // treat this as a weak update, even if a strong update was requested. kind = UpdateKind::Weak; } for (auto* memory_location : memory_locations.elements()) { write(memory_location, path, taint, kind); } } DexPosition* AnalysisEnvironment::last_position() const { return get_optional_value_or(position_.get_constant(), nullptr); } void AnalysisEnvironment::set_last_position(DexPosition* position) { position_ = DexPositionDomain(position); } const LastParameterLoadDomain& AnalysisEnvironment::last_parameter_loaded() const { return last_parameter_load_; } void AnalysisEnvironment::increment_last_parameter_loaded() { if (last_parameter_load_.is_value()) { last_parameter_load_ = LastParameterLoadDomain(*last_parameter_load_.get_constant() + 1); } } std::ostream& operator<<( std::ostream& out, const AnalysisEnvironment& environment) { return out << "(memory_locations=" << environment.memory_locations_ << ", taint=" << environment.taint_ << ", position=" << environment.position_ << ", last_parameter_load=" << environment.last_parameter_load_ << ")"; } std::ostream& operator<<( std::ostream& out, const MemoryLocationsPartition& memory_locations) { if (memory_locations.is_bottom()) { return out << "_|_"; } else if (memory_locations.is_top()) { return out << "T"; } else { out << "MemoryLocationsPartition("; for (const auto& entry : memory_locations.bindings()) { out << "\n Register(" << entry.first << ") -> " << entry.second; } return out << "\n)"; } } std::ostream& operator<<( std::ostream& out, const TaintAbstractPartition& taint) { if (taint.is_bottom()) { return out << "_|_"; } else if (taint.is_top()) { return out << "T"; } else { out << "TaintAbstractPartition("; for (const auto& entry : taint.bindings()) { out << "\n " << show(entry.first) << " -> " << entry.second; } return out << "\n)"; } } } // namespace marianatrench