<commit_before>/*
 * Copyright (c) 2016 <NAME>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

// This file was contributed by <NAME> for the ISSTA specific analysis.
// It's not officially part of Jitana.

#ifndef JITANA_RESOURCE_SHARING_HPP
#define JITANA_RESOURCE_SHARING_HPP

#include <iostream>
#include <vector>
#include <sstream>
#include <unordered_map>

#include "jitana/jitana.hpp"
#include "jitana/analysis_graph/resource_sharing_graph.hpp"

#include <boost/graph/graphviz.hpp>
#include <boost/range/adaptors.hpp>

namespace jitana {
    static std::vector<std::string> app_names; // TODO: remove.

    struct r_hdl {
        uint8_t id;
        std::string name;
    };

    inline void add_resource_graph_edges_explicit(
            virtual_machine& vm, std::vector<std::pair<r_hdl, r_hdl>>& sos_sis)
    {
        // I have fixed most of the logical errors. But there are some
        // fundamental problems from the original code design that are severely
        // affecting the accuracy:
        //
        // 1. It assumes that everyone uses Intent.setClassName(String, String),
        //    and ignore setClass(Context, Class<?>), setClassName(Context,
        //    String), setComponent(ComponentName), etc. This means that we are
        //    not detecting all explicit intents.
        //
        // 2. It assumes that the class name is generated using const-string
        //    instruction within the same class. It can be generated dynamically
        //    or created in other methods. I see only about a half of the
        //    explicit intents that were found by searching for the invoke
        //    instructions calling Intent.setClassName(String, String) use
        //    const-string in a same method, so we are missing the targets of
        //    the other half.

        jvm_method_hdl intent_mh = {{0, "Landroid/content/Intent;"},
                                    "setClassName(Ljava/lang/String;Ljava/"
                                    "lang/String;)Landroid/content/Intent;"};
        auto intent_mv = vm.find_method(intent_mh, true);

        const auto& mg = vm.methods();
        bool sink_set_flag = false;
        for (const auto& v : boost::make_iterator_range(vertices(mg))) {
            const auto& ig = mg[v].insns;
            r_hdl r_so;
            r_hdl r_si;
            register_idx sink_reg_idx;

            for (const auto& iv : boost::adaptors::reverse(vertices(ig))) {
                const auto& insn = ig[iv].insn;

                if (sink_set_flag) {
                    const auto* cs_insn = get<insn_const_string>(&insn);
                    if (cs_insn && cs_insn->regs[0] == sink_reg_idx) {
                        sink_set_flag = false;

                        r_si.name = "L";
                        r_si.name += cs_insn->const_val;
                        r_si.name += ";";
                        std::replace(begin(r_si.name), end(r_si.name), '.',
                                     '/');
                        r_si.id = -1;
                        sos_sis.emplace_back(r_so, r_si);
                    }
                }

                if (const auto* invoke_insn = get<insn_invoke>(&insn)) {
                    auto imm_mv = vm.find_method(invoke_insn->const_val, true);
                    if (imm_mv && *imm_mv == intent_mv) {
                        r_so.name = mg[v].jvm_hdl.unique_name;
                        r_so.id = mg[v].class_hdl.file_hdl.loader_hdl.idx;
                        sink_set_flag = true;
                        sink_reg_idx = invoke_insn->regs[2];
                    }
                }
            }

            sink_set_flag = false;
        }

        const auto& cg = vm.classes();
        for (const auto& v : boost::make_iterator_range(vertices(cg))) {
            for (auto& so_si : sos_sis) {
                if (cg[v].jvm_hdl.descriptor == so_si.second.name) {
                    so_si.second.id = cg[v].hdl.file_hdl.loader_hdl.idx;
                }
            }
        }
    }

    inline void add_resource_graph_edges_implicit(
            virtual_machine& vm,
            std::vector<std::pair<class_loader_hdl, std::string>>&
                    all_source_intents)
    {
        // I have fixed most of the logical errors. But there are some
        // fundamental problems from the original code design that are severely
        // affecting the accuracy. The problem is very similar to the explicit
        // version, so read the comment in them.

        jvm_method_hdl intent_mh
                = {{0, "Landroid/content/Intent;"},
                   "setAction(Ljava/lang/String;)Landroid/content/Intent;"};
        auto intent_mv = vm.find_method(intent_mh, true);

        const auto& mg = vm.methods();
        bool intent_set_flag = false;
        register_idx sink_reg_intent_idx = register_idx::idx_unknown;
        for (const auto& v : boost::make_iterator_range(vertices(mg))) {
            const auto& ig = mg[v].insns;
            for (const auto& iv : boost::adaptors::reverse(vertices(ig))) {
                const auto& insn = ig[iv].insn;

                if (intent_set_flag) {
                    const auto* cs_insn = get<insn_const_string>(&insn);
                    if (cs_insn && cs_insn->regs[0] == sink_reg_intent_idx) {
                        intent_set_flag = false;
                        all_source_intents.emplace_back(
                                mg[v].class_hdl.file_hdl.loader_hdl.idx,
                                cs_insn->const_val);
                    }
                }

                if (const auto* invoke_insn = get<insn_invoke>(&insn)) {
                    auto imm = vm.find_method(invoke_insn->const_val, true);
                    if (imm && *imm == intent_mv) {
                        intent_set_flag = true;
                        sink_reg_intent_idx = invoke_insn->regs[1];
                    }
                }
            }
        }
    }

    inline void write_graphviz_resource_sharing_graph_explicit(
            std::ostream& os, std::vector<std::pair<r_hdl, r_hdl>>& sos_sis,
            resource_sharing_graph& rsg)
    {
        for (const auto& so_si : sos_sis) {
            if (so_si.first.id != 255 && so_si.second.id != 255) {
                rsg_edge_property eprop;
                eprop.kind = rsg_edge_property::explicit_intent;
                eprop.description = so_si.second.name;
                add_edge(so_si.first.id, so_si.second.id, eprop, rsg);
            }
        }
<commit_msg>IAC: use data-flow result instead of guessing defs<commit_after>/*
 * Copyright (c) 2016 <NAME>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

// This file was contributed by Shak<NAME> for the ISSTA specific analysis.
// It's not officially part of Jitana.

#ifndef JITANA_RESOURCE_SHARING_HPP
#define JITANA_RESOURCE_SHARING_HPP

#include <iostream>
#include <vector>
#include <sstream>
#include <unordered_map>

#include "jitana/jitana.hpp"
#include "jitana/analysis_graph/resource_sharing_graph.hpp"

#include <boost/graph/graphviz.hpp>
#include <boost/range/adaptors.hpp>

namespace jitana {
    static std::vector<std::string> app_names; // TODO: remove.

    struct r_hdl {
        uint8_t id;
        std::string name;
    };

    inline void add_resource_graph_edges_explicit(
            virtual_machine& vm, std::vector<std::pair<r_hdl, r_hdl>>& sos_sis)
    {
        // I have fixed most of the logical errors. But there are some
        // fundamental problems from the original code design that are severely
        // affecting the accuracy:
        //
        // 1. It assumes that everyone uses Intent.setClassName(String, String),
        //    and ignore setClass(Context, Class<?>), setClassName(Context,
        //    String), setComponent(ComponentName), etc. This means that we are
        //    not detecting all explicit intents.
        //
        // 2. It assumes that the class name is generated using const-string
        //    instruction within the same class. It can be generated dynamically
        //    or created in other methods. I see only about a half of the
        //    explicit intents that were found by searching for the invoke
        //    instructions calling Intent.setClassName(String, String) use
        //    const-string in a same method, so we are missing the targets of
        //    the other half.

        jvm_method_hdl intent_mh = {{0, "Landroid/content/Intent;"},
                                    "setClassName(Ljava/lang/String;Ljava/"
                                    "lang/String;)Landroid/content/Intent;"};
        auto intent_mv = vm.find_method(intent_mh, true);

        const auto& mg = vm.methods();
        for (const auto& mv : boost::make_iterator_range(vertices(mg))) {
            const auto& ig = mg[mv].insns;
            for (const auto& iv : boost::make_iterator_range(vertices(ig))) {
                const auto* invoke_insn = get<insn_invoke>(&ig[iv].insn);
                if (!invoke_insn) {
                    continue;
                }

                auto target_mv = vm.find_method(invoke_insn->const_val, true);
                if (!target_mv || *target_mv != intent_mv) {
                    continue;
                }
                auto class_name_reg = invoke_insn->regs[2];

                for (const auto& e :
                     boost::make_iterator_range(in_edges(iv, ig))) {
                    using boost::type_erasure::any_cast;
                    using df_edge_prop_t = insn_data_flow_edge_property;
                    const auto* de = any_cast<const df_edge_prop_t*>(&ig[e]);
                    if (!de || de->reg != class_name_reg) {
                        continue;
                    }

                    const auto& source_insn = ig[source(e, ig)].insn;
                    const auto* cs_insn = get<insn_const_string>(&source_insn);
                    if (!cs_insn) {
                        continue;
                    }

                    r_hdl r_so;
                    r_so.name = mg[mv].jvm_hdl.unique_name;
                    r_so.id = mg[mv].class_hdl.file_hdl.loader_hdl.idx;

                    r_hdl r_si;
                    r_si.name = "L";
                    r_si.name += cs_insn->const_val;
                    r_si.name += ";";
                    std::replace(begin(r_si.name), end(r_si.name), '.', '/');
                    r_si.id = -1;

                    sos_sis.emplace_back(r_so, r_si);
                }
            }
        }

        const auto& cg = vm.classes();
        for (const auto& v : boost::make_iterator_range(vertices(cg))) {
            for (auto& so_si : sos_sis) {
                if (cg[v].jvm_hdl.descriptor == so_si.second.name) {
                    so_si.second.id = cg[v].hdl.file_hdl.loader_hdl.idx;
                }
            }
        }
    }

    inline void add_resource_graph_edges_implicit(
            virtual_machine& vm,
            std::vector<std::pair<class_loader_hdl, std::string>>&
                    all_source_intents)
    {
        // I have fixed most of the logical errors. But there are some
        // fundamental problems from the original code design that are severely
        // affecting the accuracy. The problem is very similar to the explicit
        // version, so read the comment in them.

        jvm_method_hdl intent_mh
                = {{0, "Landroid/content/Intent;"},
                   "setAction(Ljava/lang/String;)Landroid/content/Intent;"};
        auto intent_mv = vm.find_method(intent_mh, true);

        const auto& mg = vm.methods();
        for (const auto& mv : boost::make_iterator_range(vertices(mg))) {
            const auto& ig = mg[mv].insns;

            for (const auto& iv : boost::make_iterator_range(vertices(ig))) {
                const auto* invoke_insn = get<insn_invoke>(&ig[iv].insn);
                if (!invoke_insn) {
                    continue;
                }

                auto target_mv = vm.find_method(invoke_insn->const_val, true);
                if (!target_mv || *target_mv != intent_mv) {
                    continue;
                }
                auto action_string_reg = invoke_insn->regs[1];

                for (const auto& e :
                     boost::make_iterator_range(in_edges(iv, ig))) {
                    using boost::type_erasure::any_cast;
                    using df_edge_prop_t = insn_data_flow_edge_property;
                    const auto* de = any_cast<const df_edge_prop_t*>(&ig[e]);
                    if (!de || de->reg != action_string_reg) {
                        continue;
                    }

                    const auto& source_insn = ig[source(e, ig)].insn;
                    const auto* cs_insn = get<insn_const_string>(&source_insn);
                    if (!cs_insn) {
                        continue;
                    }

                    all_source_intents.emplace_back(
                            mg[mv].class_hdl.file_hdl.loader_hdl.idx,
                            cs_insn->const_val);
                }
            }
        }
    }

    inline void write_graphviz_resource_sharing_graph_explicit(
            std::ostream& os, std::vector<std::pair<r_hdl, r_hdl>>& sos_sis,
            resource_sharing_graph& rsg)
    {
        for (const auto& so_si : sos_sis) {
            if (so_si.first.id != 255 && so_si.second.id != 255) {
                rsg_edge_property eprop;
                eprop.kind = rsg_edge_property::explicit_intent;
                eprop.description = so_si.second.name;
                add_edge(so_si.first.id, so_si.second.id, eprop, rsg);
            }
        }

{'fraction_non_alphanumeric': 0.09611547159771185, 'fraction_numerical': 0.0025276040973792736, 'mean_word_length': 1.7622634576520302, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4322738', 'n_tokens_mistral': 4443, 'n_tokens_neox': 4243, 'n_words': 1114}