source/TaintV1.cpp (226 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/JsonValidation.h>
#include <mariana-trench/TaintV1.h>
namespace marianatrench {
TaintV1::TaintV1(std::initializer_list<Frame> frames) {
for (const auto& frame : frames) {
add(frame);
}
}
TaintV1::TaintV1(std::initializer_list<FrameSet> frames_set) {
for (const auto& frames : frames_set) {
add(frames);
}
}
void TaintV1::add(const Frame& frame) {
set_.add(FrameSet{frame});
}
void TaintV1::add(const FrameSet& frames) {
set_.add(frames);
}
bool TaintV1::leq(const TaintV1& other) const {
return set_.leq(other.set_);
}
bool TaintV1::equals(const TaintV1& other) const {
return set_.equals(other.set_);
}
void TaintV1::join_with(const TaintV1& other) {
set_.join_with(other.set_);
}
void TaintV1::widen_with(const TaintV1& other) {
set_.widen_with(other.set_);
}
void TaintV1::meet_with(const TaintV1& other) {
set_.meet_with(other.set_);
}
void TaintV1::narrow_with(const TaintV1& other) {
set_.narrow_with(other.set_);
}
void TaintV1::difference_with(const TaintV1& other) {
set_.difference_with(other.set_);
}
void TaintV1::add_inferred_features(const FeatureMayAlwaysSet& features) {
if (features.empty()) {
return;
}
map([&features](FrameSet& frames) {
frames.add_inferred_features(features);
});
}
void TaintV1::add_local_position(const Position* position) {
map([position](FrameSet& frames) { frames.add_local_position(position); });
}
void TaintV1::set_local_positions(const LocalPositionSet& positions) {
map([&positions](FrameSet& frames) {
frames.set_local_positions(positions);
});
}
void TaintV1::add_inferred_features_and_local_position(
const FeatureMayAlwaysSet& features,
const Position* MT_NULLABLE position) {
if (features.empty() && position == nullptr) {
return;
}
map([&features, position](FrameSet& frames) {
frames.add_inferred_features_and_local_position(features, position);
});
}
TaintV1 TaintV1::propagate(
const Method* caller,
const Method* callee,
const AccessPath& callee_port,
const Position* call_position,
int maximum_source_sink_distance,
const FeatureMayAlwaysSet& extra_features,
Context& context,
const std::vector<const DexType * MT_NULLABLE>& source_register_types,
const std::vector<std::optional<std::string>>& source_constant_arguments)
const {
TaintV1 result;
for (const auto& frames : set_) {
auto propagated = frames.propagate(
caller,
callee,
callee_port,
call_position,
maximum_source_sink_distance,
context,
source_register_types,
source_constant_arguments);
if (propagated.is_bottom()) {
continue;
}
propagated.add_inferred_features(extra_features);
result.add(propagated);
}
return result;
}
TaintV1 TaintV1::attach_position(const Position* position) const {
TaintV1 result;
for (const auto& frames : set_) {
result.add(frames.attach_position(position));
}
return result;
}
TaintV1 TaintV1::transform_kind_with_features(
const std::function<std::vector<const Kind*>(const Kind*)>& transform_kind,
const std::function<FeatureMayAlwaysSet(const Kind*)>& add_features) const {
TaintV1 new_taint;
for (const auto& frame_set : set_) {
const auto* old_kind = frame_set.kind();
auto new_kinds = transform_kind(old_kind);
if (new_kinds.empty()) {
continue;
} else if (new_kinds.size() == 1 && new_kinds.front() == old_kind) {
new_taint.add(frame_set); // no transformation
} else {
for (const auto* new_kind : new_kinds) {
// Even if new_kind == old_kind for some new_kind, perform map_frame_set
// because a transformation occurred.
auto new_frame_set = frame_set.with_kind(new_kind);
new_frame_set.add_inferred_features(add_features(new_kind));
new_taint.add(new_frame_set);
}
}
}
return new_taint;
}
TaintV1 TaintV1::from_json(const Json::Value& value, Context& context) {
TaintV1 taint;
for (const auto& frame_value : JsonValidation::null_or_array(value)) {
taint.add(Frame::from_json(frame_value, context));
}
return taint;
}
Json::Value TaintV1::to_json() const {
auto taint = Json::Value(Json::arrayValue);
for (const auto& frames : set_) {
for (const auto& frame : frames) {
taint.append(frame.to_json());
}
}
return taint;
}
std::ostream& operator<<(std::ostream& out, const TaintV1& taint) {
return out << taint.set_;
}
void TaintV1::append_callee_port(
Path::Element path_element,
const std::function<bool(const Kind*)>& filter) {
map([&](FrameSet& frames) {
if (filter(frames.kind())) {
frames.map([path_element](Frame& frame) {
frame.callee_port_append(path_element);
});
}
});
}
void TaintV1::update_non_leaf_positions(
const std::function<
const Position*(const Method*, const AccessPath&, const Position*)>&
new_call_position,
const std::function<LocalPositionSet(const LocalPositionSet&)>&
new_local_positions) {
map([&](FrameSet& frames) {
auto new_frames = FrameSet::bottom();
for (const auto& frame : frames) {
if (frame.is_leaf()) {
new_frames.add(frame);
} else {
auto new_frame = Frame(
frame.kind(),
frame.callee_port(),
frame.callee(),
frame.field_callee(),
new_call_position(
frame.callee(), frame.callee_port(), frame.call_position()),
frame.distance(),
frame.origins(),
frame.field_origins(),
frame.inferred_features(),
frame.locally_inferred_features(),
frame.user_features(),
frame.via_type_of_ports(),
frame.via_value_of_ports(),
new_local_positions(frame.local_positions()),
frame.canonical_names());
new_frames.add(new_frame);
}
}
frames = std::move(new_frames);
});
}
void TaintV1::filter_invalid_frames(
const std::function<bool(const Method*, const AccessPath&, const Kind*)>&
is_valid) {
map([&](FrameSet& frames) {
frames.filter([&](const Frame& frame) {
return is_valid(frame.callee(), frame.callee_port(), frame.kind());
});
});
}
bool TaintV1::contains_kind(const Kind* kind) const {
return std::any_of(begin(), end(), [&](const FrameSet& frames) {
return frames.kind() == kind;
});
}
std::unordered_map<const Kind*, TaintV1> TaintV1::partition_by_kind() const {
// This could also call the generic partition_by_kind<T>(map_kind).
// Sticking with a custom implementation because this is very slightly more
// optimal (does not need to check if kind already exists in the result),
// gets called rather frequently, and is quite simple.
std::unordered_map<const Kind*, TaintV1> result;
for (const auto& frame_set : *this) {
result.emplace(frame_set.kind(), TaintV1{frame_set});
}
return result;
}
FeatureMayAlwaysSet TaintV1::features_joined() const {
auto features = FeatureMayAlwaysSet::bottom();
for (const auto& frame_set : *this) {
for (const auto& frame : frame_set) {
features.join_with(frame.features());
}
}
return features;
}
void TaintV1::map(const std::function<void(FrameSet&)>& f) {
set_.map(f);
}
void TaintV1::filter(const std::function<bool(const FrameSet&)>& predicate) {
set_.filter(predicate);
}
} // namespace marianatrench