From b124c7e61173889a815d44bbb63122026ae30815 Mon Sep 17 00:00:00 2001 From: facebook-github-bot Date: Fri, 15 Mar 2024 08:39:48 -0700 Subject: [PATCH] Initial commit fbshipit-source-id: 3e921833c4e81585177d42e0c12d21cad57d6e19 --- .buckconfig | 28 + .buckroot | 13 + .github/CONTRIBUTING.md | 35 + .github/workflows/ci.yml | 37 + .gitignore | 3 + .gitmodules | 9 + BUCK | 151 ++ CODE_OF_CONDUCT.md | 80 + LICENSE | 202 +++ README.md | 88 + examples/example_gen_server.erl | 80 + examples/example_main.erl | 63 + examples/simple_example.erl | 24 + finer_taint/README.md | 343 ++++ finer_taint/include/non_lineage_modules.hrl | 24 + finer_taint/priv/finer_taint.cfg | 20 + finer_taint/priv/instrument_ignorelist.cfg | 20 + finer_taint/priv/models.cfg | 23 + finer_taint/src/abstract_machine_proclet.erl | 206 +++ .../src/abstract_machine_proclet_sup.erl | 82 + finer_taint/src/abstract_machine_util.erl | 613 +++++++ finer_taint/src/ct_finer_taint.erl | 30 + finer_taint/src/finer_taint.erl | 405 +++++ finer_taint/src/finer_taint_compiler.erl | 1139 ++++++++++++ finer_taint/src/models/modeled_erlang.erl | 169 ++ .../src/models/modeled_power_shell.erl | 31 + .../src/models/modeled_taint_lists.erl | 60 + finer_taint/src/models/modeled_taint_maps.erl | 298 ++++ finer_taint/src/online_finer_taint.erl | 46 + finer_taint/src/online_finer_taint_sup.erl | 65 + finer_taint/src/parallel_abstract_machine.erl | 79 + finer_taint/src/parallel_finer_taint.erl | 48 + finer_taint/src/run_finer_taint_escript.erl | 261 +++ finer_taint/src/taint_abstract_machine.erl | 1311 ++++++++++++++ finer_taint/src/taint_gatherer.erl | 169 ++ .../test/abstract_machine_proclet_SUITE.erl | 112 ++ .../test/abstract_machine_util_SUITE.erl | 196 ++ finer_taint/test/capture_out.erl | 46 + finer_taint/test/finer_taint_SUITE.erl | 735 ++++++++ .../basic_main_analysis_instr | 119 ++ .../bitstring_constructed_main_analysis_instr | 36 + .../bitstring_main_analysis_instr | 22 + .../finer_taint_SUITE_data/bitstrings.erl | 40 + .../calling_convention_main_analysis_instr | 119 ++ .../cartesian_main_analysis_instr | 189 ++ .../finer_taint_SUITE_data/case_clauses.erl | 84 + .../case_in_function_args_main_analysis_instr | 60 + .../case_main_analysis_instr | 58 + .../extract_pattern_analysis_instr | 62 + .../finer_taint_SUITE_data/function_calls.erl | 100 ++ .../i13n_to_not_i13n_to_i13n.erl | 37 + ...3n_to_not_i13n_to_i13n_main_analysis_instr | 49 + .../if_clause_analysis_instr | 18 + ..._constructed_extracted_main_analysis_instr | 32 + .../joining_model_main_analysis_instr | 27 + .../lambda_closures_main_analysis_instr | 73 + .../lambdas_main_analysis_instr | 150 ++ .../lineage_main_analysis_instr | 56 + .../list_comprehension.erl | 37 + .../lists_main_analysis_instr | 77 + .../local_if_clause_analysis_instr | 27 + .../macro_duplicate_main_analysis_instr | 26 + .../main_analysis_instr | 24 + .../finer_taint_SUITE_data/map_examples.erl | 78 + .../map_keys_main_analysis_instr | 42 + .../map_module_main_analysis_instr | 236 +++ .../map_update_main_analysis_instr | 47 + .../map_values_main_analysis_instr | 39 + .../maybe_expr_main_analysis_instr | 80 + .../modeled_functions.erl | 40 + .../modeled_functions_main_analysis_instr | 47 + .../nested_calls_main_analysis_instr | 51 + .../nested_case_with_call_main_analysis_instr | 32 + .../definetly_not_modeled.erl | 19 + .../not_instrumented/not_instrumented.erl | 19 + .../one_clause_analysis_instr | 41 + .../opaque_map_main_analysis_instr | 24 + .../operators_in_pattern_main_analysis_instr | 36 + .../finer_taint_SUITE_data/pattern_to_var.erl | 94 + .../pattern_to_var_analysis_instr | 41 + .../process_dict_main_analysis_instr | 321 ++++ .../finer_taint_SUITE_data/ps_example.erl | 62 + .../ps_exp_if_clause_analysis_instr | 32 + .../finer_taint_SUITE_data/ps_external.erl | 26 + .../ps_if_clause_analysis_instr | 60 + .../ps_local_if_clause_analysis_instr | 60 + .../record_main_analysis_instr | 89 + .../recursion_main_analysis_instr | 478 +++++ .../safe_shortcircuit_analysis_instr | 20 + .../set_element_main_analysis_instr | 53 + .../shortcircuiting.erl | 37 + .../finer_taint_SUITE_data/simple_example.erl | 56 + .../string_plusplus_pattern_analysis_instr | 18 + .../try_after_main_analysis_instr | 18 + .../test/finer_taint_SUITE_data/try_catch.erl | 122 ++ .../try_catch_crs_main_analysis_instr | 56 + .../try_catch_define_main_analysis_instr | 33 + .../try_catch_main2_analysis_instr | 31 + .../try_catch_nested_main_analysis_instr | 67 + .../try_main_analysis_instr | 109 ++ .../unsafe_shortcircuit_analysis_instr | 56 + finer_taint/test/parallel_taint_SUITE.erl | 362 ++++ .../parallel_taint_SUITE_data/edge_annot.erl | 44 + ...annotations_msg_analaysis_instr-2546091552 | 165 ++ .../example_gen_server.erl | 89 + .../hibernate_analaysis_instr-4240823527 | 64 + .../hibernate_analaysis_instr-441 | 73 + .../lineage_example_gen_server | 671 +++++++ ...strumented_send_analaysis_instr-4157287981 | 87 + .../not_instrumented_send_analaysis_instr-431 | 40 + .../scatter_gather.erl | 94 + ..._taint_transfer_analaysis_instr-3665323745 | 149 ++ .../spawn_taint_transfer_analaysis_instr-421 | 41 + .../spawn_taint_transfer_analaysis_instr-422 | 30 + .../spawn_taint_transfer_analaysis_instr-423 | 30 + ...fer_via_capture_analaysis_instr-2553365084 | 55 + ...t_transfer_via_capture_analaysis_instr-421 | 16 + .../parallel_taint_SUITE_data/taint_spawn.erl | 57 + ...test_gen_server_analaysis_instr-2935128038 | 960 ++++++++++ .../test_gen_server_analaysis_instr-521 | 1569 +++++++++++++++++ ...gen_server_init_analaysis_instr-3311790007 | 454 +++++ .../test_gen_server_init_analaysis_instr-521 | 955 ++++++++++ .../parallel_taint_SUITE_data/two_pids.erl | 83 + .../two_pids_analaysis_instr-2315115876 | 105 ++ .../two_pids_analaysis_instr-421 | 40 + .../two_pids_analaysis_instr-422 | 40 + finer_taint/test/taint_gatherer_SUITE.erl | 80 + mode/finer_taint | 1 + mode/online_finer_taint | 1 + prelude | 1 + taint_server/README.md | 14 + taint_server/include/taint_server.hrl | 17 + taint_server/src/abstract_machine_server.erl | 113 ++ taint_server/src/taint_message_passer.erl | 70 + taint_server/src/taint_server.app.src | 29 + taint_server/src/taint_server_app.erl | 33 + taint_server/src/taint_server_sup.erl | 74 + taint_server/test/message_passer_SUITE.erl | 149 ++ taint_server/test/taint_server_SUITE.erl | 80 + third-party/BUCK | 42 + third-party/jsone | 1 + third-party/power_shell | 1 + toolchains/.buckroot | 14 + toolchains/BUCK | 98 + toolchains/local/erl | 19 + toolchains/local/erlc | 19 + toolchains/local/escript | 19 + 147 files changed, 18784 insertions(+) create mode 100644 .buckconfig create mode 100644 .buckroot create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 BUCK create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 examples/example_gen_server.erl create mode 100644 examples/example_main.erl create mode 100644 examples/simple_example.erl create mode 100644 finer_taint/README.md create mode 100644 finer_taint/include/non_lineage_modules.hrl create mode 100644 finer_taint/priv/finer_taint.cfg create mode 100644 finer_taint/priv/instrument_ignorelist.cfg create mode 100644 finer_taint/priv/models.cfg create mode 100644 finer_taint/src/abstract_machine_proclet.erl create mode 100644 finer_taint/src/abstract_machine_proclet_sup.erl create mode 100644 finer_taint/src/abstract_machine_util.erl create mode 100644 finer_taint/src/ct_finer_taint.erl create mode 100644 finer_taint/src/finer_taint.erl create mode 100644 finer_taint/src/finer_taint_compiler.erl create mode 100644 finer_taint/src/models/modeled_erlang.erl create mode 100644 finer_taint/src/models/modeled_power_shell.erl create mode 100644 finer_taint/src/models/modeled_taint_lists.erl create mode 100644 finer_taint/src/models/modeled_taint_maps.erl create mode 100644 finer_taint/src/online_finer_taint.erl create mode 100644 finer_taint/src/online_finer_taint_sup.erl create mode 100644 finer_taint/src/parallel_abstract_machine.erl create mode 100644 finer_taint/src/parallel_finer_taint.erl create mode 100644 finer_taint/src/run_finer_taint_escript.erl create mode 100644 finer_taint/src/taint_abstract_machine.erl create mode 100644 finer_taint/src/taint_gatherer.erl create mode 100644 finer_taint/test/abstract_machine_proclet_SUITE.erl create mode 100644 finer_taint/test/abstract_machine_util_SUITE.erl create mode 100644 finer_taint/test/capture_out.erl create mode 100644 finer_taint/test/finer_taint_SUITE.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/basic_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/bitstring_constructed_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/bitstring_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/bitstrings.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/calling_convention_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/cartesian_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/case_clauses.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/case_in_function_args_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/case_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/extract_pattern_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/function_calls.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/if_clause_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/integer_constructed_extracted_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/joining_model_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/lambda_closures_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/lambdas_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/lineage_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/list_comprehension.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/lists_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/local_if_clause_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/macro_duplicate_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/map_examples.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/map_keys_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/map_module_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/map_update_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/map_values_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/maybe_expr_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/modeled_functions.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/modeled_functions_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/nested_calls_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/nested_case_with_call_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/not_instrumented/definetly_not_modeled.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/not_instrumented/not_instrumented.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/one_clause_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/opaque_map_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/operators_in_pattern_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/pattern_to_var.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/pattern_to_var_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/process_dict_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/ps_example.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/ps_exp_if_clause_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/ps_external.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/ps_if_clause_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/ps_local_if_clause_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/record_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/recursion_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/safe_shortcircuit_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/set_element_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/shortcircuiting.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/simple_example.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/string_plusplus_pattern_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/try_after_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/try_catch.erl create mode 100644 finer_taint/test/finer_taint_SUITE_data/try_catch_crs_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/try_catch_define_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/try_catch_main2_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/try_catch_nested_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/try_main_analysis_instr create mode 100644 finer_taint/test/finer_taint_SUITE_data/unsafe_shortcircuit_analysis_instr create mode 100644 finer_taint/test/parallel_taint_SUITE.erl create mode 100644 finer_taint/test/parallel_taint_SUITE_data/edge_annot.erl create mode 100644 finer_taint/test/parallel_taint_SUITE_data/edge_annotations_msg_analaysis_instr-2546091552 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/example_gen_server.erl create mode 100644 finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-4240823527 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-441 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/lineage_example_gen_server create mode 100644 finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-4157287981 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-431 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/scatter_gather.erl create mode 100644 finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-3665323745 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-421 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-422 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-423 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-2553365084 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-421 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/taint_spawn.erl create mode 100644 finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-2935128038 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-521 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-3311790007 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-521 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/two_pids.erl create mode 100644 finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-2315115876 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-421 create mode 100644 finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-422 create mode 100644 finer_taint/test/taint_gatherer_SUITE.erl create mode 100644 mode/finer_taint create mode 100644 mode/online_finer_taint create mode 160000 prelude create mode 100644 taint_server/README.md create mode 100644 taint_server/include/taint_server.hrl create mode 100644 taint_server/src/abstract_machine_server.erl create mode 100644 taint_server/src/taint_message_passer.erl create mode 100644 taint_server/src/taint_server.app.src create mode 100644 taint_server/src/taint_server_app.erl create mode 100644 taint_server/src/taint_server_sup.erl create mode 100644 taint_server/test/message_passer_SUITE.erl create mode 100644 taint_server/test/taint_server_SUITE.erl create mode 100644 third-party/BUCK create mode 160000 third-party/jsone create mode 160000 third-party/power_shell create mode 100644 toolchains/.buckroot create mode 100644 toolchains/BUCK create mode 100755 toolchains/local/erl create mode 100755 toolchains/local/erlc create mode 100755 toolchains/local/escript diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 0000000..0e72294 --- /dev/null +++ b/.buckconfig @@ -0,0 +1,28 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[repositories] +root = . +prelude = prelude +toolchains = toolchains +none = none + +[repository_aliases] +config = prelude +fbcode = none +fbsource = none +buck = none + +[parser] +target_platform_detector_spec = target:root//...->prelude//platforms:default diff --git a/.buckroot b/.buckroot new file mode 100644 index 0000000..5a1c1e3 --- /dev/null +++ b/.buckroot @@ -0,0 +1,13 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..1a5e9a9 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing to erlang taint analysis +We want to make contributing to this project as easy and transparent as +possible. + +To get a brief overview of the codebase, we recommend reading the +[finer_taint/README](../finer_taint/README.md) + + +## Pull Requests +We actively welcome your pull requests. + +1. Fork the repo and create your branch from `main`. +2. Ensure tests pass +3. Make sure the code type checks with eqwalizer +4. If you haven't already, complete the Contributor License Agreement ("CLA"). +5. In the PR explain what's the purpose of this change and how have you tested it + +## Contributor License Agreement ("CLA") +In order to accept your pull request, we need you to submit a CLA. You only need +to do this once to work on any of Meta's open source projects. + +Complete your CLA here: + +## Issues +We use GitHub issues to track public bugs. Please ensure your description is +clear and has sufficient instructions to be able to reproduce the issue. + + +Meta has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + +## License +By contributing to erlang taint analysis, you agree that your contributions will be licensed +under the LICENSE file in the root directory of this source tree. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e4710cf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI +on: + push: + branches: + - '*' + pull_request: + branches: + - main +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-22.04 + otp-version: 26 + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: 'true' + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{matrix.otp-version}} + - uses: dtolnay/install-buck2@latest + with: + prelude-submodule: prelude + - name: Build + run: buck2 build //... + - name: Test + run: buck2 test //... + - name: Run simple example + run: buck2 run @//mode/online_finer_taint :examples simple + - name: Run gen_server example + run: buck2 run @//mode/online_finer_taint :examples a_string + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30a72c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/buck-out +*.swp +buck2 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..45bb18b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "prelude"] + path = prelude + url = https://github.com/facebook/buck2-prelude.git +[submodule "third-party/jsone"] + path = third-party/jsone + url = https://github.com/sile/jsone.git +[submodule "third-party/power_shell"] + path = third-party/power_shell + url = https://github.com/WhatsApp/power_shell diff --git a/BUCK b/BUCK new file mode 100644 index 0000000..1dc330a --- /dev/null +++ b/BUCK @@ -0,0 +1,151 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License". +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load( + "@prelude//erlang:erlang_toolchain.bzl", + "erlang_parse_transform", +) + +export_file(name = "taint_models", src = "finer_taint/priv/models.cfg") + +erlang_application( + name = "finer_taint", + srcs = glob( + [ + "finer_taint/src/*.erl", + "finer_taint/src/*.hrl", + "finer_taint/src/models/*.erl", + ], + ), + includes = glob(["finer_taint/include/*.hrl"]), + applications = [ + "kernel", + "stdlib", + "compiler", + "crypto", + "syntax_tools", + ":taint_server", + "//third-party:jsone", + "//third-party:power_shell", + ], + extra_includes = [], + resources = [":taint_models"], + version = "0.1.0", +) + +erlang_parse_transform( + name = "finer_taint_compiler", + src = "finer_taint/src/finer_taint_compiler.erl", + extra_files = [ + "finer_taint/priv/finer_taint.cfg", + "finer_taint/priv/instrument_ignorelist.cfg", + ], + visibility = ["PUBLIC"], +) + +erlang_escript( + name = "script", + main_module = "run_finer_taint_escript", + script_name = "run_finer_taint", + deps = [ + ":finer_taint", + ], + include_priv = True, + emu_args = [ + "+sbtu", + "+A1", + "+JPperf", + "true", + ], +) + +erlang_tests( + suites = ["finer_taint/test/finer_taint_SUITE.erl"], + deps = [":finer_taint_unittest_deps"], + contacts = ["whatsapp_code_analysis"], + labels = ["unit"], +) + +erlang_tests( + suites = [ + "finer_taint/test/parallel_taint_SUITE.erl", + "finer_taint/test/abstract_machine_util_SUITE.erl", + "finer_taint/test/taint_gatherer_SUITE.erl", + "finer_taint/test/abstract_machine_proclet_SUITE.erl", + ], + deps = [":finer_taint_unittest_deps", ":finer_taint_SUITE"], + contacts = ["whatsapp_code_analysis"], + labels = ["unit"], +) + +erlang_application( + name = "finer_taint_unittest_deps", + srcs = ["finer_taint/test/capture_out.erl"], + applications = [ + ":finer_taint", + "stdlib", + "common_test", + ], + labels = ["test_application", "unit_test_deps"], +) + +erlang_application( + name = "taint_server", + srcs = glob(["taint_server/src/*.erl", "taint_server/src/*.hrl"]), + includes = glob(["taint_server/include/*.hrl"]), + applications = [ + "kernel", + "stdlib", + ], + app_src = "taint_server/src/taint_server.app.src", + version = "0.1.0", + visibility = ["PUBLIC"], +) + +erlang_tests( + suites = glob(["taint_server/test/*_SUITE.erl"]), + deps = [ + "stdlib", + "common_test", + ":taint_server", + ], + contacts = ["whatsapp_code_analysis"], + labels = ["unit"], +) + +erlang_application( + name = "all_examples", + srcs = glob( + [ + "examples/*.erl", + ], + ), + applications = [":finer_taint", ":taint_server"], + visibility = ["PUBLIC"], + version = "0.1.0", +) + +erlang_escript( + name = "examples", + main_module = "example_main", + script_name = "example_main", + deps = [":all_examples"], + include_priv = True, + emu_args = [ + "+sbtu", + "+A1", + "+JPperf", + "true", + ], +) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3232ed6 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,80 @@ +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic +address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a +professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when there is a +reasonable belief that an individual's behavior may have a negative impact on +the project or its community. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..57bc88a --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1fcdc1 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# A taint analysis for Erlang + +This repo contains a taint analysis for Erlang. It does not contain an +off-the-shelf integration to use it. The analysis is setup to run on the +examples, from which an inspiration for integration can be gained. For example +the analysis could be run as part of running test by setting up hooks before +and after running tests. + +To read the basic concepts behind the analysis check [finer_taint/README](./finer_taint/README.md). + +## Getting started + +This project is built with [Buck2](https://github.com/facebook/buck2). +Please [install Buck2](https://github.com/facebook/buck2?tab=readme-ov-file#installing-buck2) first. + +Run the simple example + +``` +$ buck2 run @//mode/online_finer_taint :examples simple +... +Done gathering +Dataflows found: [{leak,"simple_example.erl:23", + [{step,"simple_example.erl:23"}, + {source,"simple_example.erl:22"}]}] +``` + +The printed list, tells us of a dataflow from `simple_example.erl` line 22 to line 23. Check the [simple_example.erl](./examples/simple_example.erl) to confirm the dataflow manually. + + + +## How to run examples + +We provide two ways of running the examples: + +1. `@//mode/online_finer_taint` where the analysis is run alongside the program under test +2. `@//mode/finer_taint` where the analysis writes instructions to a file, that needs to be post-processed to get the results + + +To run the more complicated gen_server_example in the online mode, try: + +``` +$ buck2 run @mode/online_finer_taint :examples some_string +... +{leak,"example_gen_server.erl:41", + [{step,"example_gen_server.erl:41"}, + {source,"example_gen_server.erl:40"}]}] +``` + + + +To run the simple example in the second mode, run the commands below. This might be useful should you want to manually +inspect the instructions for the abstract machine. + +``` +# Clean up files that might be left over from previous runs +rm /tmp/default_instr_prefix-* + +# Run the program under test with the taint mode, which will compile the target with the parse transform +./buck2 run @//mode/finer_taint :examples non-online-mode simple + +# Extract the analysis results +./buck2 run :script -- run /tmp/default_instr* -pprint -print +Running on ["/tmp/default_instr_prefix-441874"] +[<0.110.0>] executing instructions file "/tmp/default_instr_prefix-441874" +Currently have 0 leaks +Setting up new stack at "simple_example.erl:21" +Done running abstract machine +Got 1 leaks from <0.110.0> +Terminating abstract machine <0.110.0> after 13405 reductions with {shutdown, +done_processing} +[{leak,"simple_example.erl:23", + [{step,"simple_example.erl:23"},{source,"simple_example.erl:22"}]}] +``` + + +## How to run via test + +Another way to try the analysis works is by running the test suite and inspecting the outputs. + +For example: + +``` +buck2 test :finer_taint_SUITE +``` + +## License + +erlang taint analysis is [Apache licensed](./LICENSE). diff --git a/examples/example_gen_server.erl b/examples/example_gen_server.erl new file mode 100644 index 0000000..d1b7d76 --- /dev/null +++ b/examples/example_gen_server.erl @@ -0,0 +1,80 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(example_gen_server). +-behaviour(gen_server). + +-export([ + gen_server_main/1 +]). +-export([ + start_link/0, + start_link/2, + store_value/2, + pop/1, + stop/1, + init/1, + handle_call/3, + handle_cast/2 +]). + + +id_function(X) -> X. +other_id_function(X) -> X. + +gen_server_main(String) -> + io:format("Started ~p ~n", [ok]), + {ok, Pid} = example_gen_server:start_link(), + try + TaintedVal = finer_taint:source(String), + finer_taint:sink(TaintedVal), + finer_taint:sink(example_gen_server:store_value(Pid, TaintedVal)), + finer_taint:sink(example_gen_server:store_value(Pid, id_function(43))), + finer_taint:sink(other_id_function(example_gen_server:pop(Pid))), + PoppedValue = example_gen_server:pop(Pid), + finer_taint:sink(PoppedValue), + io:format("Gen server returned ~p~n", [PoppedValue]) + after + example_gen_server:stop(Pid) + end. + + +%% ======= GEN SERVER IMPL ============= + +start_link() -> + gen_server:start_link(?MODULE, [], []). + +start_link(TaintedVal, NotTaintedVal) -> + gen_server:start_link(?MODULE, [TaintedVal, NotTaintedVal], []). + +store_value(Pid, Value) -> + gen_server:call(Pid, {store, Value}). + +pop(Pid) -> + gen_server:call(Pid, pop). + +stop(Pid) -> + gen_server:call(Pid, terminate). + +init([]) -> {ok, []}. + +handle_call({store, Value}, _From, State) -> + {reply, Value, [Value | State]}; +handle_call(pop, _From, [Head | Tail]) -> + {reply, Head, Tail}; +handle_call(terminate, _From, State) -> + {stop, normal, ok, State}. + +handle_cast(_, State) -> + {noreply, State}. diff --git a/examples/example_main.erl b/examples/example_main.erl new file mode 100644 index 0000000..585cf01 --- /dev/null +++ b/examples/example_main.erl @@ -0,0 +1,63 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(example_main). + +-export([ + main/1 +]). + +% This module is the runner of the examples. It sets up the dynamic component of the analysis +% and runs the examples. Note that this file is excluded from being instrumented + + +main(["non-online-mode" | Args]) -> + finer_taint_compiler:instrument_known_stdlibs([{finer_taint_module, parallel_finer_taint}]), + {ok, _} = application:ensure_all_started(taint_server), + + main_impl(Args), + application:stop(taint_server); +main(Args) -> + % logger:set_primary_config(level, info), %Uncommenting this and setting tracing => true + % Will show the execution of the abstract machine (very verbose) + {ok, SupPid} = online_finer_taint_sup:start_link( + #{ lineage_mode => false, tracing => false } + ), + application:set_env(taint_server, instructions_stream_prefix, "/dev/null"), + {ok, _} = application:ensure_all_started(taint_server), + finer_taint_compiler:instrument_known_stdlibs([{finer_taint_module, online_finer_taint}]), + + main_impl(Args), + + io:format("Processing analysis results~n"), + true = is_process_alive(SupPid), + % Stop all proclets, thus telling them they won't get any new instructions + abstract_machine_proclet_sup:stop_all_proclets(), + io:format("Initiated stopping proclets, gathering (20s timeout) ~n"), + Dataflows = taint_gatherer:get_gathered_leaks(taint_gatherer, 20000, [notapid]), + + io:format("Dataflows found: ~p~n", [Dataflows]), + Output = taint_abstract_machine:map_leaks_to_leaks(Dataflows), + io:format("Done gathering~n"), + io:format("Dataflows found: ~p~n", [Output]). + + +main_impl(["simple"]) -> + simple_example:simple_example_main(); +main_impl([Arg]) -> + example_gen_server:gen_server_main(Arg); +main_impl(Args) -> + % Preamble to setup taint analysis runtime component + io:format("Unknown arguments ~p running simple example", [Args]), + main_impl(["simple"]). diff --git a/examples/simple_example.erl b/examples/simple_example.erl new file mode 100644 index 0000000..d0e94a9 --- /dev/null +++ b/examples/simple_example.erl @@ -0,0 +1,24 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(simple_example). + +-export([ + simple_example_main/0 +]). + +simple_example_main() -> + TaintedVal = finer_taint:source("test"), + finer_taint:sink(TaintedVal), + io:format("Done!~n"). diff --git a/finer_taint/README.md b/finer_taint/README.md new file mode 100644 index 0000000..310bb87 --- /dev/null +++ b/finer_taint/README.md @@ -0,0 +1,343 @@ + +# Instrumentation for finer-taint analysis + +This analysis is capable of tracking taint precisely through data transformations +and is able to provide lineage data. + +## Short guide to the code + +[finer\_taint\_compiler.erl](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/src/finer_taint_compiler.erl) +is a parse transform the inserts instrumentation that emits abstract machine +instructions. The instrumentation calls into [finer\_taint.erl](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/src/finer_taint.erl) +that performs the actual emitting of instructions. + +The [taint\_abstract\_machine.erl](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/src/taint_abstract_machine.erl) +implements the abstract machine that executes the emitted instructions to produce analysis results. + +``` +buck2 run :script +``` + +Is an escript that provides a nicer interface to run the abstract machine on the emitted instructions. + +[finer\_taint\_SUITE.erl](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/test/finer_taint_SUITE.erl) +is a test suite showing how analysis works on simple (non message passing related) erlang constructs. It can be useful +to see how the analysis behaves on some easily degistable code. For example running [simple\_example.erl](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/test/finer_taint_SUITE_data/simple_example.erl?lines=1) +produces [these instructions](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/test/finer_taint_SUITE_data/one_clause_analysis_instr). + + + +## Technical Details + + +The analysis is based on [a +paper](https://people.eecs.berkeley.edu/~ksen/papers/jtaint.pdf), where the +idea is to shadow the real execution by an execution of an abstract-machine +operating on taint values. The analysis is performed in three steps: + +1. Instrument the program under test with instrumentation that emits instructions (via a side channel) for the abstract machine. +2. Run the program under test. This results in a trace of the execution in the form of a sequence of instructions for the abstract machine. +3. Execute the abstract machine instructions on the abstract machine to get the taint analysis result. + + +Intuitively, each value in the program execution has an associated taint value +in the abstract-machine execution. The taint value has one of two values: +tainted or not-tainted. The goal of instructions emitted by the instrumentation +during execution of the program under test is to keep a relation between +program and abstract machine executions. The two executions are essentially +synchronise, with the abstract machine execution significantly simplified and +only concerned about taint values. + +This analysis has two good properties: + +* The instrumentation doesn’t have expensive lookups, it just emits instructions. This means the instrumented program should only have a constant overhead due to instrumentation. +* It doesn’t rely on equality to determine if a value is tainted or not. This is useful for Erlang, because we cannot distinguish values that have the same representation, but one is tainted while the other is not. + +Next, we first describe the instructions for the abstract machine, then we show +how we instrument the program and finally we show how the abstract machine is +run to produce the taint analysis result. + +## Abstract machine + +The abstract machine is a stack machine operating on taint values. It has a +stack of taint values and a variable store associating variable names to taint +values. + +``` +type taint_value(): notaint | {taint, history()} +``` + +A `taint_value` can either represent a value not tainted, or a tainted value with some history of where the value has been. +For example a `{taint, ["sourcel.erl:2", "source.erl:1"]}` would indicate a tainted value was part of computation on line 1 in `source.erl` followed by some computation at line 2. + +We are going to represent the abstract machine as a list representing the stack +and a map representing the variable store ( `[], {}`). So an abstract machine +`[notaint, {taint, ...}], {X: notaint}` has `notaint` value on top of the stack +followed by a `taint` value. There is only variable X in the variable store +having a `notaint` value. + +A subset of instructions for the abstract machine: + +* `push(TaintValue)`: push the TaintValue to the stack +* `get(VarName)`: get the taint value of VarName and push it to the stack +* `store(VarName)`: pop a value of the stack and store it in VarName +* `apply(Module:Function/Arity)`: pop Arity arguments of the stack and push the result of applying MFA to the stack +* `sink()`: pop value of the stack and report data-flow if it’s tainted + +## Instrumentation + +The instrumentation is currently implemented as an AST transformation. The +instrumentation inserts appropriate abstract machine instruction emitters in +the program under test. Consider two simple assignments + +``` +AString = "123 is a good start of a string", +PhoneNumber = finer_taint:source("123@secret.net"), +``` + +We look at each expression in [a +clause](https://www.erlang.org/doc/apps/erts/absform.html#clauses) individually +and insert instrumentation between them. The above example is a clause with 2 +match expressions. For a match expression we insert instrumentation for the +right hand side (RHS) before the match. In the `AString` case the RHS is a +constant string, which cannot be tainted. Therefore we insert an emitter for +`push(notaint)` instruction. For `PhoneNumber` the RHS is a source so we add an +emitter for `push({taint, ["l2"]})`, where `l2` indicates source line 2. In +both cases the left hand side (LHS) is a simple variable pattern, so we can +store the values directly. In the case of pattern matching we would need to +emit additional instructions, but that’s outside the scope of this note. The +final instrumented snippet looks like: + +``` +push(notaint), +AString = "123 is a good start of a string", +store("AString"), +push({taint, ["exampler.erl:2"]}), +PhoneNumber = finer_taint:source("123@secret.net"), +store("PhoneNumber") +``` + +As another example, consider a function call `X = string:slice(PhoneNumber, 0, +3)`, which is also a match expression, where the RHS is a function call. +Therefore we first traverse the arguments of the function call. Constant +integers can’t be tainted so we need to emit `push(notaint)`, `PhoneNumber` is +a variable, which we need to look-up via `get("PhoneNumber")` instruction. +Finally we emit the `apply(string:slice/3)` after the actual function call +returns. Finally we store the result into `X`. + + +``` +push(notaint), +push(notaint), +get("PhoneNumber"), +X = string:slice(PhoneNumber, 0, 3), +apply(string:slice/3), +store("X")` +``` + +In this case `string:slice/3` is “non-instrumented” function, which means we +haven’t instrumented the body of the function. This means we have to model the +behaviour of `string:slice/3`. The analysis supports writing specific models +for functions, but we also have a default model that considers the return value +of a function tainted if any of the function arguments are tainted. + +For the cases where the body of the called functions is instrumented, the +`apply` instruction is essentially a noop as the calling convention specifies +that the arguments are popped off the stack and the return value of the +function is on top of the stack. + + +## Running the abstract machine + +Consider we have instrumented and ran the following program: + +``` +AString = "123 is ...", +Number = source("123@..."), +X = slice(Number, 0, 3), +Y = slice(AString, 0, 3), +finer_taint:sink(Y), +finer_taint:sink(X). +``` + +Below we show the trace of emitted instructions. The original executed lines +are shown as comments (prefixed with `%`). The comments at the right side of +the emitted instructions represent the state of the abstract machine after +executing that instruction. + +``` +% AString = "123 is ...", +push(notaint), % [notaint], {} +store("AString"), % [], {AString: notaint} + +% Number = source("123@..."), +push({taint, ["l2"]}), % [{taint, ["l2"]}], {AString: notaint} +store("Number"), % [], {AString: notaint, Number: {taint, ["l2"]}} + +% X = slice(Number, 0, 3), +push(notaint), % [notaint], {AString: notaint, Number: {taint, ["l2"]}} +push(notaint), % [notaint,notaint], {AString: notaint, Number: {taint, ["l2"]}} +get("Number"), % [{taint, ["l2"]}, notaint,notaint], {AString: notaint, Number: {taint, ["l2"]}} +apply(string:slice/3), % [{taint, ["l3", "l2"]}], {AString: notaint, Number: {taint, ["l2"]}} +store("X"), % [], {X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} + +% Y = slice(AString, 0, 3), +push(notaint), % [notaint], {X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} +push(notaint), % [notaint,notaint], {X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} +get("AString"), % [notaint,notaint,notaint], {X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} + apply(string:slice/3), %[notaint], {X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} +store("Y"), % [], {Y: notaint, X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} + +% finer_taint:sink(Y), +get("Y"), % [notaint], {Y: notaint, X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} +sink(), % [], {Y: notaint, X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} + +% finer_taint:sink(X). +get("X"), % [{taint, ["l3", "l2"]}], {Y: notaint, X:{taint, ["l3", "l2"]}, AString: notaint, Number: {taint, ["l2"]}} +sink(), %Report data-flow! line2 -> line3 -> line6 +``` + +We start off by pushing `notaint` to top of the stack and storing it into +variable `AString`, resulting in the abstract machine state `[], {AString: +notaint}`. The same is done for storing the tainted value into `Number`. + +Then we move on executing instructions that were emitted during execution of `X += slice(Number, 0, 3)`. We first push all the arguments to the stack, resulting +in abstract machine `[{taint, ["l2"]}, notaint,notaint], {...}`. The +`apply(string:slice/3)` instruction is then executed. Since this is a modelled +function call, it pops 3 arguments of the stack and applies the model for them. +There is one value that is tainted so the result of the function call should +also be tainted according to the default model we use. The resulting taint +value, should also indicate the value passed through line 3, so `l3` is added +to the history and taint value is pushed onto the stack, resulting in `[{taint, +["l3", "l2"]}]` stack. This value is then popped of the stack and stored into +`X` by the store instruction. + +Similar steps are followed for the Y case, except that there are no tainted +values in the arguments of `string:slice`, therefore `Y` is also not tainted. + +Finally X and Y are checked (sunk) for taint. Firstly, taint value of Y is +pushed onto the stack and `sink()` checks whether the top of the stack is +tainted. In this case it is not. Then, the value of X is pushed onto the stack +and `sink()` checks if it’s tainted. In this case the top of the stack is +tainted and therefore a data-flow is reported along with the value and flow of how +it got there. + +## Sources of imprecision + +Sources of imprecision should be documented in this section. It aims to be comprehensive covering all +cases, but it's a work in progress so please add them here if you find any not mentioned here. + + +### Control-flow data-flows + +Finer taint analysis does not consider control flow based data flows. For example in + +``` +is_member(PII, [PII | _] ) -> + true; +is_member(PII, [_, Tail]) -> + is_member(Pii, Tail); +is_member(_, []) -> + false. +``` + +finer taint analysis would not report a dataflow from arguments of `is_member` to the return +value. This is because both return values `true` and `false` are new values created inside the function. + +If this is an issue for a particular function, it can be mitigated, by adding an explicit model +for that function, which propagates the taints of its arguments. + +### Timeouts + +For message passing we have some timeouts for how long to wait for a message. This is to ensure we do +not need the analysis to be 100% to get some results. If a timeout is hit, the message is assumed untainted, +which can introduce a false positive. + +### Binary comprehension + +Binary comprehension are not supported at this time. They are assumed untainted + + +### Sockets + +Sockets are not supported, so they assume the default model. The arguments to the socket functions are likely untainted so +the data read from the socket would be marked as not tainted. + + +### Try catch + +Try/catch handling does not deal with catch clauses that are skipped (because they don't match). Also the catch clause +does not deal with error handling. + + +### binary() + +`binary()` patterns are poorly supported and likely don't work for the general +case. They have only been somewhat tested on bitstrings. This is subject to future work +when it becomes a problem. [See comment](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/src/taint_abstract_machine.erl?lines=555) + + + +## Models + +Wrong or imprecise models impact the results of the analysis. There are three kinds of models: + +* *uninstrumented models*, that is models for functions whose body is not + instrumented by the finer taint analysis. For example arithmetic operators + are modeled this way as we do not instrument the implementation of arithmetic + operators. + [model\_of](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/src/taint_abstract_machine.erl?lines=619) + function is used to model these sort of models. If a function does not have a + specific case in `model_of/2`, we use the default model. The default model + assumes the output of the function is tainted iff any arguments are tainted. +* *special uninstrumented models*, that is models whose body is not + instrumented, but they are not handled as described above. Prime example of + this is `erlang:spawn*` familiy of functions, where taint is propagated in a + special way due to process creation. These are special cased in + finer\_taint\_compiler.erl. +* *instrumented_models*, that is functions that we model by writting a simple + erlang implementation of their body, which is then instrumented with + finer\_taint instrumentation. The most common case of this is NIFs, which we + can't instrument directly, but it is easy to write their code in Erlang and + instrument that, which gives as their preciose behaviour. These models are + located unders `src/models`. Some implementation of functions are swapped by our + instrumentation, which happens in [`intercepted_functions/1`](https://github.com/WhatsApp/erlang_taint_analysis/blob/main/finer_taint/src/finer_taint_compiler.erl?lines=662) + + +### Uninstrumented models + +For *uninstrumented models* we have the following considerations when deciding if they +should propagate taint or not: + +1. Do the function arguments flow into its return value. If they do it's propagate\_taints. +2. Does the function mostly affect control flow (ie. returns a boolean) +2. Does it make the analysis cheaper by killing taint, without compromising the result + + +The first point is the most obivous/easy to justify as it just assumes an +over-approximation. These models could also be dervied from static analysis. +For example for erlang:monitor, it's easy to see that its arguments do not flow +into the return (it returns a random reference), but for string:concat it's +obvious that all arguments form its output. + +For some functions the distinction from 1) technically holds, but is not very +useful. For example with `>` operator, the RHS and LHS technically flow into the +return value, but it's just a boolean for control flow. + +Because finer\_taint doesn't do control flow taint flows, it's okay to assume +boolean operators essentially sanitize the taint values. + +erlang:length is similar, the output of erlang:length is only tainted if you +account for control flow. + +Finally another consideration is performance. Sanitising a taint value (by just +returning notaint), makes the analysis cheaper, because it doesn't have to keep +track of some history any more. + +For example with erlang:function\_exported. There is a dataflow from its +arguments (the module) to list of functions that are exported, but that will +never be interesting to us, so it's just easier/faster to assume it's not +tainted. + + diff --git a/finer_taint/include/non_lineage_modules.hrl b/finer_taint/include/non_lineage_modules.hrl new file mode 100644 index 0000000..4b0a90d --- /dev/null +++ b/finer_taint/include/non_lineage_modules.hrl @@ -0,0 +1,24 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +% Modules listed here are ignored for reporting lineage +-define(BASE_NON_LINEAGE_MODULES, [ + modeled_taint_maps, + modeled_taint_lists, + gen_server, + proc_lib, + gen, + modeled_erlang, + lists +]). diff --git a/finer_taint/priv/finer_taint.cfg b/finer_taint/priv/finer_taint.cfg new file mode 100644 index 0000000..2e6d910 --- /dev/null +++ b/finer_taint/priv/finer_taint.cfg @@ -0,0 +1,20 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +% Add modules that should always be instrumented here +modeled_taint_maps. +modeled_taint_lists. +modeled_erlang. +example_gen_server. +simple_example. diff --git a/finer_taint/priv/instrument_ignorelist.cfg b/finer_taint/priv/instrument_ignorelist.cfg new file mode 100644 index 0000000..d1f4a1c --- /dev/null +++ b/finer_taint/priv/instrument_ignorelist.cfg @@ -0,0 +1,20 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%Add new paths to ignore here +% +% Don't instrument finer_taint (and other util itself) +"finer_taint/". +"taint_server/". +"examples/example_main.erl". diff --git a/finer_taint/priv/models.cfg b/finer_taint/priv/models.cfg new file mode 100644 index 0000000..8404ee0 --- /dev/null +++ b/finer_taint/priv/models.cfg @@ -0,0 +1,23 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +% Taint models for function: +% * propagate -> if any argument is tainted, output will be tainted +% * sanitize -> output is always untainted +% +% Use `_any` for function names to match any function +{{operators, 'div'}, propagate}. +{{operators, '=='}, sanitize}. +{{supervisor, start_child}, sanitize}. + diff --git a/finer_taint/src/abstract_machine_proclet.erl b/finer_taint/src/abstract_machine_proclet.erl new file mode 100644 index 0000000..9c7e911 --- /dev/null +++ b/finer_taint/src/abstract_machine_proclet.erl @@ -0,0 +1,206 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%% @doc +%%% Taint abstract machine proclet is responsible for executing +%%% a single instruction stream (process) on the taint_abstract_machine. +%%% +%%% In essence this module wraps the taint_abstract_machine, such that +%%% multiple instruction streams can be executed in parallel. +%%% +%%% The found leaks are reported to taint_gatherer once this process +%%% stops. +%%% +%%% The process stops either due to a failure in taint_abstract_machine +%%% or when abstract_machine_proclet:stop/1 is called +%%% +%%% Usually new instances of abstract_machine_proclet can be obtained +%%% via abstract_machine_proclet_sup:new_proc() +%%% @end +-module(abstract_machine_proclet). +-compile(warn_missing_spec_all). +-behaviour(gen_server). + +% ======== gen_server / internal ====== + +-export([init/1, handle_cast/2, handle_call/3, terminate/2, handle_info/2]). + +-include_lib("kernel/include/logger.hrl"). + +-record(proclet_state, { + state :: taint_abstract_machine:state(), + % Logs every executed instruction and the state it's executing on + tracing = false :: boolean(), + % In coverage mode taint all values in the module with prefix + coverage_prefix :: string() | false, + % The taint_gatherer process to which we can report leaks to + gatherer :: gen_server:server_ref() +}). + +-type state() :: #proclet_state{}. + +% ======== PUBLIC API ============== +-export([execute_instruction/2, start_link/2, run_instructions_file/2, stop/1]). + +% Evolves the internal state of the taint_abstract_machine by executing +% a single instruction on it. +-spec execute_instruction(gen_server:server_ref(), taint_abstract_machine:instruction()) -> ok. +execute_instruction(Pid, Instruction) -> + gen_server:cast(Pid, {execute_instruction, Instruction}). + +% Evolves the internal state of the taint_abstract_machine by executing +% instructions from InstructionsFileName on it +-spec run_instructions_file(gen_server:server_ref(), string()) -> ok. +run_instructions_file(Pid, InstructionsFileName) -> + gen_server:cast(Pid, {run_instructions_file, InstructionsFileName}). + +% Tells the abstract machine proclet there will be no further instructions, +% The proclet will report leaks and exit +-spec stop(gen_server:server_ref()) -> ok. +stop(Pid) -> + gen_server:cast(Pid, {stop}). + +% Starts a new abstract_machine_proclet. InitStateArgs is passed +% to taint_abstract_machine:init_state/2. If InitStateArgs contains +% a tracing key, that is used to set the tracing flag of abstract_machine_proclet +-spec start_link(map(), gen_server:server_ref()) -> term(). +start_link(InitStateArgs, Gatherer) -> + gen_server:start_link(?MODULE, [Gatherer, InitStateArgs], []). + +% ========== gen_server / internal =============== +% +-spec init(list()) -> {ok, state()}. +init([Gatherer, InitStateArgs]) -> + % We trap the exit to ensure the messages in the queue get processed and not discarded + % when an exit is initiated + process_flag(trap_exit, true), + % This is a very important flag. The proclets can accumulate + % large ammount of messages in the queue, this makes GC of the process + % very expensive. This flag makes GC of the process with a long queue + % cheap at the expense of making receiving messages a bit more expensive. + process_flag(message_queue_data, off_heap), + put(is_abs_proclet, true), + + {ok, #proclet_state{ + state = taint_abstract_machine:init_state(InitStateArgs), + tracing = maps:get(tracing, InitStateArgs, false), + coverage_prefix = maps:get(coverage_prefix, InitStateArgs, false), + gatherer = Gatherer + }}. + +-spec terminate(term(), state()) -> ok. +terminate(Reason, _State) -> + {reductions, Reductions} = erlang:process_info(self(), reductions), + ?LOG_INFO("Terminating abstract machine ~p after ~p reductions with ~p ~n", [self(), Reductions, Reason]), + % Don't call stop_impl here, to avoid double adding leaks + ok. + +-spec stop_impl(state()) -> ok. +stop_impl(#proclet_state{state = AmState, gatherer = Gatherer}) -> + Leaks = taint_abstract_machine:get_leaks_as_map(AmState), + taint_gatherer:add_leaks(Gatherer, Leaks), + ok. + +-spec handle_call(term(), gen_server:from(), state()) -> {reply, ok, state()}. +handle_call(Msg, _From, State) -> + ?LOG_WARNING("abstract_machine_proclet doesn't expect a call, got ~p", [Msg]), + {reply, ok, State}. + +-spec handle_cast( + {execute_instruction, Instruction :: taint_abstract_machine:instruction()}, state() +) -> + {noreply, state()} + | {stop, {shutdown, term()}, state()}. +handle_cast({run_instructions_file, InstructionsFileName}, State) -> + ?LOG_INFO("[~p] executing instructions file ~p~n", [self(), InstructionsFileName]), + case run_instructions_file_impl(InstructionsFileName, State) of + {taint_machine_crash, NewState} -> + stop_impl(State), + {stop, {shutdown, taint_machine_crash}, NewState}; + NewState -> + {noreply, NewState} + end; +handle_cast({stop}, State) -> + stop_impl(State), + {stop, {shutdown, done_processing}, State}; +handle_cast({execute_instruction, Instruction}, State) -> + case execute_instruction_impl(Instruction, State) of + taint_machine_crash -> + stop_impl(State), + {stop, {shutdown, taint_machine_crash}, State}; + NewState -> + {noreply, NewState} + end. + +-spec handle_info({got_key, term(), term()}, state()) -> {noreply, state()}. +handle_info({got_key, MessageId, _}, State) -> + ?LOG_INFO("Got message with id ~p after timeout~n", [MessageId]), + {noreply, State}. + +% Execute an instruction on the internal state. Returns taint_machine_crash +% if there was an error when executing the taint_abstract_machine +-spec execute_instruction_impl(taint_abstract_machine:instruction(), state()) -> + state() | taint_machine_crash. +execute_instruction_impl(Instruction, State) -> + trace(Instruction, State), + try propagate(Instruction, State) of + S -> State#proclet_state{state = S} + catch + {abstract_machine_invalid_state, _Inst, _St} -> + ?LOG_WARNING("~p pid failed on ~p in state ~n", [self(), Instruction]), + taint_machine_crash; + Exception:Reason:StackTrace -> + ?LOG_WARNING("Proclet ~p crashed with ~p ~p~n~p~n", [ + self(), Exception, Reason, StackTrace + ]), + taint_machine_crash + end. + +% Wrapper for taint_abstract_machine propagate +-spec propagate(taint_abstract_machine:instruction(), state()) -> taint_abstract_machine:state(). +propagate(Instruction, #proclet_state{state = AmState, coverage_prefix = CoveragePrefix}) when + is_list(CoveragePrefix) +-> + taint_abstract_machine:propagate_cov(Instruction, AmState, CoveragePrefix); +propagate(Instruction, #proclet_state{state = AmState}) -> + taint_abstract_machine:propagate(Instruction, AmState). + +-spec trace(taint_abstract_machine:instruction(), state()) -> ok. +trace(_, #proclet_state{tracing = false}) -> + ok; +trace(Instruction, #proclet_state{state = AmState, tracing = true}) -> + ?LOG_INFO("[~p] executing ~p on~n~p~n", [self(), Instruction, AmState]), + ok. + +-spec instructions_folder( + taint_abstract_machine:instruction(), + state() | {taint_machine_crash, state()} +) -> state() | {taint_machine_crash, state()}. +instructions_folder(_Instruction, Acc = {taint_machine_crash, _}) -> + Acc; +instructions_folder(Instruction, PropagatedState) -> + case execute_instruction_impl(Instruction, PropagatedState) of + taint_machine_crash -> {taint_machine_crash, PropagatedState}; + State -> State + end. + +-spec run_instructions_file_impl(string(), state()) -> state() | {taint_machine_crash, state()}. +run_instructions_file_impl(InstructionsFileName, State) -> + {ok, Instructions} = file:consult(InstructionsFileName), + lists:foldl( + fun instructions_folder/2, + State, + Instructions + ). diff --git a/finer_taint/src/abstract_machine_proclet_sup.erl b/finer_taint/src/abstract_machine_proclet_sup.erl new file mode 100644 index 0000000..9f130bc --- /dev/null +++ b/finer_taint/src/abstract_machine_proclet_sup.erl @@ -0,0 +1,82 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +%% @doc +%% Supervisor for abstract_machine_proclets +%% +%% Used to create new proclets and to tell them to stop +%% @end + +-module(abstract_machine_proclet_sup). +-compile(warn_missing_spec_all). + +-behaviour(supervisor). + +-export([start_link/1, new_proclet/1, new_proclet/0, stop_all_proclets/0]). + +-export([init/1]). + +-spec start_link(map()) -> supervisor:startlink_ret(). +start_link(AbstractMachineArgs) -> + supervisor:start_link({local, ?MODULE}, ?MODULE, [AbstractMachineArgs]). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +-spec init([map()]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. +init([AbstractMachineArgs]) -> + SupFlags = #{ + strategy => simple_one_for_one, + intensity => 0, + period => 1 + }, + ChildSpecs = [ + #{ + id => abstract_machine_proclet, + start => {abstract_machine_proclet, start_link, [AbstractMachineArgs]}, + % If the proclet dies, the state is lost, no point restarting, the analysis + % of the process is dead anyway + restart => temporary, + % Short shutdown as this supervisor can have a lot of proclets to shutdown + shutdown => 500 + } + ], + {ok, {SupFlags, ChildSpecs}}. + +% Creates a new abstract_machine_proclet, used when +% starting to execute a new instruction stream +-spec new_proclet() -> pid(). +new_proclet() -> + new_proclet(taint_gatherer). +-spec new_proclet(gen_server:server_ref()) -> pid(). +new_proclet(TaintGatherer) -> + case supervisor:start_child(?MODULE, [TaintGatherer]) of + {ok, Pid} when is_pid(Pid) -> Pid + end. + +% Tell all proclets to stop. This method should be called +% before attempting to get leaks from the taint_gatherer +% as leaks will not be reported before proclets terminate. +-spec stop_all_proclets() -> ok. +stop_all_proclets() -> + Children = supervisor:which_children(?MODULE), + [ok = abstract_machine_proclet:stop(Pid) || {_, Pid, _, _} <- Children, is_pid(Pid)], + ok. diff --git a/finer_taint/src/abstract_machine_util.erl b/finer_taint/src/abstract_machine_util.erl new file mode 100644 index 0000000..e9a1713 --- /dev/null +++ b/finer_taint/src/abstract_machine_util.erl @@ -0,0 +1,613 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%% Contains functions that analyze the output of taint_abstract_machine +-module(abstract_machine_util). +-compile(warn_missing_spec_all). + +%% The client API. +-export([ + get_priv_models/0, + get_sources/1, + get_arg_lineage/2, + query_arg_lineage/2, + get_arg_lineage_raw/1, + get_graph/1, + to_dot/1, + print_leaks/1, + to_infer_report/1, + get_dataflows/1, + filter_message_pass/1, + get_covered_inst/1, + graphviz_leaks/2 +]). + +-type nodes_ty() :: #{string() => sink | source | step}. +-type edges_ty() :: [{string(), string()}]. +-type leak_evidence() :: #{ + sink => binary(), + field => binary(), + source => binary(), + trace => binary() +}. +-type infer_report() :: map(). +% The n-th parameter of MFA +-type mfan() :: {mfa(), non_neg_integer()}. + +% Url where files can be found +-define(SOURCE_URL, "https//none.com/files/"). + +-spec history_to_leakeage_evidence(string(), taint_abstract_machine:taint_history()) -> leak_evidence(). +history_to_leakeage_evidence(Sink, History) -> + Source = + case taint_abstract_machine:get_taint_sources(History, []) of + [OneSource] -> + OneSource; + [AnotherSource | Other] -> + io:format( + "History should be linear, expecting only a single source.~n" ++ + "Picking only one source, droping: ~p~n", + [Other] + ), + AnotherSource + end, + + {Field, Location} = + case Source of + {tagged_source, Tag, Loc} -> {Tag, Loc}; + {source, Loc} -> {"unknown", Loc} + end, + #{ + sink => list_to_binary(Sink), + field => list_to_binary(Field), + source => list_to_binary(Location), + trace => list_to_binary(io_lib:format("~p", [History])) + }. + +-spec get_priv_models() -> taint_abstract_machine:models(). +get_priv_models() -> + File = filename:join([code:priv_dir(finer_taint), "taint_models"]), + % Check atleast one element + [H | T] = + case file:consult(File) of + {ok, Terms} -> + Terms; + % Likely an escript run + {error, enotdir} -> + Filename = "finer_taint/priv/taint_models", + {ok, Escript} = escript:extract(escript:script_name(), []), + {archive, Archive} = lists:keyfind(archive, 1, Escript), + {ok, Zip} = zip:zip_open(Archive, [memory]), + {ok, {Filename, ModelsBin}} = zip:zip_get(Filename, Zip), + % Intentionally don't close the zip, because this terminates a process + % which might send an {exit} message to the parent gen_server + % (abstract_machine_proclet) + %ok = zip:zip_close(Zip), + Models = consult_from_string(binary_to_list(ModelsBin)), + Models + end, + maps:from_list([H | T]). + +-spec consult_from_string(string()) -> [{{module(), atom()}, propagate | sanitize}]. +consult_from_string(StringOfTerms) when is_list(StringOfTerms) -> + consult_from_string_impl(erl_scan:tokens([], StringOfTerms, 0)). + +-spec consult_from_string_impl({more, erl_scan:return_cont()} | {done, erl_scan:tokens_result(), string() | eof}) -> + [{{module(), atom()}, propagate | sanitize}]. +consult_from_string_impl({more, _}) -> + []; +consult_from_string_impl({done, Result, LeftOverChars}) -> + case Result of + {ok, Tokens, EndLocation} -> + {ok, Term} = erl_parse:parse_term(Tokens), + [Term | consult_from_string_impl(erl_scan:tokens([], LeftOverChars, EndLocation))]; + {eof, _} -> + [] + end. + +% Prints sinks and sources of leaks passed in +-spec print_leaks(taint_abstract_machine:leaks()) -> binary(). +print_leaks(Leaks) -> + print_leaks(Leaks, []). +-spec print_leaks(taint_abstract_machine:leaks(), list(leak_evidence())) -> binary(). +print_leaks([], Acc) -> + jsone:encode(Acc); +print_leaks([{leak, Sink, History} | Tail], Acc) -> + Histories = linearize_history(History), + Leaks = [history_to_leakeage_evidence(Sink, Hist) || Hist <- Histories], + print_leaks(Tail, Leaks ++ Acc). + +% Write the leaks to /tmp dir in the DOT format, +% If second argument containt pastry, also upload it to pastry. +% +% Example usage: +% abstract_machine_util:graphviz_leaks(Leak, [pastry]). +% abstract_machine_util:graphviz_leaks(Leak, []). +-spec graphviz_leaks(taint_abstract_machine:leaks(), list()) -> ok. +graphviz_leaks([], _) -> + ok; +graphviz_leaks([Leak = {leak, Sink, _History} | Tail], Options) -> + Graph = get_graph(Leak), + DotFilename = "/tmp/leak_" ++ Sink ++ ".dot", + ok = file:write_file(DotFilename, to_dot(Graph)), + io:format("Wrote ~s~n", [DotFilename]), + case lists:member(pastry, Options) of + true -> + io:format("Writing to pastry: "), + Paste = os:cmd("pastry < " ++ DotFilename), + io:format("~s~n", [Paste]); + _ -> + ok + end, + graphviz_leaks(Tail, Options). + +-spec get_dataflows(taint_abstract_machine:taint_history()) -> taint_abstract_machine:dataflow_map(). +get_dataflows(History) -> + Output = get_dataflows(History, [], [], #{}), + Output. +% lists:reverse(CallStack ++ Output). + +% Remove balanced call and return site +-spec get_dataflows( + taint_abstract_machine:taint_history(), + taint_abstract_machine:taint_history(), + taint_abstract_machine:taint_history(), + Dataflows +) -> Dataflows when Dataflows :: taint_abstract_machine:dataflow_map(). +get_dataflows( + [{call_site, {M, F, A}, CallSite1} | Tail], + [{return_site, {M, F, A}, CallSite2} | ReturnStackTail], + Output, + ArgTaints +) when CallSite1 =:= CallSite2; CallSite1 =:= "unknown"; CallSite2 =:= "unknown" -> + get_dataflows(Tail, ReturnStackTail, Output, ArgTaints); +% Push a call site onto the call stack +get_dataflows([H = {call_site, _, _} | Tail], ReturnStack, Output, ArgTaints) -> + get_dataflows(Tail, ReturnStack, [H | Output], ArgTaints); +% Unmatched return site, it's part of our output +get_dataflows([H = {return_site, _, _} | Tail], ReturnStack, Output, ArgTaints) -> + get_dataflows(Tail, [H | ReturnStack], Output, ArgTaints); +get_dataflows([_H = {arg_taint, MFAN} | Tail], ReturnStack, Output, ArgTaints) -> + get_dataflows(Tail, ReturnStack, Output, ArgTaints#{{dataflow_src, MFAN, ReturnStack ++ Output} => ok}); +% Filter out step in history, because they are not interesting for lineage and it keeps +% histories smaller +get_dataflows([_H = {step, _} | Tail], ReturnStack, Output, ArgTaints) -> + get_dataflows(Tail, ReturnStack, Output, ArgTaints); +get_dataflows([H = {blackhole, _} | Tail], ReturnStack, Output, ArgTaints) -> + get_dataflows(Tail, ReturnStack, [H | Output], ArgTaints); +% When Message passing, we go to another process so ReturnStack is reset +get_dataflows([H = {message_pass, _} | Tail], ReturnStack, Output, ArgTaints) -> + get_dataflows(Tail, [], [H | ReturnStack] ++ Output, ArgTaints); +get_dataflows([_H = {joined_history, _Type, Histories}], ReturnStack, Output, ArgTaints) -> + NewArgTaints = [get_dataflows(Hist, ReturnStack, Output, #{}) || Hist <- Histories], + NewArgTaints1 = lists:foldl(fun maps:merge/2, ArgTaints, NewArgTaints), + get_dataflows([], ReturnStack, Output, NewArgTaints1); +%Done, put remaning call stack in Output and return +get_dataflows([], _CallStack, _Output, ArgTaints) -> + ArgTaints. + +-spec get_covered_inst(taint_abstract_machine:taint_history()) -> map(). +get_covered_inst(History) -> + get_covered_inst(History, #{}). + +-spec get_covered_inst(taint_abstract_machine:taint_history(), map()) -> map(). +get_covered_inst([{joined_history, _Type, Histories}], Output) -> + HistoryOutputs = [get_covered_inst(Hist, #{}) || Hist <- Histories], + lists:foldl(fun maps:merge/2, Output, HistoryOutputs); +get_covered_inst([{blackhole, _} | Tail], Output) -> + get_covered_inst(Tail, Output); +get_covered_inst([{Type, _, Loc} | Tail], Output) when is_atom(Type), is_list(Loc) -> + get_covered_inst(Tail, Output#{Loc => ok}); +get_covered_inst([{T, Loc} | Tail], Output) when is_list(Loc), is_atom(T) -> + get_covered_inst(Tail, Output#{Loc => ok}); +get_covered_inst([], Output) -> + Output. + +% Pretty print the annotations. +-spec annotations_impl(taint_abstract_machine:taint_history()) -> list(string()). +annotations_impl([]) -> + []; +annotations_impl([{blackhole, _} | Tail]) -> + ["blackhole" | annotations_impl(Tail)]; +annotations_impl([{step, _} | Tail]) -> + annotations_impl(Tail); +annotations_impl([{call_site, _MFA, Loc} | Tail]) -> + ["call@" ++ Loc | annotations_impl(Tail)]; +annotations_impl([{return_site, _MFA, Loc} | Tail]) -> + ["ret@" ++ Loc | annotations_impl(Tail)]; +annotations_impl([{message_pass, Loc} | Tail]) -> + ["mp@" ++ Loc | annotations_impl(Tail)]. + +% Pretty print all the annotations. +-spec annotations(#{list() => ok}) -> [string()]. +annotations(Map) when is_map(Map) -> + [string:join(annotations_impl(Annot), ";") || Annot <- maps:keys(Map)]. + +% Filters out redudant message passes in a taint history For example +% step1->step2->message_pass->step->message_pass->step3 would become +% step1->step2->message_pass->step3 as the steps between message passing can be +% skipped for the lineage use case. To illustrate that consider an edge A -> B. +% +% +% For lineage we add some annotation to it as to what call/return sites the +% dataflow on edge A -> B passed through. +% +% Assume the annotation is `returnFromFoo, callBar`. +% +% What this annotation means is that our edge A -> B can only connect on the +% left, with edges that have a callFoo annotation (or no annoatation). +% Similarly A -> B can only connect to the right with edges that have +% returnFromBar annotation (or no annotation). +% +% The effect on message passing on this is that it resets the stack. If there +% is an annotation like returnFromFoo, callBar, message-pass the requirement +% that edges connecting on the right, have to returnFromBar is no longer valid, +% because we passed a message so we are in some other process with a different +% stack. +% +% Note that the annotations to the left of the message pass are still useful. +% However if there two message passes, the annotations between them are not +% useful for connecting with any other edges and are therefore not +% interesting and dropped by `filter_message_pass/1` + +-spec filter_message_pass(list()) -> list(). +filter_message_pass(Input) -> + filter_message_pass(Input, [], []). +-spec filter_message_pass(list(), list(), list()) -> list(). +filter_message_pass([{message_pass, _} | T], MaybeAfterFirstMessagePass, []) -> + filter_message_pass(T, [], MaybeAfterFirstMessagePass); +filter_message_pass([{message_pass, _} | T], _MaybeAfterFirstMessagePass, BeforeFirstMessagePass) -> + filter_message_pass(T, [], BeforeFirstMessagePass); +filter_message_pass([H | T], MaybeAfterFirstMessagePass, BeforeFirstMessagePass) -> + filter_message_pass(T, [H | MaybeAfterFirstMessagePass], BeforeFirstMessagePass); +filter_message_pass([], MaybeAfterFirstMessagePass, []) -> + lists:reverse(MaybeAfterFirstMessagePass); +filter_message_pass([], MaybeAfterFirstMessagePass, BeforeFirstMessagePass) -> + lists:reverse( + MaybeAfterFirstMessagePass ++ [{message_pass, "skipped-steps-between-message-passing"}] ++ + BeforeFirstMessagePass + ). + +% Apply filter_message_pass to the Lineage obtained from get_arg_lineage_impl +-spec fold_message_passes(#{tuple() => #{list() => ok}}) -> #{tuple() => #{list() => ok}}. +fold_message_passes(AnnotatedLineage) -> + maps:map( + fun(_, Annotation) -> + Filtered = [filter_message_pass(Annot) || Annot <- maps:keys(Annotation)], + maps:from_keys(Filtered, ok) + end, + AnnotatedLineage + ). + +% Takes a list of leaks() produced in the lineage mode +% and gives all unique edges between function arguments in it +-spec get_arg_lineage(taint_abstract_machine:leaks(), atom()) -> iodata(). +get_arg_lineage(Leaks, OutputFormat) -> + AnnotatedLineage = get_arg_lineage_impl(Leaks, #{}), + FoldedMessagesLineage = fold_message_passes(AnnotatedLineage), + Lineage = maps:keys(FoldedMessagesLineage), + case OutputFormat of + human_readable -> + lists:flatten( + lists:sort( + [ + io_lib:format( + "~p:~p/~p-Arg~p -> ~p:~p/~p-Arg~p~n", + [FromM, FromF, FromA, FromArgN, ToM, ToF, ToA, ToArgN] + ) + || {{{FromM, FromF, FromA}, FromArgN}, {{ToM, ToF, ToA}, ToArgN}} <- Lineage + ] + ) + ); + human_readable_annotated -> + lists:flatten( + lists:sort( + [ + io_lib:format( + "~p:~p/~p-Arg~p -> ~p:~p/~p-Arg~p~n ~s~n", + [ + FromM, + FromF, + FromA, + FromArgN, + ToM, + ToF, + ToA, + ToArgN, + string:join(annotations(maps:get(K, AnnotatedLineage)), "\n ") + ] + ) + || K = {{{FromM, FromF, FromA}, FromArgN}, {{ToM, ToF, ToA}, ToArgN}} <- Lineage + ] + ) + ); + csv -> + [ + "FromM,FromF,FromA,FromArgN,ToM,ToF,ToA,ToArgN,Annot\n" + | [ + [ + io_lib:format("~p,~p,~p,~p,~p,~p,~p,~p,~s~n", [ + FromM, FromF, FromA, FromArgN, ToM, ToF, ToA, ToArgN, Annot + ]) + || Annot <- annotations(maps:get(K, FoldedMessagesLineage)) + ] + || K = {{{FromM, FromF, FromA}, FromArgN}, {{ToM, ToF, ToA}, ToArgN}} <- Lineage + ] + ] + end. + +-spec get_arg_lineage_raw(taint_abstract_machine:leaks()) -> [tuple()]. +get_arg_lineage_raw(Leaks) -> + AnnotatedLineage = get_arg_lineage_impl(Leaks, #{}), + FoldedMessagesLineage = fold_message_passes(AnnotatedLineage), + maps:keys(FoldedMessagesLineage). +-spec get_arg_lineage_impl(taint_abstract_machine:leaks(), Acc) -> Acc when + Acc :: #{{mfan(), mfan()} => map()}. +get_arg_lineage_impl([_L = {arg_leak, {ToMFA, ToArgN, _Loc}, Froms} | Tail], MapAcc) -> + Acc1 = lists:foldl( + fun + HistoryFolder({dataflow_src, {FromMFA, FromArgN}, Annotation}, FoldAcc) -> + Key = {{FromMFA, FromArgN}, {ToMFA, ToArgN}}, + Value = maps:get(Key, FoldAcc, #{}), + NewValue = Value#{Annotation => ok}, + FoldAcc#{Key => NewValue}; + HistoryFolder({arg_taint, {FromMFA, FromArgN}}, FoldAcc) -> + Key = {{FromMFA, FromArgN}, {ToMFA, ToArgN}}, + Value = maps:get(Key, FoldAcc, #{}), + % arg_taint doesn't have an annotation + NewValue = Value#{[] => ok}, + FoldAcc#{Key => NewValue}; + HistoryFolder({step, _}, FoldAcc) -> + FoldAcc; + HistoryFolder({message_pass, _}, FoldAcc) -> + FoldAcc; + HistoryFolder({call_site, _, _}, FoldAcc) -> + FoldAcc; + HistoryFolder({return_site, _, _}, FoldAcc) -> + FoldAcc; + HistoryFolder({joined_history, _, Histories}, FoldAcc) -> + % eqwalizer:ignore The lists case is handeld below, but not captured in types + HistoryFolder(Histories, FoldAcc); + HistoryFolder({blackhole, Sources}, FoldAcc) -> + % eqwalizer:ignore The lists of sources case is handeld below, but not captured in types + HistoryFolder([Sources], FoldAcc); + HistoryFolder(HistoriesList, FoldAcc) when is_list(HistoriesList) -> + EachHistoryFolded = [ + lists:foldl(HistoryFolder, #{}, Hist) + || Hist <- HistoriesList + ], + NewFoldAcc = lists:foldl( + fun(FoldAcclet, Acc) -> + maps:merge_with( + fun(_, Val1, Val2) when is_map(Val1), is_map(Val2) -> + maps:merge_with(fun(_, ok, ok) -> ok end, Val1, Val2) + end, + Acc, + FoldAcclet + ) + end, + FoldAcc, + EachHistoryFolded + ), + NewFoldAcc + end, + MapAcc, + Froms + ), + get_arg_lineage_impl(Tail, Acc1); +get_arg_lineage_impl([], Acc) -> + Acc. + +% Takes a list of leaks() produced in the lineage mode and a query +% in the form of {FromMFA, FromArgN, ToMFA, ToArgN} and returns +% all the paths from FromMFA-ArgN to ToMFA-ArgN contained in the leaks(). +% +% Useful for debugging why the analysis reported an edge between two arguments. +-spec query_arg_lineage(taint_abstract_machine:leaks(), {mfa(), integer(), mfa(), integer()}) -> list(). +query_arg_lineage(Leaks, QueryLineage) -> + query_arg_lineage_impl(Leaks, [], QueryLineage). + +-spec query_arg_lineage_impl(taint_abstract_machine:leaks(), list(), {mfa(), integer(), mfa(), integer()}) -> list(). +query_arg_lineage_impl( + [{arg_leak, {ToMFA, ToArgN, _Loc}, Froms} | Tail], Acc, Query = {FromMFA, FromArgN, ToMFA, ToArgN} +) -> + ThisArgLeakPaths = fun + HistoryFolder([Step = {arg_taint, {FromMFA1, FromArgN1}}, CallSite = {call_site, _, _} | _], PathTo) when + FromMFA1 == FromMFA, FromArgN1 == FromArgN + -> + [Step, CallSite | PathTo]; + HistoryFolder([Step = {dataflow_src, {FromMFA1, FromArgN1}, _} | _], PathTo) when + FromMFA1 == FromMFA, FromArgN1 == FromArgN + -> + [Step | PathTo]; + HistoryFolder([], _) -> + []; + HistoryFolder([Step = {_, _} | Tail1], PathTo) -> + HistoryFolder(Tail1, [Step | PathTo]); + HistoryFolder([Step = {Type, _, _} | Tail1], PathTo) when Type =/= joined_history -> + HistoryFolder(Tail1, [Step | PathTo]); + HistoryFolder([{joined_history, _, Histories}], PathTo) -> + lists:concat(lists:map(fun(Hist) -> HistoryFolder(Hist, PathTo) end, Histories)) + end( + Froms, [{arg_leak, {ToMFA, ToArgN}}] + ), + case ThisArgLeakPaths of + [] -> + query_arg_lineage_impl(Tail, Acc, Query); + _ -> + query_arg_lineage_impl(Tail, [ThisArgLeakPaths | Acc], Query) + end; +query_arg_lineage_impl([_ | Tail], Acc, Query) -> + query_arg_lineage_impl(Tail, Acc, Query); +query_arg_lineage_impl([], Acc, _) -> + Acc. + +% Traverses taint_history to find all the sources +-spec get_sources(taint_abstract_machine:taint_history()) -> [string()]. +get_sources(History) -> + lists:filtermap( + fun + ({source, Loc}) -> {true, Loc}; + ({tagged_source, Tag, Loc}) -> {true, Tag ++ "@" ++ Loc}; + (_) -> false + end, + taint_abstract_machine:get_taint_sources(History, []) + ). +% +% Builds a graph representation of a Leak. Use to_dot/0 to print it in the DOT format. +% This graph is not equivalent to a trace. Namely it can containt cycles. +-spec get_graph({leak, string(), taint_abstract_machine:taint_history()}) -> + {nodes_ty(), edges_ty()}. +get_graph({leak, Sink, History}) -> + {Nodes, Edges} = get_graph_impl(Sink, History, {#{Sink => sink}, []}), + {Nodes, lists:usort(Edges)}. + +-spec get_graph_impl(string(), taint_abstract_machine:taint_history(), Acc) -> Acc when + Acc :: + {nodes_ty(), edges_ty()}. +get_graph_impl(To, [{source, From}], {AccNode, AccEdges}) -> + {AccNode#{From => source}, [{From, To} | AccEdges]}; +get_graph_impl(To, [{step, From} | Tail], {AccNode, AccEdges}) -> + % Don't overwrite source/sink nodes + Type = maps:get(From, AccNode, step), + get_graph_impl(From, Tail, {AccNode#{From => Type}, [{From, To} | AccEdges]}); +get_graph_impl(To, [{joined_history, _Type, Histories}], Acc) -> + lists:foldl(fun(History, FoldAcc) -> get_graph_impl(To, History, FoldAcc) end, Acc, Histories). + +% Given a source_filename.erl: +% this function returns the path to source_filename.erl and the line number +-spec get_file_path(string()) -> {file:filename(), string()} | not_found. +get_file_path(Node) -> + case get({filepath_cache, Node}) of + undefined -> + Return = + case string:split(Node, ":") of + [Filename, Line] -> + case filelib:wildcard("[a-z]*/**/" ++ Filename) of + [Filepath] -> + {Filepath, Line}; + [] -> + not_found; + [Head | Tail] -> + io:format("Ambiguous wildcard head: ~p, tail: ~p, picking head~n", [Head, Tail]), + {Head, Line} + end; + _ -> + not_found + end, + put({filepath_cache, Node}, Return), + Return; + X -> + X + end. + +-spec linearize_history(taint_abstract_machine:taint_history()) -> [taint_abstract_machine:taint_history()]. +linearize_history([]) -> + []; +linearize_history(X = [{tagged_source, _Tag, _Location}]) -> + [X]; +linearize_history(X = [{source, _Location}]) -> + [X]; +linearize_history([Item | Tail]) when + is_map_key(element(1, Item), #{ + step => 1, + call_site => 1, + return_site => 1, + message_pass => 1, + blackhole => 1 + }) +-> + [[Item | H] || H <- linearize_history(Tail)]; +linearize_history([{joined_history, _, Histories}]) -> + lists:append(lists:map(fun linearize_history/1, Histories)). + +-spec to_infer_bug_report(taint_abstract_machine:taint_history(), string()) -> infer_report(). +to_infer_bug_report(History, Sink) -> + [{source, Source} | OtherSteps] = lists:reverse(History), + {Filename, Line} = + case get_file_path(Source) of + Ret = {_Filepath, _Line} -> Ret; + _ -> {Source, "-1"} + end, + Report = #{ + bug_type => <<"TAINT">>, + qualifier => list_to_binary(io_lib:format("Found dataflow from ~s to ~s", [Source, Sink])), + severity => <<"INFO">>, + file => list_to_binary(Filename), + line => list_to_integer(Line), + procedure => <<"unknown">>, + procedure_start_line => list_to_integer(Line), + bug_trace => lists:map(fun to_infer_bug_trace/1, OtherSteps), + key => list_to_binary(io_lib:format("~p->~s", [Source, Sink])) + }, + Report#{hash => base64:encode(crypto:hash(sha256, io_lib:format("~p", [Report])))}. + +-spec to_infer_report(taint_abstract_machine:leaks()) -> binary(). +to_infer_report(Leaks) -> + Reports = to_infer_report(Leaks, []), + jsone:encode(Reports). + +-spec to_infer_report(taint_abstract_machine:leaks(), list(infer_report())) -> list(infer_report()). +to_infer_report([], Acc) -> + Acc; +to_infer_report([{leak, Sink, History} | Tail], Acc) -> + LinearHistories = linearize_history(History), + BugReports = lists:map(fun(Hist) -> to_infer_bug_report(Hist, Sink) end, LinearHistories), + to_infer_report(Tail, BugReports ++ Acc). + +-spec to_infer_bug_trace(taint_abstract_machine:taint_history_point()) -> map(). +to_infer_bug_trace({step, Location}) -> + {Filename, Line} = + case get_file_path(Location) of + not_found -> {Location, "-1"}; + X -> X + end, + #{ + level => 0, + filename => list_to_binary(Filename), + line_number => list_to_integer(Line), + column_number => -1, + description => <<"Taint point">> + }. + +% Returns a DOT representation of the graph obtained via get_graph/1. +-spec to_dot({nodes_ty(), edges_ty()}) -> string(). +to_dot({Nodes, Edges}) -> + F = fun(Node, Type, {Map, NodeDecl}) -> + GraphLabel = string:replace(string:replace(Node, ".erl", ""), ":", ""), + Colour = + case Type of + sink -> ",color=blue"; + source -> ",color=green"; + _ -> "" + end, + Href = + case get_file_path(Node) of + {Filepath, Line} -> + ",href=\"" ++ ?SOURCE_URL ++ Filepath ++ "?lines=" ++ Line ++ + "\",target=\"_blank\""; + _ -> + "" + end, + DotNodeDecl = io_lib:format("~s [label=\"~s\"~s~s];~n", [GraphLabel, Node, Href, Colour]), + {Map#{Node => GraphLabel}, io_lib:format("~s~s", [DotNodeDecl, NodeDecl])} + end, + {NodeToLabel, NodeDecls} = maps:fold(F, {#{}, ""}, Nodes), + + Body = lists:map( + fun({From, To}) -> io_lib:format("~s -> ~s;~n", [maps:get(From, NodeToLabel), maps:get(To, NodeToLabel)]) end, + Edges + ), + lists:flatten(io_lib:format("digraph taintflow {~s~s}", [NodeDecls, Body])). diff --git a/finer_taint/src/ct_finer_taint.erl b/finer_taint/src/ct_finer_taint.erl new file mode 100644 index 0000000..e541fb4 --- /dev/null +++ b/finer_taint/src/ct_finer_taint.erl @@ -0,0 +1,30 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%% Implementation of finer_taint behaviour that writes to stdio +%% Used by tests +-module(ct_finer_taint). + +-compile(warn_missing_spec_all). +-behaviour(finer_taint). + +%% finer_taint callback. +-export([ + write_instruction/1 +]). + +-spec write_instruction(taint_abstract_machine:instruction()) -> ok. +write_instruction(Instruction) -> + io:format("~0p.~n", [Instruction]). diff --git a/finer_taint/src/finer_taint.erl b/finer_taint/src/finer_taint.erl new file mode 100644 index 0000000..d29de5d --- /dev/null +++ b/finer_taint/src/finer_taint.erl @@ -0,0 +1,405 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%% The functions in this file are called by the instrumentation added by the finer_taint_compiler. +%% The main point of this file is that we can change how the instructions are emitted easily +%% by doing in directly in Erlang and not in the abstract forms. +%% +%% The actual writting of the instructions is done by implementing the write_instruction +%% in a behaviour +-module(finer_taint). +-compile(warn_missing_spec_all). + +-callback write_instruction(Instruction :: taint_abstract_machine:instruction()) -> ok. + +%% marker API +-export([ + source/1, source/2, + sink/1 +]). + +%% The emit instruction API. +-export([ + duplicate/2, + send/2, + receive_trace/2, + actual_source/3, + actual_source/4, + actual_sink/2, + push/3, + pop/2, + push_scope/5, + func_ret/3, + call_fun/5, + store_var/3, + get_var/3, + fun_apply/3, + construct_pattern/3, + try_catch/4, + set_element/5, + capture_closure/3, + restore_capture/3, + deconstruct_pattern/3 +]). + +-define(INSTRUMENTATION_LOC, "instrumentation"). +% SPAWN_FUNCTIONS are a map so we can use is_map_key in guards +-define(SPAWN_FUNCTIONS, #{spawn => ok, spawn_link => ok, spawn_monitor => ok, spawn_opt => ok}). + +%% =================== MARKER API ============================ +%% These functions annotate source and sinks and are meant to be +%% placed in the code under analysis by some other method: +%% Either manually or by something like taint_compiler +-spec source(string(), T) -> T. +source(_Tag, X) -> + X. +-spec source(T) -> T. +source(X) -> + X. + +-spec sink(term()) -> ok. +sink(_Sink) -> ok. + +%% =================== EMIT INSTRUCTION API ============================ +%% +%% These functions emit the instructions for the abstract machine. They are +%% the instrumentation inserted by the finer_taint_compiler +%% +%% +%% They take a CallbackModule argument, which is the callback module implementing the +%% write_instruction function. The finer_taint_compiler should emit +%% instructions with appropriate argument for CallbackModule + +-spec actual_source(string(), atom(), term(), T) -> T. +actual_source(Loc, CallbackModule, Tag, X) -> + pop(Loc, CallbackModule), + push(Loc, CallbackModule, {Tag, create_taint_value(Loc)}), + X. +-spec actual_source(string(), atom(), T) -> T. +actual_source(Loc, CallbackModule, X) -> + pop(Loc, CallbackModule), + push(Loc, CallbackModule, create_taint_value(Loc)), + X. + +-spec actual_sink(string(), atom()) -> ok. +actual_sink(Loc, CallbackModule) -> + CallbackModule:write_instruction({sink, {create_taint_value(Loc)}}), + ok. + +-spec push(string(), atom(), string() | {term(), string()}) -> ok. +push(_Loc, CallbackModule, TaintValue) -> + CallbackModule:write_instruction({push, {TaintValue}}), + ok. + +-spec capture_closure(string(), atom(), [string()]) -> ok. +capture_closure(_Loc, CallbackModule, ClosureVars) -> + CallbackModule:write_instruction({capture_closure, {ClosureVars}}), + ok. + +-spec restore_capture(string(), atom(), mfa()) -> ok. +restore_capture(Loc, CallbackModule, {Module, Func, Arity}) -> + CallbackModule:write_instruction({restore_capture, {{Module, Func, Arity}, create_taint_value(Loc)}}), + ok. + +-spec pop(string(), atom()) -> ok. +pop(_Loc, CallbackModule) -> + CallbackModule:write_instruction({pop, {}}), + ok. + +-spec duplicate(string(), atom()) -> ok. +duplicate(_Loc, CallbackModule) -> + CallbackModule:write_instruction({duplicate, {}}), + ok. + +%% This is meant to be called just before a send message operation. +%% It needs to "tag" the message, so that we can match it on the receive end. +%% Uses seq_trace, to do the tagging. +-spec send(string(), atom()) -> ok. +send(Loc, CallbackModule) -> + MessageId = ref_to_list(make_ref()), + CallbackModule:write_instruction({send, {MessageId, create_taint_value(Loc)}}), + seq_trace:set_token(label, MessageId), + ok. + +%% Implements the matching of the sent messages on the receive end. +%% Reads the tag set by send() and resets the seq_trace. +-spec receive_trace(string(), atom()) -> ok. +receive_trace(Loc, CallbackModule) -> + case seq_trace:get_token(label) of + {label, MessageId} -> + seq_trace:set_token([]), + CallbackModule:write_instruction({receive_trace, {MessageId, create_taint_value(Loc)}}); + % Token not set, receiving a message from non instrumented code + [] -> + CallbackModule:write_instruction({receive_trace, {nomsg}}) + end, + ok. + +-spec store_var(string(), atom(), string()) -> ok. +store_var(Loc, CallbackModule, VarName) -> + CallbackModule:write_instruction({store, {VarName, create_taint_value(Loc)}}), + ok. + +-spec get_var(string(), atom(), string()) -> ok. +get_var(Loc, CallbackModule, VarName) -> + CallbackModule:write_instruction({get, {VarName, create_taint_value(Loc)}}), + ok. + +-spec fun_apply(string(), atom(), {atom(), atom(), integer()}) -> ok. +fun_apply(Loc, CallbackModule, MFA = {erlang, Func, _}) when is_map_key(Func, ?SPAWN_FUNCTIONS) -> + % push_scope for spawn* functions sets a token, this function is called + % after spawn returns and must reset the token so the token does not spread further + seq_trace:set_token([]), + CallbackModule:write_instruction({apply, {MFA, create_taint_value(Loc)}}), + ok; +fun_apply(Loc, CallbackModule, MFA) -> + CallbackModule:write_instruction({apply, {MFA, create_taint_value(Loc)}}), + ok. + +-spec push_scope(string(), atom(), atom(), atom(), integer()) -> ok. +push_scope(Loc, CallbackModule, Module, Function, Arity) -> + case seq_trace:get_token(label) of + % Common case, this is a normal function call and not a start of a process + [] -> + CallbackModule:write_instruction({push_scope, {{Module, Function, Arity}, create_taint_value(Loc)}}), + ok; + % Rare case, this is a start of a process, we need to setup the function arguments + % Which should have been passed to us as a message with ProcId + {label, {new_proc, ProcId}} -> + if + % Normaly the ProcPid is a string representation of ref() + % In that case we don't override the taint_pid and let it be randomyl generated + is_list(ProcId) -> + ok; + % If the parent process set next_taint_pid in process dictonary + % It will end up in ProcId so we set the taint_pid here + true -> + put(taint_pid, ProcId) + end, + % Reset the token so it doesn't spread further + seq_trace:set_token([]), + CallbackModule:write_instruction({push_scope, {{Module, Function, Arity}, create_taint_value(Loc)}}), + % We get the function arguments as tuple of {taint_val(Function), taint_val(Args)} + CallbackModule:write_instruction({receive_trace, {ProcId, ?INSTRUMENTATION_LOC}}), + % We deconstruct the tuple to get stack + % | taint_val(Args) | + % | taint_val(Function) | <- Top of the stack + deconstruct_pattern(Loc, CallbackModule, {tuple, 2}), + % We use the taint value of the function to restore the potential capture + % This pushes a scope that is never popped. But in this case that is ok + % because the only time this scope needs to be popped is then the function + % getting spawned returns. At that point the process terminates too, so + % there is no further execution. Therefore we don't have to worry about + % cleaning this scope + CallbackModule:write_instruction({restore_capture, {{Module, Function, Arity}, create_taint_value(Loc)}}), + % We get the function arguments as a list of arguments [Arg1, Arg2, ..., ArgN] + % waiting for us as a message at ProcId + % The list should have Arity elements, so we deconstruct it Arity times + % Cons pattern deconstructs as Head,Tail, so Head is top of the stack + lists:foreach( + fun(_) -> + CallbackModule:write_instruction({deconstruct_pattern, {{cons}, ?INSTRUMENTATION_LOC}}), + %Reverse Head/Tail, so that Tail is top of the stack + CallbackModule:write_instruction({store, {"PushScopeTmpHead", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({store, {"PushScopeTmpTail", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({get, {"PushScopeTmpHead", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({get, {"PushScopeTmpTail", ?INSTRUMENTATION_LOC}}) + end, + lists:seq(1, Arity) + ), + % The stack should now be taint_val(Arg1), taint_val(Arg2), ... ,taint_val(ArgN), notaint + % The notaint either comes from [] or directly from receive_trace if there are no arguments + % We just pop it off + CallbackModule:write_instruction({pop, {}}), + % The function arguments are in the wrong order Arg1 is expected to be on top of the stack + % we reverse Arity elements + StoredVars = lists:map( + fun(Idx) -> + TmpVarName = "PushScopeTmp-" ++ integer_to_list(Idx), + CallbackModule:write_instruction({store, {TmpVarName, ?INSTRUMENTATION_LOC}}), + TmpVarName + end, + lists:seq(1, Arity) + ), + lists:foreach( + fun(TmpVarName) -> + CallbackModule:write_instruction({get, {TmpVarName, ?INSTRUMENTATION_LOC}}) + end, + StoredVars + ); + % {label, Ref} when is_string(Ref) case indicates the process got a message according + % to the seq_trace mechanism. This is not expected at a start of functions, but it could + % happen. For example if a message arrived early. It could also indicate the seq_trace + % passed through uninstrumented code and should not have ended up here. + % Note: For now we ignore this case + {label, Ref} when is_list(Ref) -> + seq_trace:set_token([]), + CallbackModule:write_instruction({push_scope, {{Module, Function, Arity}, create_taint_value(Loc)}}), + ok + end, + ok. + +-spec func_ret(string(), atom(), atom()) -> ok. +func_ret(Loc, CallbackModule, Function) -> + CallbackModule:write_instruction({func_ret, {Function, create_taint_value(Loc)}}), + ok. + +-spec call_fun(string(), atom(), atom(), atom(), integer()) -> ok. +call_fun(Loc, CallbackModule, erlang, Function, Arity) when is_map_key(Function, ?SPAWN_FUNCTIONS) -> + TempVarNames = ["CfnArg1", "CfnArg2", "CfnArg3"], + + ImaginaryArity = + if + % For spawn function with arity 1 or 2, we only have the taint value for Function arguments + % and not the args, so we invent a notaint value for the Args to keep the rest of this logic consistent + Arity == 1 -> + CallbackModule:write_instruction({store, {"Cfn1Arg0", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({push, {notaint}}), + CallbackModule:write_instruction({get, {"Cfn1Arg0", ?INSTRUMENTATION_LOC}}), + 2; + Arity == 2 -> + CallbackModule:write_instruction({store, {"Cfn1Arg0", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({store, {"Cfn1Arg1", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({push, {notaint}}), + CallbackModule:write_instruction({get, {"Cfn1Arg1", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({get, {"Cfn1Arg0", ?INSTRUMENTATION_LOC}}), + 3; + true -> + Arity + end, + % Stack looks like + % | taint_val(ArgArity) | ie. taint_val(Args) + % | taint_val(ArgArity-1) | ie. taint_val(Function) + % | .... | + % | taint_val(Arg1) | <- Top of the stack + % + % + % We want the taint_val of ArgArity and ArgArity-1, + % ArgArity-1 is the `Function` argument, potentially + % containing the lambda capture taint val + % ArgArity is the last argument to + % the spawn function, which contains the arguments + % for the called function see: + % https://www.erlang.org/doc/man/erlang.html#spawn-2 + % + % So we store the taint values Arg1 .. ArgArity-1 to TempVariables + + NumberOfArgsToStore = + case Function of + %spawn_opt has second to last argument as Args + spawn_opt -> max(0, ImaginaryArity - 3); + %All other spawn function have Args as last argument + _ -> max(0, ImaginaryArity - 2) + end, + + {TempVarNames1, _} = lists:split(NumberOfArgsToStore, TempVarNames), + lists:foreach( + fun(TmpVarName) -> + CallbackModule:write_instruction({store, {TmpVarName, ?INSTRUMENTATION_LOC}}) + end, + TempVarNames1 + ), + % Stack looks like + % | taint_val(Args) | + % | taint_val(Function) | <- Top of the stack + + % Construct/Destruct pattern reverse the order of elements, so we reverse + % the order of tuple elements here, so they get deconstructed in the correct + % order + CallbackModule:write_instruction({store, {"Cfn1Arg0", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({store, {"Cfn1Arg1", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({get, {"Cfn1Arg0", ?INSTRUMENTATION_LOC}}), + CallbackModule:write_instruction({get, {"Cfn1Arg1", ?INSTRUMENTATION_LOC}}), + % Stack looks like + % | taint_val(Function) | + % | taint_val(Args) | <- Top of the stack + construct_pattern(Loc, CallbackModule, {tuple, 2}), + CallbackModule:write_instruction({duplicate, {}}), + % Stack looks like + % |taint_val({Function, Args}) | + % |taint_val({Function, Args}) | + RestoreVars = TempVarNames1, + ProcId = + case get(next_taint_pid) of + undefined -> + ref_to_list(make_ref()); + [Pid] -> + put(next_taint_pid, undefined), + Pid; + [FirstPid | Tail] -> + put(next_taint_pid, Tail), + FirstPid; + Pid -> + Pid + end, + CallbackModule:write_instruction({send, {ProcId, ?INSTRUMENTATION_LOC}}), + deconstruct_pattern(Loc, CallbackModule, {tuple, 2}), + if + Arity /= ImaginaryArity -> + % This is not 100% correct as it should pop the value we made up (the last one) + % But for the spawn function we ignore all the other taint values so it + % doesn't matter and there is no need to complicate this more + CallbackModule:write_instruction({pop, {}}); + true -> + ok + end, + % Stack looks like + % | taint_val(Args) | + % | taint_val(Function) | <- Top of the stack + lists:foreach( + fun(TmpVarName) -> + CallbackModule:write_instruction({get, {TmpVarName, ?INSTRUMENTATION_LOC}}) + end, + lists:reverse(RestoreVars) + ), + CallbackModule:write_instruction({call_fun, {erlang, Function, Arity}, create_taint_value(Loc)}), + seq_trace:set_token(label, {new_proc, ProcId}), + ok; +call_fun(Loc, CallbackModule, Module, Function, Arity) -> + CallbackModule:write_instruction({call_fun, {Module, Function, Arity}, create_taint_value(Loc)}), + ok. + +-spec construct_pattern(string(), atom(), taint_abstract_machine:construct_pattern_types()) -> ok. +construct_pattern(Loc, CallbackModule, {map, Keys}) -> + FilteredKeys = lists:map(fun pid_to_str/1, Keys), + CallbackModule:write_instruction({construct_pattern, {{map, FilteredKeys}, create_taint_value(Loc)}}), + ok; +construct_pattern(Loc, CallbackModule, Pattern) -> + CallbackModule:write_instruction({construct_pattern, {Pattern, create_taint_value(Loc)}}), + ok. + +-spec deconstruct_pattern(string(), atom(), taint_abstract_machine:deconstruct_pattern_types()) -> ok. +deconstruct_pattern(Loc, CallbackModule, Pattern) -> + CallbackModule:write_instruction({deconstruct_pattern, {Pattern, create_taint_value(Loc)}}), + ok. + +-spec try_catch(string(), atom(), taint_abstract_machine:try_catch_state(), tuple()) -> ok. +try_catch(Loc, CallbackModule, Status, TryBlockId) -> + CallbackModule:write_instruction({try_catch, {Status, TryBlockId}, Loc}), + ok. + +-spec set_element(string(), atom(), integer(), tuple(), term()) -> tuple(). +set_element(Loc, CallbackModule, Index, Tuple, Value) -> + CallbackModule:write_instruction({set_element, {Index, erlang:tuple_size(Tuple), create_taint_value(Loc)}}), + erlang:setelement(Index, Tuple, Value). + +%% =================== HELPERS ============================ +-spec create_taint_value(string()) -> string(). +create_taint_value(Loc) -> + Loc. + +-spec pid_to_str(pid() | string()) -> string(). +pid_to_str(K) when is_pid(K) -> "a pid"; +pid_to_str(K) -> K. diff --git a/finer_taint/src/finer_taint_compiler.erl b/finer_taint/src/finer_taint_compiler.erl new file mode 100644 index 0000000..967b518 --- /dev/null +++ b/finer_taint/src/finer_taint_compiler.erl @@ -0,0 +1,1139 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +-module(finer_taint_compiler). +-compile(warn_missing_spec_all). + +%%==================================================================== +%% API +%%==================================================================== +-export([ + parse_transform/2, + instrument_with_sinks/1, + compile_helper/2, + rewrite_comprehension/1, + instrument_and_load_modules/2, + instrument_with_sinks/2, + instrument_known_stdlibs/1, + instrument_loaded_module/2 +]). + +-record(rewrite_state, { + source :: file:filename_all(), + finer_taint_module :: module(), + renamed_modules :: #{module() => module()}, + module :: module() +}). +-define(RENAMED_MODULES, #{ + queue => modeled_queue, + maps => modeled_taint_maps, + lists => modeled_lists, + gen => modeled_gen, + proc_lib => modeled_proc_lib, + gen_server => modeled_gen_server +}). + +% functions in this map will not be instrumented, this is useful +% if they need to be modeled in the taint_abstract_machine, but we +% still need some dynamic instrumentation +-define(DO_NOT_INSTRUMENT, #{ + {modeled_erlang, real_put} => ok, + {modeled_erlang, process_dict} => ok +}). + +-type forms() :: [erl_parse:abstract_form()]. +-type options() :: [compile:option()]. +-type expr() :: erl_parse:abstract_expr(). +-type clause() :: erl_parse:abstract_clause(). + +% Like a Hoare Triple, but completely different. It's a triple because you +% have {Pre, Expr, Post}, where Expr is the original expr and Pre/Post are +% instructions that need to be executed before and after the original to +% perform the analysis +-type triple() :: {[expr()], expr(), [expr()]}. + +-spec instrument_known_stdlibs(options()) -> ok. +instrument_known_stdlibs(FinerTaintOptions) -> + % This is a list we know that need to be instrumented for finer taint to work well + InstrumentedStdLibs = [gen_server, gen, lists, proc_lib, queue, modeled_erlang], + lists:foreach( + fun(Module) -> + instrument_loaded_module(Module, FinerTaintOptions) + end, + InstrumentedStdLibs + ). + +%% instrument_loaded_module finds the object code of Module +%% instruments it and loads the instrumented code back +-spec instrument_loaded_module(module(), options()) -> {module, module()}. +instrument_loaded_module(Module, FinerTaintOptions) -> + {Module, Binary, _} = code:get_object_code(Module), + {Module, Forms} = load_forms(Binary), + InstrumentedForms = instrument_with_sinks(Forms, [{finer_taint_do_checks, false}] ++ FinerTaintOptions), + {NewModuleName, Binary1} = compile_forms(InstrumentedForms), + {module, NewModuleName} = code:load_binary(NewModuleName, "/fake/module/path", Binary1). + +-spec instrument_and_load_modules([{module(), Path :: string()}], options()) -> ok. +instrument_and_load_modules([], _) -> + ok; +instrument_and_load_modules([{M, Path} | Tail], Options) -> + Binary = compile_helper(Path, [{finer_taint_do_checks, false} | Options]), + {module, M} = code:load_binary(M, Path, Binary), + instrument_and_load_modules(Tail, Options). + +-spec compile_helper(string(), options()) -> binary(). +compile_helper(ModPath, Options) -> + CompileOptions = [debug_info, binary] ++ proplists:get_value(compile_options, Options, []), + {Mod, Binary} = compile_file(ModPath, CompileOptions), + {Mod, Forms} = load_forms(Binary), + InstrumentedForms = instrument_with_sinks(Forms, Options), + {Mod, Binary1} = compile_forms(InstrumentedForms), + Binary1. + +-spec get_finer_taint_cfg(string()) -> [term()]. +get_finer_taint_cfg(CfgFileName) -> + %% buck2 use resource/ dir + FtConfig = filename:join( + [ + % we assume that code:where_is_file("finer_taint_compiler.beam") is successful + % eqwalizer:ignore + filename:dirname(code:where_is_file("finer_taint_compiler.beam")), + "resources", + CfgFileName + ] + ), + case filelib:is_regular(FtConfig) of + true -> + {ok, CfgModules} = file:consult(FtConfig), + CfgModules; + false -> + [] + end. + +%%==================================================================== +%% API Implementation +%%==================================================================== + +-spec parse_transform(forms(), options()) -> forms(). +parse_transform(Forms, Options) -> + instrument_with_sinks(Forms, Options). + +-spec instrument_with_sinks(forms()) -> forms(). +instrument_with_sinks(Forms) -> + instrument_with_sinks(Forms, []). +-spec instrument_with_sinks(forms(), options()) -> forms(). +instrument_with_sinks(Forms, Options) -> + Source = get_source(Forms), + Module = get_module(Forms), + DoChecks = proplists:get_value(finer_taint_do_checks, Options, true), + AdditionalRenames = proplists:get_value(finer_taint_renames, Options, #{}), + + CfgModules = get_finer_taint_cfg("finer_taint.cfg"), + WhiteListedModule = lists:member(Module, CfgModules), + + IgnoredModules = get_finer_taint_cfg("instrument_ignorelist.cfg"), + WhiteListedSource = string:find(Source, "taint_SUITE_data"), + FinerTaintModule = proplists:get_value(finer_taint_module, Options, ct_finer_taint), + BlacklistedModules = lists:any( + fun(Name) -> + string:find(Source, Name) =/= nomatch + end, + IgnoredModules + ), + Checks = {WhiteListedModule, WhiteListedSource, DoChecks, BlacklistedModules}, + case Checks of + {false, nomatch, true, _} -> + io:format(standard_error, "finer_taint skip compile ~s not allowlisted ~p~n", [Source, Checks]), + Forms; + {false, _, _, true} -> + io:format(standard_error, "finer_taint skip compile ~s blocklisted ~p~n", [Source, Checks]), + Forms; + _ -> + io:format(standard_error, "[finer_taint] ~s compiling ~n", [Source]), + % The expression inside ets:fun2ms might not be valid AST before ms_transform + % This can break other passes (erl_expand_records). Here we ensure ms_transform + % is ran beforehand: https://www.erlang.org/doc/man/ms_transform.html#description + Forms0 = + case has_ms_transform(Forms) of + false -> Forms; + true -> ms_transform(Forms, Options) + end, + Forms1 = erl_expand_records:module(Forms0, [debug_info]), + rewrite( + Forms1, + #rewrite_state{ + source = Source, + finer_taint_module = FinerTaintModule, + renamed_modules = maps:merge(?RENAMED_MODULES, AdditionalRenames), + module = Module + }, + [] + ) + end. + +%%==================================================================== +%% internal functions +%%==================================================================== +-spec get_source(forms()) -> file:filename_all(). +get_source([]) -> + error(taint_unknown_module); +get_source([{attribute, _, file, {Source, _}} | _]) -> + % eqwalizer:ignore - This should be cast_dynamic, but can't import eqwalizer module here because parse transform + Source; +get_source([_ | Rest]) -> + get_source(Rest). + +-spec get_module(forms()) -> module(). +get_module([]) -> error(taint_unknown_module); +get_module([{attribute, _, module, Mod} | _]) when is_atom(Mod) -> Mod; +get_module([_ | Rest]) -> get_module(Rest). + +-spec has_ms_transform(forms()) -> boolean(). +has_ms_transform([{attribute, _, file, {Path, _Line}} | Tail]) -> + case string:find(Path, "ms_transform.hrl") of + nomatch -> has_ms_transform(Tail); + _ -> true + end; +has_ms_transform([]) -> + false; +has_ms_transform([_ | Tail]) -> + has_ms_transform(Tail). + +%%==================================================================== +%% abstract forms rewrite: +%% see: http://www.erlang.org/doc/apps/erts/absform.html +%% +%%==================================================================== +-spec rewrite(forms(), #rewrite_state{}, forms()) -> forms(). +rewrite(Rest = [{attribute, _, finer_taint_compiled, _} | _], _, Acc) -> + lists:reverse(Acc) ++ Rest; +rewrite([], _, Acc) -> + %% all input has been processed + Forms = lists:reverse(Acc), + % eqwalizer:ignore This should be cast_dynamic, but can't import eqwalizer module here because parse transform + [ + erl_parse:map_anno(fun(Anno) -> erl_anno:set_generated(true, Anno) end, Form) + || Form <- Forms + ]; +rewrite( + [{attribute, Anno, module, Module} | Tail], + State = #rewrite_state{renamed_modules = Renames}, + Acc +) -> + CompiledTag = {attribute, erl_anno:new({1, 1}), finer_taint_compiled, []}, + rewrite(Tail, State, [CompiledTag, {attribute, Anno, module, maps:get(Module, Renames, Module)} | Acc]); +rewrite([{attribute, _, record, {message_stats, _}} | Tail], State, Acc) -> + rewrite(Tail, State, Acc); +rewrite( + [{function, L, Function, Arity, Clauses0} | Rest], + State, + Acc +) when not is_map_key({State#rewrite_state.module, Function}, ?DO_NOT_INSTRUMENT) -> + Clauses1 = lists:map(fun(Clause) -> instrument_function_clause(Clause, Function, State) end, Clauses0), + FunctionForm = {function, L, Function, Arity, Clauses1}, + rewrite( + Rest, + State, + [FunctionForm | Acc] + ); +rewrite([H | T], State, Acc) -> + rewrite(T, State, [H | Acc]). + +-define(EMIT_INSTR(Loc, Func, Args), + {call, Loc, {remote, Loc, {atom, Loc, finer_taint}, {atom, Loc, Func}}, [ + {string, Loc, + atom_to_list(State#rewrite_state.module) ++ ".erl:" ++ + integer_to_list(erl_anno:line(Loc))}, + {atom, Loc, State#rewrite_state.finer_taint_module} + | Args + ]} +). +-define(NILL(Loc), {atom, Loc, notaint}). + +%%These assume Anno is defined for location +-define(ATOM(Atom), {atom, Anno, Atom}). +-define(INT(Int), {integer, Anno, Int}). +-define(TUPLE(Values), {tuple, Anno, Values}). + +-define(SSA_PREFIX, "SSAify_"). + +% Returns a hash of the passed Expr +% Useful for creating unique identifiers somwehre in the abstract tree +-spec expr_hash(term()) -> string(). +expr_hash(Expr) -> + <> = crypto:hash(sha256, io_lib:format("~p", [Expr])), + lists:sublist(integer_to_list(IntHash, 36), 8). + +-spec get_temp_varname_for_expr(expr(), string()) -> string(). +get_temp_varname_for_expr(Expr, Prefix) -> + Prefix ++ expr_hash(Expr). + +-spec unzip_flatten([{[T1], T2, [T3]}]) -> {[T1], [T2], [T3]}. +unzip_flatten(List) -> + unzip_flatten_impl(List, {[], [], []}). + +-spec unzip_flatten_impl([{[T1], T2, [T3]}], {[T1 | [T1]], [T2], [T3 | [T3]]}) -> {[T1], [T2], [T3]}. +unzip_flatten_impl([], {Pres, Exprs, Posts}) -> + {lists:flatten(lists:reverse(Pres)), lists:reverse(Exprs), lists:flatten(lists:reverse(Posts))}; +unzip_flatten_impl([{Pres, Expr, Posts} | Tail], {AccPres, AccExpr, AccPosts}) -> + unzip_flatten_impl(Tail, {[Pres | AccPres], [Expr | AccExpr], [Posts | AccPosts]}). + +-spec concat([[T]]) -> [T]. +concat([]) -> []; +concat([H | T]) -> H ++ concat(T). + +% Converts a list (ie. [1]) into the Ast of the list (ie. {cons, Anno, 1, {nil, Anno}}) +% This is useful when passing multiple values to instrumentation in a single argument +-spec list_to_list_ast(erl_anno:anno(), list()) -> expr(). +list_to_list_ast(Anno, []) -> + {nil, Anno}; +list_to_list_ast(Anno, [H | T]) -> + {cons, Anno, H, list_to_list_ast(Anno, T)}. + +-spec string_list_to_ast(erl_anno:anno(), list()) -> expr(). +string_list_to_ast(Anno, List) -> + ListOfStrings = lists:map(fun(String) -> {string, Anno, lists:flatten(io_lib:format("~s", [String]))} end, List), + list_to_list_ast(Anno, ListOfStrings). + +-spec map_to_map_ast(erl_anno:anno(), map()) -> expr(). +map_to_map_ast(Anno, Map) -> + Assocs = maps:fold( + fun(K, V, Acc) when is_atom(K) andalso is_atom(V) -> + [{map_field_assoc, Anno, ?ATOM(K), ?ATOM(V)} | Acc] + end, + [], + Map + ), + {map, Anno, Assocs}. +-spec name_has_been_used(string()) -> string(). +name_has_been_used(TempName) -> + % Note: refactor this to not use process dict, but pass a state around + Dict = + case get(used_ssa_vars) of + undefined -> #{}; + X -> X + end, + case maps:get(TempName, Dict, false) of + false -> + put(used_ssa_vars, Dict#{TempName => ok}), + TempName; + _ -> + <> = crypto:hash(sha256, io_lib:format("~s", [TempName])), + Suffix = lists:sublist(integer_to_list(IntHash, 36), 8), + name_has_been_used("Rehashed" ++ Suffix) + end. + +%% Converts Expr +%% to SSAify_ = Expr, +%% SSAify_. +-spec expr_to_var(expr()) -> {expr(), expr()}. +expr_to_var(Expr) -> + [_ | [Anno | _]] = tuple_to_list(Expr), + TempName = name_has_been_used(get_temp_varname_for_expr(Expr, ?SSA_PREFIX)), + TempVar = {var, Anno, list_to_atom(TempName)}, + + NewExpr = {match, Anno, TempVar, Expr}, + {TempVar, NewExpr}. + +%% The purpose of ssa_iffy is to deal with the following problem. Consider: +%% +%% A = func_a(func_b(42)). +%% +%% Our instrumentation needs to be executed after func_b returns, but before func_a +%% is called. This is not possible to do within this expression. So ssa_iffy +%% transforms the above expression into: +%% +%% ATempVariableXyz = func_b(42), +%% DoImportantInstrumentationStuff(), +%% A = func_a(ATempVariableXyz). +%% +%% This kind of looks like transforming the code into some form of single static assigment (SSA), +%% thus the name. It only does this when we need to insert some "post" instrumentation. +-spec ssa_iffy(triple()) -> triple(). +ssa_iffy({Pre, Expr, []}) -> + {Pre, Expr, []}; +ssa_iffy({Pre, Expr, Post}) -> + {TempVar, NewExpr} = expr_to_var(Expr), + {Pre ++ [NewExpr] ++ Post, TempVar, []}. + +-spec instrument_body([expr()], [expr()], #rewrite_state{}) -> {expr(), [expr(), ...]}. +instrument_body([Expr | []], Acc, State) -> + {Pre, Expr1, []} = ssa_iffy(instrument_expression(Expr, State)), + {Expr1, Acc ++ Pre}; +instrument_body([Expr | Tail], Acc, State) -> + [_ | [Anno | _]] = tuple_to_list(Expr), + {Pre, Expr1, Post} = instrument_expression(Expr, State), + NewExprs = Pre ++ [Expr1 | Post] ++ [?EMIT_INSTR(erl_anno:set_generated(true, Anno), pop, [])], + instrument_body(Tail, Acc ++ NewExprs, State). + +-spec instrument_function_expressions([expr()], [expr()], #rewrite_state{}) -> {expr(), [expr()]}. +instrument_function_expressions([Expr | []], Acc, State) -> + {Pre, Expr1, Post} = instrument_expression(Expr, State), + [_ | [Anno | _]] = tuple_to_list(Expr), + TempName = "TaintCompReturnVarSuperSpecial", + ReturnVar = {var, Anno, list_to_atom(TempName)}, + NewExpr = {match, Anno, ReturnVar, Expr1}, + {ReturnVar, Acc ++ Pre ++ [NewExpr] ++ Post}; +instrument_function_expressions([Expr | Tail], Acc, State) -> + [_ | [Anno | _]] = tuple_to_list(Expr), + {Pre, Expr1, Post} = instrument_expression(Expr, State), + NewExprs = Pre ++ [Expr1 | Post] ++ [?EMIT_INSTR(erl_anno:set_generated(true, Anno), pop, [])], + instrument_function_expressions(Tail, Acc ++ NewExprs, State). + +-spec instrument_function_clause(clause(), atom(), #rewrite_state{}) -> clause(). +instrument_function_clause({clause, Anno, Pattern, Guard, Body}, Function, State) -> + {LastExpr, Body3} = instrument_function_expressions(Body, [], State), + FunctionPrequel = [ + ?EMIT_INSTR(Anno, push_scope, [?ATOM(State#rewrite_state.module), ?ATOM(Function), ?INT(length(Pattern))]) + | lists:flatten([instrument_pattern(P, State) || P <- Pattern]) + ], + FunctionSequel = [?EMIT_INSTR(Anno, func_ret, [?ATOM(Function)]), LastExpr], + {clause, Anno, Pattern, Guard, FunctionPrequel ++ Body3 ++ FunctionSequel}. + +-spec instrument_case_clause(clause(), [expr()], #rewrite_state{}) -> clause(). +instrument_case_clause({clause, Anno, [Pattern], Guard, Body}, PostClauseExpr, State) -> + {LastExpr, Body1} = instrument_body(Body, [], State), + PatternPrequel = instrument_pattern(Pattern, State), + {clause, Anno, [Pattern], Guard, PostClauseExpr ++ PatternPrequel ++ Body1 ++ [LastExpr]}. + +-spec instrument_catch_clause(clause(), #rewrite_state{}, expr()) -> clause(). +instrument_catch_clause({clause, Anno, [?TUPLE([ExceptionClass, Pattern, Stack])], Guard, Body}, State, TryBlockId) -> + {LastExpr, Body1} = instrument_body(Body, [], State), + PatternPrequel = instrument_pattern(Pattern, State), + RuntimeErrorTaintVal = + case ExceptionClass of + %For runtime errors, the thrown value is assumed untainted + %So we pop the value that is supposedly thrown (done by taint_abstract_machine) + %and push a new no taint value + ?ATOM(error) -> + [?EMIT_INSTR(Anno, pop, []), ?EMIT_INSTR(Anno, push, [?NILL(Anno)])]; + {var, _, '_'} -> + []; + {var, _, Name} -> + [ + ?EMIT_INSTR(Anno, push, [?NILL(Anno)]), + ?EMIT_INSTR(Anno, store_var, [{string, Anno, atom_to_list(Name)}]) + ]; + _ -> + [] + end, + RuntimeErrorTaintVal1 = + case Stack of + {var, _, '_'} -> + []; + {var, _, StackVarName} -> + [ + ?EMIT_INSTR(Anno, push, [?NILL(Anno)]), + ?EMIT_INSTR(Anno, store_var, [{string, Anno, atom_to_list(StackVarName)}]) + ]; + _ -> + [] + end ++ RuntimeErrorTaintVal, + + {clause, Anno, [?TUPLE([ExceptionClass, Pattern, Stack])], Guard, + [?EMIT_INSTR(Anno, try_catch, [?ATOM(catch_enter), TryBlockId]) | RuntimeErrorTaintVal1] ++ PatternPrequel ++ + Body1 ++ + [LastExpr]}. + +-spec instrument_if_clause(clause(), #rewrite_state{}) -> clause(). +instrument_if_clause({clause, Anno, [], Guards, Body}, State) -> + {LastExpr, Body1} = instrument_body(Body, [], State), + {clause, Anno, [], Guards, Body1 ++ [LastExpr]}. + +-spec instrument_expression(expr() | erl_parse:af_binelement(expr()), #rewrite_state{}) -> triple(). +instrument_expression({match, Anno, Pattern, RHS}, State) -> + {Pre, RHS1, []} = ssa_iffy(instrument_expression(RHS, State)), + Post = instrument_pattern(Pattern, State), + {Pre, {match, Anno, Pattern, RHS1}, [?EMIT_INSTR(Anno, duplicate, [])] ++ Post}; +% If E is a conditional match operator expression P ?= E_0, where P is a pattern, then Rep(E) = {maybe_match,ANNO,Rep(P),Rep(E_0)}. +instrument_expression({maybe_match, Anno, Pattern, RHS}, State) -> + {Pre, RHS1, []} = ssa_iffy(instrument_expression(RHS, State)), + Post = instrument_pattern(Pattern, State), + {Pre, {maybe_match, Anno, Pattern, RHS1}, [?EMIT_INSTR(Anno, duplicate, [])] ++ Post}; +instrument_expression({'receive', Anno, ReceiveCaseClauses, AfterExpr, AfterBody}, State) -> + ReceiveInstr = [?EMIT_INSTR(Anno, receive_trace, [])], + InstrumentedClauses = [instrument_case_clause(C, ReceiveInstr, State) || C <- ReceiveCaseClauses], + {LastAfterExpr, AfterBody1} = instrument_body(AfterBody, [], State), + {AfterExprPre0, AfterExpr1, []} = ssa_iffy(instrument_expression(AfterExpr, State)), + % The taint value of AfterExpr is not interesting (unless we want to detect some weird timing attacks) + % So we just pop it off the stack. + AfterExprPre1 = AfterExprPre0 ++ [?EMIT_INSTR(Anno, pop, [])], + {TempVar, NewExpr} = expr_to_var({'receive', Anno, InstrumentedClauses, AfterExpr1, AfterBody1 ++ [LastAfterExpr]}), + {AfterExprPre1 ++ [NewExpr], TempVar, []}; +instrument_expression({'receive', Anno, ReceiveCaseClauses}, State) -> + ReceiveInstr = [?EMIT_INSTR(Anno, receive_trace, [])], + InstrumentedClauses = [instrument_case_clause(C, ReceiveInstr, State) || C <- ReceiveCaseClauses], + {TempVar, NewExpr} = expr_to_var({'receive', Anno, InstrumentedClauses}), + {[NewExpr], TempVar, []}; +instrument_expression({'case', Anno, Expr, CaseClauses}, State) -> + {PreExpr, Expr1, []} = ssa_iffy(instrument_expression(Expr, State)), + InstrumentedClauses = [instrument_case_clause(C, [], State) || C <- CaseClauses], + % Since case has a body, it can't be executed at an arbirary point in expr tree, + % because stack isn't in a proper state. Therefore we replace it with a temp variable + {TempVar, NewExpr} = expr_to_var({'case', Anno, Expr1, InstrumentedClauses}), + {PreExpr ++ [NewExpr], TempVar, []}; +instrument_expression({'if', Anno, IfClauses}, State) -> + InstrumentedClauses = [instrument_if_clause(C, State) || C <- IfClauses], + % Contains body, extracting a temp var + {TempVar, NewExpr} = expr_to_var({'if', Anno, InstrumentedClauses}), + {[NewExpr], TempVar, []}; +instrument_expression({'maybe', Anno, Body}, State) -> + {LastExpr, Body1} = instrument_body(Body, [], State), + {TempVar, NewExpr} = expr_to_var({'maybe', Anno, Body1 ++ [LastExpr]}), + {[NewExpr], TempVar, []}; +% If E is a maybe expression maybe B else Ec_1 ; ... ; Ec_k end, where B is a body and each Ec_i is an else clause then Rep(E) = {'maybe',ANNO,Rep(B),{'else',ANNO,[Rep(Ec_1), ..., Rep(Tc_k)]}}:w +instrument_expression({'maybe', Anno, Body, {'else', AnnoElse, ElseClauses}}, State) -> + {_LastExpr, Body1} = instrument_body(Body, [], State), + InstrumentedClauses = [instrument_case_clause(C, [], State) || C <- ElseClauses], + % Since maybe has a body, it can't be executed at an arbirary point in expr tree, + % because stack isn't in a proper state. Therefore we replace it with a temp variable + {TempVar, NewExpr} = expr_to_var({'maybe', Anno, Body1, {'else', AnnoElse, InstrumentedClauses}}), + {[NewExpr], TempVar, []}; +instrument_expression({block, Anno, Body}, State) -> + {LastExpr, Body1} = instrument_body(Body, [], State), + % Contains body, extracting a temp var + {TempVar, NewExpr} = expr_to_var({block, Anno, Body1 ++ [LastExpr]}), + {[NewExpr], TempVar, []}; +instrument_expression({'catch', Anno, Expr}, State) -> + {PreExpr, Expr1, []} = ssa_iffy(instrument_expression(Expr, State)), + {[], {'catch', Anno, {'block', Anno, PreExpr ++ [Expr1]}}, []}; +instrument_expression({call, Anno, {remote, _, {atom, _, finer_taint}, {atom, _, source}}, Args}, State) when + length(Args) == 2; length(Args) == 1 +-> + ArgToTaint = + case Args of + [Arg] -> Arg; + [_Tag, Arg] -> Arg + end, + {Pre, ArgToTaint1, []} = ssa_iffy(instrument_expression(ArgToTaint, State)), + ActualSourceArgs = + case Args of + [_] -> [ArgToTaint1]; + [Tag, _] -> [Tag, ArgToTaint1] + end, + {Pre, ?EMIT_INSTR(Anno, actual_source, ActualSourceArgs), + % The source instruction needs to be ssa-ffied, so we insert a noop (push/pop pair) in the Post Instructions + [?EMIT_INSTR(Anno, push, [?NILL(Anno)]), ?EMIT_INSTR(Anno, pop, [])]}; +% Note: skip ets function, by assuming return value is always untainted +instrument_expression(Call = {call, Anno, {remote, _, {atom, _, ets}, _}, _}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Call, []}; +instrument_expression({call, Anno, {remote, _, {atom, _, finer_taint}, {atom, _, sink}}, [Arg]}, State) -> + {Pre, Arg1, []} = ssa_iffy(instrument_expression(Arg, State)), + {Pre, Arg1, [?EMIT_INSTR(Anno, actual_sink, [])]}; +% Skip put(enabled_taint, _) calls +instrument_expression( + Expr = {call, Anno, {remote, _, {atom, _, erlang}, {atom, _, put}}, [{atom, _, enabled_taint} | _Args]}, + State +) -> + {[], Expr, [?EMIT_INSTR(Anno, push, [?NILL(Anno)])]}; +% Same as send, because we do not want to worry about timing in the taint world +% Note: refactor implemntation to reuse the send code +instrument_expression( + {call, Anno, {remote, _, {atom, _, erlang}, {atom, _, send_after}}, [Interval, Pid, Message | Tail]}, State +) -> + {IntervalPreInstrument, IntervalE, []} = ssa_iffy(instrument_expression(Interval, State)), + {PidPreInstrument, PidE, []} = ssa_iffy(instrument_expression(Pid, State)), + {MessagePreInstrument, MessageE, []} = ssa_iffy(instrument_expression(Message, State)), + {Pre, _TailE} = + case Tail of + [] -> + {[], []}; + [Expr] -> + {P, E, []} = ssa_iffy(instrument_expression(Expr, State)), + {P, E} + end, + { + IntervalPreInstrument ++ PidPreInstrument ++ MessagePreInstrument ++ + [ + ?EMIT_INSTR(Anno, send, []) + ] ++ Pre, + {call, Anno, {remote, Anno, {atom, Anno, erlang}, {atom, Anno, send_after}}, [IntervalE, PidE, MessageE | Tail]}, + [{call, Anno, {remote, Anno, ?ATOM(seq_trace), ?ATOM(set_token)}, [{nil, Anno}]}] + }; +instrument_expression({call, Anno, {remote, _, {atom, _, erlang}, {atom, _, send}}, [Pid, Message | Tail]}, State) -> + {PidPreInstrument, PidE, []} = ssa_iffy(instrument_expression(Pid, State)), + {MessagePreInstrument, MessageE, []} = ssa_iffy(instrument_expression(Message, State)), + {Pre, _TailE} = + case Tail of + [] -> + {[], []}; + [Expr] -> + {P, E, []} = ssa_iffy(instrument_expression(Expr, State)), + {P, E} + end, + { + PidPreInstrument ++ MessagePreInstrument ++ + [ + ?EMIT_INSTR(Anno, send, []) + ] ++ Pre, + {call, Anno, {remote, Anno, {atom, Anno, erlang}, {atom, Anno, send}}, [PidE, MessageE | Tail]}, + [{call, Anno, {remote, Anno, ?ATOM(seq_trace), ?ATOM(set_token)}, [{nil, Anno}]}] + }; +%% Note deal with indirect functioncalls +instrument_expression({call, Anno, {remote, _, Module, Func}, Args}, State) -> + {PreInstruments, ReverseInstrumentedArgs, []} = unzip_flatten( + [ssa_iffy(instrument_expression(A, State)) || A <- lists:reverse(Args)] + ), + {PreFunc, Func1, []} = ssa_iffy(instrument_expression(Func, State)), + %% We assume the dynamic functions can't be tainted so we just discard the taint value of Func + %% Lambdas can't be called with this method as they aren't exported from a module. + %% Therefore we just pop the taint value of the function off the stack + PreFunc1 = PreFunc ++ [?EMIT_INSTR(Anno, pop, [])], + {Module1, Func2} = intercepted_functions(Module, Func1, State), + ReverseInstrumentedArgs1 = + case Module1 of + % finer_taint:* function expect the first argument to be a CallbackModule, which we add here + % It's added at the end, because ReverseInstrumentedArgs list is in the reverse order + {atom, _, finer_taint} -> + ReverseInstrumentedArgs ++ + [ + {atom, Anno, State#rewrite_state.finer_taint_module}, + {string, Anno, + atom_to_list(State#rewrite_state.module) ++ ".erl:" ++ integer_to_list(erl_anno:line(Anno))} + ]; + _ -> + ReverseInstrumentedArgs + end, + % The call_fun/fun_apply instructions need arity of length(Args) (and NOT ReverseInstrumentedArgs1) + % This is because length(Args) number of arguments was put on the stack, so fun_apply needs to pop + % length(Args) of arguments off it. + % + % In other words the only case where length(ReverseInstrumentedArgs1) =/= length(Args) is in the + % intrsinsic function case, where an erlang function is replaced by an abstract machine instruction. + % In that case the instrinsic should behave excatly the same as the original function. The fact that + % it also takes an additional CallbackModule arguments should be completly transparent. + { + PreFunc1 ++ PreInstruments ++ [?EMIT_INSTR(Anno, call_fun, [Module1, Func2, ?INT(length(Args))])], + {call, Anno, {remote, Anno, Module1, Func2}, lists:reverse(ReverseInstrumentedArgs1)}, + [?EMIT_INSTR(Anno, fun_apply, [?TUPLE([Module1, Func2, ?INT(length(Args))])])] + }; +instrument_expression({call, Anno, Func, Args}, State) -> + {PreInstruments, ReverseInstrumentedArgs, []} = unzip_flatten( + [ssa_iffy(instrument_expression(A, State)) || A <- lists:reverse(Args)] + ), + % erl_parse:af_remote_function() is not compatible with expr() + % eqwalizer:ignore This should be cast_dynamic, but can't import eqwalizer module here because parse transform + {PreFunc, Func1, []} = ssa_iffy(instrument_expression(Func, State)), + CurrentModule = ?ATOM(State#rewrite_state.module), + FuncName = + case Func1 of + Atm = {atom, _, _} -> Atm; + {var, _, _} -> ?ATOM(variable_func); + _ -> ?ATOM(lambda_func) + end, + PreFunc1 = + PreFunc ++ + [?EMIT_INSTR(Anno, restore_capture, [?TUPLE([CurrentModule, FuncName, ?INT(length(Args))])])], + { + PreFunc1 ++ PreInstruments ++ + [?EMIT_INSTR(Anno, call_fun, [CurrentModule, FuncName, ?INT(length(Args))])], + {call, Anno, Func1, lists:reverse(ReverseInstrumentedArgs)}, + [ + ?EMIT_INSTR(Anno, fun_apply, [?TUPLE([CurrentModule, FuncName, ?INT(length(Args))])]), + % Need to cleanup the scope we introdcued with restore_capture, + % reusing func_ret for this because it only drops the scope + ?EMIT_INSTR(Anno, func_ret, [?ATOM(dropping_lambda_capture)]) + ] + }; +instrument_expression({tuple, Anno, TupleValues}, State) -> + {PreInstruments, TupleValues1, []} = unzip_flatten( + [ssa_iffy(instrument_expression(V, State)) || V <- TupleValues] + ), + TupleInstr = [?EMIT_INSTR(Anno, construct_pattern, [?TUPLE([?ATOM(tuple), ?INT(length(TupleValues))])])], + {PreInstruments, {tuple, Anno, TupleValues1}, TupleInstr}; +% Note: do this for erlang:send too, consider erlang:apply(erlang, send, ..) too +instrument_expression({op, Anno, '!', Pid, Message}, State) -> + {PidPreInstrument, PidE, []} = ssa_iffy(instrument_expression(Pid, State)), + {MessagePreInstrument, MessageE, []} = ssa_iffy(instrument_expression(Message, State)), + { + PidPreInstrument ++ MessagePreInstrument ++ + [ + ?EMIT_INSTR(Anno, send, []) + ], + {op, Anno, '!', PidE, MessageE}, + [{call, Anno, {remote, Anno, ?ATOM(seq_trace), ?ATOM(set_token)}, [{nil, Anno}]}] + }; +instrument_expression({op, Anno, Op, Arg}, State) -> + {PreInstrument, RHS, []} = ssa_iffy(instrument_expression(Arg, State)), + OpPreInstrument = ?EMIT_INSTR(Anno, call_fun, [?ATOM(operators), ?ATOM(Op), ?INT(1)]), + OpInstrument = ?EMIT_INSTR(Anno, fun_apply, [?TUPLE([?ATOM(operators), ?ATOM(Op), ?INT(1)])]), + {PreInstrument ++ [OpPreInstrument], {op, Anno, Op, RHS}, [OpInstrument]}; +instrument_expression({op, Anno, Op, LeftArg, RightArg}, State) -> + {LeftPreInstrument, LHS, []} = ssa_iffy(instrument_expression(LeftArg, State)), + {RightPreInstrument, RHS, []} = ssa_iffy(instrument_expression(RightArg, State)), + OpPreInstrument = ?EMIT_INSTR(Anno, call_fun, [?ATOM(operators), ?ATOM(Op), ?INT(2)]), + OpInstrument = ?EMIT_INSTR(Anno, fun_apply, [?TUPLE([?ATOM(operators), ?ATOM(Op), ?INT(2)])]), + if + (Op == 'andalso' orelse Op == 'orelse') -> + {RHSTempVar, NewRHS} = expr_to_var(RHS), + { + LeftPreInstrument, + {op, Anno, Op, LHS, + {block, Anno, RightPreInstrument ++ [OpPreInstrument, NewRHS, OpInstrument, RHSTempVar]}}, + [] + }; + true -> + { + LeftPreInstrument ++ RightPreInstrument ++ [OpPreInstrument], + {op, Anno, Op, LHS, RHS}, + [OpInstrument] + } + end; +instrument_expression({cons, Anno, HeadE, TailE}, State) -> + {PreH, HeadExpr, []} = ssa_iffy(instrument_expression(HeadE, State)), + {PreT, TailExpr, []} = ssa_iffy(instrument_expression(TailE, State)), + { + PreT ++ PreH ++ [?EMIT_INSTR(Anno, construct_pattern, [?TUPLE([?ATOM(cons)])])], + {cons, Anno, HeadExpr, TailExpr}, + [] + }; +instrument_expression(Try = {'try', Anno, TryBody, CaseClauses, CatchClauses, AfterBody}, State) -> + TryBlockId = ?TUPLE([?ATOM(State#rewrite_state.module), ?INT(erl_anno:line(Anno)), ?INT(erlang:phash2(Try))]), + {TryLastBodyExpr, TryBody1} = instrument_body(TryBody, [], State), + {TryLastExpr, TryLastBodyExpr1} = expr_to_var(TryLastBodyExpr), + TryBody2 = TryBody1 ++ [TryLastBodyExpr1], + InstrumentedCaseClauses = [instrument_case_clause(C, [], State) || C <- CaseClauses], + InstrumentedCatchClauses = [instrument_catch_clause(C, State, TryBlockId) || C <- CatchClauses], + AfterBody2 = + case AfterBody of + [] -> + []; + _ -> + {AfterLastExpr, AfterBody1} = instrument_body(AfterBody, [], State), + AfterBody1 ++ [AfterLastExpr, ?EMIT_INSTR(Anno, pop, [])] + end, + TryEnter = [?EMIT_INSTR(Anno, try_catch, [?ATOM(try_enter), TryBlockId])], + TryBody3 = TryEnter ++ TryBody2 ++ [?EMIT_INSTR(Anno, try_catch, [?ATOM(try_exit), TryBlockId]), TryLastExpr], + {[], {'try', Anno, TryBody3, InstrumentedCaseClauses, InstrumentedCatchClauses, AfterBody2}, []}; +instrument_expression({'fun', Anno, {clauses, FuncClauses}}, State) -> + {L, Col} = erl_anno:location(Anno), + Function = list_to_atom( + lists:flatten(io_lib:format("lambda_~p_~p_~p_anon", [State#rewrite_state.module, L, Col])) + ), + VarsInLambda = get_all_variable_names(FuncClauses), + Clauses1 = lists:map(fun(Clause) -> instrument_function_clause(Clause, Function, State) end, FuncClauses), + { + [?EMIT_INSTR(Anno, capture_closure, [string_list_to_ast(Anno, VarsInLambda)])], + {'fun', Anno, {clauses, Clauses1}}, + [] + }; +instrument_expression({'fun', Anno, {function, Name, Arity}}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], {'fun', Anno, {function, Name, Arity}}, []}; +instrument_expression(F = {'fun', Anno, {function, _Module, _Name, _Arity}}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], F, []}; +instrument_expression({named_fun, Anno, Name, FuncClauses}, State) -> + {L, Col} = erl_anno:location(Anno), + Function = list_to_atom( + lists:flatten( + io_lib:format("lambda_~p_~p_~p_named_~s", [State#rewrite_state.module, L, Col, atom_to_list(Name)]) + ) + ), + Clauses1 = lists:map(fun(Clause) -> instrument_function_clause(Clause, Function, State) end, FuncClauses), + VarsInLambda = get_all_variable_names(FuncClauses), + { + [ + ?EMIT_INSTR(Anno, capture_closure, [string_list_to_ast(Anno, VarsInLambda)]), + % Here we store the closure of the recursive function under the name of the recursive function + ?EMIT_INSTR(Anno, duplicate, []), + ?EMIT_INSTR(Anno, store_var, [{string, Anno, lists:flatten(io_lib:format("~s", [Name]))}]) + ], + {'named_fun', Anno, Name, Clauses1}, + [] + }; +instrument_expression({bin, Anno, BinValues}, State) -> + {PreInstruments, BinValues1, []} = unzip_flatten( + [instrument_binelement(V, State) || V <- BinValues] + ), + % This computes the runtime byte sizes of each bin_element + % Currently if there is a function call in the bin_element + % it will be called twice. If this becomes a problem we + % can introduce a temp variable. + Sizes = lists:map( + fun(Expr = {bin_element, _, _, _, _}) -> + Args = [{bin, Anno, [Expr]}], + % Do byte precision for now, we can change to bit when needed + {call, Anno, {remote, Anno, ?ATOM(erlang), ?ATOM(byte_size)}, Args} + end, + BinValues + ), + Sizes1 = list_to_list_ast(Anno, Sizes), + { + PreInstruments ++ [?EMIT_INSTR(Anno, construct_pattern, [?TUPLE([?ATOM(bitstring), Sizes1])])], + {bin, Anno, BinValues1}, + [] + }; +%An empty map constructor (#{}) is untainted +instrument_expression({map, Anno, []}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], {map, Anno, []}, []}; +%When constructing a map, we pretend we are updating an empty map +instrument_expression({map, Anno, Associations}, State) -> + instrument_expression({map, Anno, {map, Anno, []}, Associations}, State); +%For updating a map we push onto the stack: +% 1) taint values for all the keys +% 2) taint values for all the values +% 3) taint value for the map being updated +% 4) construct pattern instruction with the value of keys evaluated at runtime in +% the same order as taint keys/values were pushed onto the stack +instrument_expression({map, Anno, MapToUpdate, Associations}, State) -> + {MapInstruments, MapToUpdate1, []} = ssa_iffy(instrument_expression(MapToUpdate, State)), + {KeyInstruments, ValueInstruments} = lists:unzip( + lists:map( + %AssocType can be map_field_assoc or map_field_exact, we don't care which one it is) + fun({_AssocType, _Anno, Key, Value}) -> + {ssa_iffy(instrument_expression(Key, State)), ssa_iffy(instrument_expression(Value, State))} + end, + Associations + ) + ), + {PreKeyInstruments, NewKeys, []} = unzip_flatten(KeyInstruments), + {PreValueInstruments, NewValues, []} = unzip_flatten(ValueInstruments), + Associations1 = [ + {AssocType, AnnoAssoc, Key, Value} + || {{AssocType, AnnoAssoc, _OldKey, _OldValue}, {Key, Value}} <- + lists:zip(Associations, lists:zip(NewKeys, NewValues)) + ], + Expr = {map, Anno, MapToUpdate1, Associations1}, + PreInstruments = PreKeyInstruments ++ PreValueInstruments ++ MapInstruments, + %The right most Key in the expression is top of the stack, so we reverse the list of match the order + ListOfKeys = list_to_list_ast(Anno, lists:reverse(NewKeys)), + {PreInstruments ++ [?EMIT_INSTR(Anno, construct_pattern, [?TUPLE([?ATOM(map), ListOfKeys])])], Expr, []}; +instrument_expression(Expr = {var, Anno, Var}, State) -> + {[?EMIT_INSTR(Anno, get_var, [{string, Anno, atom_to_list(Var)}])], Expr, []}; +instrument_expression(Expr = {integer, Anno, _}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Expr, []}; +instrument_expression(Expr = {string, Anno, _}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Expr, []}; +% This is a workaround to fix ?MODULE macro in modules we rename +% The ?MODULE macro is expanded before this instrumentation so we have +% to change all atoms that match the original module name +% This is needed to handle code like in proc_lib like: +% https://github.com/erlang/otp/blob/master/lib/stdlib/src/proc_lib.erl#L189 +instrument_expression({atom, Anno, Module}, State = #rewrite_state{module = Module, renamed_modules = Renames}) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], ?ATOM(maps:get(Module, Renames, Module)), []}; +% gen_server atom is used outside of gen_server too, so we have to always rename it +instrument_expression({atom, Anno, gen_server}, State = #rewrite_state{renamed_modules = Renames}) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], ?ATOM(maps:get(gen_server, Renames)), []}; +instrument_expression(Expr = {atom, Anno, _}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Expr, []}; +instrument_expression(Expr = {char, Anno, _}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Expr, []}; +instrument_expression(Expr = {float, Anno, _}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Expr, []}; +instrument_expression(Expr = {nil, Anno}, State) -> + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Expr, []}; +instrument_expression(Comprehension = {lc, _, _, _}, State) -> + instrument_expression(rewrite_comprehension(Comprehension), State); +instrument_expression(Comprehension = {mc, _, _, _}, State) -> + instrument_expression(rewrite_comprehension(Comprehension), State); +instrument_expression(Comprehension = {bc, Anno, _, _}, State) -> + % Note: implement binary comprehension + %For now we just pretend it's not tainted and don't do anything. + {[?EMIT_INSTR(Anno, push, [?NILL(Anno)])], Comprehension, []}. + +-spec instrument_binelement(erl_parse:af_binelement(expr()), #rewrite_state{}) -> + {[expr()], erl_parse:af_binelement(expr()), [expr()]}. +instrument_binelement({bin_element, Anno, Expr, Size, Tsl}, State) -> + {Pre, NewExpr, []} = ssa_iffy(instrument_expression(Expr, State)), + {Pre, {bin_element, Anno, NewExpr, Size, Tsl}, []}. + +-spec instrument_pattern(expr() | erl_parse:af_binelement(expr()), #rewrite_state{}) -> [expr()]. +% If P is a universal pattern _, then Rep(P) = {var,ANNO,'_'}. +instrument_pattern({var, Anno, '_'}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +% If P is a variable pattern V, then Rep(P) = {var,ANNO,A}, where A is an atom +instrument_pattern({var, Anno, Var}, State) -> + [?EMIT_INSTR(Anno, store_var, [{string, Anno, atom_to_list(Var)}])]; +% If P is a compound pattern P_1 = P_2, then Rep(P) = {match,ANNO,Rep(P_1),Rep(P_2)} +instrument_pattern(Expr = {match, Anno, Lhs, Rhs}, State) -> + RhsInstr = instrument_pattern(Rhs, State), + LhsInstr = instrument_pattern(Lhs, State), + TempName = get_temp_varname_for_expr(Expr, "PatternTmpVar_"), + %% We store what we are matching against + [?EMIT_INSTR(Anno, store_var, [{string, Anno, TempName}])] ++ + %% Then we put it back on top of the stack + [?EMIT_INSTR(Anno, get_var, [{string, Anno, TempName}])] ++ + %% Then pattern match the RHS + RhsInstr ++ + %% Then we get the original expression again and match against LHS + [?EMIT_INSTR(Anno, get_var, [{string, Anno, TempName}])] ++ + LhsInstr; +% Very simple handling of "a constant string" ++ AVar pattern +instrument_pattern({'op', _Anno, '++', {string, _, _}, RHS}, State) -> + instrument_pattern(RHS, State); +% Patterns over literals, ie. -1, 1 bsl 0 +% Note that using variables (-X = -1) is not legal +% so these will always be constants and we can just +% pop the respective taint value, same as the other constants +instrument_pattern({op, Anno, _Op, _}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +instrument_pattern({op, Anno, _Op, _, _}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +% If L is an integer literal, then Rep(L) = {integer,ANNO,L}. +instrument_pattern({integer, Anno, _}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +% If L is a character literal, then Rep(L) = {char,ANNO,L}. +instrument_pattern({char, Anno, _}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +% If L is a string literal consisting of the characters C_1, ..., C_k, then Rep(L) = {string,ANNO,[C_1, ..., C_k]} +instrument_pattern({string, Anno, _}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +% If L is an atom literal, then Rep(L) = {atom,ANNO,L}. +instrument_pattern({atom, Anno, _}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +instrument_pattern({float, Anno, _}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +% If P is a nil pattern [], then Rep(P) = {nil,ANNO} +instrument_pattern({nil, Anno}, State) -> + [?EMIT_INSTR(Anno, pop, [])]; +% If E is a bitstring constructor <>, +% where each Size_i is an expression and each TSL_i is a type specificer list, +% then Rep(E) = {bin,ANNO,[{bin_element,ANNO,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,ANNO,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]} +instrument_pattern({bin_element, _Anno, Expr, _Size, _Tsl}, State) -> + instrument_pattern(Expr, State); +instrument_pattern({bin, Anno, BinValues}, State) -> + % GatherSizeTsl walks the bin_elements of the bin expression + % and puts all the Sizes and Tsl into a list. The sizes and Tsl + % can be used to infer the required bit offsets in the bitstrings + % https://www.erlang.org/doc/programming_examples/bit_syntax.html#segments + TslToAst = fun + (X) when is_atom(X) -> ?ATOM(X); + ({X, I}) -> ?TUPLE([?ATOM(X), ?INT(I)]) + end, + + GatherSizeTsl = fun + GatherSizeTsl([], Acc) -> + Acc; + GatherSizeTsl([Expr = {bin_element, _Anno, _Expr, Size, Tsl} | Tail], {AccExpr, AccSizes}) -> + ExprInstr = instrument_pattern(Expr, State), + Size1 = + case {Size, Tsl} of + {default, default} -> + ?TUPLE([?ATOM(default), ?ATOM(default)]); + {default, _} -> + ?TUPLE([ + ?ATOM(default), + list_to_list_ast(Anno, lists:map(TslToAst, Tsl)) + ]); + {_, default} -> + ?TUPLE([Size, ?ATOM(default)]); + {_Size, _Tsl} -> + ?TUPLE([Size, list_to_list_ast(Anno, lists:map(TslToAst, Tsl))]) + end, + GatherSizeTsl(Tail, {AccExpr ++ ExprInstr, AccSizes ++ [Size1]}) + end, + + {Instruments, Sizes} = GatherSizeTsl(BinValues, {[], []}), + [?EMIT_INSTR(Anno, deconstruct_pattern, [?TUPLE([?ATOM(bitstring), list_to_list_ast(Anno, Sizes)])]) | Instruments]; +% If P is a map pattern #{A_1, ..., A_k}, where each A_i is an association P_i_1 := P_i_2, +% then Rep(P) = {map,ANNO,[Rep(A_1), ..., Rep(A_k)]} +% If A is an association K => V, then Rep(A) = {map_field_assoc,ANNO,Rep(K),Rep(V)}. +% If A is an association K := V, then Rep(A) = {map_field_exact,ANNO,Rep(K),Rep(V)} +instrument_pattern({map, Anno, Associations}, State) -> + {KeyInstrumentPair, ValueInstruments} = lists:unzip( + lists:map( + % AssocType can be map_field_assoc or map_field_exact, we don't care which one it is) + fun({_AssocType, _Anno, Key, Value}) -> + {{Key, instrument_pattern(Key, State)}, instrument_pattern(Value, State)} + end, + Associations + ) + ), + {Keys, KeyInstruments} = lists:unzip(KeyInstrumentPair), + ListOfKeys = list_to_list_ast(Anno, Keys), + [?EMIT_INSTR(Anno, deconstruct_pattern, [?TUPLE([?ATOM(map), ListOfKeys])])] ++ + concat(ValueInstruments) ++ + concat(KeyInstruments); +% If P is a cons pattern [P_h | P_t], then Rep(P) = {cons,ANNO,Rep(P_h),Rep(P_t)} +instrument_pattern({cons, Anno, Head, Tail}, State) -> + TailPattern = instrument_pattern(Tail, State), + HeadPattern = instrument_pattern(Head, State), + [?EMIT_INSTR(Anno, deconstruct_pattern, [?TUPLE([?ATOM(cons)])]) | (HeadPattern ++ TailPattern)]; +% If P is a tuple pattern {P_1, ..., P_k}, then Rep(P) = {tuple,ANNO,[Rep(P_1), ..., Rep(P_k)]} +instrument_pattern({tuple, Anno, TuplePatterns}, State) -> + Instruments = lists:flatten([instrument_pattern(P, State) || P <- TuplePatterns]), + [?EMIT_INSTR(Anno, deconstruct_pattern, [?TUPLE([?ATOM(tuple), ?INT(length(TuplePatterns))])]) | Instruments]. + +% intercepted_functions/2 lets us replace some function implementation +% with ones specified in our own modules. This is mainly useful for +% intercepting stdlib functions. +-spec intercepted_functions(T, T, #rewrite_state{}) -> {T, T} when + T :: expr(). +intercepted_functions(M = {atom, _, maps}, F = {atom, _, iterator}, #rewrite_state{module = modeled_taint_maps}) -> + {M, F}; +intercepted_functions(M = {atom, _, maps}, F = {atom, _, next}, #rewrite_state{module = modeled_taint_maps}) -> + {M, F}; +intercepted_functions({atom, Anno, erlang}, {atom, _, setelement}, _State) -> + {?ATOM(finer_taint), ?ATOM(set_element)}; +intercepted_functions({atom, Anno, lists}, F = {atom, _, member}, _State) -> + {?ATOM(modeled_taint_lists), F}; +intercepted_functions({atom, Anno, lists}, F = {atom, _, keyfind}, _State) -> + {?ATOM(modeled_taint_lists), F}; +intercepted_functions({atom, Anno, lists}, F = {atom, _, reverse}, _State) -> + {?ATOM(modeled_taint_lists), F}; +intercepted_functions({atom, Anno, erlang}, {atom, _, apply}, _State) -> + {?ATOM(modeled_erlang), ?ATOM(mapply)}; +intercepted_functions({atom, Anno, erlang}, {atom, _, is_map_key}, _State) -> + {?ATOM(modeled_erlang), ?ATOM(is_map_key)}; +% Do not rewrite put/get inside modeled_erlang +intercepted_functions({atom, Anno, erlang}, {atom, _, hibernate}, #rewrite_state{module = M}) when + M =/= modeled_erlang +-> + {?ATOM(modeled_erlang), ?ATOM(mhibernate)}; +intercepted_functions({atom, Anno, erlang}, {atom, _, put}, #rewrite_state{module = M}) when M =/= modeled_erlang -> + {?ATOM(modeled_erlang), ?ATOM(mput)}; +intercepted_functions({atom, Anno, erlang}, {atom, _, get}, #rewrite_state{module = M}) when M =/= modeled_erlang -> + {?ATOM(modeled_erlang), ?ATOM(mget)}; +intercepted_functions({atom, Anno, erlang}, {atom, _, element}, _State) -> + {?ATOM(modeled_erlang), ?ATOM(elemnt)}; +intercepted_functions({atom, Anno, power_shell}, {atom, _, eval}, _State) -> + {?ATOM(modeled_power_shell), ?ATOM(eval)}; +intercepted_functions({atom, Anno, ModuleName}, Func, #rewrite_state{renamed_modules = Renames}) -> + {?ATOM(maps:get(ModuleName, Renames, ModuleName)), Func}; +% Module can be any expression +intercepted_functions(Module, Func, #rewrite_state{renamed_modules = Renames}) when is_tuple(Module) -> + Anno = element(2, Module), + ModuleExpr = + {call, Anno, {remote, Anno, ?ATOM(maps), ?ATOM(get)}, [Module, map_to_map_ast(Anno, Renames), Module]}, + {ModuleExpr, Func}. + +% If all types were exported this should be +% split_to_next_qualifier(af_qualifier_seq(), [expr() | af_guard()]) -> {[expr()| af_guard()], af_qualifier_seq()}. +-spec split_to_next_qualifier([expr() | erl_parse:af_generator()], [expr() | dynamic()]) -> + {dynamic(), [expr() | erl_parse:af_generator()]}. +split_to_next_qualifier([], Filters) -> + {lists:reverse(Filters), []}; +split_to_next_qualifier(Tail = [{generate, _, _, _} | _], Filters) -> + {lists:reverse(Filters), Tail}; +split_to_next_qualifier(Tail = [{b_generate, _, _, _} | _], Filters) -> + {lists:reverse(Filters), Tail}; +split_to_next_qualifier(Tail = [{m_generate, _, _, _} | _], Filters) -> + {lists:reverse(Filters), Tail}; +split_to_next_qualifier([Head | Tail], Filters) -> + % eqwalizer:ignore eqWAlizer limitation in occurrence typing + case erl_lint:is_guard_test(Head) of + true -> split_to_next_qualifier(Tail, [[Head] | Filters]); + _ -> {Filters, [Head | Tail]} + end. + +-spec rewrite_qualifier([expr() | erl_parse:af_generator()], erl_parse:abstract_expr(), [expr()]) -> expr(). +rewrite_qualifier([], Body, [RecursiveCall]) -> + Anno = element(2, RecursiveCall), + {cons, Anno, Body, RecursiveCall}; +rewrite_qualifier([Head = {generate, Anno, Pattern, ListExpr} | Tail0], Body, EmptyClauseBody) -> + {Guards, Tail} = split_to_next_qualifier(Tail0, []), + Suffix = expr_hash(Head), + GenLc = list_to_atom("GenLc" ++ Suffix), + GenLcVar = {var, Anno, GenLc}, + TailVar = {var, Anno, list_to_atom("GenlcTail" ++ Suffix)}, + % GenLc([]) -> ; + EmptyClause = {clause, Anno, [{nil, Anno}], [], EmptyClauseBody}, + % GenLc([Pattern | TailVar]) -> ; + MainClause = + {clause, Anno, [{cons, Anno, Pattern, TailVar}], Guards, [ + rewrite_qualifier(Tail, Body, [{call, Anno, GenLcVar, [TailVar]}]) + ]}, + % GenLc([_ | TailVar]) -> GenLc(TailVar); + FallbackClause = {clause, Anno, [{cons, Anno, {var, Anno, '_'}, TailVar}], [], [{call, Anno, GenLcVar, [TailVar]}]}, + Clauses = [EmptyClause, MainClause, FallbackClause], + {call, Anno, {named_fun, Anno, GenLc, Clauses}, [ListExpr]}; +rewrite_qualifier( + [ + {m_generate, Anno, {map_field_exact, _Anno, K, V}, MapExpr} + | Tail0 + ], + Body, + EmptyClauseBody +) -> + ToListExpr = {call, Anno, {remote, Anno, ?ATOM(modeled_taint_maps), ?ATOM(to_list)}, [MapExpr]}, + TuplePattern = ?TUPLE([K, V]), + rewrite_qualifier([{generate, Anno, TuplePattern, ToListExpr} | Tail0], Body, EmptyClauseBody); +rewrite_qualifier([Head = {b_generate, Anno, Pattern, BinaryExpr} | Tail0], Body, EmptyClauseBody) -> + {Guards, Tail} = split_to_next_qualifier(Tail0, []), + Suffix = expr_hash(Head), + BinGenLc = list_to_atom("BinGenLc" ++ Suffix), + BinGenLcVar = {var, Anno, BinGenLc}, + TailVar = {var, Anno, list_to_atom("BinGenLcTail" ++ Suffix)}, + TailSegment = {bin_element, Anno, TailVar, default, [binary, {unit, 1}]}, + % GenLc(<>) -> ; + {bin, _, Segments} = Pattern, + MainPattern = {bin, Anno, lists:append(Segments, [TailSegment])}, + MainClause = + {clause, Anno, [MainPattern], Guards, [ + rewrite_qualifier(Tail, Body, [{call, Anno, BinGenLcVar, [TailVar]}]) + ]}, + % GenLc(<<_ | TailVar>>) -> GenLc(TailVar); + FallbackClause = {clause, Anno, [MainPattern], [], [{call, Anno, BinGenLcVar, [TailVar]}]}, + % GenLc(<<_>>) -> ; + EmptyClause = {clause, Anno, [{bin, Anno, [TailSegment]}], [], EmptyClauseBody}, + Clauses = [MainClause, FallbackClause, EmptyClause], + {call, Anno, {named_fun, Anno, BinGenLc, Clauses}, [BinaryExpr]}; +rewrite_qualifier([BoolExpr | Tail], Body, EmptyClauseBody) -> + Anno = element(2, lists:nth(1, EmptyClauseBody)), + TrueClause = {clause, Anno, [?ATOM(true)], [], [rewrite_qualifier(Tail, Body, EmptyClauseBody)]}, + FalseClause = {clause, Anno, [?ATOM(false)], [], EmptyClauseBody}, + % Eqwalizer cannot figure out BoolExpr is just expr() and not af_generator() + % eqwalizer:ignore This should be cast_dynamic, but can't import eqwalizer module here because parse transform + {'case', Anno, BoolExpr, [TrueClause, FalseClause]}. + +% Reverse engineered from core erlang transforms, see +% https://fb.workplace.com/groups/314858790176161/permalink/457623689233003/ +-spec rewrite_comprehension(expr()) -> expr(). +rewrite_comprehension({lc, Anno, ExprBody, Qualifiers}) -> + rewrite_qualifier(Qualifiers, ExprBody, [{nil, Anno}]); +rewrite_comprehension({mc, Anno, {map_field_assoc, _Anno, K, V}, Qualifiers}) -> + TupleExpr = ?TUPLE([K, V]), + ListExpr = rewrite_qualifier(Qualifiers, TupleExpr, [{nil, Anno}]), + {call, Anno, {remote, Anno, ?ATOM(modeled_taint_maps), ?ATOM(from_list)}, [ListExpr]}. + +-spec get_all_variable_names([expr() | clause()]) -> [atom()]. +get_all_variable_names(Forms) -> + AllVars = lists:foldl( + fun(Form, ListAcc) -> + erl_syntax_lib:fold( + fun + ({var, _Anno, Name}, Acc) -> + [Name | Acc]; + (_, Acc) -> + Acc + end, + ListAcc, + Form + ) + end, + [], + Forms + ), + lists:usort(AllVars). + +-spec compile_file(file:filename(), options()) -> {module(), binary()}. +compile_file(Forms, Options) -> + {ok, Module, <>} = compile:file(Forms, Options), + {Module, Binary}. + +-spec compile_forms(forms()) -> {module(), binary()}. +compile_forms(Forms) -> + {ok, Module, <>} = compile:forms(Forms, [debug_info]), + {Module, Binary}. + +-spec load_forms(binary()) -> {module(), forms()}. +load_forms(Binary) -> + {ok, {Module, [{abstract_code, {_, Forms}} | _]}} = beam_lib:chunks(Binary, [abstract_code, compile_info]), + % eqwalizer:ignore optimistic use of beam_lib API + {Module, Forms}. + +-spec ms_transform(forms(), options()) -> forms(). +ms_transform(Forms, Options) -> + % eqwalizer:ignore optimistic use of ms_transform API + ms_transform:parse_transform(Forms, Options). diff --git a/finer_taint/src/models/modeled_erlang.erl b/finer_taint/src/models/modeled_erlang.erl new file mode 100644 index 0000000..9ce3e91 --- /dev/null +++ b/finer_taint/src/models/modeled_erlang.erl @@ -0,0 +1,169 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +-module(modeled_erlang). +-compile(warn_missing_spec_all). +-wacov(ignore). + +-export([mapply/3, mapply/2, is_map_key/2, elemnt/2, mput/2, mget/1, real_put/2, mhibernate/3]). + +% This module contains some models for functions in the erlang +% module: https://www.erlang.org/doc/man/erlang.html +% There is a lot of functions in the erlang module and +% not all of them need a model. Models should be added on-demand +% as needed. +% +% Examples of functions not needing a model: +% * spawn family is handled by the instrumentation directly in finer_taint.erl +% * is_* functions likely don't need a model, because they return boolean which isn't tainted +% +% binary_to_list/iolist_to_* and similar might need a model +% if we want to accurantely track taint through these datastructures +% + +-spec mapply(atom(), atom(), list()) -> term(). +mapply(M, F, []) -> + M:F(); +mapply(M, F, [Arg1]) -> + M:F(Arg1); +mapply(M, F, [Arg1, Arg2]) -> + M:F(Arg1, Arg2); +mapply(M, F, [Arg1, Arg2, Arg3]) -> + M:F(Arg1, Arg2, Arg3); +mapply(M, F, [Arg1, Arg2, Arg3, Arg4]) -> + M:F(Arg1, Arg2, Arg3, Arg4); +mapply(M, F, [Arg1, Arg2, Arg3, Arg4, Arg5]) -> + M:F(Arg1, Arg2, Arg3, Arg4, Arg5); +mapply(M, F, [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6]) -> + M:F(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); +mapply(M, F, [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7]) -> + M:F(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7); +mapply(M, F, [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8]) -> + M:F(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8). + +-spec is_map_key(Key, #{Key => term()}) -> boolean(). +is_map_key(Key, Map) -> + maps:is_key(Key, Map). + +-spec elemnt(integer(), tuple()) -> term(). +elemnt(1, {A1}) -> + A1; +elemnt(1, {A1, _A2}) -> + A1; +elemnt(1, {A1, _A2, _A3}) -> + A1; +elemnt(1, {A1, _A2, _A3, _A4}) -> + A1; +elemnt(1, {A1, _A2, _A3, _A4, _A5}) -> + A1; +elemnt(1, {A1, _A2, _A3, _A4, _A5, _A6}) -> + A1; +elemnt(1, {A1, _A2, _A3, _A4, _A5, _A6, _A7}) -> + A1; +elemnt(1, {A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8}) -> + A1; +elemnt(1, {A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8, _A9}) -> + A1; +elemnt(2, {_A1, A2}) -> + A2; +elemnt(2, {_A1, A2, _A3}) -> + A2; +elemnt(2, {_A1, A2, _A3, _A4}) -> + A2; +elemnt(2, {_A1, A2, _A3, _A4, _A5}) -> + A2; +elemnt(2, {_A1, A2, _A3, _A4, _A5, _A6}) -> + A2; +elemnt(2, {_A1, A2, _A3, _A4, _A5, _A6, _A7}) -> + A2; +elemnt(2, {_A1, A2, _A3, _A4, _A5, _A6, _A7, _A8}) -> + A2; +elemnt(2, {_A1, A2, _A3, _A4, _A5, _A6, _A7, _A8, _A9}) -> + A2; +elemnt(3, {_A1, _A2, A3}) -> + A3; +elemnt(3, {_A1, _A2, A3, _A4}) -> + A3; +elemnt(3, {_A1, _A2, A3, _A4, _A5}) -> + A3; +elemnt(3, {_A1, _A2, A3, _A4, _A5, _A6}) -> + A3; +elemnt(3, {_A1, _A2, A3, _A4, _A5, _A6, _A7}) -> + A3; +elemnt(3, {_A1, _A2, A3, _A4, _A5, _A6, _A7, _A8}) -> + A3; +elemnt(3, {_A1, _A2, A3, _A4, _A5, _A6, _A7, _A8, _A9}) -> + A3; +elemnt(Index, Tuple) -> + lists:nth(Index, tuple_to_list(Tuple)). + +-spec mapply(fun(), list()) -> term(). +mapply(F, []) -> + F(); +mapply(F, [Arg1]) -> + F(Arg1); +mapply(F, [Arg1, Arg2]) -> + F(Arg1, Arg2); +mapply(F, [Arg1, Arg2, Arg3]) -> + F(Arg1, Arg2, Arg3); +mapply(F, [Arg1, Arg2, Arg3, Arg4]) -> + F(Arg1, Arg2, Arg3, Arg4); +mapply(F, [Arg1, Arg2, Arg3, Arg4, Arg5]) -> + F(Arg1, Arg2, Arg3, Arg4, Arg5); +mapply(F, [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6]) -> + F(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); +mapply(F, [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7]) -> + F(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7); +mapply(F, [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8]) -> + F(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8). + +% erlang:get/1 and erlang:put/2 are not rewritten in modeled erlang +% process_dict/0,1 is NOT instrumented by finer taint instrumentation +% instead it is modeled to set or return a process dict taint value +% inside the taint_abstract_machine. We still give an implementation +% here so that it works in real runs. +-spec process_dict() -> map(). +process_dict() -> + case erlang:get(process_dict) of + undefined -> #{}; + Dict -> Dict + end. + +-spec process_dict(map()) -> ok. +process_dict(Pd) -> + erlang:put(process_dict, Pd). + +% This is not instrumented so that parralel_taint_SUITE can put taint pids +% in process dictonary +-spec real_put(term(), term()) -> undefined | term(). +real_put(Key, Value) -> + erlang:put(Key, Value). + +-spec mput(term(), term()) -> undefined | map(). +mput(Key, Value) -> + Pd = process_dict(), + ReturnValue = maps:get(Key, Pd, undefined), + NewDict = Pd#{Key => Value}, + process_dict(NewDict), + ReturnValue. + +-spec mget(term()) -> undefined | term(). +mget(Key) -> + Pd = process_dict(), + maps:get(Key, Pd, undefined). + +-spec mhibernate(module(), atom(), list()) -> no_return(). +mhibernate(M, F, Args) -> + erlang:hibernate(?MODULE, mapply, [M, F, Args]). diff --git a/finer_taint/src/models/modeled_power_shell.erl b/finer_taint/src/models/modeled_power_shell.erl new file mode 100644 index 0000000..ccba434 --- /dev/null +++ b/finer_taint/src/models/modeled_power_shell.erl @@ -0,0 +1,31 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +%% Shallow, but usable, model of power_shell +-module(modeled_power_shell). +-compile(warn_missing_spec_all). +-wacov(ignore). +-export([ + eval/3 +]). + +% +%% Model a call to eval as exporting all + apply. +%% This is actually better for testing since we will be running compiled code, +%% not interpreted. So both faster and semantically equivalent. +-spec eval(module(), atom(), list()) -> term(). +eval(Module, Function, Args) -> + power_shell:export(Module), + apply(Module, Function, Args). diff --git a/finer_taint/src/models/modeled_taint_lists.erl b/finer_taint/src/models/modeled_taint_lists.erl new file mode 100644 index 0000000..f157615 --- /dev/null +++ b/finer_taint/src/models/modeled_taint_lists.erl @@ -0,0 +1,60 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +-module(modeled_taint_lists). +-compile(warn_missing_spec_all). + +% This module implements the nifs for the lists module +-export([ + keyfind/3, + keysearch/3, + member/2, + reverse/1, + reverse/2 +]). + +% +-spec member(T, [T]) -> boolean(). +member(_X, []) -> + false; +member(X, [X | _]) -> + true; +member(X, [_ | Tail]) -> + member(X, Tail). + +-spec reverse([T]) -> [T]. +reverse(List) -> + reverse(List, []). +-spec reverse([T], [T]) -> [T]. +reverse([], Acc) -> + Acc; +reverse([H | T], Acc) -> + reverse(T, [H | Acc]). + +-spec keyfind(term(), integer(), [tuple()]) -> tuple() | false. +keyfind(_, _, []) -> + false; +keyfind(Key, N, [Head | Tail]) -> + case element(N, Head) of + Key -> Head; + _ -> keyfind(Key, N, Tail) + end. + +-spec keysearch(term(), integer(), [tuple()]) -> {value, tuple()} | false. +keysearch(Key, N, List) -> + case keyfind(Key, N, List) of + false -> false; + Val -> {value, Val} + end. diff --git a/finer_taint/src/models/modeled_taint_maps.erl b/finer_taint/src/models/modeled_taint_maps.erl new file mode 100644 index 0000000..a3afc76 --- /dev/null +++ b/finer_taint/src/models/modeled_taint_maps.erl @@ -0,0 +1,298 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +-module(modeled_taint_maps). +-compile(warn_missing_spec_all). +-wacov(ignore). + +% This module should be equivalent to maps module +% https://www.erlang.org/doc/man/maps.html . +% Its used to implement all the functions in maps +% that are otherwise implemented in C. + +-export([ + get/2, get/3, + fold/3, + without/2, + iterator/1, + filter/2, + new/0, + filtermap/2, + next/1, + merge/2, + take/2, + keys/1, + is_key/2, + values/1, + update_with/3, + update_with/4, + from_list/1, + from_keys/2, + groups_from_list/3, + with/2, + foreach/2, + size/1, + remove/2, + to_list/1, + put/3, + map/2, + find/2 +]). + +% Space to keep line numbers the same so we don't have to update tests, delete +% if adding a small amount of lines instead of updating test fixture +% +% +% +% +% +% +% +% +% + +-spec get(term(), map(), term()) -> term(). +% Health metrics is a gen_factory setting that creates a very noisy gen_server +% which is not interesting to taint analayis. We disable it here. +% https://www.internalfb.com/code/whatsapp-server/[720c0b71b314]/erl/wa/src/gen_factory.erl?lines=1459 +get(report_health_metrics, _, _) -> + false; +get(Key, Map, Default) -> + try get(Key, Map) of + Val -> Val + catch + error:{badmatch, _} -> Default + end. + +-spec get(term(), map()) -> term(). +get(Key, Map) -> + #{Key := Value} = Map, + Value. + +-spec find(Key, #{Key => Value, term() => term()}) -> {ok, Value} | error. +find(Key, Map) -> + fold( + fun + (K, V, error) when K =:= Key -> + {ok, V}; + (_K, _V, {ok, Val}) -> + {ok, Val}; + (_K, _V, error) -> + error + end, + error, + Map + ). + +-spec put(term(), term(), map()) -> term(). +put(Key, Value, Map) -> + Map#{Key => Value}. + +-spec take(term(), map()) -> {term(), map()} | error. +take(Key, Map) -> + case get(Key, Map, error) of + error -> + error; + Value -> + NewMap = fold( + fun + (MatchedKey, _, Acc) when MatchedKey =:= Key -> Acc; + (NonMatchedKey, NonMatchedValue, Acc) -> Acc#{NonMatchedKey => NonMatchedValue} + end, + #{}, + Map + ), + {Value, NewMap} + end. + +-spec keys(map()) -> [term()]. +keys(Map) -> + fold(fun(Key, _, Acc) -> [Key | Acc] end, [], Map). + +-spec values(#{term() => Values}) -> [Values]. +values(Map) -> + fold(fun(_, Value, Acc) -> [Value | Acc] end, [], Map). + +-spec merge(map(), map()) -> map(). +merge(Map1, Map2) -> + fold(fun(Map1Key, Map1Val, Acc) -> Acc#{Map1Key => Map1Val} end, Map2, Map1). + +-spec from_list(list()) -> map(). +from_list(List) -> + from_list_impl(List, #{}). + +-spec from_list_impl(list(), map()) -> map(). +from_list_impl([], Map) -> + Map; +from_list_impl([{Key, Value} | Tail], Map) -> + from_list_impl(Tail, Map#{Key => Value}). + +-spec with([K], #{K => V, term() => term()}) -> #{K => V}. +with(Ks, Map) -> + with_impl(Ks, Map, #{}). + +-spec with_impl(list(), map(), map()) -> map(). +with_impl([], _Map, Acc) -> + Acc; +with_impl([Key | Tail], Map, Acc) -> + case modeled_taint_maps:get(Key, Map, notfound) of + notfound -> with_impl(Tail, Map, Acc); + Value -> with_impl(Tail, Map, Acc#{Key => Value}) + end. + +-spec size(map()) -> integer(). +size(Map) -> + fold(fun(_, _, Cnt) -> Cnt + 1 end, 0, Map). + +-spec remove(term(), map()) -> map(). +remove(Key, Map) -> + fold( + fun(K, V, Acc) -> + if + K =:= Key -> Acc; + true -> Acc#{K => V} + end + end, + #{}, + Map + ). + +-spec to_list(#{Key => Value}) -> [{Key, Value}]. +to_list(Map) -> + fold(fun(K, V, Acc) -> [{K, V} | Acc] end, [], Map). + +-spec map(fun((Key, Value1) -> Value2), #{Key => Value1}) -> #{Key => Value2}. +map(F, Map) -> + fold(fun(Key, Value, Acc) -> Acc#{Key => F(Key, Value)} end, #{}, Map). + +-spec fold(fun((Key, Value, Acc) -> Acc), Acc, #{Key => Value}) -> Acc. +fold(Fun, Acc, Map) when is_map(Map) -> + Iter = maps:iterator(Map), + fold_impl(Fun, Acc, Iter, Map). + +-spec fold_impl( + fun((Key, Value, Acc) -> Acc), + Acc, + maps:iterator(Key, Value), + #{Key => Value} +) -> Acc. +fold_impl(Fun, Acc, MapIter, Map) -> + % maps:next is special cased to never return tainted value + case maps:next(MapIter) of + none -> + Acc; + {IterKey, _Val, NextIter} -> + %We need to lookup the key again in order to "catch" the taint + #{IterKey := Value} = Map, + fold_impl(Fun, Fun(IterKey, Value, Acc), NextIter, Map) + end. + +-spec is_key(Key, #{Key => term()}) -> boolean(). +is_key(Key, Map) -> + try modeled_taint_maps:get(Key, Map) of + _ -> true + catch + error:{badmatch, _} -> false + end. + +-spec new() -> map(). +new() -> + #{}. + +-spec update_with(term(), fun((term()) -> term()), map()) -> map(). +update_with(Key, Fun, Map) -> + Val = modeled_taint_maps:get(Key, Map), + Map#{Key => Fun(Val)}. + +-spec update_with(term(), fun((term()) -> term()), term(), map()) -> + map(). +update_with(Key, Fun, Init, Map) -> + case modeled_taint_maps:get(Key, Map, notthere) of + notthere -> Map#{Key => Init}; + Val -> Map#{Key => Fun(Val)} + end. + +-spec iterator(map()) -> {map(), maps:iterator()}. +iterator(Map) -> + {Map, maps:iterator(Map)}. + +-spec next({map(), maps:iterator()}) -> {term(), term(), {map(), maps:iterator()}} | none. +next({Map, Iterator}) -> + case maps:next(Iterator) of + none -> + none; + {RawKey, _Value, Iterator2} -> + %We need to lookup the key again in order to "catch" the taint + #{RawKey := Value} = Map, + {RawKey, Value, {Map, Iterator2}} + end. + +-spec groups_from_list(fun((Elem) -> Key), fun((Elem) -> ValOut), [Elem]) -> #{Key := [ValOut]}. +groups_from_list(Fun, ValueFun, List) -> + groups_from_list_impl(Fun, ValueFun, List, #{}). + +-spec groups_from_list_impl(fun((Elem) -> Key), fun((Elem) -> ValOut), [Elem], #{Key := [ValOut]}) -> + #{Key := [ValOut]}. +groups_from_list_impl(KeyFun, ValueFun, [H | T], Acc) -> + Key = KeyFun(H), + OldList = maps:get(Key, Acc, []), + groups_from_list_impl(KeyFun, ValueFun, T, Acc#{Key => OldList ++ [ValueFun(H)]}); +groups_from_list_impl(_, _, [], Acc) -> + Acc. + +-spec foreach(fun((term(), term()) -> term()), map()) -> ok. +foreach(Fun, Map) -> + map(Fun, Map), + ok. + +-spec without(Ks, Map1) -> Map2 when + Ks :: [K], + Map1 :: map(), + Map2 :: map(), + K :: term(). + +without(Ks, M) when is_list(Ks), is_map(M) -> + lists:foldl(fun remove/2, M, Ks). + +% Relaxing the spec of filter so that it can be reused for filtermap +-spec filter(Pred, map()) -> map() when + Pred :: fun((term(), term()) -> boolean() | {true, term()}). +filter(Pred, Map) -> + fold( + fun(K, V, Acc) -> + case Pred(K, V) of + false -> Acc; + {true, NewVal} -> Acc#{K => NewVal}; + true -> Acc#{K => V} + end + end, + #{}, + Map + ). + +-spec filtermap(Fun, map()) -> map() when + Fun :: fun((term(), term()) -> boolean() | {true, term()}). +filtermap(Fun, Map) -> + filter(Fun, Map). + +-spec from_keys(list(), term()) -> map(). +from_keys(List, Value) -> + from_keys_impl(List, Value, #{}). + +-spec from_keys_impl(list(), term(), map()) -> map(). +from_keys_impl([], _, Map) -> + Map; +from_keys_impl([Key | Tail], Value, Map) -> + from_keys_impl(Tail, Value, Map#{Key => Value}). diff --git a/finer_taint/src/online_finer_taint.erl b/finer_taint/src/online_finer_taint.erl new file mode 100644 index 0000000..2b5f8cc --- /dev/null +++ b/finer_taint/src/online_finer_taint.erl @@ -0,0 +1,46 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%% Implementation of the finer_taint behaviour that handles parallelism +%% in an online mode. That is the finer taint analysis is ran as the +%% program under test is executing and not afterwards. +-module(online_finer_taint). +-compile(warn_missing_spec_all). +-behaviour(finer_taint). + +%% Finer taint callbacks. +-export([ + write_instruction/1 +]). + +% Similar to self(), but returns a pid() of the abstract machine proclet +% shadowing this process +-spec slf() -> pid(). +slf() -> + case get(taint_proclet_pid) of + undefined -> + put(taint_proclet_pid, abstract_machine_proclet_sup:new_proclet()), + slf(); + TaintPid -> + TaintPid + end. + +-spec write_instruction(taint_abstract_machine:instruction()) -> ok. +write_instruction(Instruction) -> + case get(is_abs_proclet) of + true -> error(recursive_call_in_finer_taint); + undefined -> ok + end, + abstract_machine_proclet:execute_instruction(slf(), Instruction). diff --git a/finer_taint/src/online_finer_taint_sup.erl b/finer_taint/src/online_finer_taint_sup.erl new file mode 100644 index 0000000..e2c22c6 --- /dev/null +++ b/finer_taint/src/online_finer_taint_sup.erl @@ -0,0 +1,65 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +%% @doc +%% A top level supervisor for abstract machines, that starts +%% everything needed for executing multiple taint_abstract_machines +%% +%% NOTE: currently this is missing taint_message_passer initialization +%% @end + +-module(online_finer_taint_sup). +-compile(warn_missing_spec_all). + +-behaviour(supervisor). + +-export([start_link/1, start_link/0]). + +-export([init/1]). + +-spec start_link() -> supervisor:startlink_ret(). +start_link() -> + start_link(#{}). +-spec start_link(map()) -> supervisor:startlink_ret(). +start_link(AbstractMachineArgs) -> + supervisor:start_link({local, ?MODULE}, ?MODULE, [AbstractMachineArgs]). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +-spec init([map()]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. +init([AbstractMachineArgs]) -> + SupFlags = #{ + strategy => one_for_all, + intensity => 0, + period => 1 + }, + ChildSpecs = [ + #{ + id => proclets, + start => {abstract_machine_proclet_sup, start_link, [AbstractMachineArgs]} + }, + #{ + id => gatherer, + start => {taint_gatherer, start_link, []} + } + ], + {ok, {SupFlags, ChildSpecs}}. diff --git a/finer_taint/src/parallel_abstract_machine.erl b/finer_taint/src/parallel_abstract_machine.erl new file mode 100644 index 0000000..cbfca0c --- /dev/null +++ b/finer_taint/src/parallel_abstract_machine.erl @@ -0,0 +1,79 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%% Runs multiple instructions streams exchanging messages +-module(parallel_abstract_machine). +-compile(warn_missing_spec_all). + +-include_lib("kernel/include/logger.hrl"). +-export([run/1, run_tracing/1, run_lineage/1, run_tracing_lineage/1, run_lineage_with_line_history/1]). + +% Takes a list of filenames containg instruction streams. +% Runs all the instructions and returns a list of leaks it found. +% Leaks are returned in reverse order as they are found, but the +% order might not be deterministic due to scheduling differences +-spec run([string()]) -> taint_abstract_machine:leaks(). +run(Filepaths) -> + run_impl(Filepaths, #{}). + +-spec run_tracing([string()]) -> taint_abstract_machine:leaks(). +run_tracing(Filepaths) -> + run_impl(Filepaths, #{tracing => true}). + +% Runs lineage analysis in the recommended setting (with function_history). +% Meaning it keeps track of function arguments that a value passed through. +% For more info look at lineage_mode() type. +-spec run_lineage([string()]) -> taint_abstract_machine:leaks(). +run_lineage(Filepaths) -> + run_impl(Filepaths, #{lineage_mode => function_history}). + +% Runs lineage analysis, while keeping track of every line a value has passed through. +% This enables use of -query_arg_lineage in run_finer_taint escript, but it is very +% expensive and unlikely to run on our code. For more info look at lineage_mode() type. +-spec run_lineage_with_line_history([string()]) -> taint_abstract_machine:leaks(). +run_lineage_with_line_history(Filepaths) -> + run_impl(Filepaths, #{lineage_mode => line_history}). + +% Runs lineage analysis with function history, while printing the instruction and the complete +% abstract machine state before executing each abstract machine instruction +% very VERBOSE, useful only for debugging small programs to see how an abstract machine executed +-spec run_tracing_lineage([string()]) -> taint_abstract_machine:leaks(). +run_tracing_lineage(Filepaths) -> + run_impl(Filepaths, #{lineage_mode => function_history, tracing => true}). + +-spec run_impl([string()], map()) -> taint_abstract_machine:leaks(). +run_impl(Filepaths, TaintMachineArgs) -> + {ok, SupPid} = online_finer_taint_sup:start_link(TaintMachineArgs), + % Start 1 proclet for each filepath + Proclets = [abstract_machine_proclet_sup:new_proclet() || _ <- Filepaths], + % Tell the proclets to start executing the file + [ + abstract_machine_proclet:run_instructions_file(Pid, Fp) + || {Pid, Fp} <- lists:zip(Proclets, Filepaths) + ], + % Stop all proclets, thus telling them they won't get any new instructions + abstract_machine_proclet_sup:stop_all_proclets(), + Leaks = taint_gatherer:get_gathered_leaks(taint_gatherer, 30000, Proclets), + Ref = monitor(process, SupPid), + unlink(SupPid), + exit(SupPid, shutdown), + receive + {'DOWN', Ref, process, SupPid, shutdown} -> + ok + after 10000 -> + ?LOG_WARNING("Failed to shutdown ~p~n", [SupPid]) + end, + + taint_abstract_machine:map_leaks_to_leaks(Leaks). diff --git a/finer_taint/src/parallel_finer_taint.erl b/finer_taint/src/parallel_finer_taint.erl new file mode 100644 index 0000000..488882a --- /dev/null +++ b/finer_taint/src/parallel_finer_taint.erl @@ -0,0 +1,48 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%% Implementation of the finer_taint behaviour that handles parallelism +%% It uses abstract_machine_server to split the instruction streams per +%% "taint_pid". The taint_pid should be very similar to normal pid(), +%% just created via the slf() function. +-module(parallel_finer_taint). +-compile(warn_missing_spec_all). +-behaviour(finer_taint). + +%% Finer taint callbacks. +-export([ + write_instruction/1 +]). + +% Similar to self(), but returns a "taint_pid" +-spec slf() -> abstract_machine_server:tid(). +slf() -> + case get(taint_pid) of + undefined -> + put(taint_pid, erlang:unique_integer([positive]) * 100000 + (erlang:system_time(seconds) rem (24 * 3600))), + abstract_machine_server:write_instruction( + slf(), {push, {lists:flatten(io_lib:format("% ancestors ~p", [get('$ancestors')]))}} + ), + abstract_machine_server:write_instruction( + slf(), {pop, {}} + ), + slf(); + TaintPid -> + TaintPid + end. + +-spec write_instruction(taint_abstract_machine:instruction()) -> ok. +write_instruction(Instruction) -> + abstract_machine_server:write_instruction(slf(), Instruction). diff --git a/finer_taint/src/run_finer_taint_escript.erl b/finer_taint/src/run_finer_taint_escript.erl new file mode 100644 index 0000000..9cec7b5 --- /dev/null +++ b/finer_taint/src/run_finer_taint_escript.erl @@ -0,0 +1,261 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format + +-module(run_finer_taint_escript). +-compile(warn_missing_spec_all). + +%% API exports +-export([main/1]). + +%%==================================================================== +%% Escript that runs the finer taint abstract machine and +%% produces useable outputs +%%==================================================================== +-type stack() :: list(mfa()). + +%% escript Entry point +-spec main([string()]) -> ok. +main(["infer-traceview", Path]) -> + main(infer, Path); +main(["dot", Path]) -> + main(dot, Path); +main(["run-lineage" | Tail]) -> + run_subcommand(run_lineage, Tail); +main(["run-line-lineage" | Tail]) -> + run_subcommand(run_lineage_with_line_history, Tail); +main(["run-tracing-lineage" | Tail]) -> + run_subcommand(run_tracing_lineage, Tail); +main(["run" | Tail]) -> + run_subcommand(run, Tail); +main(["run-tracing" | Tail]) -> + run_subcommand(run_tracing, Tail); +main(["flamegraph" | Tail]) -> + run_subcommand(flamegraph, Tail); +main(_) -> + SubCommandHelp = + "\n" + " is of the form\n" + "\n" + " [analysis_instruction_files]+ \n" + "\n" + "SUBCOMMAND\n" + "\n" + " chooses which function to use to process [analysis_instruction_files]\n" + "flamegraph generate a flamegraph of finer_taint instructions\n" + "run-lineage run parallel_abstract_machine:run_lineage on [analysis_instruction_files]\n" + "run-tracing-lineage run parallel_abstract_machine:run_tracing_lineage on [analysis_instruction_files]\n" + "run run parallel_abstract_machine:run on [analysis_instruction_files]\n" + " This runs the analysis in non-lineage mode\n" + "\n" + "PROCESSING COMMANDS\n" + "\n" + " apply a function the output of or previous \n" + "-print Print using io:format(~~s)\n" + "-pprint Print using io:format(~~p)\n" + "-to-file FILE Write to FILE\n" + "-count Get number of elements\n" + "-arg-lineage-hr Get arg lineage in human_readable form\n" + "-arg-lineage-csv Get arg lineage in csv form\n" + "-query-arg-lineage QUERY Query full arg lineage for QUERY\n" + "\n" + "\n" + "QUERY has the same format as one line in the human_readable form, ie.\n" + "example_gen_server:store_value/2-Arg1 -> example_gen_server:handle_call/3-Arg2\n" + "\n" + "\n" + "EXMAPLES:\n" + "\n" + "run_finer_taint run-lineage util/taint/test/parallel_taint_SUITE_data/test_gen_server_an* -query-arg-lineage \"example_gen_server:store_value/2-Arg1 -> example_gen_server:handle_call/3-Arg2\" -pprint\n" + "\n" + "Runs parallel_abstract_machine:run_lineage on all files matching the wildcard, finds\n" + "all the paths that had a flow between store_value/2 argument 1 and handle_Call/3 argument2\n" + "and prints them with ~~p.\n" + "\n" + "\n" + "\n" + "run_finer_taint run-lineage /tmp/abstr_instr-* -arg-lineage-csv -to-file /tmp/ctsd_SUITE_lineage.csv\n" + "\n" + "Gets the lineage from all files in /tmp/abstr_inst* , get the lineage in CSV format and writes it to /tmp/ctsd_SUITE_lineage.csv\n", + io:format("Usage: ~n~n"), + io:format("\trun_finer_taint dot path/to/abstract_machine_instructions~n"), + io:format("\trun_finer_taint infer-traceview path/to/abstract_machine_instructions~n"), + io:format("\trun_finer_taint dynamic_lineage path/to/dynamic_lineage.cfg ~n", []), + io:format("\trun_finer_taint detect_leaks path/to/dynamic_lineage.cfg ~n", []), + io:format("\trun_finer_taint ~n~n~s", [SubCommandHelp]). + +-spec run_subcommand(atom(), list(string())) -> ok. +run_subcommand(flamegraph, Tail) -> + {Paths, Commands} = lists:splitwith( + fun + ([$- | _]) -> false; + (_) -> true + end, + Tail + ), + Flamegraph = flamegraph(Paths), + extract_from_state_subcommand(Flamegraph, Commands); +run_subcommand(RunFunction, Tail) -> + Handler = default, + ok = logger:set_primary_config(level, info), + ok = logger:update_formatter_config(Handler, template, [msg]), + ok = logger:update_proxy_config(#{burst_limit_enable => false, overload_kill_qlen => 200000}), + ok = logger:update_handler_config(Handler, config, #{burst_limit_enable => false}), + logger:add_handler_filter(Handler, no_progress, {fun logger_filters:progress/2, stop}), + {Paths, Commands} = lists:splitwith( + fun + ([$- | _]) -> false; + (_) -> true + end, + Tail + ), + + io:format("Running on ~p~n", [Paths]), + application:ensure_all_started(taint_server), + Leaks = parallel_abstract_machine:RunFunction(Paths), + io:format("Done running abstract machine~n"), + extract_from_state_subcommand(Leaks, Commands). + +-spec flamegraph(list(file:filename())) -> list(string()). +flamegraph(Paths) -> + StackMap = lists:foldl( + fun(Path, Acc) -> + {ok, Instructions} = file:consult(Path), + flamegraph(Instructions, [], Acc) + end, + #{}, + Paths + ), + maps:fold( + fun(Stack, Count, Acc) -> + StringStack = [lists:flatten(io_lib:format("~p:~p/~p", [M, F, A])) || {M, F, A} <- lists:reverse(Stack)], + Stck = string:join(StringStack, ";"), + [Stck, " ", integer_to_list(Count), "\n" | Acc] + end, + [], + StackMap + ). + +-define(TWO_RECURSIVE_LOOP, + {M1, F1, A1}, + {M2, F2, A2} +). + +-define(THREE_RECURSIVE_LOOP, + ?TWO_RECURSIVE_LOOP, + {M3, F3, A3} +). + +-define(FOUR_RECURSIVE_LOOP, + ?THREE_RECURSIVE_LOOP, + {M4, F4, A4} +). + +-spec remove_recursive_loops(stack()) -> stack(). +remove_recursive_loops([?FOUR_RECURSIVE_LOOP, ?FOUR_RECURSIVE_LOOP | Stack]) -> + remove_recursive_loops([?FOUR_RECURSIVE_LOOP | remove_recursive_loops(Stack)]); +remove_recursive_loops([?THREE_RECURSIVE_LOOP, ?THREE_RECURSIVE_LOOP | Stack]) -> + remove_recursive_loops([?THREE_RECURSIVE_LOOP | remove_recursive_loops(Stack)]); +remove_recursive_loops([?TWO_RECURSIVE_LOOP, ?TWO_RECURSIVE_LOOP | Stack]) -> + remove_recursive_loops([?TWO_RECURSIVE_LOOP | remove_recursive_loops(Stack)]); +remove_recursive_loops([{M, F, A}, {M, F, A} | Stack]) -> + [{M, F, A} | remove_recursive_loops(Stack)]; +remove_recursive_loops([]) -> + []; +remove_recursive_loops([H | T]) -> + [H | remove_recursive_loops(T)]. + +-spec flamegraph(list(), list(), T) -> T when T :: #{stack() => integer()}. +flamegraph([], [], State) -> + io:format("Done on one file\n"), + State; +flamegraph([], Stack, State) -> + io:format("WARNING: leftover stack ~p~n", [Stack]), + State; +flamegraph([{try_catch, {try_enter, TryId}, _Loc} | OtherInstructions], CurrentStack, State) -> + flamegraph(OtherInstructions, [TryId | CurrentStack], State); +flamegraph([{try_catch, {try_exit, TryId}, _Loc} | OtherInstructions], [TryId | CurrentStack], State) -> + flamegraph(OtherInstructions, CurrentStack, State); +flamegraph([{try_catch, {catch_enter, TryId}, _Loc} | OtherInstructions], CurrentStack, State) -> + [TryId | NewStack] = lists:dropwhile(fun(TryId1) -> TryId =/= TryId1 end, CurrentStack), + flamegraph(OtherInstructions, NewStack, State); +flamegraph([{call_fun, MFA, _Loc} | OtherInstructions], CurrentStack, State) -> + flamegraph(OtherInstructions, [MFA | CurrentStack], State); +flamegraph([{apply, {MFA, _Loc}} | OtherInstructions], [MFA | NewStack], State) -> + flamegraph(OtherInstructions, NewStack, State); +flamegraph([_SomeInstruction | OtherInstructions], Stack, State) -> + flamegraph(OtherInstructions, Stack, maps:update_with(remove_recursive_loops(Stack), fun add_one/1, 1, State)). + +-spec add_one(integer()) -> integer(). +add_one(Value) -> Value + 1. + +-spec extract_from_state_subcommand(taint_abstract_machine:leaks() | iodata(), list(string())) -> ok. +extract_from_state_subcommand(Leaks, ["-query-arg-lineage", Query | Tail]) -> + [FromM, FromF, FromA, FromArgN, ToM, ToF, ToA, ToArgN] = split_query(Query), + FromMFA = {list_to_atom(FromM), list_to_atom(FromF), list_to_integer(FromA)}, + ToMFA = {list_to_atom(ToM), list_to_atom(ToF), list_to_integer(ToA)}, + Query1 = {FromMFA, list_to_integer(FromArgN), ToMFA, list_to_integer(ToArgN)}, + % eqwalizer:ignore Assume Leaks is of taint_abstract_machine:leak() type + Result = abstract_machine_util:query_arg_lineage(Leaks, Query1), + extract_from_state_subcommand(Result, Tail); +extract_from_state_subcommand(Leaks, ["-arg-lineage-hr" | Tail]) -> + % eqwalizer:ignore Assume Leaks is of taint_abstract_machine:leak() type + Lineage = abstract_machine_util:get_arg_lineage(Leaks, human_readable), + extract_from_state_subcommand(Lineage, Tail); +extract_from_state_subcommand(Leaks, ["-arg-lineage-csv" | Tail]) when is_list(Leaks) -> + io:format("Converting to CSV~n"), + % eqwalizer:ignore Assume Leaks is of taint_abstract_machine:leak() type + Lineage = abstract_machine_util:get_arg_lineage(Leaks, csv), + io:format("Done converting to CSV~n"), + extract_from_state_subcommand(Lineage, Tail); +extract_from_state_subcommand(_, []) -> + ok; +extract_from_state_subcommand(Lineage, ["-count" | Tail]) when is_list(Lineage) -> + io:format("Counted ~p~n", [length(Lineage)]), + extract_from_state_subcommand(Lineage, Tail); +extract_from_state_subcommand(Lineage, ["-to-file", OutputFile | Tail]) -> + % eqwalizer:ignore + file:write_file(OutputFile, Lineage), + extract_from_state_subcommand(Lineage, Tail); +extract_from_state_subcommand(Lineage, ["-pprint" | Tail]) -> + Printed = io_lib:format("~p", [Lineage]), + extract_from_state_subcommand(Printed, Tail); +extract_from_state_subcommand(Lineage, ["-print" | Tail]) -> + io:format("~s~n", [Lineage]), + extract_from_state_subcommand(Lineage, Tail). + +-spec split_query(Query) -> [string()] when Query :: iodata() | unicode:charlist(). +split_query(Query) -> + % eqwalizer:ignore - {return, list} in options forces the result to be a list of strings + re:split(Query, ":|/|-Arg| -> ", [{return, list}]). + +-spec main(infer | dot, string()) -> ok. +main(ReportType, Path) -> + AmState = + try taint_abstract_machine:run_tracing(Path) of + S -> S + catch + {abstract_machine_invalid_state, _Instruction, State} -> + io:format("Abstract machine exited with errors, analysis incomplete~n"), + State + end, + Leaks = taint_abstract_machine:get_leaks(AmState), + io:format("Found ~p leaks~n", [length(Leaks)]), + Output = + case ReportType of + infer -> abstract_machine_util:to_infer_report(Leaks); + dot -> abstract_machine_util:graphviz_leaks(Leaks, [pastry]) + end, + io:format("~s~n", [Output]). diff --git a/finer_taint/src/taint_abstract_machine.erl b/finer_taint/src/taint_abstract_machine.erl new file mode 100644 index 0000000..f9c0c9b --- /dev/null +++ b/finer_taint/src/taint_abstract_machine.erl @@ -0,0 +1,1311 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%% Runs the instructions emitted by running a program instrumented with finer_taint_compiler to +%%% compute the result of the analysis and find the leaks +-module(taint_abstract_machine). +-compile(warn_missing_spec_all). +-compile({inline, [taint_value/1]}). + +-include_lib("kernel/include/logger.hrl"). + +%% The client API. +-export([ + get_leaks/1, + get_leaks_as_map/1, + map_leaks_to_leaks/1, + get_stack/1, + is_no_taint/1, + run_tracing/1, + init_state/1, + propagate/2, + propagate_cov/3, + get_taint_sources/2 +]). +-export_type([ + taint_value/0, + leaks/0, + leaks_map/0, + state/0, + taint_history/0, + taint_history_point/0, + instruction/0, + construct_pattern_types/0, + deconstruct_pattern_types/0, + dataflow_map/0, + models/0, + try_catch_state/0 +]). + +-include_lib("finer_taint/include/non_lineage_modules.hrl"). + +% lineage_point represents an argument of a function, mfa() identifies the function +% and integer() is the argument number starting with 1 +-type lineage_point() :: {mfa(), integer()}. + +-type taint_source() :: + % arg_taint is similar to source, but it is not neccessarly the begining + % of taint_history. That is multiple arg_taint points can be found + % in a single history + {arg_taint, lineage_point()} + % The source location where this history started. + | {tagged_source, string(), string()} + | annot_dataflow_src() + | {source, string()}. + +% taint_history_point() represent a point in the history of a taint value. +% strings() are usually in the format: some_file.erl: +-type taint_history_point() :: + % A source location where the taint value was + {step, string()} + % Represent a point where multiple taint_values were put + % in a pattern and therefore multiple histories merge + | {joined_history, pattern, [taint_history()]} + | {joined_history, model, [taint_history()]} + | {joined_history, lambda_closure, [taint_history()]} + % Represent a point where the value was passed via a message + | {message_pass, string()} + % {call_site, MFA, Loc} Represents a point in history where the value was + % used in a function call to Function/Arity at a source location `Loc` + % `Loc` is in .erl: format + | {call_site, mfa(), string()} + % {return_site,MFA, Loc} Represents a point in history where the value was + % returned to source location Loc from MFA function call. + % The Loc should match the one call_site history point + | {return_site, mfa(), string()} + % Represents a point in history of the taint value where the value + % traveled outside of the instrumented code. Therefore we don't + % know what happned to it. In order to improve scalablity we drop + % the full history and just keep the taint sources that went into it + % This is probably ok, because the full history is unknown anyway + | {blackhole, [taint_source()]} + | taint_source(). + +-type taint_history() :: [taint_history_point()]. +-type taint_value() :: + % untainted value + {notaint, []} + %A normal tainted values + | {taint, taint_history()} + % Represents a taint value of a function. The only function that + % can be tainted are lambdas, because they can containt captured + % variables. {lambda_closure, Scope} stores all the captured + % taint variables in the Scope. The scope needs to be restored + % via restore_capture instruction when the function is called. + | {lambda_closure, scopes_map()} + % A taint value of patterns + % For tuple {Val1, Val2, ..., ValN}, the taint value will look like + % {pattern_taint, tuple, [ValN, ValN-1, ..., Val1]} + | {pattern_taint, tuple, [taint_value()]} + % the elements of the pattern are just put in a list in the same order + | {pattern_taint, cons, [taint_value()]} + % The [number()] containts the byte sizes of taint values + | {pattern_taint, {bitstring, [number()]}, [taint_value()]} + % For map #{Key => Value} the corresponding taint_value looks like + % #{Key => Taint(Value), abstract_machine_mapkey_taints => #{Key => Taint(Value)} + | {pattern_taint, map, #{term() => #{term() => taint_value()} | taint_value()}}. + +-type scopes_map() :: #{string() => taint_value()}. + +-type function_arity() :: {atom(), integer()}. + +% Contains {Size, TSL} tuple, more info on TSL here: +% https://www.erlang.org/doc/apps/erts/absform.html#bitstring-element-type-specifiers +-type bin_pattern_segment() :: {integer() | default, [atom()]}. +-type deconstruct_pattern_types() :: + % For destructing the bitstring pattern we also have the TSL + % in addition to size in bin_pattern_segment() + {bitstring, [bin_pattern_segment()]} + | pattern_types_shared(). +-type construct_pattern_types() :: + % When constructing the bitstring pattern we pass in the byte sizes of each segment + {bitstring, [number()]} + | pattern_types_shared(). +-type pattern_types_shared() :: + % map has a list of Keys + {map, [string()]} + % tuple has arity, ie the number of elements in the tuple + | {tuple, number()} + % Cons is always just a head and a tail + | {cons}. + +-type try_block_id() :: {module(), integer()}. +-type try_marker() :: {try_enter, try_block_id()}. +-type try_catch_state() :: + % Indicates try block entry. That is exceptions + % after this point should be caught by this try/catch expression + try_enter + % Indicates try block exit. That is exceptions should no longer + % be caught by this try/catch block + | try_exit + % Indicates catch entry, that is exception was caught by this catch block + | catch_enter. +% The last argument of instructions is always a source location +-type instruction() :: + % Push TaintVal instruction - push TaintVal to the stack. + {push, {notaint | string() | {string(), string()}}} + % Pop instruction - pop a taint value off the stack + | {pop, {}} + % Duplicate instruction - Duplicate the top of the stack + | {duplicate, {}} + % Pop a value of the stack and check if it's tainted + | {sink, {string()}} + % Get Varname instruction - lookup Varname in the scopes and push its value to the stack + | {get, {string(), string()}} + % Pop top of the stack and send it as MessageId + | {send, {MessageId :: string(), string()}} + % Receive messageID and push it onto the stack, assume notaint if nomsg + | {receive_trace, {MessageId :: string(), string()}} + | {receive_trace, {nomsg}} + % Apply {M,F,A} - apply M(odule):F(unction)/A(rity) function + | {apply, {mfa(), string()}} + % Construct PatternType instruction - Pop values needed by PatternType of the stack + % and construct a pattern taint value of PatternType + | {construct_pattern, {construct_pattern_types(), string()}} + % Deconstruct PatternType - pop a pattern taint value of the stack and + % push consitutients of PatternType to the stack + | {deconstruct_pattern, {deconstruct_pattern_types(), string()}} + % Instruction to handle try/catch blocks + | {try_catch, {try_catch_state(), try_block_id()}, string()} + % Expecting to call a function, used to determine if the stack is setup correctly, + % The stack can be setup incorrectly if the called function is not instrumented, + % but calls an instrumented function + | {call_fun, mfa(), string()} + % Push_Scope FunctionName - push a new scope for the FunctionName function + | {push_scope, {mfa(), string()}} + % Func_Ret FunctionName - Return from FunctionName, mostly just pops the scope + | {func_ret, {string(), string()}} + % capture/restore_closure functions are used to implement capturing of values + % by lambdas. + % Capture_Closure VariableNames - Store all taint values of Variables in VariableNames + % into a {lambda_closur, Scope} taint value and push it onto the stack + | {capture_closure, {[string()]}} + % Pops a value of the stack, if untainted push an empty scope + % If the value is {lambda_closure, Scope}, push the Scope + | {restore_capture, {string()}} + % Store VarName - Pop a value of the stack and store it in scope with VarName + | {store, {string(), string()}} + | {set_element, {integer(), integer(), string()}}. +-type leak() :: + % Leak in normal mode {leak, Sink, History} + {leak, string(), taint_history()} + % Leak in coverage mode. Contains a set of all locations the taint value passed through + | {coverage_leak, string()} + % Leak in lineage mode to integer() argument of mfa() + | {arg_leak, {mfa(), integer(), string()}, taint_history()} + % Similar to arg_leak, but instead of full taint history, + % it contains a set (dataflow_map) of all the dataflow_src-es + % that ended up in this argument. + | {{arg_dataflow, {mfa(), integer(), string()}}, annot_dataflow_src()} + | {arg_dataflow, {mfa(), integer(), string()}, dataflow_map()}. +-type leaks() :: [leak()]. + +% Similar to {arg_taint, lineage_point()}, but also containts some taint_history +% that can be used for annotations. The taint history is usually not fully detailed +-type annot_dataflow_src() :: {dataflow_src, lineage_point(), taint_history()}. +% Each key in a map represent some dataflow from lineage_point() via taint_history() +% taint_history() might be reduced. This is a map for dedpulication and efficency +-type dataflow_map() :: #{annot_dataflow_src() => ok}. +-type leaks_map() :: #{leak() => ok}. + +-type scopes() :: [scopes_map() | try_marker() | #{mfa() => taint_value()}]. +-type stack() :: [taint_value() | try_marker()]. +-type models() :: #{{module(), atom()} => sanitize | propagate}. + +% false -> don't run in lineage mode +% line_history -> run lineage mode with lineage containing the line_history history (ie. every executed line) +% This is very expensive and not practical for our code +% function_history -> run lineage mode containing only the history of arguments +% Both lineage modes give the same lineage edges, however line_history mode enables +% one to use `-query-arg-lineage' in run_lineage_escript to find the full history of an edge. +% This is useful for debugging why and edge is reported +-type lineage_mode() :: false | line_history | function_history | coverage. +-type lineage_modules_denylist() :: #{module() => ok}. + +-record(taint_am_state, { + stack = [] :: stack(), + scopes = [#{}] :: scopes(), + process_dict = {pattern_taint, map, #{}} :: taint_value(), + instrumented_return = [] :: [function_arity() | true | try_marker()], + leaks = [] :: leaks(), + % Lineage is not reported for functions for modules in this map + lineage_modules_denymap = #{} :: lineage_modules_denylist(), + lineage_mode = false :: lineage_mode(), + models = #{} :: models() +}). + +-type state() :: #taint_am_state{}. + +-define(NOT_TRY_ENTER(TaintValue), element(1, TaintValue) =/= try_enter). + +%% Run the abstract machine, printing instructions and state for every step +%% Only useful for debugging. Real users of the taint machine +%% should run it via abstract_machine_proclet gen_server +-spec run_tracing(string()) -> state(). +run_tracing(Filepath) -> + Basename = filename:basename(Filepath), + run( + Filepath, + fun(Instruction, State) -> + io:format("~s executing ~p~n~p~n==========~n", [ + Basename, Instruction, State + ]), + propagate(Instruction, State) + end, + #taint_am_state{} + ). + +-spec init_state(map()) -> state(). +init_state(Args) when is_map(Args) -> + Models = maps:get(taint_models, Args, abstract_machine_util:get_priv_models()), + #taint_am_state{ + models = Models, + lineage_mode = maps:get(lineage_mode, Args, false), + lineage_modules_denymap = maps:merge( + maps:get( + lineage_modules_denymap, + Args, + #{} + ), + maps:from_keys(?BASE_NON_LINEAGE_MODULES, ok) + ) + }. + +-spec run(string(), fun((instruction(), state()) -> state()), state()) -> + state(). +run(Filepath, PropagateFunction, InitState) -> + {ok, Instructions} = file:consult(Filepath), + lists:foldl(PropagateFunction, InitState, Instructions). + +-spec map_leaks_to_leaks(leaks_map()) -> leaks(). +map_leaks_to_leaks(MapLeaks) -> + maps:fold( + fun + ({{arg_dataflow, {MFA, N, Loc}}, Src}, ok, Acc) -> + [{arg_leak, {MFA, N, Loc}, [Src]} | Acc]; + (L = {arg_leak, _, _}, ok, Acc) -> + [L | Acc]; + (L = {leak, _, _}, ok, Acc) -> + [L | Acc]; + (L = {coverage_leak, _}, ok, Acc) -> + [L | Acc] + end, + [], + MapLeaks + ). + +-spec leaks_as_map_folder(leak(), leaks_map()) -> leaks_map(). +leaks_as_map_folder({arg_dataflow, {MFA, N, Loc}, TaintedArgs}, LeaksAcc) when is_map(TaintedArgs) -> + NewDataflows = maps:fold( + fun(Src = {dataflow_src, _MFAN, _Annotations}, ok, InnerLeaksAcc) -> + InnerLeaksAcc#{{{arg_dataflow, {MFA, N, Loc}}, Src} => ok} + end, + #{}, + TaintedArgs + ), + maps:merge(NewDataflows, LeaksAcc); +leaks_as_map_folder(L = {leak, _Sink, _History}, LeaksAcc) -> + LeaksAcc#{L => ok}; +leaks_as_map_folder(L = {arg_leak, _Sink, _History}, LeaksAcc) -> + LeaksAcc#{L => ok}; +leaks_as_map_folder(L = {coverage_leak, _}, LeaksAcc) -> + LeaksAcc#{L => ok}. + +-spec get_leaks_as_map(state()) -> leaks_map(). +get_leaks_as_map(#taint_am_state{leaks = Leaks}) -> + lists:foldl( + fun leaks_as_map_folder/2, + #{}, + Leaks + ). + +-spec get_leaks(state()) -> leaks(). +get_leaks(#taint_am_state{leaks = Leaks}) -> + Leaks. + +-spec get_stack(state()) -> stack(). +get_stack(#taint_am_state{stack = Stack}) -> Stack. + +-spec history_folder(term(), #{term() => taint_value()} | taint_value(), [taint_history()]) -> [taint_history()]. +history_folder(abstract_machine_mapkey_taints, KeyTaintMap, Acc) when is_map(KeyTaintMap) -> + maps:fold( + fun + (_Key, {notaint, []}, AccInner) -> AccInner; + (_Key, Value, AccInner) -> [get_history(Value) | AccInner] + end, + Acc, + KeyTaintMap + ); +history_folder(_Key, {notaint, []}, Acc) -> + Acc; +history_folder(_, Value, Acc) when not is_map(Value) -> + [get_history(Value) | Acc]. + +-spec history_filter(taint_value()) -> {true, taint_history()} | false. +history_filter(TaintVal) -> + case get_history(TaintVal) of + [] -> false; + History -> {true, History} + end. + +-spec get_history(taint_value()) -> taint_history(). +get_history({lambda_closure, Closure}) -> + Histories = maps:fold(fun history_folder/3, [], Closure), + case Histories of + % Lambdas can capture just untainted values + [] -> []; + [OneHistory] -> OneHistory; + Histories = [_ | _] -> [{joined_history, lambda_closure, Histories}] + end; +get_history({notaint, []}) -> + []; +get_history({pattern_taint, map, MapVals}) when is_map(MapVals) -> + Histories = maps:fold(fun history_folder/3, [], MapVals), + case Histories of + % Lambdas can capture just untainted values + [] -> []; + [OneHistory] -> OneHistory; + Histories = [_ | _] -> [{joined_history, pattern, Histories}] + end; +get_history({pattern_taint, _Type, PatternVals}) -> + case lists:filtermap(fun history_filter/1, PatternVals) of + % Lambdas can capture just untainted values + [] -> []; + [OneHistory] -> OneHistory; + Histories = [_ | _] -> [{joined_history, pattern, Histories}] + end; +get_history({taint, History}) -> + History. + +-spec add_arg_taint(taint_value(), lineage_point(), string()) -> taint_value(). +add_arg_taint({notaint, _}, MFAN = {{M, F, A}, _}, Loc) -> + {taint, [{arg_taint, MFAN}, {call_site, {M, F, A}, Loc}]}; +add_arg_taint(TaintValue, MFAN, _) -> + append_taint_history_base(TaintValue, {arg_taint, MFAN}). + +-spec append_taint_history(taint_value(), string()) -> taint_value(). +append_taint_history(Value, Loc) -> + append_taint_history_base(Value, {step, Loc}). + +-spec append_taint_history_base(taint_value(), taint_history_point()) -> taint_value(). + +append_taint_history_base({lambda_closure, ScopesMap}, HistoryPoint) -> + NewScopesMap = maps:map( + fun(_K, V) -> + append_taint_history_base(V, HistoryPoint) + end, + ScopesMap + ), + + {lambda_closure, NewScopesMap}; +append_taint_history_base(T = {notaint, _}, _HistoryPoint) -> + T; +append_taint_history_base({pattern_taint, map, MapVal}, HistoryPoint) when is_map(MapVal) -> + NewMapVal = maps:filtermap( + fun + (abstract_machine_mapkey_taints, KeyTaintMap) when is_map(KeyTaintMap) -> + {true, + maps:filtermap( + fun(_K, V) -> {true, append_taint_history_base(V, HistoryPoint)} end, KeyTaintMap + )}; + (_K, V) -> + if + not is_map(V) -> {true, append_taint_history_base(V, HistoryPoint)} + end + end, + MapVal + ), + {pattern_taint, map, NewMapVal}; +append_taint_history_base({pattern_taint, tuple, PatternVals}, HistoryPoint) when is_list(PatternVals) -> + {pattern_taint, tuple, lists:map(fun(V) -> append_taint_history_base(V, HistoryPoint) end, PatternVals)}; +append_taint_history_base({pattern_taint, cons, PatternVals}, HistoryPoint) when is_list(PatternVals) -> + {pattern_taint, cons, lists:map(fun(V) -> append_taint_history_base(V, HistoryPoint) end, PatternVals)}; +append_taint_history_base({pattern_taint, {bitstring, Bs}, PatternVals}, HistoryPoint) when is_list(PatternVals) -> + {pattern_taint, {bitstring, Bs}, lists:map(fun(V) -> append_taint_history_base(V, HistoryPoint) end, PatternVals)}; +append_taint_history_base({taint, History}, HistoryPoint) -> + {taint, [HistoryPoint | History]}. + +-spec map_value_lineage_folder( + term(), + #{term() => taint_value()} | taint_value(), + taint_history() +) -> taint_history(). +map_value_lineage_folder(abstract_machine_mapkey_taints, KeyTaintMap, Acc) when is_map(KeyTaintMap) -> + maps:fold( + fun(_Key, Value, AccInner) -> get_tainted_args(Value) ++ AccInner end, + Acc, + KeyTaintMap + ); +map_value_lineage_folder(_, Value, Acc) when not is_map(Value) -> + get_tainted_args(Value) ++ Acc. + +%% Traverses the history outputs a filtered history +%% that only containts [{arg_taint, _}] +-spec extract_tainted_args(taint_history()) -> taint_history(). +extract_tainted_args(History) -> + lists:filter( + fun + ({arg_taint, _}) -> true; + (_) -> false + end, + get_taint_sources(History, []) + ). + +%% Traverses the history of taint_value() and outputs a history +%% that only contains [{arg_taint, _}] +-spec get_tainted_args(taint_value()) -> taint_history(). +get_tainted_args({notaint, []}) -> + []; +get_tainted_args({pattern_taint, map, MapVals}) -> + lists:flatten(maps:fold(fun map_value_lineage_folder/3, [], MapVals)); +get_tainted_args({pattern_taint, _Type, PatternVals}) -> + lists:flatten(lists:map(fun get_tainted_args/1, PatternVals)); +get_tainted_args({lambda_closure, _History}) -> + []; +get_tainted_args({taint, History}) -> + extract_tainted_args(History). + +-spec is_no_taint(taint_value()) -> boolean(). +is_no_taint({notaint, _}) -> true; +is_no_taint(_) -> false. + +-spec is_in_scope(mfa() | string(), scopes()) -> boolean(). +is_in_scope(_, []) -> + false; +is_in_scope(VarName, [TopScope | _]) when is_map_key(VarName, TopScope) -> + true; +is_in_scope(VarName, [_ | Scopes]) -> + is_in_scope(VarName, Scopes). + +-spec find_in_scope(mfa() | string(), scopes()) -> taint_value(). +find_in_scope(VarName, [{try_enter, _} | Scopes]) -> + find_in_scope(VarName, Scopes); +% Assume untainted if not found in scope. This happens +% if a {notaint, []} lambda closure is restored and +% the lambda then lookups up a captured variable. +% One example of lambda closure becoming {notaint, []} is when +% the receive_trace times out and returns {notaint, []} +find_in_scope(_VarName, []) -> + {notaint, []}; +find_in_scope(VarName, [TopScope | Scopes]) when is_map(TopScope) -> + case maps:get(VarName, TopScope, notfound) of + notfound -> find_in_scope(VarName, Scopes); + Value -> Value + end. +%If there are no scopes left, this crashes because it shouldn't happen +% + +-spec insert_in_scope(string(), taint_value(), scopes()) -> scopes(). +insert_in_scope(VarName, Value, [Te = {try_enter, _} | Scopes]) -> + [Te | insert_in_scope(VarName, Value, Scopes)]; +insert_in_scope(VarName, Value, [Vars | Scopes]) -> + % eqwalizer:ignore - we assume that Vars :: scopes_map() here + [Vars#{VarName => Value} | Scopes]. + +% Same as propagate, except that for all instructions whose +% location matches Prefix, replace the top of the stack +% with a tainted value if it's not tainted already. This +% is useful to track all taint originating from a module +-spec propagate_cov(instruction(), state(), string()) -> state(). +propagate_cov(Inst, State, Prefix) -> + NewState = propagate(Inst, State), + Loc = + case Inst of + {capture_closure, _} -> ""; + {_, {L}} when is_list(L) -> L; + {_, {_, L}} when is_list(L) -> L; + {_, _, L} when is_list(L) -> L; + _ -> "" + end, + case {NewState#taint_am_state.stack, string:prefix(Loc, Prefix)} of + {[{notaint, []} | Tail], Lc} when is_list(Lc) -> + NewState#taint_am_state{stack = [{taint, [{source, Loc}]} | Tail]}; + _ -> + NewState + end. + +-spec propagate(instruction(), state()) -> state(). +propagate({duplicate, {}}, State = #taint_am_state{stack = [First | Stack]}) -> + State#taint_am_state{stack = [First, First | Stack]}; +% Push a no taint value to the stack +propagate({push, {notaint}}, State = #taint_am_state{stack = Stack}) -> + State#taint_am_state{stack = [{notaint, []} | Stack]}; +% Push tagged taint value onto the stack in non-lineage mode +propagate({push, {{Tag, SourceLoc}}}, State = #taint_am_state{stack = Stack, lineage_mode = false}) when + is_list(Tag), is_list(SourceLoc) +-> + State#taint_am_state{stack = [{taint, [{tagged_source, Tag, SourceLoc}]} | Stack]}; +% Push a taint value onto the stack in non-lineage mode +propagate({push, {Source}}, State = #taint_am_state{stack = Stack, lineage_mode = false}) when is_list(Source) -> + State#taint_am_state{stack = [{taint, [{source, Source}]} | Stack]}; +% Ignore tainted values in lineage mode +propagate({push, {_}}, State = #taint_am_state{stack = Stack}) -> + State#taint_am_state{stack = [{notaint, []} | Stack]}; +%% Pop a taint value from the stack and discard it +propagate({pop, {}}, State = #taint_am_state{stack = [_ | Stack]}) -> + State#taint_am_state{stack = Stack}; +%% Push the message to the message passer, leave the message on top of the stack +propagate({send, {MsgId, Loc}}, State = #taint_am_state{stack = [MsgTaintVal, _PidTaintVal | Stack]}) -> + if + ?NOT_TRY_ENTER(MsgTaintVal) -> + taint_message_passer:set(MsgId, append_taint_history_base(taint_value(MsgTaintVal), {message_pass, Loc})) + end, + %The return value of send operation is the message, so we leave it on top of the stack + State#taint_am_state{stack = [MsgTaintVal | Stack]}; +% Wait until the message becomes available in the message passer, push it onto the stack +propagate({receive_trace, {nomsg}}, State = #taint_am_state{stack = Stack}) -> + io:format("Not intstrumented message receive~n"), + State#taint_am_state{stack = [{notaint, []} | Stack]}; +propagate({receive_trace, {MsgId, Loc}}, State = #taint_am_state{stack = Stack}) -> + TaintMsg0 = taint_message_passer:blocking_get(MsgId), + TaintMsg = append_taint_history(TaintMsg0, Loc), + State#taint_am_state{stack = [TaintMsg | Stack]}; +% Pop a value ofthe stack and push a scope. If the popped value is a {lambda_closure, Scope}, +% push Scope as the scope, otherwise push an empty scope. This function should be called +% before a lambda is potentially executed, to setup the variables captured by the lambda +propagate( + {restore_capture, {{Module, Function, Arity}, Loc}}, + State = #taint_am_state{stack = [Capture | Stack], scopes = Vars} +) when + ?NOT_TRY_ENTER(Capture) +-> + % Note: maybe add a marker to taint history that it went through restore_capture + Capture1 = append_taint_history_base(Capture, {call_site, {Module, Function, Arity}, Loc}), + NewScope = + case Capture1 of + {lambda_closure, LambdaCapture} -> [LambdaCapture | Vars]; + % The Capture could be a {taint, ...} value in the lineage case + _ -> [#{} | Vars] + end, + State#taint_am_state{stack = Stack, scopes = NewScope}; +% Capture the variables captured by a lambda. +% +% Note that VariableNames contains all the variable names a lambda uses, some +% of which may be arguments or local variables. The capture_closure instruction +% is called in the creator of the lambda, so the variables local to the lambda +% are not in scope, because the lambda scope hasn't been created yet. +% Therefore we only capture variables that are in the scope of the creator, which +% are the variables captured by the lambda due to no shadowing assumption. +% This is done to simplify the process as we don't have to statically determine +% which varibale is captured and which is local to the lambda +propagate({capture_closure, {VariableNames}}, State = #taint_am_state{stack = Stack, scopes = Vars}) -> + LambdaCapture = lists:foldl( + fun(VarName, Acc) -> + case is_in_scope(VarName, Vars) of + % The variable in a lambda is not in scope, which means + % it's either a lambda local variable or a lambda's argument + % We don't have to capture it + false -> + Acc; + true -> + Acc#{VarName => find_in_scope(VarName, Vars)} + end + end, + #{}, + VariableNames + ), + State#taint_am_state{stack = [{lambda_closure, LambdaCapture}] ++ Stack}; +%% Lookup VarName in the scope and push it onto the stack +propagate({get, {VarName, Loc}}, State = #taint_am_state{stack = Stack, scopes = Vars}) -> + VarTaint = find_in_scope(VarName, Vars), + %% io:format("State ~p~n", [State]), + VarTaint1 = append_taint_history(VarTaint, Loc), + %% io:format("Var taint ~p Var taint 1: ~p ~n", [VarTaint, VarTaint1]), + State#taint_am_state{stack = [VarTaint1] ++ Stack}; +%% Apply (call) the MFA function. If instrumented_return = true, the callee should have executed already +%% in this abstract machine, leaving the stack in the correct state. +%% Therefore there shouldn't be anything to do, but reset the flag +propagate( + {apply, {MFA = {_Module, _Fun, _Arity}, Loc}}, + State = #taint_am_state{stack = [Top | Stack], instrumented_return = [true | Ir]} +) when ?NOT_TRY_ENTER(Top) -> + %% The top of the stack should be return value and all argument should be popped in the called functions + %% so there is nothing to do + Top1 = append_taint_history_base(taint_value(Top), {return_site, MFA, Loc}), + State#taint_am_state{stack = [Top1 | Stack], instrumented_return = Ir}; +%% If the callee is not instrumented, we have to pop the arguments and push the return value +%% according to a model. +%% Currently the model just propagates taint +propagate( + {apply, {MFA = {Module, Fun, Arity}, Loc}}, + State = #taint_am_state{stack = Stack, instrumented_return = [{Fun, Arity} | Ir]} +) -> + {ApplyArgs0, NewStack} = lists:split(Arity, Stack), + ApplyArgs = is_all_taint_value(ApplyArgs0), + %%For now we just merge all the taints + {TaintValue, State1} = + case model_of(State#taint_am_state.models, MFA, ApplyArgs) of + get_process_dict -> + {State#taint_am_state.process_dict, State}; + {put_process_dict, Val} -> + {{notaint, []}, State#taint_am_state{process_dict = Val}}; + notmodeled -> + case [Arg || Arg <- ApplyArgs, not is_no_taint(Arg)] of + % If there is no tainted args, the return is assumed not tainted too + [] -> + {{notaint, []}, State}; + % If there is tainted args, the return is tainted and could have any structure + TaintedArgs -> + ?LOG_INFO("MFA ~p, ~p at ~p~n", [Module, Fun, Loc]), + {create_blackhole(TaintedArgs), State} + end; + TaintVal -> + {TaintVal, State} + end, + TaintValue1 = + if + not is_tuple(Loc) -> append_taint_history_base(TaintValue, {return_site, MFA, Loc}) + end, + + %% io:format("Apply taint ~p args: ~p ~n", [TaintValue1, ApplyArgs]), + State1#taint_am_state{stack = [TaintValue1 | NewStack], instrumented_return = Ir}; +%% Pop a value of the stack and check if it's tainted. If it is tainted add it to leaks +propagate({sink, {_Loc}}, State = #taint_am_state{stack = [{notaint, []} | _Stack]}) -> + State; +propagate({sink, {Loc}}, State = #taint_am_state{stack = [TaintValue | Stack], leaks = Leaks, lineage_mode = false}) -> + if + ?NOT_TRY_ENTER(TaintValue) -> + State#taint_am_state{ + stack = [TaintValue | Stack], leaks = [{leak, Loc, get_history(taint_value(TaintValue))} | Leaks] + } + end; +% In lineage mode we are not interested in manally annotated sinks, so we just skip them +propagate({sink, {_Loc}}, State = #taint_am_state{stack = [_ | _Stack]}) -> + State; +%% Constructs a binary pattern. BinValSizes is a list of runtime sizes (in bytes) of each segment that +%% constitutes the binary pattern being constructed +propagate({construct_pattern, {{bitstring, BinValSizes}, Loc}}, State = #taint_am_state{stack = Stack}) -> + {BinTaintVals0, NewStack} = lists:split(length(BinValSizes), Stack), + BinTaintVals = is_all_taint_value(BinTaintVals0), + BinaryTaintValue = + case lists:all(fun is_no_taint/1, BinTaintVals) of + %%If all bin values have no taint, the whole binary has no taint + true -> + {notaint, []}; + _ -> + BinTaintVals1 = lists:map(fun(V) -> append_taint_history(V, Loc) end, lists:reverse(BinTaintVals)), + {pattern_taint, {bitstring, BinValSizes}, BinTaintVals1} + end, + State#taint_am_state{stack = [BinaryTaintValue | NewStack]}; +%% Pop 2 values of the stack, put them in a cons pattern and push +%% {pattern_taint, cons, ...} to the stack +propagate({construct_pattern, {{cons}, Loc}}, State = #taint_am_state{stack = Stack}) -> + {HeadTail, NewStack} = lists:split(2, Stack), + [Head, Tail] = is_all_taint_value(HeadTail), + ListValue = + case {Head, Tail} of + {{notaint, []}, {notaint, []}} -> {notaint, []}; + _ -> {pattern_taint, cons, [append_taint_history(Head, Loc), append_taint_history(Tail, Loc)]} + end, + State#taint_am_state{stack = [ListValue | NewStack]}; +% For constructing map_patterns the top of the stack should be +% all the taint value for Values, followed by all the taint values for Keys +%% Therefore we pop one value off the stack for each Key and construct a map Key => taint_value +%% Then we pop one value of the stack for each Key again, which represent the taint-ednes of +%% the Key and construct a map Key => taint_value. +propagate({construct_pattern, {{map, Keys}, Loc}}, State = #taint_am_state{stack = Stack}) -> + {MapKeyValues0, NewStack} = lists:split(length(Keys) * 2 + 1, Stack), + MapKeyValues = is_all_taint_value(MapKeyValues0), + [MapTaint | KeyValuesTaint] = MapKeyValues, + MapTaintValue = + case lists:all(fun is_no_taint/1, MapKeyValues) of + true -> + {notaint, []}; + _ -> + BaseTaintMap = + case MapTaint of + {notaint, []} -> #{}; + {taint, _} -> #{base_map => MapTaint}; + {pattern_taint, map, Val} when is_map(Val) -> Val + end, + {MapValues, MapKeys} = lists:split(length(Keys), KeyValuesTaint), + % maps/merge will overwrite values from BaseTaintMap + ValueTaintMap = maps:merge(BaseTaintMap, maps:from_list(lists:zip(Keys, MapValues))), + PreviousKeyTaintMap = maps:get(abstract_machine_mapkey_taints, BaseTaintMap, #{}), + KeyTaintMap = + if + is_map(PreviousKeyTaintMap) -> + maps:merge( + PreviousKeyTaintMap, + maps:from_list(lists:zip(Keys, MapKeys)) + ) + end, + MapValueBody = ValueTaintMap#{abstract_machine_mapkey_taints => KeyTaintMap}, + MapTaintValue1 = {pattern_taint, map, MapValueBody}, + append_taint_history(MapTaintValue1, Loc) + end, + State#taint_am_state{stack = [MapTaintValue | NewStack]}; +%% Pop Arity values of the stack, put them in the pattern and push {pattern_taint, ...} +%% value onto the stack +propagate({construct_pattern, {{tuple, Arity}, Loc}}, State = #taint_am_state{stack = Stack}) -> + {TupleValues0, NewStack} = lists:split(Arity, Stack), + TupleValues = is_all_taint_value(TupleValues0), + TaintValue = + case lists:all(fun is_no_taint/1, TupleValues) of + %%If all tuple values have no taint, tuple has no taint + true -> + {notaint, []}; + %%Otherwise we construct the tuple + _ -> + TupleValues1 = lists:map(fun(V) -> append_taint_history(V, Loc) end, TupleValues), + {pattern_taint, tuple, TupleValues1} + end, + State#taint_am_state{stack = [TaintValue | NewStack]}; +%% Pop a map pattern taint value of the stack, extract Keys from the map and push their values +%% onto the stack +propagate( + {deconstruct_pattern, {{map, Keys}, Loc}}, + State = #taint_am_state{stack = [{pattern_taint, map, MapValue} | Stack]} +) when is_map(MapValue) -> + MapValues = is_all_taint_value([maps:get(Key, MapValue, {notaint, []}) || Key <- Keys]), + KeyTaintMap = maps:get(abstract_machine_mapkey_taints, MapValue), + MapKeys = + if + is_map(KeyTaintMap) -> [maps:get(Key, KeyTaintMap, {notaint, []}) || Key <- Keys] + end, + DeconstructedValues = is_all_taint_value(MapValues ++ MapKeys), + DeconstructedValues1 = [append_taint_history(V, Loc) || V <- DeconstructedValues], + State#taint_am_state{stack = DeconstructedValues1 ++ Stack}; +%% To deconstruct a map pattern of an untainted value, just push +%% enough notaint values back onto the stack +propagate( + {deconstruct_pattern, {{map, Keys}, _Loc}}, + State = #taint_am_state{stack = [{notaint, []} | Stack]} +) -> + %Push taint values for both keys and values + MapValues = [{notaint, []} || _ <- lists:seq(1, 2 * length(Keys))], + State#taint_am_state{stack = MapValues ++ Stack}; +% This clause deconstructs pattern of an opaque taint value. That +% is a tainted value that is either {taint, _} or {blackhole, ...} +% In this case we just duplicate the value for each Key/Value +% in the requested deconstruction +propagate( + {deconstruct_pattern, {{map, Keys}, Loc}}, + State = #taint_am_state{stack = [TaintVal | Stack]} +) -> + %Push taint values for both keys and values + MapValues = + if + ?NOT_TRY_ENTER(TaintVal) -> + [append_taint_history(taint_value(TaintVal), Loc) || _ <- lists:seq(1, 2 * length(Keys))] + end, + State#taint_am_state{stack = MapValues ++ Stack}; +%% Pop a pattern value of the stack and deconstruct it into the pattern to pattern match +%% Deconstructing a tuple pattern when top of stack is tainted +propagate( + {deconstruct_pattern, {{tuple, Arity}, Loc}}, + State = #taint_am_state{stack = [{pattern_taint, tuple, TupleValues} | Stack]} +) when Arity == length(TupleValues) -> + TupleValues1 = lists:map(fun(V) -> append_taint_history(V, Loc) end, TupleValues), + State#taint_am_state{stack = lists:reverse(TupleValues1) ++ Stack}; +%% Deconstructing a tuple pattern when top of stack is tainted +propagate( + {deconstruct_pattern, {{cons}, Loc}}, + State = #taint_am_state{stack = [{pattern_taint, cons, ListValues} | Stack]} +) -> + [Head, Tail] = lists:map(fun(V) when ?NOT_TRY_ENTER(V) -> append_taint_history(V, Loc) end, ListValues), + State#taint_am_state{stack = [Head, Tail] ++ Stack}; +%% Deconstructing pattern when top of stack is not tained +%% Or is tainted directly meaning the whole list is marked as tainted +%% not the individual elements. +%% +%% That can happen if a string (or any list is marked as source), ie. +%% finer_taint:source("some string") +%% +%% In this case we just duplicate the taint value +propagate( + {deconstruct_pattern, {{cons}, _Loc}}, + State = #taint_am_state{stack = [H = {TaintValueType, _} | Stack]} +) when TaintValueType =:= taint orelse TaintValueType =:= notaint -> + State#taint_am_state{stack = [H, H] ++ Stack}; +%% Deconstructs a binary pattern. BinPattern is a list of Size/Tsl expressions +%% that can be used to infer the segment size. Most of the heavy lifting +%% is done by match_binary_pattern function +propagate( + {deconstruct_pattern, {{bitstring, BinPattern}, Loc}}, + State = #taint_am_state{stack = [TopStack | Stack]} +) -> + NewTopStack = + case TopStack of + {notaint, []} -> + lists:map(fun(_) -> {notaint, []} end, BinPattern); + {pattern_taint, {bitstring, Sizes}, BinVals} -> + [append_taint_history(Val, Loc) || Val <- match_binary_pattern({Sizes, BinVals}, BinPattern)]; + {try_enter, _} -> + error(bad_taint_value); + Val = {_Source, _History} -> + NewTaint = append_taint_history(taint_value(Val), Loc), + lists:map(fun(_) -> NewTaint end, BinPattern) + end, + State#taint_am_state{stack = lists:reverse(NewTopStack) ++ Stack}; +propagate( + {deconstruct_pattern, {{tuple, Arity}, _Loc}}, + State = #taint_am_state{stack = [{notaint, []} | Stack]} +) -> + State#taint_am_state{stack = [{notaint, []} || _ <- lists:seq(1, Arity)] ++ Stack}; +propagate( + {deconstruct_pattern, {{tuple, Arity}, Loc}}, + State = #taint_am_state{stack = [Val = {_, _History} | Stack]} +) -> + NewVal = append_taint_history(taint_value(Val), Loc), + State#taint_am_state{stack = [NewVal || _ <- lists:seq(1, Arity)] ++ Stack}; +propagate( + {set_element, {Index, TupleSize, _Loc}}, + State = #taint_am_state{stack = [IndexTaint, TupleTaint, ValueTaint | Stack]} +) -> + ValueTaintV = taint_value(ValueTaint), + NewTupleTaint = + case TupleTaint of + {notaint, []} -> + case ValueTaintV of + {notaint, []} -> + {notaint, []}; + _ -> + TupleValues0 = [{notaint, []} || _ <- lists:seq(1, TupleSize)], + TupleValues1 = setnth(Index, TupleValues0, ValueTaintV), + % The tuple pattern has taint values in reverse order + {pattern_taint, tuple, lists:reverse(TupleValues1)} + end; + {taint, _} -> + {taint, [{joined_history, pattern, lists:map(fun get_history/1, [TupleTaint, ValueTaintV])}]}; + {pattern_taint, tuple, TupleValues} -> + TupleValues1 = setnth(Index, lists:reverse(TupleValues), ValueTaintV), + {pattern_taint, tuple, lists:reverse(TupleValues1)} + end, + + State#taint_am_state{stack = [IndexTaint, NewTupleTaint, ValueTaintV] ++ Stack}; +% When we enter a try statement we push a marker on the stack and scope +propagate( + {try_catch, EnterMark = {try_enter, _TryBlockId}, _Loc}, + State = #taint_am_state{stack = Stack, scopes = Scopes, instrumented_return = Ir} +) -> + State#taint_am_state{ + stack = [EnterMark | Stack], scopes = [EnterMark | Scopes], instrumented_return = [EnterMark | Ir] + }; +% Happy case where nothing was thrown so we clean up the markers +propagate( + {try_catch, {try_exit, TryBlockId}, _Loc}, + State = #taint_am_state{ + stack = [RetVar | Stack], + scopes = [{try_enter, TryBlockId} | Scopes], + instrumented_return = [{try_enter, TryBlockId} | Ir] + } +) -> + [{try_enter, TryBlockId} | NewStack] = lists:dropwhile( + try_enter_predicate(TryBlockId), + Stack + ), + State#taint_am_state{stack = [RetVar | NewStack], scopes = Scopes, instrumented_return = Ir}; +% Something was thrown, we clean up to the closest try_enter marker +propagate( + {try_catch, {catch_enter, TryBlockId}, _Loc}, State = #taint_am_state{stack = [ThrownVal | Stack], scopes = Scopes} +) -> + [{try_enter, _} | NewScopes] = lists:dropwhile(try_enter_predicate(TryBlockId), Scopes), + [{try_enter, _} | NewStack] = lists:dropwhile(try_enter_predicate(TryBlockId), Stack), + [{try_enter, _} | NewIr] = lists:dropwhile( + try_enter_predicate(TryBlockId), State#taint_am_state.instrumented_return + ), + State#taint_am_state{stack = [ThrownVal | NewStack], scopes = NewScopes, instrumented_return = NewIr}; +% Model for erlang:hibernate/3, this resets the state as if it were a new process +propagate( + {call_fun, {erlang, hibernate, 3}, _Loc}, State = #taint_am_state{stack = Stack0} +) -> + % hibernate should be only called inside modeled_erlang:mhibernate/3. + % Args for hibernate are ?MODULE, mapply, [M, F, Args], + % we transform this in M, F, Args + {[_, _, ArgsList], _RestOfStack} = lists:split(3, Stack0), + {MArg, FArg, ArgsArg} = + case ArgsList of + {pattern_taint, cons, [ + MA, + {pattern_taint, cons, [ + FA, + {pattern_taint, cons, [AA, _Nil]} + ]} + ]} -> + {MA, FA, AA}; + {notaint, []} -> + {{notaint, []}, {notaint, []}, {notaint, []}} + end, + + State#taint_am_state{stack = [MArg, FArg, ArgsArg], scopes = [#{}], instrumented_return = [{mapply, 3}]}; +% Normal call_fun +propagate( + {call_fun, {Module, Function, Arity}, Loc}, State = #taint_am_state{stack = Stack0, instrumented_return = Ir} +) -> + {Args0, RestOfStack} = lists:split(Arity, Stack0), + Args = is_all_taint_value(Args0), + Args1 = [append_taint_history_base(Arg, {call_site, {Module, Function, Arity}, Loc}) || Arg <- Args], + State#taint_am_state{stack = Args1 ++ RestOfStack, instrumented_return = [{Function, Arity} | Ir]}; +%% Denotes function entry, it pushes a new scope for variables to avoid name clashes +%% coverage analysis core +propagate( + {push_scope, {_MFA = {Module, PushedFunction, Arity}, _Loc}}, + State = #taint_am_state{ + stack = Stack0, + scopes = Scope0, + instrumented_return = [{CalledFunction, Arity} | Ir], + leaks = Leaks, + lineage_modules_denymap = LineageModulesDenyMap, + lineage_mode = coverage + } +) when + PushedFunction == CalledFunction orelse CalledFunction == lambda_func orelse CalledFunction == variable_func, + length(Stack0) >= Arity, + not is_map_key(Module, LineageModulesDenyMap) +-> + {Args, RestOfStack} = lists:split(Arity, Stack0), + ArgLeaks = [ + begin + TaintValue = taint_value(TVal), + abstract_machine_util:get_covered_inst(get_history(TaintValue)) + end + || TVal <- Args + ], + NewCoverage = maps:fold( + fun(L, ok, Acc) -> [{coverage_leak, L} | Acc] end, + Leaks, + lists:foldl(fun maps:merge/2, #{}, ArgLeaks) + ), + + State#taint_am_state{ + scopes = [#{} | Scope0], + stack = Args ++ RestOfStack, + instrumented_return = [true | Ir], + leaks = NewCoverage + }; +% Lineage analaysis core +propagate( + {push_scope, {MFA = {Module, PushedFunction, Arity}, Loc}}, + State = #taint_am_state{ + stack = Stack0, + scopes = Scope0, + instrumented_return = [{CalledFunction, Arity} | Ir], + leaks = Leaks, + lineage_modules_denymap = LineageModulesDenyMap, + lineage_mode = LineageMode + } +) when + PushedFunction == CalledFunction orelse CalledFunction == lambda_func orelse CalledFunction == variable_func, + length(Stack0) >= Arity, + LineageMode == line_history orelse LineageMode == function_history, + not is_map_key(Module, LineageModulesDenyMap) +-> + {Args, RestOfStack} = lists:split(Arity, Stack0), + EnumArgs = lists:zip(lists:seq(1, Arity), Args), + ArgLeaks = [ + begin + TaintValue = taint_value(TVal), + % Storing the line_history history for each arg_leak is prohibitvely expensive + % In that case, we only want to store the tainted arguments in the history + case LineageMode of + line_history -> + {arg_dataflow, {MFA, N, Loc}, abstract_machine_util:get_dataflows(get_history(TaintValue))}; + function_history -> + {arg_leak, {MFA, N, Loc}, lists:usort(get_tainted_args(TaintValue))} + end + end + || {N, TVal} <- EnumArgs + ], + {Stack, Scope} = + case is_in_scope(MFA, Scope0) of + %If the args of this function haven't been tainted in this scope yet, we taint them + false -> + Args1 = [ + if + ?NOT_TRY_ENTER(Arg) -> add_arg_taint(taint_value(Arg), {MFA, N}, "unknown") + end + || {N, Arg} <- EnumArgs + ], + {Args1 ++ RestOfStack, [#{MFA => {taint, []}} | Scope0]}; + _ -> + {Args ++ RestOfStack, [#{} | Scope0]} + end, + State#taint_am_state{ + scopes = Scope, + stack = Stack, + instrumented_return = [true | Ir], + leaks = ArgLeaks ++ Leaks + }; +propagate( + {push_scope, {{Module, PushedFunction, Arity}, _Loc}}, + State = #taint_am_state{ + stack = Stack, + scopes = Scope, + instrumented_return = [{CalledFunction, Arity} | Ir], + lineage_mode = LineageMode, + lineage_modules_denymap = LineageModulesDenyMap + } +) when + PushedFunction == CalledFunction orelse CalledFunction == lambda_func orelse CalledFunction == variable_func, + LineageMode == false orelse is_map_key(Module, LineageModulesDenyMap) +-> + State#taint_am_state{ + scopes = [#{} | Scope], + stack = Stack, + instrumented_return = [true | Ir] + }; +propagate( + {push_scope, {{_Module, PushedFunction, Arity}, _Loc}}, + State = #taint_am_state{scopes = Scope, instrumented_return = [{CalledFunction, _} | _], stack = Stack} +) -> + io:format("Expected ~p, but got into ~p, assuming ~p is uninstrumented and it called ~p~n", [ + CalledFunction, PushedFunction, CalledFunction, PushedFunction + ]), + %Note lineage mode + State#taint_am_state{ + scopes = [#{} | Scope], + stack = [{notaint, []} || _ <- lists:seq(1, Arity)] ++ Stack + }; +propagate( + {push_scope, {MFA = {Module, _Function, Arity}, _Loc}}, + State = #taint_am_state{ + stack = Stack, + scopes = Scope, + lineage_modules_denymap = LineageModulesDenyMap + } +) -> + ?LOG_INFO("Setting up new stack at ~p~n", [_Loc]), + StartingStack = + case State#taint_am_state.lineage_mode of + false -> [{notaint, []} || _ <- lists:seq(1, Arity)]; + coverage -> [{notaint, []} || _ <- lists:seq(1, Arity)]; + % Make sure non lineage modules don't get tainted + _LineageMode when is_map_key(Module, LineageModulesDenyMap) -> [{notaint, []} || _ <- lists:seq(1, Arity)]; + _ -> [add_arg_taint({notaint, []}, {MFA, N}, "unknown") || N <- lists:seq(1, Arity)] + end, + + State#taint_am_state{scopes = [#{} | Scope], stack = StartingStack ++ Stack}; +%% Denotes function return. The top value ofhe stack should be the return value. +%% This function only destroys the current scope as it is not needed anymore. +%% sets the instrumented_return flag to be used by the {apply, ...} instruction +propagate( + {func_ret, {Function, _Loc}}, + State = #taint_am_state{ + stack = [_DroppedReturn | Stack], + scopes = [_DropedScope | Others], + instrumented_return = [{CalledFunction, _} | _] + } +) when + Function =/= CalledFunction, Function =/= true, Function =/= dropping_lambda_capture +-> + State#taint_am_state{scopes = Others, stack = Stack}; +propagate({func_ret, {_Function, _Loc}}, State = #taint_am_state{scopes = [_DropedScope | Others]}) -> + State#taint_am_state{scopes = Others}; +%% Pops a value of the stack and stores it in the scope with VarName +propagate({store, {VarName, Loc}}, State = #taint_am_state{stack = [First | Stack], scopes = Scopes}) -> + case First of + {try_enter, _} -> throw({"Trying to store a try_enter marker, stack in non-sense state", VarName, Loc, State}); + First1 -> State#taint_am_state{stack = Stack, scopes = insert_in_scope(VarName, First1, Scopes)} + end; +propagate(Instruction, State) -> + % io:format("Cannot apply instruction ~p~nto state:~n~p~n", [Instruction, State]), + throw({abstract_machine_invalid_state, Instruction, State}). + +% Note: bit_pattern_take_value currently assumes all the bit patterns are binary +% There are many other options: https://www.erlang.org/doc/programming_examples/bit_syntax.html#segments +-spec bit_pattern_take_value([bin_pattern_segment()], [taint_value()], [taint_value()]) -> + [taint_value()]. +% There are no bytes left, we are done and return all the values matched +bit_pattern_take_value([{Size, [binary]}], [], CurrentTaintValues) when Size =:= 0 orelse Size =:= default -> + CurrentTaintValues; +bit_pattern_take_value([], [], CurrentTaintValues) -> + CurrentTaintValues; +% Default segment is integer, default integer size is 8 (bits) +bit_pattern_take_value([{default, [default]} | PatternTail], TaintValuesAtByte, CurrentTaintValues) -> + bit_pattern_take_value([{8, [integer]} | PatternTail], TaintValuesAtByte, CurrentTaintValues); +bit_pattern_take_value([{default, [integer]} | PatternTail], TaintValuesAtByte, CurrentTaintValues) -> + bit_pattern_take_value([{8, [integer]} | PatternTail], TaintValuesAtByte, CurrentTaintValues); +bit_pattern_take_value([{Size0, [integer]} | PatternTail], TaintValuesAtByte, CurrentTaintValues) when + is_integer(Size0) +-> + % integers have size in bits + Size = Size0 div 8, + {IntegerTaintValueBytes, OtherBytes} = lists:split(Size, TaintValuesAtByte), + NewCurrentTaintValue = + case lists:usort(IntegerTaintValueBytes) of + [AllSameTaintValues] -> AllSameTaintValues + end, + bit_pattern_take_value(PatternTail, OtherBytes, [NewCurrentTaintValue | CurrentTaintValues]); +% If the current pattern has no bytes left, we move to the next pattern +bit_pattern_take_value([{0, [binary]} | PatternTail], TaintValuesAtByte, CurrentTaintValues) -> + bit_pattern_take_value(PatternTail, TaintValuesAtByte, [{notaint, []} | CurrentTaintValues]); +% The current pattern has some bytes left, we look at the next byte (HeadByteTaintValue) and +% compare it to the CurrentTaintValues for this pattern. +bit_pattern_take_value(Pattern = [{_, [binary]} | _], TaintValues, []) -> + bit_pattern_take_value(Pattern, TaintValues, [{notaint, []}]); +bit_pattern_take_value([{Size, [binary]} | PatternTail], [HeadByteTaintValue | Tail], [CurrentTaintValue | Others]) -> + NewCurrentTaintValue = + case {HeadByteTaintValue, CurrentTaintValue} of + {Same, Same} -> Same; + % Note: this should return binary pattern vals with the right offsets + {Head, {notaint, []}} -> Head; + {{notaint, []}, Current} -> Current; + % Note: join two different taint values + {Val1, Val2} -> throw({todo, Val1, Val2}) + end, + % default Size means the remainder of this pattern should be matched + NewSize = + case Size of + default -> default; + _ -> Size - 1 + end, + bit_pattern_take_value([{NewSize, [binary]} | PatternTail], Tail, [NewCurrentTaintValue | Others]). + +% This function crashes if the list doesn't containt only taint values +-spec is_all_taint_value([taint_value() | try_marker() | map()]) -> [taint_value()]. +is_all_taint_value([H = {lambda_closure, _} | T]) -> + [H | is_all_taint_value(T)]; +is_all_taint_value([H = {notaint, _} | T]) -> + [H | is_all_taint_value(T)]; +is_all_taint_value([H = {taint, _} | T]) -> + [H | is_all_taint_value(T)]; +is_all_taint_value([H = {pattern_taint, _, _} | T]) -> + [H | is_all_taint_value(T)]; +is_all_taint_value([]) -> + []. + +-spec get_taint_sources(taint_history(), [taint_source()]) -> [taint_source()]. +get_taint_sources([], Acc) -> + Acc; +get_taint_sources([{step, _} | Tail], Acc) -> + get_taint_sources(Tail, Acc); +get_taint_sources([{message_pass, _} | Tail], Acc) -> + get_taint_sources(Tail, Acc); +get_taint_sources([{return_site, _, _} | Tail], Acc) -> + get_taint_sources(Tail, Acc); +get_taint_sources([{call_site, _, _} | Tail], Acc) -> + get_taint_sources(Tail, Acc); +get_taint_sources([T = {arg_taint, _} | Tail], Acc) -> + get_taint_sources(Tail, [T | Acc]); +get_taint_sources([T = {source, _} | Tail], Acc) -> + get_taint_sources(Tail, [T | Acc]); +get_taint_sources([T = {tagged_source, _, _} | Tail], Acc) -> + get_taint_sources(Tail, [T | Acc]); +get_taint_sources([{blackhole, Sources} | Tail], Acc) -> + get_taint_sources(Tail, Sources ++ Acc); +get_taint_sources([{joined_history, _, History} | Tail], Acc) -> + get_taint_sources(Tail, lists:foldl(fun get_taint_sources/2, Acc, History)). + +-spec create_blackhole([taint_value()]) -> taint_value(). +create_blackhole(TaintValues) when is_list(TaintValues) -> + Sources = [get_taint_sources(get_history(TVal), []) || TVal <- TaintValues], + {taint, [{blackhole, lists:usort(lists:foldl(fun lists:append/2, [], Sources))}]}. + +-spec try_enter_predicate(try_block_id()) -> fun((taint_value() | dynamic()) -> boolean()). +try_enter_predicate(TryBlockId) -> + fun + ({try_enter, TBlockId}) when TryBlockId =:= TBlockId -> false; + (_) -> true + end. + +% Matches a binary patterns to the tainted vals. +% It first constructs a byte-level view of the tainted values (TaintValuesAtByte). +% For example if {Sizes, BinVals} is {[1,2], [taintA, taintB]}, +% TaintValuesAtByte would be [taintA, taintB, taintB]. +% bit_pattern_take_value then consumes bytes in TaintValuesAtByte +% according to the BinPattern +-spec match_binary_pattern({[integer()], [taint_value()]}, [bin_pattern_segment()]) -> + [taint_value()]. +match_binary_pattern({Sizes, BinVals}, BinPatterns) -> + TaintValuesAtByte = lists:append( + lists:map( + fun({Size, BinVal}) -> [BinVal || _ <- lists:seq(1, Size)] end, + lists:zip(Sizes, BinVals) + ) + ), + bit_pattern_take_value(BinPatterns, TaintValuesAtByte, []). + +-spec propagate_taints_for_models([taint_value()]) -> taint_value(). +propagate_taints_for_models(Args) -> + case [T || T <- Args, not is_no_taint(T)] of + [] -> + {notaint, []}; + [OneTaintedArg] -> + OneTaintedArg; + TaintedArgs -> + {taint, [{joined_history, model, lists:map(fun get_history/1, TaintedArgs)}]} + end. + +-spec setnth(non_neg_integer(), [A], A) -> [A]. +setnth(1, [_ | Rest], New) -> [New | Rest]; +setnth(I, [E | Rest], New) -> [E | setnth(I - 1, Rest, New)]. + +% This function forces the type into a taint_value() to make +% eqWAlizer happy +-spec taint_value(taint_value() | try_marker()) -> taint_value(). +taint_value({try_enter, _}) -> error(bad_taint_value); +taint_value(X) -> X. + +-spec maybe_to_opaque_taint(taint_value()) -> taint_value(). +maybe_to_opaque_taint({notaint, []}) -> {notaint, []}; +maybe_to_opaque_taint(Val) -> {taint, get_history(Val)}. + +-spec model_of(models(), {atom(), atom(), integer()}, [taint_value()]) -> + taint_value() | notmodeled | get_process_dict | {put_process_dict, taint_value()}. +model_of(_, {persistent_term, _, _}, _) -> {notaint, []}; +model_of(_, {supervisor, start_child, _}, _) -> {notaint, []}; +model_of(_, {operators, '+', 2}, Args) -> propagate_taints_for_models(Args); +model_of(_, {operators, '*', 2}, Args) -> propagate_taints_for_models(Args); +model_of(_, {operators, '++', 2}, Args) -> propagate_taints_for_models(Args); +model_of(_, {operators, '/', 2}, Args) -> propagate_taints_for_models(Args); +model_of(_, {operators, '-', 2}, Args) -> propagate_taints_for_models(Args); +model_of(_, {operators, 'rem', 2}, Args) -> propagate_taints_for_models(Args); +model_of(_, {operators, 'div', 2}, Args) -> propagate_taints_for_models(Args); +model_of(_, {operators, '==', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, '=/=', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, '/=', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, '=:=', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, '<', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, '>', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, '>=', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, '=<', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, 'andalso', 2}, _Args) -> {notaint, []}; +model_of(_, {operators, 'orelse', 2}, _Args) -> {notaint, []}; +model_of(_, {string, 'slice', 3}, [String, _Start, _Length]) -> propagate_taints_for_models([String]); +model_of(_, {string, 'concat', 2}, [String1, String2]) -> propagate_taints_for_models([String1, String2]); +% maps:next/1 maps:iterator/1 should only be called in modeled_taint_maps, where the taint +% is propagated manually, so the return value of maps needs to be untainted to destruct +% into any tuple and be effecitvely discarded +model_of(_, {maps, 'next', 1}, [_MapIterator]) -> {notaint, []}; +model_of(_, {maps, 'iterator', 1}, [_MapIterator]) -> {notaint, []}; +model_of(_, {crypto, 'hash', _}, _) -> {notaint, []}; +% erlang:monitor gives a new ref which is always untainted +model_of(_, {erlang, 'monitor', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'function_exported', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'length', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'binary_to_integer', _}, Args) -> propagate_taints_for_models(Args); +model_of(_, {erlang, 'phash2', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'put', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'whereis', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'is_pid', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'persistent_term', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'spawn_opt', _}, _) -> {notaint, []}; +model_of(_, {erlang, 'list_to_binary', _}, [Val | _]) -> maybe_to_opaque_taint(Val); +model_of(_, {erlang, 'list_to_atom', _}, [Val | _]) -> maybe_to_opaque_taint(Val); +model_of(_, {erlang, 'io_list_to_binary', _}, [Val | _]) -> maybe_to_opaque_taint(Val); +model_of(_, {erlang, 'integer_to_list', _}, [Val | _]) -> maybe_to_opaque_taint(Val); +model_of(_, {erlang, 'atom_to_binary', _}, [Val | _]) -> Val; +model_of(_, {erlang, 'integer_to_binary', _}, [Val | _]) -> Val; +model_of(_, {erlang, 'term_to_binary', _}, [Val | _]) -> Val; +model_of(_, {erlang, 'binary_to_term', _}, [Val | _]) -> Val; +model_of(_, {modeled_erlang, 'process_dict', _}, []) -> get_process_dict; +model_of(_, {modeled_erlang, 'process_dict', _}, [Val]) -> {put_process_dict, Val}; +model_of(_, {finer_taint, set_element, 3}, [_, Tuple, _]) -> Tuple; +model_of(Ms, {M, F, _}, _) when map_get({M, F}, Ms) =:= sanitize -> {notaint, []}; +model_of(Ms, {M, F, _}, Args) when map_get({M, F}, Ms) =:= propagate -> propagate_taints_for_models(Args); +model_of(Ms, {M, _, _}, _) when map_get({M, '_any'}, Ms) =:= sanitize -> {notaint, []}; +model_of(Ms, {M, _, _}, Args) when map_get({M, '_any'}, Ms) =:= propagate -> propagate_taints_for_models(Args); +model_of(_, _, _) -> notmodeled. diff --git a/finer_taint/src/taint_gatherer.erl b/finer_taint/src/taint_gatherer.erl new file mode 100644 index 0000000..0c59387 --- /dev/null +++ b/finer_taint/src/taint_gatherer.erl @@ -0,0 +1,169 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%% This module is used to collect various "leaks" (dataflows) that +%%% are detected while a taint_abstract_machine is executed on an +%%% instruction stream. parallel_abstract_machine is an example +%%% of how this module is meant to be used. +-module(taint_gatherer). + +-compile(warn_missing_spec_all). + +-behaviour(gen_server). + +-export([init/1, handle_cast/2, handle_call/3, handle_info/2]). + +% List of pids of proclets that are expected to register leaks +% [notapid] is an atom that will never match a pid and thus wait until timeout +-type proc_pids() :: [pid() | notapid]. + +-record(gatherer_state, { + leaks :: taint_abstract_machine:leaks_map(), + reply_to = noone :: noone | {gen_server:from(), proc_pids()}, + % In native units + last_added_time :: number(), + % List of pid() that have added a leak already + proclet_pids = [] :: proc_pids() +}). +-include_lib("kernel/include/logger.hrl"). + +-type state() :: #gatherer_state{}. + +% ============== PUBLIC API ============== +-export([add_leaks/2, get_gathered_leaks/3, start_link/0]). + +% Called by a "proclet" (taint_abstract_machine that is executing an instruction stream) +% to register leaks that it has found. +-spec add_leaks(gen_server:server_ref(), taint_abstract_machine:leaks_map()) -> ok. +add_leaks(Pid, Leaks) -> + gen_server:cast(Pid, {add_leaks, self(), Leaks}). + +% Gets the gathered leaks if all ProcletPids have returned or +% WaitTime seconds has passed since the last added leak. +% ProcletPids is the list of pids that are expected to add leaks +-spec get_gathered_leaks(gen_server:server_ref(), number(), proc_pids()) -> taint_abstract_machine:leaks_map(). +get_gathered_leaks(Pid, WaitTime, ProcletPids) -> + % Timeout is implemented via the check_progress message mechanism + gen_server:call(Pid, {get_gathered_leaks, WaitTime, ProcletPids}, infinity). + +-spec start_link() -> gen_server:start_ret(). +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% ==================== gen_server internal ====== +-spec init([]) -> {ok, state()}. +init([]) -> + {ok, #gatherer_state{leaks = #{}, last_added_time = erlang:monotonic_time()}}. + +-spec handle_call({get_gathered_leaks, number(), proc_pids()}, gen_server:from(), state()) -> + {reply, term(), state()} | {stop, normal, ok, state()} | {noreply, state()}. +handle_call( + {get_gathered_leaks, WaitTime, RequestedProcletPids}, + From, + State = #gatherer_state{leaks = Leaks, last_added_time = LastAddTime, proclet_pids = AlreadyAddedProcPids} +) -> + ?LOG_INFO("Currently have ~p leaks~n", [maps:size(Leaks)]), + TimeSinceLastAdd = erlang:convert_time_unit(erlang:monotonic_time() - LastAddTime, native, millisecond), + AllPidsAlreadyAdded = lists:sort(RequestedProcletPids) =:= lists:sort(AlreadyAddedProcPids), + if + % More than WaitTime has passed since last leak was added, client does not want to wait more time + TimeSinceLastAdd > WaitTime orelse AllPidsAlreadyAdded -> + % Or all requested pids have already added a leak + % return now, with what is currently gathered. + {reply, Leaks, State}; + TimeSinceLastAdd =< WaitTime -> + % The time passed since the last message is less than the WaitTime, + % we schedule a check_progress message and remember the client to reply to later + erlang:send_after( + WaitTime - TimeSinceLastAdd, + self(), + {check_progress, WaitTime} + ), + {noreply, State#gatherer_state{reply_to = {From, RequestedProcletPids}}} + end. + +-spec handle_info({check_progress, number()}, state()) -> {noreply, state()}. +% The {check_progress, WaitTime} is sent when a client is waiting for leaks to be gathered and is willing +% to tollerate WaitTime milliseconds since the last leak was gathered +handle_info( + {check_progress, WaitTime}, + State = #gatherer_state{ + leaks = Leaks, + last_added_time = LastAddTime, + reply_to = {ReplyTo, RequestedProcletPids}, + proclet_pids = AlreadyAddedProcPids + } +) -> + TimeSinceLastAddedLeaks = erlang:convert_time_unit(erlang:monotonic_time() - LastAddTime, native, millisecond), + AllPidsAlreadyAdded = lists:sort(RequestedProcletPids) =:= lists:sort(AlreadyAddedProcPids), + if + TimeSinceLastAddedLeaks > WaitTime -> + RemainingPids = RequestedProcletPids -- AlreadyAddedProcPids, + ?LOG_WARNING("Gather_leaks timeout, skipping ~p proclets, dead proclets: ~p~n", [ + length(RemainingPids), [Pid || Pid <- RemainingPids, is_pid(Pid) andalso not is_process_alive(Pid)] + ]), + gen_server:reply(ReplyTo, Leaks), + {noreply, State}; + AllPidsAlreadyAdded -> + gen_server:reply(ReplyTo, Leaks), + {noreply, State}; + true -> + erlang:send_after( + WaitTime, + self(), + {check_progress, WaitTime} + ), + {noreply, State} + end; +handle_info(_, State) -> + {noreply, State}. + +-spec handle_cast( + {add_leaks, pid(), taint_abstract_machine:leaks_map()}, state() +) -> {noreply, state()}. +handle_cast({add_leaks, AdderPid, Leaks}, State) -> + {noreply, add_leaks(Leaks, AdderPid, State)}. + +-spec add_leaks(taint_abstract_machine:leaks_map(), pid(), state()) -> state(). +add_leaks( + Leaks, AdderPid, State = #gatherer_state{leaks = StoredLeaks, proclet_pids = ProcletPids, reply_to = ReplyTo} +) -> + ?LOG_INFO("Got ~p leaks from ~p~n", [maps:size(Leaks), AdderPid]), + NewLeaks = maps:merge(StoredLeaks, Leaks), + NewProcletPids = [AdderPid | ProcletPids], + % If there is someone waiting for gathered leaks and this instance is the + % last pid we are waiting for, return to that client now, instead of later + % in the check_progress message + NewReplyTo = + case ReplyTo of + noone -> + noone; + {From, RequestedProcletPids} -> + AllPidsAlreadyAdded = lists:sort(RequestedProcletPids) =:= lists:sort(NewProcletPids), + if + AllPidsAlreadyAdded -> + gen_server:reply(From, NewLeaks), + noone; + true -> + ReplyTo + end + end, + + State#gatherer_state{ + leaks = NewLeaks, + reply_to = NewReplyTo, + proclet_pids = NewProcletPids, + last_added_time = erlang:monotonic_time() + }. diff --git a/finer_taint/test/abstract_machine_proclet_SUITE.erl b/finer_taint/test/abstract_machine_proclet_SUITE.erl new file mode 100644 index 0000000..e67dd8f --- /dev/null +++ b/finer_taint/test/abstract_machine_proclet_SUITE.erl @@ -0,0 +1,112 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%%------------------------------------------------------------------- +%%% @doc +%%% Tests abstract_machine_proclet +%%% @end +%%% ------------------------------------------------------------------- +-module(abstract_machine_proclet_SUITE). + +-include_lib("stdlib/include/assert.hrl"). + +%% Test server callbacks +-export([ + all/0, + init_per_suite/1, + end_per_suite/1, + groups/0 +]). + +%% Test cases +-export([ + message_pass_between_proclets/1, + runs_some_instructions/1 +]). + +groups() -> + [ + {basic, [sequence], [ + message_pass_between_proclets, + runs_some_instructions + ]} + ]. + +all() -> + [{group, basic}]. + +init_per_suite(Config) -> + Config. +end_per_suite(_Config) -> + ok. +%%-------------------------------------------------------------------- +%% TEST CASES + +runs_some_instructions(_Config) -> + {ok, _SupPid} = abstract_machine_proclet_sup:start_link(#{}), + {ok, GathererPid} = taint_gatherer:start_link(), + ProcletPid = abstract_machine_proclet_sup:new_proclet(GathererPid), + abstract_machine_proclet:execute_instruction(ProcletPid, {push, {"some_source.erl:12"}}), + abstract_machine_proclet:execute_instruction(ProcletPid, {sink, {"some_sink.erl:12"}}), + abstract_machine_proclet:stop(ProcletPid), + Leaks = taint_gatherer:get_gathered_leaks(GathererPid, 1000, [ProcletPid]), + gen_server:stop(GathererPid), + ?assertEqual(#{{leak, "some_sink.erl:12", [{source, "some_source.erl:12"}]} => ok}, Leaks). + +done() -> + receive + done -> ok + end. +message_pass_between_proclets(_Config) -> + taint_message_passer:init(), + {ok, SupPid} = online_finer_taint_sup:start_link(), + ?assertNotEqual(whereis(taint_gatherer), undefined), + ?assertNotEqual(whereis(abstract_machine_proclet_sup), undefined), + Parent = self(), + spawn(fun() -> + online_finer_taint:write_instruction({push, {notaint}}), + online_finer_taint:write_instruction({push, {"src:1"}}), + online_finer_taint:write_instruction({send, {"msg-id-42", "src:1"}}), + Parent ! done + end), + spawn(fun() -> + online_finer_taint:write_instruction({receive_trace, {"msg-id-42", "other_proc:1"}}), + online_finer_taint:write_instruction({sink, {"sink.erl1"}}), + Parent ! done + end), + done(), + done(), + abstract_machine_proclet_sup:stop_all_proclets(), + Leaks = taint_gatherer:get_gathered_leaks(taint_gatherer, 1000, [wait_till_timeout]), + ?assertEqual( + #{ + {leak, "sink.erl1", [ + {step, "other_proc:1"}, + {message_pass, "src:1"}, + {source, "src:1"} + ]} => + ok + }, + Leaks + ), + unlink(SupPid), + Ref = monitor(process, SupPid), + exit(SupPid, shutdown), + receive + {'DOWN', Ref, process, SupPid, shutdown} -> + ok + after 1000 -> + ?assert(false, "Shouldn't timeout") + end. diff --git a/finer_taint/test/abstract_machine_util_SUITE.erl b/finer_taint/test/abstract_machine_util_SUITE.erl new file mode 100644 index 0000000..c8ca215 --- /dev/null +++ b/finer_taint/test/abstract_machine_util_SUITE.erl @@ -0,0 +1,196 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%%------------------------------------------------------------------- +%%% @doc +%%% Unit tests for abstract_machine_util.erl +%%% @end +%%% ------------------------------------------------------------------- +-module(abstract_machine_util_SUITE). + +-include_lib("stdlib/include/assert.hrl"). + +%% Test server callbacks +-export([ + all/0 +]). + +%% Test cases +-export([ + models_cfg_is_correct/1, + can_balance_call_ret_with_joined_history/1, + can_get_dataflows/1, + can_balance_call_ret_with_joined_message_pass/1 +]). + +all() -> + [ + models_cfg_is_correct, + can_balance_call_ret_with_joined_history, + can_get_dataflows, + can_balance_call_ret_with_joined_message_pass + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%% + +models_cfg_is_correct(_Config) -> + Models = abstract_machine_util:get_priv_models(), + ?assertMatch(#{{'operators', 'div'} := propagate}, Models), + maps:foreach( + fun(MF, Action) -> + ?assertMatch({M, F} when is_atom(M) and is_atom(F), MF), + ?assert(Action =:= propagate orelse Action =:= sanitize) + end, + Models + ). + +can_get_dataflows(_Config) -> + TaintHistory = [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"}, + {return_site, {operators, '+', 2}, "simple_example.erl:30"}, + {joined_history, model, [ + [ + {call_site, {operators, '+', 2}, "simple_example.erl:30"}, + {arg_taint, {{simple_example, lineage_entry, 2}, 2}}, + {call_site, {current_module, lineage_entry, 2}, "unknown"} + ], + [ + {call_site, {operators, '+', 2}, "simple_example.erl:30"}, + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {arg_taint, {{simple_example, unpack, 1}, 1}}, + {call_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {return_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {arg_taint, {{simple_example, pack, 1}, 1}}, + {call_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {arg_taint, {{simple_example, lineage_entry, 2}, 1}}, + {call_site, {current_module, lineage_entry, 2}, "unknown"} + ] + ]} + ], + ?assertEqual( + [ + {dataflow_src, {{simple_example, lineage_entry, 2}, 1}, [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, lineage_entry, 2}, 2}, [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, pack, 1}, 1}, [ + {return_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, unpack, 1}, 1}, [ + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]} + ], + maps:keys(abstract_machine_util:get_dataflows(TaintHistory)) + ). + +can_balance_call_ret_with_joined_history(_Config) -> + TaintHistory = [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"}, + {return_site, {operators, '+', 2}, "simple_example.erl:30"}, + {joined_history, model, [ + [ + {call_site, {operators, '+', 2}, "simple_example.erl:30"}, + {arg_taint, {{simple_example, lineage_entry, 2}, 2}}, + {call_site, {current_module, lineage_entry, 2}, "unknown"} + ], + [ + {call_site, {operators, '+', 2}, "simple_example.erl:30"}, + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {arg_taint, {{simple_example, unpack, 1}, 1}}, + {call_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {return_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {arg_taint, {{simple_example, pack, 1}, 1}}, + {call_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {arg_taint, {{simple_example, lineage_entry, 2}, 1}}, + {call_site, {current_module, lineage_entry, 2}, "unknown"} + ] + ]} + ], + ?assertEqual( + [ + {dataflow_src, {{simple_example, lineage_entry, 2}, 1}, [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, lineage_entry, 2}, 2}, [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, pack, 1}, 1}, [ + {return_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, unpack, 1}, 1}, [ + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]} + ], + maps:keys(abstract_machine_util:get_dataflows(TaintHistory)) + ). + +can_balance_call_ret_with_joined_message_pass(_Config) -> + TaintHistory = [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"}, + {return_site, {operators, '+', 2}, "simple_example.erl:30"}, + {joined_history, model, [ + [ + {call_site, {operators, '+', 2}, "simple_example.erl:30"}, + {arg_taint, {{simple_example, lineage_entry, 2}, 2}}, + {call_site, {current_module, lineage_entry, 2}, "unknown"} + ], + [ + {call_site, {operators, '+', 2}, "simple_example.erl:30"}, + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {message_pass, "some messagepass"}, + {arg_taint, {{simple_example, unpack, 1}, 1}}, + {call_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {return_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {arg_taint, {{simple_example, pack, 1}, 1}}, + {call_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {arg_taint, {{simple_example, lineage_entry, 2}, 1}}, + {call_site, {current_module, lineage_entry, 2}, "unknown"} + ] + ]} + ], + ?assertEqual( + [ + {dataflow_src, {{simple_example, lineage_entry, 2}, 1}, [ + {call_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {message_pass, "some messagepass"}, + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, lineage_entry, 2}, 2}, [ + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, pack, 1}, 1}, [ + {return_site, {current_module, pack, 1}, "simple_example.erl:29"}, + {call_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {message_pass, "some messagepass"}, + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]}, + {dataflow_src, {{simple_example, unpack, 1}, 1}, [ + {message_pass, "some messagepass"}, + {return_site, {current_module, unpack, 1}, "simple_example.erl:30"}, + {call_site, {current_module, pack, 1}, "simple_example.erl:30"} + ]} + ], + maps:keys(abstract_machine_util:get_dataflows(TaintHistory)) + ). diff --git a/finer_taint/test/capture_out.erl b/finer_taint/test/capture_out.erl new file mode 100644 index 0000000..c0f3f2f --- /dev/null +++ b/finer_taint/test/capture_out.erl @@ -0,0 +1,46 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(capture_out). +-export([capture_output/1]). +-include_lib("kernel/include/logger.hrl"). + +%% capture_output's help function +-spec tracer([string()]) -> ok. +tracer(Trace) when is_list(Trace) -> + receive + {io_request, From, ReplyAs, {put_chars, _Encoding, Module, Function, Args}} -> + Text = erlang:apply(Module, Function, Args), + From ! {io_reply, ReplyAs, ok}, + tracer([Text | Trace]); + {'$gen_call', From, get} -> + gen:reply(From, Trace); + Other -> + ?LOG_WARNING("Unexpected I/O request: ~p", [Other]), + tracer(Trace) + end. +-spec capture_output(fun()) -> {term(), [string()]}. +capture_output(Fun) -> + OldLeader = group_leader(), + Tracer = spawn_link(fun() -> tracer([]) end), + true = group_leader(Tracer, self()), + + Return = + try + Fun() + after + group_leader(OldLeader, self()) + end, + IOCaptured = lists:flatten(lists:reverse(gen_server:call(Tracer, get))), + {Return, IOCaptured}. diff --git a/finer_taint/test/finer_taint_SUITE.erl b/finer_taint/test/finer_taint_SUITE.erl new file mode 100644 index 0000000..9ef9874 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE.erl @@ -0,0 +1,735 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%%------------------------------------------------------------------- +%%% @doc +%%% Tests for finer taint analysis +%%% +%%% They instrument an erlang file, run a function in the instrumented module, +%%% capture output and run it throught the abstract machine to get the leaks +%%% @end +%%% ------------------------------------------------------------------- +-module(finer_taint_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("stdlib/include/assert.hrl"). + +%% Test server callbacks +-export([ + all/0, + groups/0, + init_per_suite/1, + end_per_suite/1 +]). + +%% Test cases +-export([ + finds_simple_taint/1, + extract_pattern/1, + calling_convention/1, + recursion/1, + case_clauses/1, + list_patterns/1, + record_main/1, + nested_case_main/1, + nested_calls_main/1, + modeled_functions/1, + joining_model/1, + process_dict_model/1, + lambdas/1, + lambda_closures/1, + try_catch/1, + try_catch_nested/1, + try_catch_define/1, + try_catch_crs/1, + try_after/1, + if_clause/1, + bitstrings/1, + bitstring_constructed/1, + integer_constructed_extracted/1, + map_values/1, + map_keys/1, + map_updates/1, + map_module/1, + opaque_map/1, + case_in_function_args/1, + i13n_to_not_i13n_to_i13n/1, + comprehension_transform/1, + basic_list_comprehension/1, + pattern_to_var/1, + maybe_expr/1, + string_plusplus_pattern/1, + set_element/1, + operators_in_pattern/1, + lineage_annotations_merge_taint/1, + shortcircuiting/1, + local_if_clause/1, + ps_if_clause/1, + ps_local_if_clause/1, + macro_duplicator/1, + ps_exp_if_clause/1 +]). + +%Helpers +-export([assert_instruction_stream_equal/2]). + +groups() -> + [ + {basic, [], [ + finds_simple_taint, + extract_pattern, + calling_convention, + recursion, + case_clauses, + list_patterns, + record_main, + nested_case_main, + nested_calls_main, + modeled_functions, + lambdas, + lambda_closures, + try_catch, + try_catch_nested, + try_catch_define, + try_catch_crs, + try_after, + joining_model, + process_dict_model, + bitstrings, + bitstring_constructed, + integer_constructed_extracted, + if_clause, + map_values, + map_keys, + map_updates, + map_module, + opaque_map, + case_in_function_args, + i13n_to_not_i13n_to_i13n, + basic_list_comprehension, + comprehension_transform, + pattern_to_var, + maybe_expr, + string_plusplus_pattern, + set_element, + operators_in_pattern, + lineage_annotations_merge_taint, + shortcircuiting, + local_if_clause, + ps_if_clause, + ps_local_if_clause, + macro_duplicator, + ps_exp_if_clause + ]} + ]. + +all() -> + [{group, basic}]. + +init_per_suite(Config) -> + {module, modeled_taint_maps} = finer_taint_compiler:instrument_loaded_module(modeled_taint_maps, [ + {finer_taint_module, ct_finer_taint} + ]), + {module, modeled_power_shell} = finer_taint_compiler:instrument_loaded_module(modeled_power_shell, [ + {finer_taint_module, ct_finer_taint} + ]), + {module, modeled_erlang} = finer_taint_compiler:instrument_loaded_module(modeled_erlang, [ + {finer_taint_module, ct_finer_taint} + ]), + Config. + +end_per_suite(_Config) -> + ok. + +compile(Modules, Config) -> + DataDir = ?config(data_dir, Config), + CompileMod = fun(Mod) -> + ModFilename = unicode:characters_to_list(io_lib:format("~p.erl", [Mod])), + ModPath = filename:join([DataDir, ModFilename]), + {ok, Mod, Binary} = compile:file(ModPath, [debug_info, binary]), + {ok, {Mod, [{abstract_code, {_, Forms}} | _]}} = beam_lib:chunks(Binary, [abstract_code, compile_info]), + io:format("~p~n", [Forms]), + {Mod, Forms} + end, + lists:map(CompileMod, Modules) ++ Config. + +compile_and_load(Config, Module) -> + [{_, Forms} | _] = compile([Module], Config), + io:format("~p Forms: ~p~n", [Module, Forms]), + InstrumentedForms = finer_taint_compiler:instrument_with_sinks(Forms), + % erl_expand_records can crash in erl_expand_records:guard_tests/2 (erl_expand_records.erl, line 161) + % if guards aren't set as lists of lists. This call ensure that the expanded code + % does not crash erl_expand_records + ?assertNotException(error, function_clause, erl_expand_records:module(InstrumentedForms, [debug_info])), + io:format("~p: ~n~s~n", [Module, erl_prettypr:format(erl_syntax:form_list(InstrumentedForms))]), + {ok, Module, Binary} = compile:forms(InstrumentedForms, [debug_info, {feature, maybe_expr, enable}]), + DataDir = ?config(data_dir, Config), + code:add_patha(DataDir), + BeamFileName = unicode:characters_to_list(io_lib:format("~p.beam", [Module])), + BeamPath = filename:join(DataDir, BeamFileName), + ok = file:write_file(BeamPath, Binary), + {module, Module} = code:load_binary(Module, BeamPath, Binary). + +compile_and_run_function(Config, Module, Func) -> + compile_and_load(Config, Module), + {_Ret, OutputRaw} = capture_out:capture_output(fun() -> Module:Func() end), + %% Prepend generated to ignore these files in phabricator + Output = "{push,{\"@ge" ++ "nerated\"}}.\n{pop,{}}.\n" ++ OutputRaw, + FixtureFilename = get_instr_fixture_filename(Config, Func), + {Status, RepoRoot} = file:read_file("/tmp/erlang_taint_root"), + ReadFileResult = file:read_file(FixtureFilename), + case {ct:get_verbosity(default), Status} of + %% Run with: echo -n `pwd` > /tmp/erlang_taint_root ; buck2 test -c 'erlang.erlang_test_ct_opts=[{verbosity,101}]' + %% to update the local fixture + {101, ok} -> + ok = file:write_file( + filename:join([ + RepoRoot, "finer_taint/test/finer_taint_SUITE_data", io_lib:format("~p_analysis_instr", [Func]) + ]), + Output + ); + _ -> + ok + end, + {ok, FileCont} = ReadFileResult, + assert_instruction_stream_equal(binary_to_list(FileCont), Output), + State = taint_abstract_machine:run_tracing(FixtureFilename), + % Make sure there is only the return value of the main function left on the stack + ?assertMatch([_], taint_abstract_machine:get_stack(State)), + taint_abstract_machine:get_leaks(State). + +% Compares live instruction stream with the one in fixture +% accoutning for differences due to OTP versions +assert_instruction_stream_equal(Expected, Actual) -> + Replace = fun(Instructions) -> + % GenLc variable names depend on a hash of a subexpression AST, + % that can change with OTP versions + re:replace(Instructions, "\"[^\"]*Gen.c[^\"]*\"", "", [{return, list}, global]) + end, + ?assertEqual(Replace(Expected), Replace(Actual)). + +get_instr_fixture_filename(Config, Func) -> + DataDir = ?config(data_dir, Config), + filename:join(DataDir, io_lib:format("~p_analysis_instr", [Func])). + +run_lineage_analysis(Config, Func) -> + FixtureFilename = get_instr_fixture_filename(Config, Func), + FullLineage = parallel_abstract_machine:run_lineage_with_line_history([FixtureFilename]), + ReducedLineage = parallel_abstract_machine:run_lineage([FixtureFilename]), + ?assertEqual( + length(abstract_machine_util:get_arg_lineage_raw(FullLineage)), + length(abstract_machine_util:get_arg_lineage_raw(ReducedLineage)) + ), + FullLineage. + +%%-------------------------------------------------------------------- +%% TEST CASES +finds_simple_taint(Config) -> + [{leak, _, Leaks}] = compile_and_run_function(Config, simple_example, one_clause), + ?assertEqual( + [ + {step, "simple_example.erl:26"}, + {return_site, {string, slice, 3}, "simple_example.erl:23"}, + {call_site, {string, slice, 3}, "simple_example.erl:23"}, + {step, "simple_example.erl:23"}, + {tagged_source, "phone_num", "simple_example.erl:21"} + ], + Leaks + ). + +if_clause(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, simple_example, if_clause), + ?assertEqual(Sink, "simple_example.erl:33"), + ?assertEqual(abstract_machine_util:get_sources(History), ["simple_example.erl:29"]). + +lineage_annotations_merge_taint(Config) -> + [] = compile_and_run_function(Config, simple_example, lineage_main), + ArgLeaks = run_lineage_analysis(Config, lineage_main), + Lineage = abstract_machine_util:get_arg_lineage(ArgLeaks, human_readable_annotated), + ?assertEqual( + "simple_example:lineage_entry/2-Arg1 -> simple_example:pack/1-Arg1\n" + " call@simple_example.erl:43\n" + " call@simple_example.erl:44\n" + "simple_example:lineage_entry/2-Arg1 -> simple_example:unpack/1-Arg1\n" + " call@simple_example.erl:44\n" + "simple_example:lineage_entry/2-Arg2 -> simple_example:pack/1-Arg1\n" + " call@simple_example.erl:44\n" + "simple_example:pack/1-Arg1 -> simple_example:pack/1-Arg1\n" + " ret@simple_example.erl:43;call@simple_example.erl:44\n" + "simple_example:pack/1-Arg1 -> simple_example:unpack/1-Arg1\n" + " ret@simple_example.erl:43;call@simple_example.erl:44\n" + "simple_example:unpack/1-Arg1 -> simple_example:pack/1-Arg1\n" + " ret@simple_example.erl:44;call@simple_example.erl:44\n", + Lineage + ). + +pattern_to_var(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, pattern_to_var, pattern_to_var), + ?assertEqual(Sink, "pattern_to_var.erl:26"), + ?assertEqual(abstract_machine_util:get_sources(History), ["pattern_to_var.erl:21"]). + +maybe_expr(Config) -> + [{leak, Sink2, History2}, {leak, Sink, History}] = compile_and_run_function( + Config, pattern_to_var, maybe_expr_main + ), + ?assertEqual(Sink, "pattern_to_var.erl:88"), + ?assertEqual(abstract_machine_util:get_sources(History), ["pattern_to_var.erl:86"]), + ?assertEqual(Sink2, "pattern_to_var.erl:93"), + ?assertEqual(abstract_machine_util:get_sources(History2), ["pattern_to_var.erl:86", "pattern_to_var.erl:81"]). + +operators_in_pattern(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, pattern_to_var, operators_in_pattern_main), + ?assertEqual(Sink, "pattern_to_var.erl:65"), + ?assertEqual(abstract_machine_util:get_sources(History), ["pattern_to_var.erl:60"]). + +set_element(Config) -> + [{leak, Sink2, History2}, {leak, Sink1, History1}] = compile_and_run_function( + Config, pattern_to_var, set_element_main + ), + ?assertEqual(Sink1, "pattern_to_var.erl:55"), + ?assertEqual(abstract_machine_util:get_sources(History1), ["pattern_to_var.erl:49"]), + ?assertEqual(Sink2, "pattern_to_var.erl:56"), + ?assertEqual(abstract_machine_util:get_sources(History2), ["pattern_to_var.erl:50"]). + +string_plusplus_pattern(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, pattern_to_var, string_plusplus_pattern), + ?assertEqual(Sink, "pattern_to_var.erl:45"), + ?assertEqual(abstract_machine_util:get_sources(History), ["pattern_to_var.erl:43"]). + +extract_pattern(Config) -> + Leaks = compile_and_run_function(Config, pattern_to_var, extract_pattern), + ?assertEqual(length(Leaks), 3), + LeakedSinks = [Sink || {leak, Sink, _History} <- Leaks], + ?assertEqual(LeakedSinks, ["pattern_to_var.erl:38", "pattern_to_var.erl:37", "pattern_to_var.erl:32"]). + +nested_calls_main(Config) -> + Leaks = compile_and_run_function(Config, function_calls, nested_calls_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual(Sink, "function_calls.erl:69"), + ?assertEqual(abstract_machine_util:get_sources(History), ["function_calls.erl:68"]), + ?assertEqual( + [ + {return_site, {function_calls, at, 2}, "function_calls.erl:69"}, + {step, "function_calls.erl:64"}, + {step, "function_calls.erl:64"}, + {call_site, {function_calls, at, 2}, "function_calls.erl:69"}, + {step, "function_calls.erl:69"}, + {return_site, {function_calls, insert_at, 3}, "function_calls.erl:68"}, + {step, "function_calls.erl:59"}, + {step, "function_calls.erl:59"}, + {call_site, {function_calls, insert_at, 3}, "function_calls.erl:68"}, + {source, "function_calls.erl:68"} + ], + History + ). + +lambda_closures(Config) -> + Leaks = compile_and_run_function(Config, function_calls, lambda_closures_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual(Sink, "function_calls.erl:94"), + ?assertEqual(abstract_machine_util:get_sources(History), ["function_calls.erl:92"]), + ?assertEqual( + [ + {return_site, {function_calls, variable_func, 0}, "function_calls.erl:94"}, + {return_site, {operators, '+', 2}, "function_calls.erl:89"}, + {call_site, {operators, '+', 2}, "function_calls.erl:89"}, + {step, "function_calls.erl:89"}, + {call_site, {function_calls, variable_func, 0}, "function_calls.erl:94"}, + {step, "function_calls.erl:94"}, + {return_site, {function_calls, create_lambda, 1}, "function_calls.erl:93"}, + {call_site, {function_calls, create_lambda, 1}, "function_calls.erl:93"}, + {step, "function_calls.erl:93"}, + {source, "function_calls.erl:92"} + ], + History + ). + +lambdas(Config) -> + Leaks = compile_and_run_function(Config, function_calls, lambdas_main), + [{leak, Sink1, History1}, {leak, Sink, History}] = Leaks, + ?assertEqual(Sink, "function_calls.erl:75"), + ?assertEqual(abstract_machine_util:get_sources(History), ["function_calls.erl:73"]), + ?assertEqual(Sink1, "function_calls.erl:83"), + ?assertEqual(abstract_machine_util:get_sources(History1), ["function_calls.erl:73"]), + ?assertEqual( + [ + {step, "function_calls.erl:83"}, + {step, "function_calls.erl:82"}, + {return_site, {function_calls, variable_func, 2}, "function_calls.erl:82"}, + {step, "function_calls.erl:79"}, + {return_site, {function_calls, variable_func, 1}, "function_calls.erl:79"}, + {return_site, {operators, '+', 2}, "function_calls.erl:77"}, + {call_site, {operators, '+', 2}, "function_calls.erl:77"}, + {step, "function_calls.erl:77"}, + {call_site, {function_calls, variable_func, 1}, "function_calls.erl:79"}, + {step, "function_calls.erl:79"}, + {call_site, {function_calls, variable_func, 2}, "function_calls.erl:82"}, + {step, "function_calls.erl:82"}, + {source, "function_calls.erl:73"} + ], + History1 + ). + +calling_convention(Config) -> + Leaks = compile_and_run_function(Config, function_calls, calling_convention_main), + ?assertEqual(length(Leaks), 1), + [{leak, Sink, History}] = Leaks, + ?assertEqual(Sink, "function_calls.erl:26"), + ?assertEqual(History, [ + {step, "function_calls.erl:26"}, + {return_site, {function_calls, process_two_lists, 2}, "function_calls.erl:23"}, + {return_site, {function_calls, concat_two_lists, 2}, "function_calls.erl:37"}, + {return_site, {operators, '++', 2}, "function_calls.erl:34"}, + {call_site, {operators, '++', 2}, "function_calls.erl:34"}, + {step, "function_calls.erl:34"}, + {call_site, {function_calls, concat_two_lists, 2}, "function_calls.erl:37"}, + {step, "function_calls.erl:37"}, + {return_site, {function_calls, append_to_list, 1}, "function_calls.erl:36"}, + {return_site, {operators, '++', 2}, "function_calls.erl:30"}, + {call_site, {operators, '++', 2}, "function_calls.erl:30"}, + {step, "function_calls.erl:30"}, + {call_site, {function_calls, append_to_list, 1}, "function_calls.erl:36"}, + {step, "function_calls.erl:36"}, + {call_site, {function_calls, process_two_lists, 2}, "function_calls.erl:23"}, + {step, "function_calls.erl:23"}, + {source, "function_calls.erl:22"} + ]). + +recursion(Config) -> + Leaks = compile_and_run_function(Config, function_calls, recursion_main), + [{leak, Sink2, _History}, {leak, Sink, History}] = Leaks, + ?assertEqual(Sink, "function_calls.erl:47"), + ?assertEqual(Sink2, "function_calls.erl:52"), + ?assertEqual( + [ + {step, "function_calls.erl:47"}, + {return_site, {function_calls, insert_at, 3}, "function_calls.erl:44"}, + {step, "function_calls.erl:62"}, + {step, "function_calls.erl:62"}, + {return_site, {function_calls, insert_at, 3}, "function_calls.erl:61"}, + {step, "function_calls.erl:62"}, + {step, "function_calls.erl:62"}, + {return_site, {function_calls, insert_at, 3}, "function_calls.erl:61"}, + {step, "function_calls.erl:59"}, + {step, "function_calls.erl:59"}, + {call_site, {function_calls, insert_at, 3}, "function_calls.erl:61"}, + {step, "function_calls.erl:61"}, + {call_site, {function_calls, insert_at, 3}, "function_calls.erl:61"}, + {step, "function_calls.erl:61"}, + {call_site, {function_calls, insert_at, 3}, "function_calls.erl:44"}, + {step, "function_calls.erl:44"}, + {source, "function_calls.erl:43"} + ], + History + ), + ArgLeaks = run_lineage_analysis(Config, recursion_main), + Lineage = abstract_machine_util:get_arg_lineage(ArgLeaks, human_readable), + ?assertEqual( + lists:flatten( + lists:join("\n", [ + "function_calls:at/2-Arg1 -> function_calls:at/2-Arg1", + "function_calls:at/2-Arg2 -> function_calls:at/2-Arg2", + "function_calls:generate_list/1-Arg1 -> function_calls:at/2-Arg2", + "function_calls:generate_list/1-Arg1 -> function_calls:generate_list/1-Arg1", + "function_calls:generate_list/1-Arg1 -> function_calls:insert_at/3-Arg3", + "function_calls:insert_at/3-Arg1 -> function_calls:insert_at/3-Arg1", + "function_calls:insert_at/3-Arg2 -> function_calls:at/2-Arg2", + "function_calls:insert_at/3-Arg2 -> function_calls:insert_at/3-Arg2", + "function_calls:insert_at/3-Arg3 -> function_calls:at/2-Arg2", + "function_calls:insert_at/3-Arg3 -> function_calls:insert_at/3-Arg3", + "" + ]) + ), + Lineage + ), + InsertAt_To_At_Lineage = abstract_machine_util:query_arg_lineage(ArgLeaks, { + {function_calls, insert_at, 3}, 2, {function_calls, at, 2}, 2 + }), + ?assertEqual( + 6, + length(InsertAt_To_At_Lineage), + "There are 6 times function_calls:insert_at/3-Arg2 flows to function_calls:at/2-Arg2 " + ), + ?assertEqual( + [ + {dataflow_src, {{function_calls, insert_at, 3}, 2}, [ + {return_site, {function_calls, insert_at, 3}, "function_calls.erl:44"}, + {call_site, {function_calls, at, 2}, "function_calls.erl:50"} + ]}, + + {arg_leak, {{function_calls, at, 2}, 2}} + ], + lists:nth(1, lists:usort(InsertAt_To_At_Lineage)) + ). + +try_catch_define(Config) -> + Leaks = compile_and_run_function(Config, try_catch, try_catch_define_main), + [{leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual("try_catch.erl:94", FirstSink), + ?assertEqual(["try_catch.erl:91"], abstract_machine_util:get_sources(FirstHistory)). + +try_catch_crs(Config) -> + Leaks = compile_and_run_function(Config, try_catch, try_catch_crs_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual("try_catch.erl:104", FirstSink), + ?assertEqual(["try_catch.erl:99"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("try_catch.erl:114", SecondSink), + ?assertEqual(["try_catch.erl:110"], abstract_machine_util:get_sources(SecondHistory)). + +try_catch_nested(Config) -> + Leaks = compile_and_run_function(Config, try_catch, try_catch_nested_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual("try_catch.erl:82", FirstSink), + ?assertEqual(["try_catch.erl:73"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("try_catch.erl:85", SecondSink), + ?assertEqual(["try_catch.erl:73"], abstract_machine_util:get_sources(SecondHistory)). + +try_after(Config) -> + Leaks = compile_and_run_function(Config, try_catch, try_after_main), + [{leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["try_catch.erl:118"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("try_catch.erl:122", FirstSink). + +try_catch(Config) -> + Leaks = compile_and_run_function(Config, try_catch, try_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["try_catch.erl:37"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("try_catch.erl:49", FirstSink), + ?assertEqual(["try_catch.erl:37"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("try_catch.erl:53", SecondSink), + + Leaks1 = compile_and_run_function(Config, try_catch, try_catch_main2), + [{leak, Sink1, History1}] = Leaks1, + ?assertEqual("try_catch.erl:69", Sink1), + ?assertEqual(["try_catch.erl:60"], abstract_machine_util:get_sources(History1)). + +bitstrings(Config) -> + Leaks = compile_and_run_function(Config, bitstrings, bitstring_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual("bitstrings.erl:24", Sink), + ?assertEqual(["bitstrings.erl:22"], abstract_machine_util:get_sources(History)). + +bitstring_constructed(Config) -> + Leaks = compile_and_run_function(Config, bitstrings, bitstring_constructed_main), + [{leak, Sink2, History2}, {leak, Sink1, History1}] = Leaks, + ?assertEqual("bitstrings.erl:32", Sink1), + ?assertEqual(["bitstrings.erl:27"], abstract_machine_util:get_sources(History1)), + ?assertEqual("bitstrings.erl:33", Sink2), + ?assertEqual(["bitstrings.erl:27"], abstract_machine_util:get_sources(History2)). + +integer_constructed_extracted(Config) -> + Leaks = compile_and_run_function(Config, bitstrings, integer_constructed_extracted_main), + [{leak, Sink1, History1}] = Leaks, + ?assertEqual("bitstrings.erl:40", Sink1), + ?assertEqual(["bitstrings.erl:36"], abstract_machine_util:get_sources(History1)). + +case_clauses(Config) -> + Leaks = compile_and_run_function(Config, case_clauses, case_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual("case_clauses.erl:30", Sink), + ?assertEqual(["case_clauses.erl:27"], abstract_machine_util:get_sources(History)). + +record_main(Config) -> + Leaks = compile_and_run_function(Config, case_clauses, record_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["case_clauses.erl:44"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("case_clauses.erl:48", FirstSink), + ?assertEqual(["case_clauses.erl:44"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("case_clauses.erl:49", SecondSink). + +list_patterns(Config) -> + Leaks = compile_and_run_function(Config, case_clauses, lists_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["case_clauses.erl:54"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("case_clauses.erl:35", FirstSink), + ?assertEqual(["case_clauses.erl:34"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("case_clauses.erl:36", SecondSink). + +map_updates(Config) -> + Leaks = compile_and_run_function(Config, map_examples, map_update_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["map_examples.erl:49"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("map_examples.erl:53", FirstSink), + ?assertEqual(["map_examples.erl:49"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("map_examples.erl:56", SecondSink). + +map_values(Config) -> + Leaks = compile_and_run_function(Config, map_examples, map_values_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["map_examples.erl:24"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("map_examples.erl:26", FirstSink), + ?assertEqual(["map_examples.erl:24"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("map_examples.erl:28", SecondSink). + +map_keys(Config) -> + Leaks = compile_and_run_function(Config, map_examples, map_keys_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["map_examples.erl:33"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("map_examples.erl:36", FirstSink), + ?assertEqual(["map_examples.erl:33"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("map_examples.erl:44", SecondSink). + +opaque_map(Config) -> + Leaks = compile_and_run_function(Config, map_examples, opaque_map_main), + [{leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["map_examples.erl:76"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("map_examples.erl:78", FirstSink). + +map_module(Config) -> + Leaks = compile_and_run_function(Config, map_examples, map_module_main), + [{leak, ThirdSink, ThirdHistory}, {leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["map_examples.erl:59"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("map_examples.erl:61", FirstSink), + ?assertEqual(["map_examples.erl:59"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("map_examples.erl:62", SecondSink), + ?assertEqual(["map_examples.erl:59"], abstract_machine_util:get_sources(ThirdHistory)), + ?assertEqual("map_examples.erl:73", ThirdSink). + +process_dict_model(Config) -> + Leaks = compile_and_run_function(Config, modeled_functions, process_dict_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["modeled_functions.erl:35"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("modeled_functions.erl:38", FirstSink), + ?assertEqual(["modeled_functions.erl:35"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("modeled_functions.erl:39", SecondSink). + +joining_model(Config) -> + Leaks = compile_and_run_function(Config, modeled_functions, joining_model_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual("modeled_functions.erl:32", Sink), + ?assertEqual(["modeled_functions.erl:31", "modeled_functions.erl:30"], abstract_machine_util:get_sources(History)). + +i13n_to_not_i13n_to_i13n(Config) -> + UnmodeledFilePath = io_lib:format("~s/not_instrumented/not_instrumented.erl", [?config(data_dir, Config)]), + {ok, not_instrumented} = c:c(UnmodeledFilePath), + Leaks = compile_and_run_function(Config, i13n_to_not_i13n_to_i13n, i13n_to_not_i13n_to_i13n_main), + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = Leaks, + ?assertEqual(["i13n_to_not_i13n_to_i13n.erl:29"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("i13n_to_not_i13n_to_i13n.erl:31", FirstSink), + ?assertEqual(["i13n_to_not_i13n_to_i13n.erl:35"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("i13n_to_not_i13n_to_i13n.erl:37", SecondSink). + +modeled_functions(Config) -> + UnmodeledFilePath = io_lib:format("~s/not_instrumented/definetly_not_modeled.erl", [?config(data_dir, Config)]), + {ok, definetly_not_modeled} = c:c(UnmodeledFilePath), + Leaks = compile_and_run_function(Config, modeled_functions, modeled_functions_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual("modeled_functions.erl:25", Sink), + ?assertEqual(["modeled_functions.erl:22", "modeled_functions.erl:23"], abstract_machine_util:get_sources(History)). + +basic_list_comprehension(Config) -> + Leaks = compile_and_run_function(Config, list_comprehension, basic_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual("list_comprehension.erl:31", Sink), + ?assertEqual(["list_comprehension.erl:23"], abstract_machine_util:get_sources(History)), + Leaks1 = compile_and_run_function(Config, list_comprehension, cartesian_main), + [{leak, Sink1, History1}] = Leaks1, + ?assertEqual("list_comprehension.erl:37", Sink1), + ?assertEqual(["list_comprehension.erl:23"], abstract_machine_util:get_sources(History1)). + +case_in_function_args(Config) -> + Leaks = compile_and_run_function(Config, case_clauses, case_in_function_args_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual("case_clauses.erl:83", Sink), + ?assertEqual(["case_clauses.erl:81"], abstract_machine_util:get_sources(History)). + +shortcircuiting(Config) -> + [] = compile_and_run_function(Config, shortcircuiting, safe_shortcircuit), + Leaks = compile_and_run_function(Config, shortcircuiting, unsafe_shortcircuit), + [{leak, Sink0, _}, {leak, Sink1, _}, {leak, Sink2, _}] = Leaks, + ?assertEqual("shortcircuiting.erl:36", Sink0), + ?assertEqual("shortcircuiting.erl:29", Sink1), + ?assertEqual("shortcircuiting.erl:26", Sink2), + F = fun({_, _, Hist}) -> ?assertEqual(["shortcircuiting.erl:25"], abstract_machine_util:get_sources(Hist)) end, + lists:foreach(F, Leaks). + +test_comprehension(Comprehension, CompareWith) -> + {ok, Tokens, _} = erl_scan:string(Comprehension), + {ok, [Forms]} = erl_parse:parse_exprs(Tokens), + InstrumentedLc = finer_taint_compiler:rewrite_comprehension(Forms), + case CompareWith of + nocompare -> ok; + print -> io:format(erl_pp:expr(InstrumentedLc)); + map -> ok; + map_c -> ok; + _ -> ?assertEqual(CompareWith, lists:flatten(erl_pp:expr(InstrumentedLc))) + end, + {value, Result, #{}} = erl_eval:expr(Forms, #{}), + {value, InstrumentedResult, #{}} = erl_eval:expr(InstrumentedLc, #{}), + case CompareWith of + map -> + ?assertEqual(lists:sort(Result), lists:sort(InstrumentedResult)); + _ -> + ?assertEqual(Result, InstrumentedResult) + end. + +comprehension_transform(_Config) -> + BasicComprehensionExpected = + "fun GenLc5ENHDPWZ([]) ->\n" + " [];\n" + " GenLc5ENHDPWZ([X | GenlcTail5ENHDPWZ]) ->\n" + " [X | GenLc5ENHDPWZ(GenlcTail5ENHDPWZ)];\n" + " GenLc5ENHDPWZ([_ | GenlcTail5ENHDPWZ]) ->\n" + " GenLc5ENHDPWZ(GenlcTail5ENHDPWZ)\n" + "end([1, 2, 3])", + test_comprehension("[X || X <- [1,2,3]].", BasicComprehensionExpected), + test_comprehension("[{X, Y} || X <- [1,2,3], Y <- [a,b,c]].", nocompare), + test_comprehension("[{X, Y} || X <- [1,2,3], {a,Y} <- [{a,1},b,c]].", print), + test_comprehension("[Y || {X1, Y} <- [{1,2}, {1,3}, {2,2}], 1 == X1, _ <- [1,2]].", print), + test_comprehension("[X || X <- [1,2,3], fun(Y) -> Y == 1 end(X)].", print), + test_comprehension("[{X, Y} || {a,Y} <- [{a,1},b,c], X <- [1,2,3]].", print), + test_comprehension("[X || <> <= <<1,2,3>>].", print), + test_comprehension("[X || <> <= <<1:8,2:8,3:8>>].", print), + test_comprehension("[A || A := _V <- #{1 => a, 2 => b}].", map), + test_comprehension("#{A => a || A := _V <- #{1 => a, 2 => b}}.", map_c), + test_comprehension("[{X, Y} || <> <= <<27:8>>, X rem 2 == 0, <> <= <<42,255,127>>].", print). + +nested_case_main(Config) -> + Leaks = compile_and_run_function(Config, case_clauses, nested_case_with_call_main), + [{leak, Sink, History}] = Leaks, + ?assertEqual("case_clauses.erl:71", Sink), + ?assertEqual(["case_clauses.erl:67"], abstract_machine_util:get_sources(History)). + +macro_duplicator(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, simple_example, macro_duplicate_main), + ?assertEqual("simple_example.erl:56", Sink), + ?assertEqual(["simple_example.erl:53"], abstract_machine_util:get_sources(History)). +%% Tests using power_shell - based on if_clause above, but with variations using power_shell +%% This is just a baseline calling a local function being the sink. +local_if_clause(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, ps_example, local_if_clause), + ?assertEqual("ps_example.erl:34", Sink), + ?assertEqual(["ps_example.erl:25"], abstract_machine_util:get_sources(History)). + +%% Call unexported local sink using power_shell:eval/3 +ps_local_if_clause(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, ps_example, ps_local_if_clause), + ?assertEqual("ps_example.erl:34", Sink), + ?assertEqual(["ps_example.erl:37"], abstract_machine_util:get_sources(History)). + +%% Call unexported sink in another module using power_shell:eval/3 +ps_if_clause(Config) -> + compile_and_load(Config, ps_external), + [{leak, Sink, History}] = compile_and_run_function(Config, ps_example, ps_if_clause), + ?assertEqual("ps_external.erl:26", Sink), + ?assertEqual(["ps_example.erl:46"], abstract_machine_util:get_sources(History)). + +%% Call unexported sink in another module by first exporting all functions +%% with power_shell:export/1 and then calling as if it were exported. +ps_exp_if_clause(Config) -> + compile_and_load(Config, ps_external), + [{leak, Sink, History}] = compile_and_run_function(Config, ps_example, ps_exp_if_clause), + ?assertEqual("ps_external.erl:26", Sink), + ?assertEqual(["ps_example.erl:55"], abstract_machine_util:get_sources(History)). diff --git a/finer_taint/test/finer_taint_SUITE_data/basic_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/basic_main_analysis_instr new file mode 100644 index 0000000..efdb6be --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/basic_main_analysis_instr @@ -0,0 +1,119 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{list_comprehension,basic_main,0},"list_comprehension.erl:27"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"list_comprehension.erl:28"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"list_comprehension.erl:28"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"list_comprehension.erl:28"}}. +{construct_pattern,{{tuple,2},"list_comprehension.erl:28"}}. +{duplicate,{}}. +{store,{"ListWrap","list_comprehension.erl:28"}}. +{pop,{}}. +{capture_closure,{["GenLc2FEGIBFA","GenlcTail2FEGIBFA","X","_"]}}. +{duplicate,{}}. +{store,{"GenLc2FEGIBFA","list_comprehension.erl:29"}}. +{restore_capture,{{list_comprehension,lambda_func,1},"list_comprehension.erl:29"}}. +{get,{"ListWrap","list_comprehension.erl:29"}}. +{deconstruct_pattern,{{tuple,2},"list_comprehension.erl:29"}}. +{pop,{}}. +{store,{"rec0","list_comprehension.erl:29"}}. +{get,{"rec0","list_comprehension.erl:29"}}. +{call_fun,{list_comprehension,lambda_func,1},"list_comprehension.erl:29"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,1},"list_comprehension.erl:29"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:29"}}. +{store,{"X","list_comprehension.erl:29"}}. +{store,{"GenlcTail2FEGIBFA","list_comprehension.erl:29"}}. +{get,{"GenLc2FEGIBFA","list_comprehension.erl:29"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:29"}}. +{get,{"GenlcTail2FEGIBFA","list_comprehension.erl:29"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:29"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,1},"list_comprehension.erl:29"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:29"}}. +{store,{"X","list_comprehension.erl:29"}}. +{store,{"GenlcTail2FEGIBFA","list_comprehension.erl:29"}}. +{get,{"GenLc2FEGIBFA","list_comprehension.erl:29"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:29"}}. +{get,{"GenlcTail2FEGIBFA","list_comprehension.erl:29"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:29"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,1},"list_comprehension.erl:29"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:29"}}. +{store,{"X","list_comprehension.erl:29"}}. +{store,{"GenlcTail2FEGIBFA","list_comprehension.erl:29"}}. +{get,{"GenLc2FEGIBFA","list_comprehension.erl:29"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:29"}}. +{get,{"GenlcTail2FEGIBFA","list_comprehension.erl:29"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:29"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,1},"list_comprehension.erl:29"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,"list_comprehension.erl:29"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:29"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:29"}}. +{push,{notaint}}. +{restore_capture,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}}. +{get,{"X","list_comprehension.erl:29"}}. +{call_fun,{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}. +{push_scope,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:24"}}. +{store,{"X","list_comprehension.erl:24"}}. +{get,{"X","list_comprehension.erl:25"}}. +{func_ret,{maybe_taint,"list_comprehension.erl:24"}}. +{apply,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:29"}}. +{construct_pattern,{{cons},"list_comprehension.erl:29"}}. +{func_ret,{lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,"list_comprehension.erl:29"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:29"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:29"}}. +{push,{notaint}}. +{restore_capture,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}}. +{get,{"X","list_comprehension.erl:29"}}. +{call_fun,{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}. +{push_scope,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"list_comprehension.erl:23"}}. +{push,{notaint}}. +{pop,{}}. +{func_ret,{maybe_taint,"list_comprehension.erl:22"}}. +{apply,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:29"}}. +{construct_pattern,{{cons},"list_comprehension.erl:29"}}. +{func_ret,{lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,"list_comprehension.erl:29"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:29"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:29"}}. +{push,{notaint}}. +{restore_capture,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}}. +{get,{"X","list_comprehension.erl:29"}}. +{call_fun,{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}. +{push_scope,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:24"}}. +{store,{"X","list_comprehension.erl:24"}}. +{get,{"X","list_comprehension.erl:25"}}. +{func_ret,{maybe_taint,"list_comprehension.erl:24"}}. +{apply,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:29"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:29"}}. +{construct_pattern,{{cons},"list_comprehension.erl:29"}}. +{func_ret,{lambda_list_comprehension_29_63_named_GenLc2FEGIBFA,"list_comprehension.erl:29"}}. +{apply,{{list_comprehension,lambda_func,1},"list_comprehension.erl:29"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:29"}}. +{duplicate,{}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:29"}}. +{store,{"NotTainted1","list_comprehension.erl:29"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:29"}}. +{store,{"Tainted","list_comprehension.erl:29"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:29"}}. +{store,{"NotTainted2","list_comprehension.erl:29"}}. +{pop,{}}. +{pop,{}}. +{get,{"NotTainted1","list_comprehension.erl:30"}}. +{sink,{"list_comprehension.erl:30"}}. +{pop,{}}. +{get,{"Tainted","list_comprehension.erl:31"}}. +{sink,{"list_comprehension.erl:31"}}. +{pop,{}}. +{get,{"NotTainted2","list_comprehension.erl:32"}}. +{sink,{"list_comprehension.erl:32"}}. +{func_ret,{basic_main,"list_comprehension.erl:27"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/bitstring_constructed_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/bitstring_constructed_main_analysis_instr new file mode 100644 index 0000000..4ee7bba --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/bitstring_constructed_main_analysis_instr @@ -0,0 +1,36 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{bitstrings,bitstring_constructed_main,0},"bitstrings.erl:26"}}. +{push,{notaint}}. +{construct_pattern,{{bitstring,[14]},"bitstrings.erl:27"}}. +{pop,{}}. +{push,{"bitstrings.erl:27"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedBitstring","bitstrings.erl:27"}}. +{pop,{}}. +{push,{notaint}}. +{construct_pattern,{{bitstring,"\v"},"bitstrings.erl:28"}}. +{get,{"TaintedBitstring","bitstrings.erl:28"}}. +{construct_pattern,{{bitstring,[11,14]},"bitstrings.erl:28"}}. +{duplicate,{}}. +{store,{"HalfTaintedString","bitstrings.erl:28"}}. +{pop,{}}. +{get,{"HalfTaintedString","bitstrings.erl:30"}}. +{duplicate,{}}. +{deconstruct_pattern,{{bitstring,[{8,[binary]},{7,[binary]},{5,[binary]},{default,[binary]}]},"bitstrings.erl:29"}}. +{store,{"NotTainted","bitstrings.erl:29"}}. +{store,{"SemiTainted","bitstrings.erl:29"}}. +{store,{"FullTainted","bitstrings.erl:29"}}. +{pop,{}}. +{pop,{}}. +{get,{"NotTainted","bitstrings.erl:31"}}. +{sink,{"bitstrings.erl:31"}}. +{pop,{}}. +{get,{"SemiTainted","bitstrings.erl:32"}}. +{sink,{"bitstrings.erl:32"}}. +{pop,{}}. +{get,{"FullTainted","bitstrings.erl:33"}}. +{sink,{"bitstrings.erl:33"}}. +{func_ret,{bitstring_constructed_main,"bitstrings.erl:26"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/bitstring_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/bitstring_main_analysis_instr new file mode 100644 index 0000000..4051133 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/bitstring_main_analysis_instr @@ -0,0 +1,22 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{bitstrings,bitstring_main,0},"bitstrings.erl:21"}}. +{push,{notaint}}. +{construct_pattern,{{bitstring,[16]},"bitstrings.erl:22"}}. +{pop,{}}. +{push,{"bitstrings.erl:22"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedBitstring","bitstrings.erl:22"}}. +{pop,{}}. +{get,{"TaintedBitstring","bitstrings.erl:23"}}. +{duplicate,{}}. +{deconstruct_pattern,{{bitstring,[{8,default},{8,default},{default,[bitstring]}]},"bitstrings.erl:23"}}. +{pop,{}}. +{store,{"Tainted","bitstrings.erl:23"}}. +{store,{"Rest","bitstrings.erl:23"}}. +{pop,{}}. +{get,{"Tainted","bitstrings.erl:24"}}. +{sink,{"bitstrings.erl:24"}}. +{func_ret,{bitstring_main,"bitstrings.erl:21"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/bitstrings.erl b/finer_taint/test/finer_taint_SUITE_data/bitstrings.erl new file mode 100644 index 0000000..9785796 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/bitstrings.erl @@ -0,0 +1,40 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('bitstrings'). +-export([ + bitstring_main/0, integer_constructed_extracted_main/0, + bitstring_constructed_main/0 +]). + +bitstring_main() -> + TaintedBitstring = finer_taint:source(<<"a tainted string">>), + <<$a:8, Tainted:8, Rest/bitstring>> = TaintedBitstring, + finer_taint:sink(Tainted). + +bitstring_constructed_main() -> + TaintedBitstring = finer_taint:source(<<"tainted string">>), + HalfTaintedString = <<<<"not tainted">>/binary, TaintedBitstring/binary>>, + <> = + HalfTaintedString, + finer_taint:sink(NotTainted), + finer_taint:sink(SemiTainted), + finer_taint:sink(FullTainted). + +integer_constructed_extracted_main() -> + TaintedSixBytes = finer_taint:source(<<"123456">>), + HalfTaintedString = <<<<"ab">>/binary, TaintedSixBytes/binary>>, + <> = HalfTaintedString, + finer_taint:sink(NotTainted), + finer_taint:sink(Tainted). diff --git a/finer_taint/test/finer_taint_SUITE_data/calling_convention_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/calling_convention_main_analysis_instr new file mode 100644 index 0000000..4a4b475 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/calling_convention_main_analysis_instr @@ -0,0 +1,119 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{function_calls,calling_convention_main,0},"function_calls.erl:21"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:22"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","function_calls.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,process_two_lists,2},"function_calls.erl:23"}}. +{push,{notaint}}. +{get,{"PhoneNumber","function_calls.erl:23"}}. +{call_fun,{function_calls,process_two_lists,2},"function_calls.erl:23"}. +{push_scope,{{function_calls,process_two_lists,2},"function_calls.erl:35"}}. +{store,{"L1","function_calls.erl:35"}}. +{store,{"L2","function_calls.erl:35"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,append_to_list,1},"function_calls.erl:36"}}. +{get,{"L1","function_calls.erl:36"}}. +{call_fun,{function_calls,append_to_list,1},"function_calls.erl:36"}. +{push_scope,{{function_calls,append_to_list,1},"function_calls.erl:29"}}. +{store,{"L1","function_calls.erl:29"}}. +{get,{"L1","function_calls.erl:30"}}. +{push,{notaint}}. +{call_fun,{operators,'++',2},"function_calls.erl:30"}. +{apply,{{operators,'++',2},"function_calls.erl:30"}}. +{func_ret,{append_to_list,"function_calls.erl:29"}}. +{apply,{{function_calls,append_to_list,1},"function_calls.erl:36"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:36"}}. +{duplicate,{}}. +{store,{"L3","function_calls.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,concat_two_lists,2},"function_calls.erl:37"}}. +{get,{"L3","function_calls.erl:37"}}. +{get,{"L2","function_calls.erl:37"}}. +{call_fun,{function_calls,concat_two_lists,2},"function_calls.erl:37"}. +{push_scope,{{function_calls,concat_two_lists,2},"function_calls.erl:32"}}. +{store,{"L1","function_calls.erl:32"}}. +{store,{"L2","function_calls.erl:32"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:33"}}. +{push,{notaint}}. +{pop,{}}. +{pop,{}}. +{get,{"L1","function_calls.erl:34"}}. +{get,{"L2","function_calls.erl:34"}}. +{call_fun,{operators,'++',2},"function_calls.erl:34"}. +{apply,{{operators,'++',2},"function_calls.erl:34"}}. +{func_ret,{concat_two_lists,"function_calls.erl:32"}}. +{apply,{{function_calls,concat_two_lists,2},"function_calls.erl:37"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:37"}}. +{func_ret,{process_two_lists,"function_calls.erl:35"}}. +{apply,{{function_calls,process_two_lists,2},"function_calls.erl:23"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:23"}}. +{duplicate,{}}. +{store,{"Tainted","function_calls.erl:23"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,process_two_lists,2},"function_calls.erl:24"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{function_calls,process_two_lists,2},"function_calls.erl:24"}. +{push_scope,{{function_calls,process_two_lists,2},"function_calls.erl:35"}}. +{store,{"L1","function_calls.erl:35"}}. +{store,{"L2","function_calls.erl:35"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,append_to_list,1},"function_calls.erl:36"}}. +{get,{"L1","function_calls.erl:36"}}. +{call_fun,{function_calls,append_to_list,1},"function_calls.erl:36"}. +{push_scope,{{function_calls,append_to_list,1},"function_calls.erl:29"}}. +{store,{"L1","function_calls.erl:29"}}. +{get,{"L1","function_calls.erl:30"}}. +{push,{notaint}}. +{call_fun,{operators,'++',2},"function_calls.erl:30"}. +{apply,{{operators,'++',2},"function_calls.erl:30"}}. +{func_ret,{append_to_list,"function_calls.erl:29"}}. +{apply,{{function_calls,append_to_list,1},"function_calls.erl:36"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:36"}}. +{duplicate,{}}. +{store,{"L3","function_calls.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,concat_two_lists,2},"function_calls.erl:37"}}. +{get,{"L3","function_calls.erl:37"}}. +{get,{"L2","function_calls.erl:37"}}. +{call_fun,{function_calls,concat_two_lists,2},"function_calls.erl:37"}. +{push_scope,{{function_calls,concat_two_lists,2},"function_calls.erl:32"}}. +{store,{"L1","function_calls.erl:32"}}. +{store,{"L2","function_calls.erl:32"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:33"}}. +{push,{notaint}}. +{pop,{}}. +{pop,{}}. +{get,{"L1","function_calls.erl:34"}}. +{get,{"L2","function_calls.erl:34"}}. +{call_fun,{operators,'++',2},"function_calls.erl:34"}. +{apply,{{operators,'++',2},"function_calls.erl:34"}}. +{func_ret,{concat_two_lists,"function_calls.erl:32"}}. +{apply,{{function_calls,concat_two_lists,2},"function_calls.erl:37"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:37"}}. +{func_ret,{process_two_lists,"function_calls.erl:35"}}. +{apply,{{function_calls,process_two_lists,2},"function_calls.erl:24"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:24"}}. +{duplicate,{}}. +{store,{"NoTainted","function_calls.erl:24"}}. +{pop,{}}. +{get,{"NoTainted","function_calls.erl:25"}}. +{sink,{"function_calls.erl:25"}}. +{pop,{}}. +{get,{"Tainted","function_calls.erl:26"}}. +{sink,{"function_calls.erl:26"}}. +{func_ret,{calling_convention_main,"function_calls.erl:21"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/cartesian_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/cartesian_main_analysis_instr new file mode 100644 index 0000000..39777dd --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/cartesian_main_analysis_instr @@ -0,0 +1,189 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{list_comprehension,cartesian_main,0},"list_comprehension.erl:34"}}. +{capture_closure,{["GenLc2WUUYQ7Z","GenlcTail2WUUYQ7Z","X","_"]}}. +{duplicate,{}}. +{store,{"GenLc2WUUYQ7Z","list_comprehension.erl:35"}}. +{restore_capture,{{list_comprehension,lambda_func,1},"list_comprehension.erl:35"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"list_comprehension.erl:35"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"list_comprehension.erl:35"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"list_comprehension.erl:35"}}. +{call_fun,{list_comprehension,lambda_func,1},"list_comprehension.erl:35"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,1},"list_comprehension.erl:35"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:35"}}. +{store,{"X","list_comprehension.erl:35"}}. +{store,{"GenlcTail2WUUYQ7Z","list_comprehension.erl:35"}}. +{get,{"GenLc2WUUYQ7Z","list_comprehension.erl:35"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:35"}}. +{get,{"GenlcTail2WUUYQ7Z","list_comprehension.erl:35"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:35"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,1},"list_comprehension.erl:35"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:35"}}. +{store,{"X","list_comprehension.erl:35"}}. +{store,{"GenlcTail2WUUYQ7Z","list_comprehension.erl:35"}}. +{get,{"GenLc2WUUYQ7Z","list_comprehension.erl:35"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:35"}}. +{get,{"GenlcTail2WUUYQ7Z","list_comprehension.erl:35"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:35"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,1},"list_comprehension.erl:35"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:35"}}. +{store,{"X","list_comprehension.erl:35"}}. +{store,{"GenlcTail2WUUYQ7Z","list_comprehension.erl:35"}}. +{get,{"GenLc2WUUYQ7Z","list_comprehension.erl:35"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:35"}}. +{get,{"GenlcTail2WUUYQ7Z","list_comprehension.erl:35"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:35"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,1},"list_comprehension.erl:35"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,"list_comprehension.erl:35"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:35"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:35"}}. +{get,{"X","list_comprehension.erl:35"}}. +{get,{"X","list_comprehension.erl:35"}}. +{construct_pattern,{{tuple,2},"list_comprehension.erl:35"}}. +{construct_pattern,{{cons},"list_comprehension.erl:35"}}. +{func_ret,{lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,"list_comprehension.erl:35"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:35"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:35"}}. +{get,{"X","list_comprehension.erl:35"}}. +{get,{"X","list_comprehension.erl:35"}}. +{construct_pattern,{{tuple,2},"list_comprehension.erl:35"}}. +{construct_pattern,{{cons},"list_comprehension.erl:35"}}. +{func_ret,{lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,"list_comprehension.erl:35"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:35"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:35"}}. +{get,{"X","list_comprehension.erl:35"}}. +{get,{"X","list_comprehension.erl:35"}}. +{construct_pattern,{{tuple,2},"list_comprehension.erl:35"}}. +{construct_pattern,{{cons},"list_comprehension.erl:35"}}. +{func_ret,{lambda_list_comprehension_35_20_named_GenLc2WUUYQ7Z,"list_comprehension.erl:35"}}. +{apply,{{list_comprehension,lambda_func,1},"list_comprehension.erl:35"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:35"}}. +{duplicate,{}}. +{store,{"L","list_comprehension.erl:35"}}. +{pop,{}}. +{capture_closure,{["GenLc5CL4INUL","GenLc9T47OW6X","GenlcTail5CL4INUL","GenlcTail9T47OW6X","L","X","Y","_"]}}. +{duplicate,{}}. +{store,{"GenLc5CL4INUL","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,lambda_func,1},"list_comprehension.erl:36"}}. +{get,{"L","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,lambda_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_49_named_GenLc5CL4INUL,1},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{tuple,2},"list_comprehension.erl:36"}}. +{pop,{}}. +{store,{"X","list_comprehension.erl:36"}}. +{store,{"GenlcTail5CL4INUL","list_comprehension.erl:36"}}. +{capture_closure,{["GenLc5CL4INUL","GenLc9T47OW6X","GenlcTail5CL4INUL","GenlcTail9T47OW6X","X","Y","_"]}}. +{duplicate,{}}. +{store,{"GenLc9T47OW6X","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,lambda_func,1},"list_comprehension.erl:36"}}. +{get,{"L","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,lambda_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_62_named_GenLc9T47OW6X,1},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:36"}}. +{pop,{}}. +{store,{"GenlcTail9T47OW6X","list_comprehension.erl:36"}}. +{get,{"GenLc9T47OW6X","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{get,{"GenlcTail9T47OW6X","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_62_named_GenLc9T47OW6X,1},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{tuple,2},"list_comprehension.erl:36"}}. +{pop,{}}. +{store,{"Y","list_comprehension.erl:36"}}. +{store,{"GenlcTail9T47OW6X","list_comprehension.erl:36"}}. +{get,{"GenLc9T47OW6X","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{get,{"GenlcTail9T47OW6X","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_62_named_GenLc9T47OW6X,1},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:36"}}. +{pop,{}}. +{store,{"GenlcTail9T47OW6X","list_comprehension.erl:36"}}. +{get,{"GenLc9T47OW6X","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{get,{"GenlcTail9T47OW6X","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_62_named_GenLc9T47OW6X,1},"list_comprehension.erl:36"}}. +{pop,{}}. +{get,{"GenLc5CL4INUL","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{get,{"GenlcTail5CL4INUL","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_49_named_GenLc5CL4INUL,1},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:36"}}. +{pop,{}}. +{store,{"GenlcTail5CL4INUL","list_comprehension.erl:36"}}. +{get,{"GenLc5CL4INUL","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{get,{"GenlcTail5CL4INUL","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_49_named_GenLc5CL4INUL,1},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:36"}}. +{pop,{}}. +{store,{"GenlcTail5CL4INUL","list_comprehension.erl:36"}}. +{get,{"GenLc5CL4INUL","list_comprehension.erl:36"}}. +{restore_capture,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{get,{"GenlcTail5CL4INUL","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,variable_func,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,lambda_list_comprehension_36_49_named_GenLc5CL4INUL,1},"list_comprehension.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{lambda_list_comprehension_36_49_named_GenLc5CL4INUL,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{func_ret,{lambda_list_comprehension_36_49_named_GenLc5CL4INUL,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{func_ret,{lambda_list_comprehension_36_49_named_GenLc5CL4INUL,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{func_ret,{lambda_list_comprehension_36_62_named_GenLc9T47OW6X,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{func_ret,{lambda_list_comprehension_36_62_named_GenLc9T47OW6X,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{get,{"X","list_comprehension.erl:36"}}. +{push,{notaint}}. +{restore_capture,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:36"}}. +{get,{"Y","list_comprehension.erl:36"}}. +{call_fun,{list_comprehension,maybe_taint,1},"list_comprehension.erl:36"}. +{push_scope,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"list_comprehension.erl:23"}}. +{push,{notaint}}. +{pop,{}}. +{func_ret,{maybe_taint,"list_comprehension.erl:22"}}. +{apply,{{list_comprehension,maybe_taint,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{construct_pattern,{{tuple,2},"list_comprehension.erl:36"}}. +{construct_pattern,{{cons},"list_comprehension.erl:36"}}. +{func_ret,{lambda_list_comprehension_36_62_named_GenLc9T47OW6X,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,variable_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{func_ret,{lambda_list_comprehension_36_62_named_GenLc9T47OW6X,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,lambda_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{func_ret,{lambda_list_comprehension_36_49_named_GenLc5CL4INUL,"list_comprehension.erl:36"}}. +{apply,{{list_comprehension,lambda_func,1},"list_comprehension.erl:36"}}. +{func_ret,{dropping_lambda_capture,"list_comprehension.erl:36"}}. +{duplicate,{}}. +{deconstruct_pattern,{{cons},"list_comprehension.erl:36"}}. +{deconstruct_pattern,{{tuple,2},"list_comprehension.erl:36"}}. +{pop,{}}. +{store,{"Tainted","list_comprehension.erl:36"}}. +{pop,{}}. +{pop,{}}. +{get,{"Tainted","list_comprehension.erl:37"}}. +{sink,{"list_comprehension.erl:37"}}. +{func_ret,{cartesian_main,"list_comprehension.erl:34"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/case_clauses.erl b/finer_taint/test/finer_taint_SUITE_data/case_clauses.erl new file mode 100644 index 0000000..6e7b6e3 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/case_clauses.erl @@ -0,0 +1,84 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('case_clauses'). +-export([ + case_main/0, case_in_function_args_main/0, + record_main/0, + nested_case_with_call_main/0, + lists_main/0 +]). + +case_main() -> + TaintedAtom = finer_taint:source(sanitized), + SanitizedIsSanitized = big_pattern_extractor(TaintedAtom), + finer_taint:sink(SanitizedIsSanitized), + PhoneNumber = finer_taint:source(" 123@secret.net"), + TaintedPhoneNumber = big_pattern_extractor(PhoneNumber), + finer_taint:sink(SanitizedIsSanitized), + finer_taint:sink(TaintedPhoneNumber). + + +lists_main() -> + PhoneNumber = finer_taint:source(" 123@secret.net"), + finer_taint:sink(big_pattern_extractor([])), + finer_taint:sink(big_pattern_extractor([notsanitized, PhoneNumber, 1.0])), + finer_taint:sink(big_pattern_extractor([PhoneNumber | [sanitized]])). + +-record(value, {type :: tainted | nottainted, + int_value :: integer(), + history :: list()}). + +record_main() -> + TaintedValue = finer_taint:source(42), + NotTaintedRecord = #value{type = nottainted, history = [], int_value = TaintedValue}, + TaintedRecord = #value{type = tainted, history = [], int_value = TaintedValue}, + finer_taint:sink(big_pattern_extractor(NotTaintedRecord)), + finer_taint:sink(big_pattern_extractor(TaintedRecord)), + finer_taint:sink(big_pattern_extractor([TaintedRecord])). + + +big_pattern_extractor(X) -> + case X of + [] -> finer_taint:source([]); + [notsanitized, PhoneNumber, 1.0] -> PhoneNumber; + [_PhoneNumber, sanitized] -> sanitized; + sanitized -> ok; + #value{type = nottainted, history = Hist} -> Hist; + #value{type = tainted, int_value = Val} -> Val; + [_Record = #value{type = tainted, int_value = IntVal}] -> IntVal; + _ -> X + end. + + +get_ok() -> ok. +nested_case_with_call_main() -> + TaintedValue = finer_taint:source(42), + case get_ok() of + ok -> FourtyThree = TaintedValue + 1, + case FourtyThree of + 43 -> finer_taint:sink(FourtyThree); + _ -> ok + end; + _ -> ok + end. + +get_1_arg(Arg, _, _) -> Arg. +get_2_arg(_, Arg, _) -> Arg. +get_tuple() -> {finer_taint:source(1),2}. +case_in_function_args_main() -> + TaintedValue = finer_taint:source(42), + ARecord = #value{type = tainted, history = [], int_value = TaintedValue}, + finer_taint:sink(get_2_arg(get_tuple(),ARecord#value.int_value, ARecord#value.type)). + diff --git a/finer_taint/test/finer_taint_SUITE_data/case_in_function_args_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/case_in_function_args_main_analysis_instr new file mode 100644 index 0000000..a6045c6 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/case_in_function_args_main_analysis_instr @@ -0,0 +1,60 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{case_clauses,case_in_function_args_main,0},"case_clauses.erl:80"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:81"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedValue","case_clauses.erl:81"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"TaintedValue","case_clauses.erl:82"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,4},"case_clauses.erl:82"}}. +{duplicate,{}}. +{store,{"ARecord","case_clauses.erl:82"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,get_2_arg,3},"case_clauses.erl:83"}}. +{get,{"ARecord","case_clauses.erl:83"}}. +{deconstruct_pattern,{{tuple,4},"case_clauses.erl:83"}}. +{pop,{}}. +{store,{"rec1","case_clauses.erl:83"}}. +{pop,{}}. +{pop,{}}. +{get,{"rec1","case_clauses.erl:83"}}. +{get,{"ARecord","case_clauses.erl:83"}}. +{deconstruct_pattern,{{tuple,4},"case_clauses.erl:83"}}. +{pop,{}}. +{pop,{}}. +{store,{"rec0","case_clauses.erl:83"}}. +{pop,{}}. +{get,{"rec0","case_clauses.erl:83"}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,get_tuple,0},"case_clauses.erl:83"}}. +{call_fun,{case_clauses,get_tuple,0},"case_clauses.erl:83"}. +{push_scope,{{case_clauses,get_tuple,0},"case_clauses.erl:79"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:79"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"case_clauses.erl:79"}}. +{func_ret,{get_tuple,"case_clauses.erl:79"}}. +{apply,{{case_clauses,get_tuple,0},"case_clauses.erl:83"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:83"}}. +{call_fun,{case_clauses,get_2_arg,3},"case_clauses.erl:83"}. +{push_scope,{{case_clauses,get_2_arg,3},"case_clauses.erl:78"}}. +{pop,{}}. +{store,{"Arg","case_clauses.erl:78"}}. +{pop,{}}. +{get,{"Arg","case_clauses.erl:78"}}. +{func_ret,{get_2_arg,"case_clauses.erl:78"}}. +{apply,{{case_clauses,get_2_arg,3},"case_clauses.erl:83"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:83"}}. +{sink,{"case_clauses.erl:83"}}. +{func_ret,{case_in_function_args_main,"case_clauses.erl:80"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/case_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/case_main_analysis_instr new file mode 100644 index 0000000..ba28568 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/case_main_analysis_instr @@ -0,0 +1,58 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{case_clauses,case_main,0},"case_clauses.erl:23"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:24"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedAtom","case_clauses.erl:24"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:25"}}. +{get,{"TaintedAtom","case_clauses.erl:25"}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:25"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:25"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:25"}}. +{duplicate,{}}. +{store,{"SanitizedIsSanitized","case_clauses.erl:25"}}. +{pop,{}}. +{get,{"SanitizedIsSanitized","case_clauses.erl:26"}}. +{sink,{"case_clauses.erl:26"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:27"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","case_clauses.erl:27"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:28"}}. +{get,{"PhoneNumber","case_clauses.erl:28"}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:28"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{pop,{}}. +{get,{"X","case_clauses.erl:61"}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:28"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:28"}}. +{duplicate,{}}. +{store,{"TaintedPhoneNumber","case_clauses.erl:28"}}. +{pop,{}}. +{get,{"SanitizedIsSanitized","case_clauses.erl:29"}}. +{sink,{"case_clauses.erl:29"}}. +{pop,{}}. +{get,{"TaintedPhoneNumber","case_clauses.erl:30"}}. +{sink,{"case_clauses.erl:30"}}. +{func_ret,{case_main,"case_clauses.erl:23"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/extract_pattern_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/extract_pattern_analysis_instr new file mode 100644 index 0000000..7ea4288 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/extract_pattern_analysis_instr @@ -0,0 +1,62 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{pattern_to_var,extract_pattern,0},"pattern_to_var.erl:28"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","pattern_to_var.erl:29"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"PhoneNumber","pattern_to_var.erl:30"}}. +{construct_pattern,{{tuple,2},"pattern_to_var.erl:30"}}. +{duplicate,{}}. +{store,{"Tuple","pattern_to_var.erl:30"}}. +{pop,{}}. +{get,{"Tuple","pattern_to_var.erl:31"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"pattern_to_var.erl:31"}}. +{store,{"X","pattern_to_var.erl:31"}}. +{store,{"Y","pattern_to_var.erl:31"}}. +{pop,{}}. +{get,{"Y","pattern_to_var.erl:32"}}. +{sink,{"pattern_to_var.erl:32"}}. +{pop,{}}. +{get,{"X","pattern_to_var.erl:33"}}. +{sink,{"pattern_to_var.erl:33"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"PhoneNumber","pattern_to_var.erl:34"}}. +{construct_pattern,{{tuple,2},"pattern_to_var.erl:34"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"pattern_to_var.erl:34"}}. +{pop,{}}. +{store,{"T1","pattern_to_var.erl:34"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"pattern_to_var.erl:35"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"pattern_to_var.erl:35"}}. +{pop,{}}. +{store,{"UT1","pattern_to_var.erl:35"}}. +{pop,{}}. +{get,{"PhoneNumber","pattern_to_var.erl:36"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"pattern_to_var.erl:36"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"pattern_to_var.erl:36"}}. +{store,{"T2","pattern_to_var.erl:36"}}. +{pop,{}}. +{pop,{}}. +{get,{"T1","pattern_to_var.erl:37"}}. +{sink,{"pattern_to_var.erl:37"}}. +{pop,{}}. +{get,{"T2","pattern_to_var.erl:38"}}. +{sink,{"pattern_to_var.erl:38"}}. +{pop,{}}. +{get,{"UT1","pattern_to_var.erl:39"}}. +{sink,{"pattern_to_var.erl:39"}}. +{func_ret,{extract_pattern,"pattern_to_var.erl:28"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/function_calls.erl b/finer_taint/test/finer_taint_SUITE_data/function_calls.erl new file mode 100644 index 0000000..ed4b1d9 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/function_calls.erl @@ -0,0 +1,100 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('function_calls'). +-export([ + calling_convention_main/0, nested_calls_main/0, + recursion_main/0, lambdas_main/0, lambda_closures_main/0 +]). + +calling_convention_main() -> + PhoneNumber = finer_taint:source(" 123@secret.net"), + Tainted = process_two_lists(PhoneNumber, "String"), + NoTainted = process_two_lists("AnotherString", "A String"), + finer_taint:sink(NoTainted), + finer_taint:sink(Tainted). + + +append_to_list(L1) -> + L1 ++ " list suffix". + +concat_two_lists(L1, L2) -> + finer_taint:source("dropped source"), + L1 ++ L2. +process_two_lists(L1, L2) -> + L3 = append_to_list(L1), + concat_two_lists(L2, L3). + + + +recursion_main() -> + List = generate_list(10), + TaintedNumber = finer_taint:source(42), + TaintedList = insert_at(2, TaintedNumber, List), + NotTaintedList = insert_at(2, 41, List), + SanitizedList = insert_at(2, 2, List), + finer_taint:sink(TaintedList), + finer_taint:sink(NotTaintedList), + finer_taint:sink(SanitizedList), + TaintedElem = at(2, TaintedList), + NotTaintedElem = at(2, SanitizedList), + finer_taint:sink(TaintedElem), + finer_taint:sink(NotTaintedElem). + + +generate_list(0) -> empty; +generate_list(N) -> {N, generate_list(N-1)}. + +insert_at(0, Elem, Tail) -> {Elem, Tail}; +insert_at(N, Elem, {Head, Tail}) -> + NewTail = insert_at(N-1, Elem, Tail), + {Head, NewTail}. + +at(0, {E, _Tail}) -> E; +at(N, {_Head, Tail}) -> at(N-1, Tail). + +nested_calls_main() -> + List = insert_at(0, finer_taint:source(32), generate_list(0)), + finer_taint:sink(at(0, List)). + + +lambdas_main() -> + A = finer_taint:source(42), + LazyAddOne = fun() -> A + 1 end(), + finer_taint:sink(LazyAddOne), + AddOneOrA = fun (A) when A rem 2 =:= 0 -> A + 1; + (B) -> B + A + end, + Map = fun Map(F, [H| T]) -> [F(H) | Map(F,T)] ; + Map(_, []) -> [] + end, + [Tainted, NotTainted| _] = Map(AddOneOrA, [1,2,3]), + finer_taint:sink(Tainted), + finer_taint:sink(NotTainted). + + +create_lambda(ArgName) -> + Untainted = 1, + fun() -> ArgName + Untainted end. + +lambda_closures_main() -> + A = finer_taint:source(42), + Func = create_lambda(A), + finer_taint:sink(Func()), + % This is a test that makes sure lambdas with only + % untainted values being captured can create blackholes + % erlang:fun_info/2 is used as an unmodeled function + % that creates a blackhole node in the history + ItemInfo = finer_taint:source(arity), + {arity, 0} = erlang:fun_info(create_lambda(1), ItemInfo). diff --git a/finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n.erl b/finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n.erl new file mode 100644 index 0000000..6539c56 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n.erl @@ -0,0 +1,37 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('i13n_to_not_i13n_to_i13n'). +% This module tests the case where instrumented code (this module) +% calls not instrumented code (not_instrumented module) that then +% calls some instrumented code again. i13n_to_not_i13n_to_i13n is short for instrumentation +% to not instrumentation to instrumentation +-export([ + i13n_to_not_i13n_to_i13n_main/0 +]). + +%We can't trace dataflow through non instrumented code, so we +%do not attempt to track taint through Arg1 in this test. +%The test only tests if each of the instrumented function can +%track taint independently +call_back_fn(_Arg1, ok, ok, ok, ok) -> + Number = finer_taint:source(6), + Number0 = Number + 1, + finer_taint:sink(Number), + 5. + +i13n_to_not_i13n_to_i13n_main() -> + Number = finer_taint:source(6), + Return = not_instrumented:call_fn(fun call_back_fn/5, Number), + finer_taint:sink(Number). diff --git a/finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n_main_analysis_instr new file mode 100644 index 0000000..2ecc280 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/i13n_to_not_i13n_to_i13n_main_analysis_instr @@ -0,0 +1,49 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{i13n_to_not_i13n_to_i13n,i13n_to_not_i13n_to_i13n_main,0},"i13n_to_not_i13n_to_i13n.erl:34"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"i13n_to_not_i13n_to_i13n.erl:35"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Number","i13n_to_not_i13n_to_i13n.erl:35"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Number","i13n_to_not_i13n_to_i13n.erl:36"}}. +{push,{notaint}}. +{call_fun,{not_instrumented,call_fn,2},"i13n_to_not_i13n_to_i13n.erl:36"}. +{push_scope,{{i13n_to_not_i13n_to_i13n,call_back_fn,5},"i13n_to_not_i13n_to_i13n.erl:28"}}. +{store,{"_Arg1","i13n_to_not_i13n_to_i13n.erl:28"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"i13n_to_not_i13n_to_i13n.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Number","i13n_to_not_i13n_to_i13n.erl:29"}}. +{pop,{}}. +{get,{"Number","i13n_to_not_i13n_to_i13n.erl:30"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"i13n_to_not_i13n_to_i13n.erl:30"}. +{apply,{{operators,'+',2},"i13n_to_not_i13n_to_i13n.erl:30"}}. +{duplicate,{}}. +{store,{"Number0","i13n_to_not_i13n_to_i13n.erl:30"}}. +{pop,{}}. +{get,{"Number","i13n_to_not_i13n_to_i13n.erl:31"}}. +{sink,{"i13n_to_not_i13n_to_i13n.erl:31"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{call_back_fn,"i13n_to_not_i13n_to_i13n.erl:28"}}. +{apply,{{not_instrumented,call_fn,2},"i13n_to_not_i13n_to_i13n.erl:36"}}. +{duplicate,{}}. +{store,{"Return","i13n_to_not_i13n_to_i13n.erl:36"}}. +{pop,{}}. +{get,{"Number","i13n_to_not_i13n_to_i13n.erl:37"}}. +{sink,{"i13n_to_not_i13n_to_i13n.erl:37"}}. +{func_ret,{i13n_to_not_i13n_to_i13n_main,"i13n_to_not_i13n_to_i13n.erl:34"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/if_clause_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/if_clause_analysis_instr new file mode 100644 index 0000000..9174bb1 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/if_clause_analysis_instr @@ -0,0 +1,18 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{simple_example,if_clause,0},"simple_example.erl:28"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"simple_example.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","simple_example.erl:29"}}. +{pop,{}}. +{get,{"PhoneNumber","simple_example.erl:31"}}. +{duplicate,{}}. +{store,{"Result","simple_example.erl:30"}}. +{pop,{}}. +{get,{"Result","simple_example.erl:33"}}. +{sink,{"simple_example.erl:33"}}. +{func_ret,{if_clause,"simple_example.erl:28"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/integer_constructed_extracted_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/integer_constructed_extracted_main_analysis_instr new file mode 100644 index 0000000..079e872 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/integer_constructed_extracted_main_analysis_instr @@ -0,0 +1,32 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{bitstrings,integer_constructed_extracted_main,0},"bitstrings.erl:35"}}. +{push,{notaint}}. +{construct_pattern,{{bitstring,[6]},"bitstrings.erl:36"}}. +{pop,{}}. +{push,{"bitstrings.erl:36"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedSixBytes","bitstrings.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{construct_pattern,{{bitstring,[2]},"bitstrings.erl:37"}}. +{get,{"TaintedSixBytes","bitstrings.erl:37"}}. +{construct_pattern,{{bitstring,[2,6]},"bitstrings.erl:37"}}. +{duplicate,{}}. +{store,{"HalfTaintedString","bitstrings.erl:37"}}. +{pop,{}}. +{get,{"HalfTaintedString","bitstrings.erl:38"}}. +{duplicate,{}}. +{deconstruct_pattern,{{bitstring,[{default,[integer]},{default,[integer]},{48,[integer]}]},"bitstrings.erl:38"}}. +{store,{"NotTainted","bitstrings.erl:38"}}. +{pop,{}}. +{store,{"Tainted","bitstrings.erl:38"}}. +{pop,{}}. +{get,{"NotTainted","bitstrings.erl:39"}}. +{sink,{"bitstrings.erl:39"}}. +{pop,{}}. +{get,{"Tainted","bitstrings.erl:40"}}. +{sink,{"bitstrings.erl:40"}}. +{func_ret,{integer_constructed_extracted_main,"bitstrings.erl:35"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/joining_model_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/joining_model_main_analysis_instr new file mode 100644 index 0000000..6516694 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/joining_model_main_analysis_instr @@ -0,0 +1,27 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{modeled_functions,joining_model_main,0},"modeled_functions.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"modeled_functions.erl:30"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Number","modeled_functions.erl:30"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"modeled_functions.erl:31"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Domain","modeled_functions.erl:31"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Domain","modeled_functions.erl:32"}}. +{get,{"Number","modeled_functions.erl:32"}}. +{call_fun,{string,concat,2},"modeled_functions.erl:32"}. +{apply,{{string,concat,2},"modeled_functions.erl:32"}}. +{sink,{"modeled_functions.erl:32"}}. +{func_ret,{joining_model_main,"modeled_functions.erl:29"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/lambda_closures_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/lambda_closures_main_analysis_instr new file mode 100644 index 0000000..2a4c0d6 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/lambda_closures_main_analysis_instr @@ -0,0 +1,73 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{function_calls,lambda_closures_main,0},"function_calls.erl:91"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:92"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"A","function_calls.erl:92"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,create_lambda,1},"function_calls.erl:93"}}. +{get,{"A","function_calls.erl:93"}}. +{call_fun,{function_calls,create_lambda,1},"function_calls.erl:93"}. +{push_scope,{{function_calls,create_lambda,1},"function_calls.erl:87"}}. +{store,{"ArgName","function_calls.erl:87"}}. +{push,{notaint}}. +{duplicate,{}}. +{store,{"Untainted","function_calls.erl:88"}}. +{pop,{}}. +{capture_closure,{["ArgName","Untainted"]}}. +{func_ret,{create_lambda,"function_calls.erl:87"}}. +{apply,{{function_calls,create_lambda,1},"function_calls.erl:93"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:93"}}. +{duplicate,{}}. +{store,{"Func","function_calls.erl:93"}}. +{pop,{}}. +{get,{"Func","function_calls.erl:94"}}. +{restore_capture,{{function_calls,variable_func,0},"function_calls.erl:94"}}. +{call_fun,{function_calls,variable_func,0},"function_calls.erl:94"}. +{push_scope,{{function_calls,lambda_function_calls_89_3_anon,0},"function_calls.erl:89"}}. +{get,{"ArgName","function_calls.erl:89"}}. +{get,{"Untainted","function_calls.erl:89"}}. +{call_fun,{operators,'+',2},"function_calls.erl:89"}. +{apply,{{operators,'+',2},"function_calls.erl:89"}}. +{func_ret,{lambda_function_calls_89_3_anon,"function_calls.erl:89"}}. +{apply,{{function_calls,variable_func,0},"function_calls.erl:94"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:94"}}. +{sink,{"function_calls.erl:94"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:99"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"ItemInfo","function_calls.erl:99"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"ItemInfo","function_calls.erl:100"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,create_lambda,1},"function_calls.erl:100"}}. +{push,{notaint}}. +{call_fun,{function_calls,create_lambda,1},"function_calls.erl:100"}. +{push_scope,{{function_calls,create_lambda,1},"function_calls.erl:87"}}. +{store,{"ArgName","function_calls.erl:87"}}. +{push,{notaint}}. +{duplicate,{}}. +{store,{"Untainted","function_calls.erl:88"}}. +{pop,{}}. +{capture_closure,{["ArgName","Untainted"]}}. +{func_ret,{create_lambda,"function_calls.erl:87"}}. +{apply,{{function_calls,create_lambda,1},"function_calls.erl:100"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:100"}}. +{call_fun,{erlang,fun_info,2},"function_calls.erl:100"}. +{apply,{{erlang,fun_info,2},"function_calls.erl:100"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:100"}}. +{pop,{}}. +{pop,{}}. +{func_ret,{lambda_closures_main,"function_calls.erl:91"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/lambdas_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/lambdas_main_analysis_instr new file mode 100644 index 0000000..2ad22c3 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/lambdas_main_analysis_instr @@ -0,0 +1,150 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{function_calls,lambdas_main,0},"function_calls.erl:72"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:73"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"A","function_calls.erl:73"}}. +{pop,{}}. +{capture_closure,{["A"]}}. +{restore_capture,{{function_calls,lambda_func,0},"function_calls.erl:74"}}. +{call_fun,{function_calls,lambda_func,0},"function_calls.erl:74"}. +{push_scope,{{function_calls,lambda_function_calls_74_16_anon,0},"function_calls.erl:74"}}. +{get,{"A","function_calls.erl:74"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"function_calls.erl:74"}. +{apply,{{operators,'+',2},"function_calls.erl:74"}}. +{func_ret,{lambda_function_calls_74_16_anon,"function_calls.erl:74"}}. +{apply,{{function_calls,lambda_func,0},"function_calls.erl:74"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:74"}}. +{duplicate,{}}. +{store,{"LazyAddOne","function_calls.erl:74"}}. +{pop,{}}. +{get,{"LazyAddOne","function_calls.erl:75"}}. +{sink,{"function_calls.erl:75"}}. +{pop,{}}. +{capture_closure,{["A","B"]}}. +{duplicate,{}}. +{store,{"AddOneOrA","function_calls.erl:76"}}. +{pop,{}}. +{capture_closure,{["F","H","Map","T","_"]}}. +{duplicate,{}}. +{store,{"Map","function_calls.erl:79"}}. +{duplicate,{}}. +{store,{"Map","function_calls.erl:79"}}. +{pop,{}}. +{get,{"Map","function_calls.erl:82"}}. +{restore_capture,{{function_calls,variable_func,2},"function_calls.erl:82"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"function_calls.erl:82"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"function_calls.erl:82"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"function_calls.erl:82"}}. +{get,{"AddOneOrA","function_calls.erl:82"}}. +{call_fun,{function_calls,variable_func,2},"function_calls.erl:82"}. +{push_scope,{{function_calls,lambda_function_calls_79_9_named_Map,2},"function_calls.erl:79"}}. +{store,{"F","function_calls.erl:79"}}. +{deconstruct_pattern,{{cons},"function_calls.erl:79"}}. +{store,{"H","function_calls.erl:79"}}. +{store,{"T","function_calls.erl:79"}}. +{get,{"Map","function_calls.erl:79"}}. +{restore_capture,{{function_calls,variable_func,2},"function_calls.erl:79"}}. +{get,{"T","function_calls.erl:79"}}. +{get,{"F","function_calls.erl:79"}}. +{call_fun,{function_calls,variable_func,2},"function_calls.erl:79"}. +{push_scope,{{function_calls,lambda_function_calls_79_9_named_Map,2},"function_calls.erl:79"}}. +{store,{"F","function_calls.erl:79"}}. +{deconstruct_pattern,{{cons},"function_calls.erl:79"}}. +{store,{"H","function_calls.erl:79"}}. +{store,{"T","function_calls.erl:79"}}. +{get,{"Map","function_calls.erl:79"}}. +{restore_capture,{{function_calls,variable_func,2},"function_calls.erl:79"}}. +{get,{"T","function_calls.erl:79"}}. +{get,{"F","function_calls.erl:79"}}. +{call_fun,{function_calls,variable_func,2},"function_calls.erl:79"}. +{push_scope,{{function_calls,lambda_function_calls_79_9_named_Map,2},"function_calls.erl:79"}}. +{store,{"F","function_calls.erl:79"}}. +{deconstruct_pattern,{{cons},"function_calls.erl:79"}}. +{store,{"H","function_calls.erl:79"}}. +{store,{"T","function_calls.erl:79"}}. +{get,{"Map","function_calls.erl:79"}}. +{restore_capture,{{function_calls,variable_func,2},"function_calls.erl:79"}}. +{get,{"T","function_calls.erl:79"}}. +{get,{"F","function_calls.erl:79"}}. +{call_fun,{function_calls,variable_func,2},"function_calls.erl:79"}. +{push_scope,{{function_calls,lambda_function_calls_79_9_named_Map,2},"function_calls.erl:80"}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{lambda_function_calls_79_9_named_Map,"function_calls.erl:80"}}. +{apply,{{function_calls,variable_func,2},"function_calls.erl:79"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:79"}}. +{get,{"F","function_calls.erl:79"}}. +{restore_capture,{{function_calls,variable_func,1},"function_calls.erl:79"}}. +{get,{"H","function_calls.erl:79"}}. +{call_fun,{function_calls,variable_func,1},"function_calls.erl:79"}. +{push_scope,{{function_calls,lambda_function_calls_76_15_anon,1},"function_calls.erl:77"}}. +{store,{"B","function_calls.erl:77"}}. +{get,{"B","function_calls.erl:77"}}. +{get,{"A","function_calls.erl:77"}}. +{call_fun,{operators,'+',2},"function_calls.erl:77"}. +{apply,{{operators,'+',2},"function_calls.erl:77"}}. +{func_ret,{lambda_function_calls_76_15_anon,"function_calls.erl:77"}}. +{apply,{{function_calls,variable_func,1},"function_calls.erl:79"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:79"}}. +{construct_pattern,{{cons},"function_calls.erl:79"}}. +{func_ret,{lambda_function_calls_79_9_named_Map,"function_calls.erl:79"}}. +{apply,{{function_calls,variable_func,2},"function_calls.erl:79"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:79"}}. +{get,{"F","function_calls.erl:79"}}. +{restore_capture,{{function_calls,variable_func,1},"function_calls.erl:79"}}. +{get,{"H","function_calls.erl:79"}}. +{call_fun,{function_calls,variable_func,1},"function_calls.erl:79"}. +{push_scope,{{function_calls,lambda_function_calls_76_15_anon,1},"function_calls.erl:76"}}. +{store,{"A","function_calls.erl:76"}}. +{get,{"A","function_calls.erl:76"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"function_calls.erl:76"}. +{apply,{{operators,'+',2},"function_calls.erl:76"}}. +{func_ret,{lambda_function_calls_76_15_anon,"function_calls.erl:76"}}. +{apply,{{function_calls,variable_func,1},"function_calls.erl:79"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:79"}}. +{construct_pattern,{{cons},"function_calls.erl:79"}}. +{func_ret,{lambda_function_calls_79_9_named_Map,"function_calls.erl:79"}}. +{apply,{{function_calls,variable_func,2},"function_calls.erl:79"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:79"}}. +{get,{"F","function_calls.erl:79"}}. +{restore_capture,{{function_calls,variable_func,1},"function_calls.erl:79"}}. +{get,{"H","function_calls.erl:79"}}. +{call_fun,{function_calls,variable_func,1},"function_calls.erl:79"}. +{push_scope,{{function_calls,lambda_function_calls_76_15_anon,1},"function_calls.erl:77"}}. +{store,{"B","function_calls.erl:77"}}. +{get,{"B","function_calls.erl:77"}}. +{get,{"A","function_calls.erl:77"}}. +{call_fun,{operators,'+',2},"function_calls.erl:77"}. +{apply,{{operators,'+',2},"function_calls.erl:77"}}. +{func_ret,{lambda_function_calls_76_15_anon,"function_calls.erl:77"}}. +{apply,{{function_calls,variable_func,1},"function_calls.erl:79"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:79"}}. +{construct_pattern,{{cons},"function_calls.erl:79"}}. +{func_ret,{lambda_function_calls_79_9_named_Map,"function_calls.erl:79"}}. +{apply,{{function_calls,variable_func,2},"function_calls.erl:82"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:82"}}. +{duplicate,{}}. +{deconstruct_pattern,{{cons},"function_calls.erl:82"}}. +{store,{"Tainted","function_calls.erl:82"}}. +{deconstruct_pattern,{{cons},"function_calls.erl:82"}}. +{store,{"NotTainted","function_calls.erl:82"}}. +{pop,{}}. +{pop,{}}. +{get,{"Tainted","function_calls.erl:83"}}. +{sink,{"function_calls.erl:83"}}. +{pop,{}}. +{get,{"NotTainted","function_calls.erl:84"}}. +{sink,{"function_calls.erl:84"}}. +{func_ret,{lambdas_main,"function_calls.erl:72"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/lineage_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/lineage_main_analysis_instr new file mode 100644 index 0000000..cd90f79 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/lineage_main_analysis_instr @@ -0,0 +1,56 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{simple_example,lineage_main,0},"simple_example.erl:46"}}. +{push,{notaint}}. +{restore_capture,{{simple_example,lineage_entry,2},"simple_example.erl:47"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{simple_example,lineage_entry,2},"simple_example.erl:47"}. +{push_scope,{{simple_example,lineage_entry,2},"simple_example.erl:42"}}. +{store,{"Arg1","simple_example.erl:42"}}. +{store,{"Arg2","simple_example.erl:42"}}. +{push,{notaint}}. +{restore_capture,{{simple_example,pack,1},"simple_example.erl:43"}}. +{get,{"Arg1","simple_example.erl:43"}}. +{call_fun,{simple_example,pack,1},"simple_example.erl:43"}. +{push_scope,{{simple_example,pack,1},"simple_example.erl:36"}}. +{store,{"Arg","simple_example.erl:36"}}. +{get,{"Arg","simple_example.erl:37"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"simple_example.erl:37"}}. +{func_ret,{pack,"simple_example.erl:36"}}. +{apply,{{simple_example,pack,1},"simple_example.erl:43"}}. +{func_ret,{dropping_lambda_capture,"simple_example.erl:43"}}. +{duplicate,{}}. +{store,{"PackedArg1","simple_example.erl:43"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{simple_example,pack,1},"simple_example.erl:44"}}. +{push,{notaint}}. +{restore_capture,{{simple_example,unpack,1},"simple_example.erl:44"}}. +{get,{"PackedArg1","simple_example.erl:44"}}. +{call_fun,{simple_example,unpack,1},"simple_example.erl:44"}. +{push_scope,{{simple_example,unpack,1},"simple_example.erl:39"}}. +{deconstruct_pattern,{{tuple,2},"simple_example.erl:39"}}. +{store,{"Arg","simple_example.erl:39"}}. +{pop,{}}. +{get,{"Arg","simple_example.erl:40"}}. +{func_ret,{unpack,"simple_example.erl:39"}}. +{apply,{{simple_example,unpack,1},"simple_example.erl:44"}}. +{func_ret,{dropping_lambda_capture,"simple_example.erl:44"}}. +{get,{"Arg2","simple_example.erl:44"}}. +{call_fun,{operators,'+',2},"simple_example.erl:44"}. +{apply,{{operators,'+',2},"simple_example.erl:44"}}. +{call_fun,{simple_example,pack,1},"simple_example.erl:44"}. +{push_scope,{{simple_example,pack,1},"simple_example.erl:36"}}. +{store,{"Arg","simple_example.erl:36"}}. +{get,{"Arg","simple_example.erl:37"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"simple_example.erl:37"}}. +{func_ret,{pack,"simple_example.erl:36"}}. +{apply,{{simple_example,pack,1},"simple_example.erl:44"}}. +{func_ret,{dropping_lambda_capture,"simple_example.erl:44"}}. +{func_ret,{lineage_entry,"simple_example.erl:42"}}. +{apply,{{simple_example,lineage_entry,2},"simple_example.erl:47"}}. +{func_ret,{dropping_lambda_capture,"simple_example.erl:47"}}. +{func_ret,{lineage_main,"simple_example.erl:46"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/list_comprehension.erl b/finer_taint/test/finer_taint_SUITE_data/list_comprehension.erl new file mode 100644 index 0000000..5827d2e --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/list_comprehension.erl @@ -0,0 +1,37 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(list_comprehension). + +-export([basic_main/0, + cartesian_main/0]). + +-record(list_wrap, {a_list :: list()}). + +maybe_taint(2) -> + finer_taint:source(2); +maybe_taint(X) -> + X. + +basic_main() -> + ListWrap = #list_wrap{a_list = [1,2,3]}, + [NotTainted1, Tainted, NotTainted2] = [maybe_taint(X) || X <- ListWrap#list_wrap.a_list, X /= no_pid], + finer_taint:sink(NotTainted1), + finer_taint:sink(Tainted), + finer_taint:sink(NotTainted2). + +cartesian_main() -> + L = [{X, X} || X <- [1,2,3]], + [{1, Tainted}] = [{X,maybe_taint(Y)} || {1,X} <- L, {2, Y} <- L], + finer_taint:sink(Tainted). diff --git a/finer_taint/test/finer_taint_SUITE_data/lists_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/lists_main_analysis_instr new file mode 100644 index 0000000..f78e813 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/lists_main_analysis_instr @@ -0,0 +1,77 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{case_clauses,lists_main,0},"case_clauses.erl:33"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:34"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","case_clauses.erl:34"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:35"}}. +{push,{notaint}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:35"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:54"}}. +{push,{notaint}}. +{pop,{}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:35"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:35"}}. +{sink,{"case_clauses.erl:35"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:36"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"case_clauses.erl:36"}}. +{get,{"PhoneNumber","case_clauses.erl:36"}}. +{construct_pattern,{{cons},"case_clauses.erl:36"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"case_clauses.erl:36"}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:36"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{deconstruct_pattern,{{cons},"case_clauses.erl:55"}}. +{pop,{}}. +{deconstruct_pattern,{{cons},"case_clauses.erl:55"}}. +{store,{"PhoneNumber","case_clauses.erl:55"}}. +{deconstruct_pattern,{{cons},"case_clauses.erl:55"}}. +{pop,{}}. +{pop,{}}. +{get,{"PhoneNumber","case_clauses.erl:55"}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:36"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:36"}}. +{sink,{"case_clauses.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:37"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"case_clauses.erl:37"}}. +{get,{"PhoneNumber","case_clauses.erl:37"}}. +{construct_pattern,{{cons},"case_clauses.erl:37"}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:37"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{deconstruct_pattern,{{cons},"case_clauses.erl:56"}}. +{store,{"_PhoneNumber","case_clauses.erl:56"}}. +{deconstruct_pattern,{{cons},"case_clauses.erl:56"}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:37"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:37"}}. +{sink,{"case_clauses.erl:37"}}. +{func_ret,{lists_main,"case_clauses.erl:33"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/local_if_clause_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/local_if_clause_analysis_instr new file mode 100644 index 0000000..c6194c8 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/local_if_clause_analysis_instr @@ -0,0 +1,27 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{ps_example,local_if_clause,0},"ps_example.erl:24"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"ps_example.erl:25"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","ps_example.erl:25"}}. +{pop,{}}. +{get,{"PhoneNumber","ps_example.erl:29"}}. +{duplicate,{}}. +{store,{"Result","ps_example.erl:26"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{ps_example,local_sink,1},"ps_example.erl:31"}}. +{get,{"Result","ps_example.erl:31"}}. +{call_fun,{ps_example,local_sink,1},"ps_example.erl:31"}. +{push_scope,{{ps_example,local_sink,1},"ps_example.erl:33"}}. +{store,{"Result","ps_example.erl:33"}}. +{get,{"Result","ps_example.erl:34"}}. +{sink,{"ps_example.erl:34"}}. +{func_ret,{local_sink,"ps_example.erl:33"}}. +{apply,{{ps_example,local_sink,1},"ps_example.erl:31"}}. +{func_ret,{dropping_lambda_capture,"ps_example.erl:31"}}. +{func_ret,{local_if_clause,"ps_example.erl:24"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/macro_duplicate_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/macro_duplicate_main_analysis_instr new file mode 100644 index 0000000..49a022b --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/macro_duplicate_main_analysis_instr @@ -0,0 +1,26 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{simple_example,macro_duplicate_main,0},"simple_example.erl:52"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"simple_example.erl:53"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Tainted","simple_example.erl:53"}}. +{pop,{}}. +{get,{"Tainted","simple_example.erl:55"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"simple_example.erl:55"}. +{apply,{{operators,'+',2},"simple_example.erl:55"}}. +{pop,{}}. +{get,{"Tainted","simple_example.erl:55"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"simple_example.erl:55"}. +{apply,{{operators,'+',2},"simple_example.erl:55"}}. +{duplicate,{}}. +{store,{"X","simple_example.erl:54"}}. +{pop,{}}. +{get,{"X","simple_example.erl:56"}}. +{sink,{"simple_example.erl:56"}}. +{func_ret,{macro_duplicate_main,"simple_example.erl:52"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/main_analysis_instr new file mode 100644 index 0000000..94758df --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/main_analysis_instr @@ -0,0 +1,24 @@ +{push, ["@generated"]}. +{pop, []}. +{push_scope,[main, "case_clauses.erl:6"]}. +{push, ["case_clauses.erl:7"]}. +{store,["TaintedAtom", "case_clauses.erl:7"]}. +{get,["TaintedAtom", "case_clauses.erl:8"]}. +{push_scope,[big_pattern_extractor, "case_clauses.erl:15"]}. +{store,["X", "case_clauses.erl:15"]}. +{func_ret,[big_pattern_extractor, "case_clauses.erl:15"]}. +{apply,[{current_module,big_pattern_extractor,1}, "case_clauses.erl:8"]}. +{store,["SanitizedIsSanitized", "case_clauses.erl:8"]}. +{get,["SanitizedIsSanitized", "case_clauses.erl:9"]}. +{sink, ["case_clauses.erl:9"]}. +{push, ["case_clauses.erl:10"]}. +{store,["PhoneNumber", "case_clauses.erl:10"]}. +{get,["PhoneNumber", "case_clauses.erl:11"]}. +{push_scope,[big_pattern_extractor, "case_clauses.erl:15"]}. +{store,["X", "case_clauses.erl:15"]}. +{func_ret,[big_pattern_extractor, "case_clauses.erl:15"]}. +{apply,[{current_module,big_pattern_extractor,1}, "case_clauses.erl:11"]}. +{store,["TaintedPhoneNumber", "case_clauses.erl:11"]}. +{get,["TaintedPhoneNumber", "case_clauses.erl:12"]}. +{sink, ["case_clauses.erl:12"]}. +{func_ret,[main, "case_clauses.erl:6"]}. diff --git a/finer_taint/test/finer_taint_SUITE_data/map_examples.erl b/finer_taint/test/finer_taint_SUITE_data/map_examples.erl new file mode 100644 index 0000000..ef09416 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/map_examples.erl @@ -0,0 +1,78 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('map_examples'). +-export([ + map_values_main/0, opaque_map_main/0, + map_update_main/0, + map_keys_main/0, + map_module_main/0 +]). + +map_values_main() -> + PhoneNumber = finer_taint:source(" 123@secret.net"), + AMap = #{phone_number => PhoneNumber, a_tag => a_tag, not_used => 1}, + finer_taint:sink(AMap), + #{phone_number := Pn, a_tag := Tag} = AMap, + finer_taint:sink(Pn), + finer_taint:sink(Tag). + +map_keys_main() -> + InitialyUntaintedPhoneNumber = "123@secret.net", + PhoneNumber = finer_taint:source(InitialyUntaintedPhoneNumber), + AMap = #{PhoneNumber => ok, "Some string" => 2}, + % This should be tainted + finer_taint:sink(AMap), + %This should be untainted + finer_taint:sink(InitialyUntaintedPhoneNumber), + #{InitialyUntaintedPhoneNumber := Status} = AMap, + + %Looking up a tainted key in a map should "catch" taint + %This is needed to implement iterations over the map + %This should be tainted + finer_taint:sink(InitialyUntaintedPhoneNumber), + % This should not be tainted + finer_taint:sink(Status). + +map_update_main() -> + PhoneNumber = finer_taint:source(" 123@secret.net"), + AMap = #{a_tag => a_tag, not_used => 1}, + finer_taint:sink(AMap), + ATaintedMap = AMap#{phone_number => PhoneNumber}, + finer_taint:sink(ATaintedMap), + #{phone_number := Pn, a_tag := Tag} = ATaintedMap, + finer_taint:sink(Tag), + finer_taint:sink(Pn). + +map_module_main() -> + PhoneNumber = finer_taint:source(" 123@secret.net"), + AMap = #{phone_number => PhoneNumber, a_tag => a_tag, not_used => 1}, + finer_taint:sink(maps:get(phone_number, AMap)), + finer_taint:sink(maps:get(not_in_map, AMap, PhoneNumber)), + KeyTaintedMap = #{PhoneNumber => a_number, somethingelse => 0}, + {[TheSingleTaintedKey], OtherKeys} = maps:fold( + fun + (Key, 0, {Other, Acc}) -> {Other, [Key | Acc]}; + (Num, _, {Acc, Other}) -> {[Num | Acc], Other} + end, + {[], []}, + KeyTaintedMap + ), + finer_taint:sink(OtherKeys), + finer_taint:sink(TheSingleTaintedKey). + +opaque_map_main() -> + TaintedMap = finer_taint:source(#{a => ok}), + TaintedMap1 = TaintedMap#{notainted => nottainted}, + finer_taint:sink(TaintedMap1). diff --git a/finer_taint/test/finer_taint_SUITE_data/map_keys_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/map_keys_main_analysis_instr new file mode 100644 index 0000000..cd06fe0 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/map_keys_main_analysis_instr @@ -0,0 +1,42 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{map_examples,map_keys_main,0},"map_examples.erl:31"}}. +{push,{notaint}}. +{duplicate,{}}. +{store,{"InitialyUntaintedPhoneNumber","map_examples.erl:32"}}. +{pop,{}}. +{get,{"InitialyUntaintedPhoneNumber","map_examples.erl:33"}}. +{pop,{}}. +{push,{"map_examples.erl:33"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","map_examples.erl:33"}}. +{pop,{}}. +{get,{"PhoneNumber","map_examples.erl:34"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{map,["Some string","123@secret.net"]},"map_examples.erl:34"}}. +{duplicate,{}}. +{store,{"AMap","map_examples.erl:34"}}. +{pop,{}}. +{get,{"AMap","map_examples.erl:36"}}. +{sink,{"map_examples.erl:36"}}. +{pop,{}}. +{get,{"InitialyUntaintedPhoneNumber","map_examples.erl:38"}}. +{sink,{"map_examples.erl:38"}}. +{pop,{}}. +{get,{"AMap","map_examples.erl:39"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,["123@secret.net"]},"map_examples.erl:39"}}. +{store,{"Status","map_examples.erl:39"}}. +{store,{"InitialyUntaintedPhoneNumber","map_examples.erl:39"}}. +{pop,{}}. +{get,{"InitialyUntaintedPhoneNumber","map_examples.erl:44"}}. +{sink,{"map_examples.erl:44"}}. +{pop,{}}. +{get,{"Status","map_examples.erl:46"}}. +{sink,{"map_examples.erl:46"}}. +{func_ret,{map_keys_main,"map_examples.erl:31"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/map_module_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/map_module_main_analysis_instr new file mode 100644 index 0000000..479e9c5 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/map_module_main_analysis_instr @@ -0,0 +1,236 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{map_examples,map_module_main,0},"map_examples.erl:58"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"map_examples.erl:59"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","map_examples.erl:59"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"PhoneNumber","map_examples.erl:60"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{map,[not_used,a_tag,phone_number]},"map_examples.erl:60"}}. +{duplicate,{}}. +{store,{"AMap","map_examples.erl:60"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"AMap","map_examples.erl:61"}}. +{push,{notaint}}. +{call_fun,{modeled_taint_maps,get,2},"map_examples.erl:61"}. +{push_scope,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:80"}}. +{store,{"Key","modeled_taint_maps.erl:80"}}. +{store,{"Map","modeled_taint_maps.erl:80"}}. +{get,{"Map","modeled_taint_maps.erl:81"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[phone_number]},"modeled_taint_maps.erl:81"}}. +{store,{"Value","modeled_taint_maps.erl:81"}}. +{store,{"Key","modeled_taint_maps.erl:81"}}. +{pop,{}}. +{get,{"Value","modeled_taint_maps.erl:82"}}. +{func_ret,{get,"modeled_taint_maps.erl:80"}}. +{apply,{{modeled_taint_maps,get,2},"map_examples.erl:61"}}. +{sink,{"map_examples.erl:61"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"PhoneNumber","map_examples.erl:62"}}. +{get,{"AMap","map_examples.erl:62"}}. +{push,{notaint}}. +{call_fun,{modeled_taint_maps,get,3},"map_examples.erl:62"}. +{push_scope,{{modeled_taint_maps,get,3},"modeled_taint_maps.erl:72"}}. +{store,{"Key","modeled_taint_maps.erl:72"}}. +{store,{"Map","modeled_taint_maps.erl:72"}}. +{store,{"Default","modeled_taint_maps.erl:72"}}. +{try_catch,{try_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{get,{"Map","modeled_taint_maps.erl:73"}}. +{get,{"Key","modeled_taint_maps.erl:73"}}. +{call_fun,{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}. +{push_scope,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:80"}}. +{store,{"Key","modeled_taint_maps.erl:80"}}. +{store,{"Map","modeled_taint_maps.erl:80"}}. +{get,{"Map","modeled_taint_maps.erl:81"}}. +{try_catch,{catch_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:76"}. +{pop,{}}. +{push,{notaint}}. +{deconstruct_pattern,{{tuple,2},"modeled_taint_maps.erl:76"}}. +{pop,{}}. +{pop,{}}. +{get,{"Default","modeled_taint_maps.erl:76"}}. +{func_ret,{get,"modeled_taint_maps.erl:72"}}. +{apply,{{modeled_taint_maps,get,3},"map_examples.erl:62"}}. +{sink,{"map_examples.erl:62"}}. +{pop,{}}. +{get,{"PhoneNumber","map_examples.erl:63"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{map,[somethingelse," 123@secret.net"]},"map_examples.erl:63"}}. +{duplicate,{}}. +{store,{"KeyTaintedMap","map_examples.erl:63"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"KeyTaintedMap","map_examples.erl:70"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"map_examples.erl:69"}}. +{capture_closure,{["Acc","Key","Num","Other","_"]}}. +{call_fun,{modeled_taint_maps,fold,3},"map_examples.erl:64"}. +{push_scope,{{modeled_taint_maps,fold,3},"modeled_taint_maps.erl:181"}}. +{store,{"Fun","modeled_taint_maps.erl:181"}}. +{store,{"Acc","modeled_taint_maps.erl:181"}}. +{store,{"Map","modeled_taint_maps.erl:181"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Map","modeled_taint_maps.erl:182"}}. +{call_fun,{maps,iterator,1},"modeled_taint_maps.erl:182"}. +{apply,{{maps,iterator,1},"modeled_taint_maps.erl:182"}}. +{duplicate,{}}. +{store,{"Iter","modeled_taint_maps.erl:182"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:183"}}. +{get,{"Map","modeled_taint_maps.erl:183"}}. +{get,{"Iter","modeled_taint_maps.erl:183"}}. +{get,{"Acc","modeled_taint_maps.erl:183"}}. +{get,{"Fun","modeled_taint_maps.erl:183"}}. +{call_fun,{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:183"}. +{push_scope,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:191"}}. +{store,{"Fun","modeled_taint_maps.erl:191"}}. +{store,{"Acc","modeled_taint_maps.erl:191"}}. +{store,{"MapIter","modeled_taint_maps.erl:191"}}. +{store,{"Map","modeled_taint_maps.erl:191"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"MapIter","modeled_taint_maps.erl:193"}}. +{call_fun,{maps,next,1},"modeled_taint_maps.erl:193"}. +{apply,{{maps,next,1},"modeled_taint_maps.erl:193"}}. +{deconstruct_pattern,{{tuple,3},"modeled_taint_maps.erl:196"}}. +{store,{"IterKey","modeled_taint_maps.erl:196"}}. +{store,{"_Val","modeled_taint_maps.erl:196"}}. +{store,{"NextIter","modeled_taint_maps.erl:196"}}. +{get,{"Map","modeled_taint_maps.erl:198"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[somethingelse]},"modeled_taint_maps.erl:198"}}. +{store,{"Value","modeled_taint_maps.erl:198"}}. +{store,{"IterKey","modeled_taint_maps.erl:198"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:199"}}. +{get,{"Map","modeled_taint_maps.erl:199"}}. +{get,{"NextIter","modeled_taint_maps.erl:199"}}. +{get,{"Fun","modeled_taint_maps.erl:199"}}. +{restore_capture,{{modeled_taint_maps,variable_func,3},"modeled_taint_maps.erl:199"}}. +{get,{"Acc","modeled_taint_maps.erl:199"}}. +{get,{"Value","modeled_taint_maps.erl:199"}}. +{get,{"IterKey","modeled_taint_maps.erl:199"}}. +{call_fun,{modeled_taint_maps,variable_func,3},"modeled_taint_maps.erl:199"}. +{push_scope,{{map_examples,lambda_map_examples_65_9_anon,3},"map_examples.erl:66"}}. +{store,{"Key","map_examples.erl:66"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"map_examples.erl:66"}}. +{store,{"Other","map_examples.erl:66"}}. +{store,{"Acc","map_examples.erl:66"}}. +{get,{"Other","map_examples.erl:66"}}. +{get,{"Acc","map_examples.erl:66"}}. +{get,{"Key","map_examples.erl:66"}}. +{construct_pattern,{{cons},"map_examples.erl:66"}}. +{construct_pattern,{{tuple,2},"map_examples.erl:66"}}. +{func_ret,{lambda_map_examples_65_9_anon,"map_examples.erl:66"}}. +{apply,{{modeled_taint_maps,variable_func,3},"modeled_taint_maps.erl:199"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:199"}}. +{get,{"Fun","modeled_taint_maps.erl:199"}}. +{call_fun,{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:199"}. +{push_scope,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:191"}}. +{store,{"Fun","modeled_taint_maps.erl:191"}}. +{store,{"Acc","modeled_taint_maps.erl:191"}}. +{store,{"MapIter","modeled_taint_maps.erl:191"}}. +{store,{"Map","modeled_taint_maps.erl:191"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"MapIter","modeled_taint_maps.erl:193"}}. +{call_fun,{maps,next,1},"modeled_taint_maps.erl:193"}. +{apply,{{maps,next,1},"modeled_taint_maps.erl:193"}}. +{deconstruct_pattern,{{tuple,3},"modeled_taint_maps.erl:196"}}. +{store,{"IterKey","modeled_taint_maps.erl:196"}}. +{store,{"_Val","modeled_taint_maps.erl:196"}}. +{store,{"NextIter","modeled_taint_maps.erl:196"}}. +{get,{"Map","modeled_taint_maps.erl:198"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[" 123@secret.net"]},"modeled_taint_maps.erl:198"}}. +{store,{"Value","modeled_taint_maps.erl:198"}}. +{store,{"IterKey","modeled_taint_maps.erl:198"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:199"}}. +{get,{"Map","modeled_taint_maps.erl:199"}}. +{get,{"NextIter","modeled_taint_maps.erl:199"}}. +{get,{"Fun","modeled_taint_maps.erl:199"}}. +{restore_capture,{{modeled_taint_maps,variable_func,3},"modeled_taint_maps.erl:199"}}. +{get,{"Acc","modeled_taint_maps.erl:199"}}. +{get,{"Value","modeled_taint_maps.erl:199"}}. +{get,{"IterKey","modeled_taint_maps.erl:199"}}. +{call_fun,{modeled_taint_maps,variable_func,3},"modeled_taint_maps.erl:199"}. +{push_scope,{{map_examples,lambda_map_examples_65_9_anon,3},"map_examples.erl:67"}}. +{store,{"Num","map_examples.erl:67"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"map_examples.erl:67"}}. +{store,{"Acc","map_examples.erl:67"}}. +{store,{"Other","map_examples.erl:67"}}. +{get,{"Acc","map_examples.erl:67"}}. +{get,{"Num","map_examples.erl:67"}}. +{construct_pattern,{{cons},"map_examples.erl:67"}}. +{get,{"Other","map_examples.erl:67"}}. +{construct_pattern,{{tuple,2},"map_examples.erl:67"}}. +{func_ret,{lambda_map_examples_65_9_anon,"map_examples.erl:67"}}. +{apply,{{modeled_taint_maps,variable_func,3},"modeled_taint_maps.erl:199"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:199"}}. +{get,{"Fun","modeled_taint_maps.erl:199"}}. +{call_fun,{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:199"}. +{push_scope,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:191"}}. +{store,{"Fun","modeled_taint_maps.erl:191"}}. +{store,{"Acc","modeled_taint_maps.erl:191"}}. +{store,{"MapIter","modeled_taint_maps.erl:191"}}. +{store,{"Map","modeled_taint_maps.erl:191"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"MapIter","modeled_taint_maps.erl:193"}}. +{call_fun,{maps,next,1},"modeled_taint_maps.erl:193"}. +{apply,{{maps,next,1},"modeled_taint_maps.erl:193"}}. +{pop,{}}. +{get,{"Acc","modeled_taint_maps.erl:195"}}. +{func_ret,{fold_impl,"modeled_taint_maps.erl:191"}}. +{apply,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:199"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:199"}}. +{func_ret,{fold_impl,"modeled_taint_maps.erl:191"}}. +{apply,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:199"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:199"}}. +{func_ret,{fold_impl,"modeled_taint_maps.erl:191"}}. +{apply,{{modeled_taint_maps,fold_impl,4},"modeled_taint_maps.erl:183"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:183"}}. +{func_ret,{fold,"modeled_taint_maps.erl:181"}}. +{apply,{{modeled_taint_maps,fold,3},"map_examples.erl:64"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"map_examples.erl:64"}}. +{deconstruct_pattern,{{cons},"map_examples.erl:64"}}. +{store,{"TheSingleTaintedKey","map_examples.erl:64"}}. +{pop,{}}. +{store,{"OtherKeys","map_examples.erl:64"}}. +{pop,{}}. +{get,{"OtherKeys","map_examples.erl:72"}}. +{sink,{"map_examples.erl:72"}}. +{pop,{}}. +{get,{"TheSingleTaintedKey","map_examples.erl:73"}}. +{sink,{"map_examples.erl:73"}}. +{func_ret,{map_module_main,"map_examples.erl:58"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/map_update_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/map_update_main_analysis_instr new file mode 100644 index 0000000..64fc4d5 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/map_update_main_analysis_instr @@ -0,0 +1,47 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{map_examples,map_update_main,0},"map_examples.erl:48"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"map_examples.erl:49"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","map_examples.erl:49"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{map,[not_used,a_tag]},"map_examples.erl:50"}}. +{duplicate,{}}. +{store,{"AMap","map_examples.erl:50"}}. +{pop,{}}. +{get,{"AMap","map_examples.erl:51"}}. +{sink,{"map_examples.erl:51"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"PhoneNumber","map_examples.erl:52"}}. +{get,{"AMap","map_examples.erl:52"}}. +{construct_pattern,{{map,[phone_number]},"map_examples.erl:52"}}. +{duplicate,{}}. +{store,{"ATaintedMap","map_examples.erl:52"}}. +{pop,{}}. +{get,{"ATaintedMap","map_examples.erl:53"}}. +{sink,{"map_examples.erl:53"}}. +{pop,{}}. +{get,{"ATaintedMap","map_examples.erl:54"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[phone_number,a_tag]},"map_examples.erl:54"}}. +{store,{"Pn","map_examples.erl:54"}}. +{store,{"Tag","map_examples.erl:54"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{get,{"Tag","map_examples.erl:55"}}. +{sink,{"map_examples.erl:55"}}. +{pop,{}}. +{get,{"Pn","map_examples.erl:56"}}. +{sink,{"map_examples.erl:56"}}. +{func_ret,{map_update_main,"map_examples.erl:48"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/map_values_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/map_values_main_analysis_instr new file mode 100644 index 0000000..1ea1e3e --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/map_values_main_analysis_instr @@ -0,0 +1,39 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{map_examples,map_values_main,0},"map_examples.erl:23"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"map_examples.erl:24"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","map_examples.erl:24"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"PhoneNumber","map_examples.erl:25"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{map,[not_used,a_tag,phone_number]},"map_examples.erl:25"}}. +{duplicate,{}}. +{store,{"AMap","map_examples.erl:25"}}. +{pop,{}}. +{get,{"AMap","map_examples.erl:26"}}. +{sink,{"map_examples.erl:26"}}. +{pop,{}}. +{get,{"AMap","map_examples.erl:27"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[phone_number,a_tag]},"map_examples.erl:27"}}. +{store,{"Pn","map_examples.erl:27"}}. +{store,{"Tag","map_examples.erl:27"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{get,{"Pn","map_examples.erl:28"}}. +{sink,{"map_examples.erl:28"}}. +{pop,{}}. +{get,{"Tag","map_examples.erl:29"}}. +{sink,{"map_examples.erl:29"}}. +{func_ret,{map_values_main,"map_examples.erl:23"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/maybe_expr_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/maybe_expr_main_analysis_instr new file mode 100644 index 0000000..f1b42c1 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/maybe_expr_main_analysis_instr @@ -0,0 +1,80 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{pattern_to_var,maybe_expr_main,0},"pattern_to_var.erl:85"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:86"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","pattern_to_var.erl:86"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:87"}}. +{get,{"PhoneNumber","pattern_to_var.erl:87"}}. +{call_fun,{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:87"}. +{push_scope,{{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:70"}}. +{store,{"Match","pattern_to_var.erl:70"}}. +{get,{"Match","pattern_to_var.erl:72"}}. +{func_ret,{maybe_expr,"pattern_to_var.erl:70"}}. +{apply,{{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:87"}}. +{func_ret,{dropping_lambda_capture,"pattern_to_var.erl:87"}}. +{duplicate,{}}. +{store,{"Tainted","pattern_to_var.erl:87"}}. +{pop,{}}. +{get,{"Tainted","pattern_to_var.erl:88"}}. +{sink,{"pattern_to_var.erl:88"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:89"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber1","pattern_to_var.erl:89"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:90"}}. +{get,{"PhoneNumber1","pattern_to_var.erl:90"}}. +{call_fun,{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:90"}. +{push_scope,{{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:70"}}. +{store,{"Match","pattern_to_var.erl:70"}}. +{get,{"Match","pattern_to_var.erl:72"}}. +{duplicate,{}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{maybe_expr,"pattern_to_var.erl:70"}}. +{apply,{{pattern_to_var,maybe_expr,1},"pattern_to_var.erl:90"}}. +{func_ret,{dropping_lambda_capture,"pattern_to_var.erl:90"}}. +{duplicate,{}}. +{store,{"NotTainted","pattern_to_var.erl:90"}}. +{pop,{}}. +{get,{"NotTainted","pattern_to_var.erl:91"}}. +{sink,{"pattern_to_var.erl:91"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{pattern_to_var,maybe_expr_else,1},"pattern_to_var.erl:92"}}. +{get,{"PhoneNumber","pattern_to_var.erl:92"}}. +{call_fun,{pattern_to_var,maybe_expr_else,1},"pattern_to_var.erl:92"}. +{push_scope,{{pattern_to_var,maybe_expr_else,1},"pattern_to_var.erl:76"}}. +{store,{"Match","pattern_to_var.erl:76"}}. +{get,{"Match","pattern_to_var.erl:78"}}. +{store,{"Rest","pattern_to_var.erl:81"}}. +{get,{"Rest","pattern_to_var.erl:81"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:81"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{operators,'++',2},"pattern_to_var.erl:81"}. +{apply,{{operators,'++',2},"pattern_to_var.erl:81"}}. +{func_ret,{maybe_expr_else,"pattern_to_var.erl:76"}}. +{apply,{{pattern_to_var,maybe_expr_else,1},"pattern_to_var.erl:92"}}. +{func_ret,{dropping_lambda_capture,"pattern_to_var.erl:92"}}. +{duplicate,{}}. +{store,{"Tainted1","pattern_to_var.erl:92"}}. +{pop,{}}. +{get,{"Tainted1","pattern_to_var.erl:93"}}. +{sink,{"pattern_to_var.erl:93"}}. +{func_ret,{maybe_expr_main,"pattern_to_var.erl:85"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/modeled_functions.erl b/finer_taint/test/finer_taint_SUITE_data/modeled_functions.erl new file mode 100644 index 0000000..69797fc --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/modeled_functions.erl @@ -0,0 +1,40 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('modeled_functions'). +-export([ + modeled_functions_main/0, process_dict_main/0, + joining_model_main/0 +]). + +modeled_functions_main() -> + Number = finer_taint:source("123"), + Domain = finer_taint:source("@secret.net"), + {ok, Tainted} = definetly_not_modeled:get(Number, Domain), + finer_taint:sink(Tainted), + {ok, NotTainted} = definetly_not_modeled:get(1, 2), + finer_taint:sink(NotTainted). + +joining_model_main() -> + Number = finer_taint:source("123"), + Domain = finer_taint:source("@secret.net"), + finer_taint:sink(string:concat(Number, Domain)). + +process_dict_main() -> + Number = finer_taint:source("123"), + finer_taint:sink(put(notsecret, 1)), + finer_taint:sink(put(secret, Number)), %ret: undefined -> not tainted + finer_taint:sink(get(secret)), %tainted + finer_taint:sink(put(secret, 1)), %ret: Number -> tainted + finer_taint:sink(get(secret)). % not tainted diff --git a/finer_taint/test/finer_taint_SUITE_data/modeled_functions_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/modeled_functions_main_analysis_instr new file mode 100644 index 0000000..97d2cb8 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/modeled_functions_main_analysis_instr @@ -0,0 +1,47 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{modeled_functions,modeled_functions_main,0},"modeled_functions.erl:21"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"modeled_functions.erl:22"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Number","modeled_functions.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"modeled_functions.erl:23"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Domain","modeled_functions.erl:23"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Domain","modeled_functions.erl:24"}}. +{get,{"Number","modeled_functions.erl:24"}}. +{call_fun,{definetly_not_modeled,get,2},"modeled_functions.erl:24"}. +{apply,{{definetly_not_modeled,get,2},"modeled_functions.erl:24"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"modeled_functions.erl:24"}}. +{pop,{}}. +{store,{"Tainted","modeled_functions.erl:24"}}. +{pop,{}}. +{get,{"Tainted","modeled_functions.erl:25"}}. +{sink,{"modeled_functions.erl:25"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{definetly_not_modeled,get,2},"modeled_functions.erl:26"}. +{apply,{{definetly_not_modeled,get,2},"modeled_functions.erl:26"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"modeled_functions.erl:26"}}. +{pop,{}}. +{store,{"NotTainted","modeled_functions.erl:26"}}. +{pop,{}}. +{get,{"NotTainted","modeled_functions.erl:27"}}. +{sink,{"modeled_functions.erl:27"}}. +{func_ret,{modeled_functions_main,"modeled_functions.erl:21"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/nested_calls_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/nested_calls_main_analysis_instr new file mode 100644 index 0000000..5aef157 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/nested_calls_main_analysis_instr @@ -0,0 +1,51 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{function_calls,nested_calls_main,0},"function_calls.erl:67"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:68"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:68"}}. +{push,{notaint}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:68"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:56"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{generate_list,"function_calls.erl:56"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:68"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:68"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:68"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:68"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:59"}}. +{pop,{}}. +{store,{"Elem","function_calls.erl:59"}}. +{store,{"Tail","function_calls.erl:59"}}. +{get,{"Elem","function_calls.erl:59"}}. +{get,{"Tail","function_calls.erl:59"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:59"}}. +{func_ret,{insert_at,"function_calls.erl:59"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:68"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:68"}}. +{duplicate,{}}. +{store,{"List","function_calls.erl:68"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,at,2},"function_calls.erl:69"}}. +{get,{"List","function_calls.erl:69"}}. +{push,{notaint}}. +{call_fun,{function_calls,at,2},"function_calls.erl:69"}. +{push_scope,{{function_calls,at,2},"function_calls.erl:64"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:64"}}. +{store,{"E","function_calls.erl:64"}}. +{store,{"_Tail","function_calls.erl:64"}}. +{get,{"E","function_calls.erl:64"}}. +{func_ret,{at,"function_calls.erl:64"}}. +{apply,{{function_calls,at,2},"function_calls.erl:69"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:69"}}. +{sink,{"function_calls.erl:69"}}. +{func_ret,{nested_calls_main,"function_calls.erl:67"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/nested_case_with_call_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/nested_case_with_call_main_analysis_instr new file mode 100644 index 0000000..aed35db --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/nested_case_with_call_main_analysis_instr @@ -0,0 +1,32 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{case_clauses,nested_case_with_call_main,0},"case_clauses.erl:66"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:67"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedValue","case_clauses.erl:67"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,get_ok,0},"case_clauses.erl:68"}}. +{call_fun,{case_clauses,get_ok,0},"case_clauses.erl:68"}. +{push_scope,{{case_clauses,get_ok,0},"case_clauses.erl:65"}}. +{push,{notaint}}. +{func_ret,{get_ok,"case_clauses.erl:65"}}. +{apply,{{case_clauses,get_ok,0},"case_clauses.erl:68"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:68"}}. +{pop,{}}. +{get,{"TaintedValue","case_clauses.erl:69"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"case_clauses.erl:69"}. +{apply,{{operators,'+',2},"case_clauses.erl:69"}}. +{duplicate,{}}. +{store,{"FourtyThree","case_clauses.erl:69"}}. +{pop,{}}. +{get,{"FourtyThree","case_clauses.erl:70"}}. +{pop,{}}. +{get,{"FourtyThree","case_clauses.erl:71"}}. +{sink,{"case_clauses.erl:71"}}. +{func_ret,{nested_case_with_call_main,"case_clauses.erl:66"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/not_instrumented/definetly_not_modeled.erl b/finer_taint/test/finer_taint_SUITE_data/not_instrumented/definetly_not_modeled.erl new file mode 100644 index 0000000..e4646b6 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/not_instrumented/definetly_not_modeled.erl @@ -0,0 +1,19 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(definetly_not_modeled). +-export([get/2]). + +get(A,B) -> + {ok, A}. diff --git a/finer_taint/test/finer_taint_SUITE_data/not_instrumented/not_instrumented.erl b/finer_taint/test/finer_taint_SUITE_data/not_instrumented/not_instrumented.erl new file mode 100644 index 0000000..bd43941 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/not_instrumented/not_instrumented.erl @@ -0,0 +1,19 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(not_instrumented). +-export([call_fn/2]). + +call_fn(Fun,Arg1) -> + Fun(Arg1, ok, ok, ok, ok). diff --git a/finer_taint/test/finer_taint_SUITE_data/one_clause_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/one_clause_analysis_instr new file mode 100644 index 0000000..b8a5838 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/one_clause_analysis_instr @@ -0,0 +1,41 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{simple_example,one_clause,0},"simple_example.erl:20"}}. +{push,{notaint}}. +{pop,{}}. +{push,{{"phone_num","simple_example.erl:21"}}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","simple_example.erl:21"}}. +{pop,{}}. +{push,{notaint}}. +{duplicate,{}}. +{store,{"AString","simple_example.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"PhoneNumber","simple_example.erl:23"}}. +{call_fun,{string,slice,3},"simple_example.erl:23"}. +{apply,{{string,slice,3},"simple_example.erl:23"}}. +{duplicate,{}}. +{store,{"X","simple_example.erl:23"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"AString","simple_example.erl:24"}}. +{call_fun,{string,slice,3},"simple_example.erl:24"}. +{apply,{{string,slice,3},"simple_example.erl:24"}}. +{duplicate,{}}. +{store,{"Y","simple_example.erl:24"}}. +{pop,{}}. +{get,{"Y","simple_example.erl:25"}}. +{sink,{"simple_example.erl:25"}}. +{pop,{}}. +{get,{"X","simple_example.erl:26"}}. +{sink,{"simple_example.erl:26"}}. +{func_ret,{one_clause,"simple_example.erl:20"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/opaque_map_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/opaque_map_main_analysis_instr new file mode 100644 index 0000000..28f4590 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/opaque_map_main_analysis_instr @@ -0,0 +1,24 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{map_examples,opaque_map_main,0},"map_examples.erl:75"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{map,[a]},"map_examples.erl:76"}}. +{pop,{}}. +{push,{"map_examples.erl:76"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedMap","map_examples.erl:76"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"TaintedMap","map_examples.erl:77"}}. +{construct_pattern,{{map,[notainted]},"map_examples.erl:77"}}. +{duplicate,{}}. +{store,{"TaintedMap1","map_examples.erl:77"}}. +{pop,{}}. +{get,{"TaintedMap1","map_examples.erl:78"}}. +{sink,{"map_examples.erl:78"}}. +{func_ret,{opaque_map_main,"map_examples.erl:75"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/operators_in_pattern_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/operators_in_pattern_main_analysis_instr new file mode 100644 index 0000000..0a6ab84 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/operators_in_pattern_main_analysis_instr @@ -0,0 +1,36 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{pattern_to_var,operators_in_pattern_main,0},"pattern_to_var.erl:59"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:60"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","pattern_to_var.erl:60"}}. +{pop,{}}. +{get,{"PhoneNumber","pattern_to_var.erl:64"}}. +{duplicate,{}}. +{store,{"Domain","pattern_to_var.erl:64"}}. +{pop,{}}. +{get,{"Domain","pattern_to_var.erl:65"}}. +{sink,{"pattern_to_var.erl:65"}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{operators,'-',1},"pattern_to_var.erl:66"}. +{apply,{{operators,'-',1},"pattern_to_var.erl:66"}}. +{pop,{}}. +{push,{"pattern_to_var.erl:66"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:67"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{pop,{}}. +{func_ret,{operators_in_pattern_main,"pattern_to_var.erl:59"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/pattern_to_var.erl b/finer_taint/test/finer_taint_SUITE_data/pattern_to_var.erl new file mode 100644 index 0000000..2f1ce6b --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/pattern_to_var.erl @@ -0,0 +1,94 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('pattern_to_var'). +-feature(maybe_expr, enable). +-export([ + pattern_to_var/0, extract_pattern/0, string_plusplus_pattern/0, set_element_main/0, operators_in_pattern_main/0, maybe_expr_main/0 +]). +pattern_to_var() -> + PhoneNumber = finer_taint:source(" 123@secret.net"), + AString = "123 os a good start of a string ", + X = {ok, string:strip(PhoneNumber)}, + Y = {ok, string:strip(AString)}, + finer_taint:sink(Y), + finer_taint:sink(X). + +extract_pattern() -> + PhoneNumber = finer_taint:source("123@secret.net"), + Tuple = {"A nice string", PhoneNumber}, + {X, Y} = Tuple, + finer_taint:sink(Y), + finer_taint:sink(X), + {ok, T1} = {ok, PhoneNumber}, + {ok, UT1} = {ok, "A nice string"}, + {T2, ok} = {PhoneNumber, ok}, + finer_taint:sink(T1), + finer_taint:sink(T2), + finer_taint:sink(UT1). + + +string_plusplus_pattern() -> + PhoneNumber = finer_taint:source(" 123@secret.net"), + " 123" ++ RestOfNumber = PhoneNumber, + finer_taint:sink(RestOfNumber). + + +set_element_main() -> + PhoneNumber = finer_taint:source("123@secret.net"), + ANumber = finer_taint:source(42), + Tuple = {"Some string", "Another string", ANumber}, + Tuple1 = erlang:setelement(2, Tuple, PhoneNumber), + {NotTainted, Tainted, Tainted2} = Tuple1, + finer_taint:sink(NotTainted), + finer_taint:sink(Tainted), + finer_taint:sink(Tainted2). + + +operators_in_pattern_main() -> + PhoneNumber = finer_taint:source("123@secret.net"), + % These are illegal patterns + % Number ++ "@secret.net" = PhoneNumber, + % -X = -1 + "123" ++ Domain = PhoneNumber, + finer_taint:sink(Domain), + -1 = finer_taint:source(-1), + 2 bsl 2 = finer_taint:source(8). + + +maybe_expr(Match) -> + maybe + "123" ++ _ ?= Match, + it_matches + end. + +maybe_expr_else(Match) -> + maybe + "123" ++ _ ?= Match, + it_matches + else + "3" ++ Rest -> Rest ++ finer_taint:source("a new source") + end. + + +maybe_expr_main() -> + PhoneNumber = finer_taint:source("3@secret.net"), + Tainted = maybe_expr(PhoneNumber), + finer_taint:sink(Tainted), + PhoneNumber1 = finer_taint:source("123@secret.net"), + NotTainted = maybe_expr(PhoneNumber1), + finer_taint:sink(NotTainted), + Tainted1 = maybe_expr_else(PhoneNumber), + finer_taint:sink(Tainted1). + diff --git a/finer_taint/test/finer_taint_SUITE_data/pattern_to_var_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/pattern_to_var_analysis_instr new file mode 100644 index 0000000..0af8573 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/pattern_to_var_analysis_instr @@ -0,0 +1,41 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{pattern_to_var,pattern_to_var,0},"pattern_to_var.erl:20"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:21"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","pattern_to_var.erl:21"}}. +{pop,{}}. +{push,{notaint}}. +{duplicate,{}}. +{store,{"AString","pattern_to_var.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"PhoneNumber","pattern_to_var.erl:23"}}. +{call_fun,{string,strip,1},"pattern_to_var.erl:23"}. +{apply,{{string,strip,1},"pattern_to_var.erl:23"}}. +{construct_pattern,{{tuple,2},"pattern_to_var.erl:23"}}. +{duplicate,{}}. +{store,{"X","pattern_to_var.erl:23"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"AString","pattern_to_var.erl:24"}}. +{call_fun,{string,strip,1},"pattern_to_var.erl:24"}. +{apply,{{string,strip,1},"pattern_to_var.erl:24"}}. +{construct_pattern,{{tuple,2},"pattern_to_var.erl:24"}}. +{duplicate,{}}. +{store,{"Y","pattern_to_var.erl:24"}}. +{pop,{}}. +{get,{"Y","pattern_to_var.erl:25"}}. +{sink,{"pattern_to_var.erl:25"}}. +{pop,{}}. +{get,{"X","pattern_to_var.erl:26"}}. +{sink,{"pattern_to_var.erl:26"}}. +{func_ret,{pattern_to_var,"pattern_to_var.erl:20"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/process_dict_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/process_dict_main_analysis_instr new file mode 100644 index 0000000..78e5ddc --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/process_dict_main_analysis_instr @@ -0,0 +1,321 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{modeled_functions,process_dict_main,0},"modeled_functions.erl:34"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"modeled_functions.erl:35"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Number","modeled_functions.erl:35"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mput,2},"modeled_functions.erl:36"}. +{push_scope,{{modeled_erlang,mput,2},"modeled_erlang.erl:155"}}. +{store,{"Key","modeled_erlang.erl:155"}}. +{store,{"Value","modeled_erlang.erl:155"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:156"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:156"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:157"}}. +{get,{"Key","modeled_erlang.erl:157"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}. +{push_scope,{{modeled_taint_maps,get,3},"modeled_taint_maps.erl:72"}}. +{store,{"Key","modeled_taint_maps.erl:72"}}. +{store,{"Map","modeled_taint_maps.erl:72"}}. +{store,{"Default","modeled_taint_maps.erl:72"}}. +{try_catch,{try_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{get,{"Map","modeled_taint_maps.erl:73"}}. +{get,{"Key","modeled_taint_maps.erl:73"}}. +{call_fun,{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}. +{push_scope,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:80"}}. +{store,{"Key","modeled_taint_maps.erl:80"}}. +{store,{"Map","modeled_taint_maps.erl:80"}}. +{get,{"Map","modeled_taint_maps.erl:81"}}. +{try_catch,{catch_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:76"}. +{pop,{}}. +{push,{notaint}}. +{deconstruct_pattern,{{tuple,2},"modeled_taint_maps.erl:76"}}. +{pop,{}}. +{pop,{}}. +{get,{"Default","modeled_taint_maps.erl:76"}}. +{func_ret,{get,"modeled_taint_maps.erl:72"}}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}}. +{duplicate,{}}. +{store,{"ReturnValue","modeled_erlang.erl:157"}}. +{pop,{}}. +{get,{"Key","modeled_erlang.erl:158"}}. +{get,{"Value","modeled_erlang.erl:158"}}. +{get,{"Pd","modeled_erlang.erl:158"}}. +{construct_pattern,{{map,[notsecret]},"modeled_erlang.erl:158"}}. +{duplicate,{}}. +{store,{"NewDict","modeled_erlang.erl:158"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{get,{"NewDict","modeled_erlang.erl:159"}}. +{call_fun,{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}. +{apply,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:159"}}. +{pop,{}}. +{get,{"ReturnValue","modeled_erlang.erl:160"}}. +{func_ret,{mput,"modeled_erlang.erl:155"}}. +{apply,{{modeled_erlang,mput,2},"modeled_functions.erl:36"}}. +{sink,{"modeled_functions.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Number","modeled_functions.erl:37"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mput,2},"modeled_functions.erl:37"}. +{push_scope,{{modeled_erlang,mput,2},"modeled_erlang.erl:155"}}. +{store,{"Key","modeled_erlang.erl:155"}}. +{store,{"Value","modeled_erlang.erl:155"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:156"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:156"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:157"}}. +{get,{"Key","modeled_erlang.erl:157"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}. +{push_scope,{{modeled_taint_maps,get,3},"modeled_taint_maps.erl:72"}}. +{store,{"Key","modeled_taint_maps.erl:72"}}. +{store,{"Map","modeled_taint_maps.erl:72"}}. +{store,{"Default","modeled_taint_maps.erl:72"}}. +{try_catch,{try_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{get,{"Map","modeled_taint_maps.erl:73"}}. +{get,{"Key","modeled_taint_maps.erl:73"}}. +{call_fun,{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}. +{push_scope,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:80"}}. +{store,{"Key","modeled_taint_maps.erl:80"}}. +{store,{"Map","modeled_taint_maps.erl:80"}}. +{get,{"Map","modeled_taint_maps.erl:81"}}. +{try_catch,{catch_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:76"}. +{pop,{}}. +{push,{notaint}}. +{deconstruct_pattern,{{tuple,2},"modeled_taint_maps.erl:76"}}. +{pop,{}}. +{pop,{}}. +{get,{"Default","modeled_taint_maps.erl:76"}}. +{func_ret,{get,"modeled_taint_maps.erl:72"}}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}}. +{duplicate,{}}. +{store,{"ReturnValue","modeled_erlang.erl:157"}}. +{pop,{}}. +{get,{"Key","modeled_erlang.erl:158"}}. +{get,{"Value","modeled_erlang.erl:158"}}. +{get,{"Pd","modeled_erlang.erl:158"}}. +{construct_pattern,{{map,[secret]},"modeled_erlang.erl:158"}}. +{duplicate,{}}. +{store,{"NewDict","modeled_erlang.erl:158"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{get,{"NewDict","modeled_erlang.erl:159"}}. +{call_fun,{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}. +{apply,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:159"}}. +{pop,{}}. +{get,{"ReturnValue","modeled_erlang.erl:160"}}. +{func_ret,{mput,"modeled_erlang.erl:155"}}. +{apply,{{modeled_erlang,mput,2},"modeled_functions.erl:37"}}. +{sink,{"modeled_functions.erl:37"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mget,1},"modeled_functions.erl:38"}. +{push_scope,{{modeled_erlang,mget,1},"modeled_erlang.erl:163"}}. +{store,{"Key","modeled_erlang.erl:163"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:164"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:164"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:165"}}. +{get,{"Key","modeled_erlang.erl:165"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}. +{push_scope,{{modeled_taint_maps,get,3},"modeled_taint_maps.erl:72"}}. +{store,{"Key","modeled_taint_maps.erl:72"}}. +{store,{"Map","modeled_taint_maps.erl:72"}}. +{store,{"Default","modeled_taint_maps.erl:72"}}. +{try_catch,{try_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{get,{"Map","modeled_taint_maps.erl:73"}}. +{get,{"Key","modeled_taint_maps.erl:73"}}. +{call_fun,{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}. +{push_scope,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:80"}}. +{store,{"Key","modeled_taint_maps.erl:80"}}. +{store,{"Map","modeled_taint_maps.erl:80"}}. +{get,{"Map","modeled_taint_maps.erl:81"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[secret]},"modeled_taint_maps.erl:81"}}. +{store,{"Value","modeled_taint_maps.erl:81"}}. +{store,{"Key","modeled_taint_maps.erl:81"}}. +{pop,{}}. +{get,{"Value","modeled_taint_maps.erl:82"}}. +{func_ret,{get,"modeled_taint_maps.erl:80"}}. +{apply,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:73"}}. +{try_catch,{try_exit,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{store,{"Val","modeled_taint_maps.erl:74"}}. +{get,{"Val","modeled_taint_maps.erl:74"}}. +{func_ret,{get,"modeled_taint_maps.erl:72"}}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}}. +{func_ret,{mget,"modeled_erlang.erl:163"}}. +{apply,{{modeled_erlang,mget,1},"modeled_functions.erl:38"}}. +{sink,{"modeled_functions.erl:38"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mput,2},"modeled_functions.erl:39"}. +{push_scope,{{modeled_erlang,mput,2},"modeled_erlang.erl:155"}}. +{store,{"Key","modeled_erlang.erl:155"}}. +{store,{"Value","modeled_erlang.erl:155"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:156"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:156"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:157"}}. +{get,{"Key","modeled_erlang.erl:157"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}. +{push_scope,{{modeled_taint_maps,get,3},"modeled_taint_maps.erl:72"}}. +{store,{"Key","modeled_taint_maps.erl:72"}}. +{store,{"Map","modeled_taint_maps.erl:72"}}. +{store,{"Default","modeled_taint_maps.erl:72"}}. +{try_catch,{try_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{get,{"Map","modeled_taint_maps.erl:73"}}. +{get,{"Key","modeled_taint_maps.erl:73"}}. +{call_fun,{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}. +{push_scope,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:80"}}. +{store,{"Key","modeled_taint_maps.erl:80"}}. +{store,{"Map","modeled_taint_maps.erl:80"}}. +{get,{"Map","modeled_taint_maps.erl:81"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[secret]},"modeled_taint_maps.erl:81"}}. +{store,{"Value","modeled_taint_maps.erl:81"}}. +{store,{"Key","modeled_taint_maps.erl:81"}}. +{pop,{}}. +{get,{"Value","modeled_taint_maps.erl:82"}}. +{func_ret,{get,"modeled_taint_maps.erl:80"}}. +{apply,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:73"}}. +{try_catch,{try_exit,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{store,{"Val","modeled_taint_maps.erl:74"}}. +{get,{"Val","modeled_taint_maps.erl:74"}}. +{func_ret,{get,"modeled_taint_maps.erl:72"}}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}}. +{duplicate,{}}. +{store,{"ReturnValue","modeled_erlang.erl:157"}}. +{pop,{}}. +{get,{"Key","modeled_erlang.erl:158"}}. +{get,{"Value","modeled_erlang.erl:158"}}. +{get,{"Pd","modeled_erlang.erl:158"}}. +{construct_pattern,{{map,[secret]},"modeled_erlang.erl:158"}}. +{duplicate,{}}. +{store,{"NewDict","modeled_erlang.erl:158"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{get,{"NewDict","modeled_erlang.erl:159"}}. +{call_fun,{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}. +{apply,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:159"}}. +{pop,{}}. +{get,{"ReturnValue","modeled_erlang.erl:160"}}. +{func_ret,{mput,"modeled_erlang.erl:155"}}. +{apply,{{modeled_erlang,mput,2},"modeled_functions.erl:39"}}. +{sink,{"modeled_functions.erl:39"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mget,1},"modeled_functions.erl:40"}. +{push_scope,{{modeled_erlang,mget,1},"modeled_erlang.erl:163"}}. +{store,{"Key","modeled_erlang.erl:163"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:164"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:164"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:165"}}. +{get,{"Key","modeled_erlang.erl:165"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}. +{push_scope,{{modeled_taint_maps,get,3},"modeled_taint_maps.erl:72"}}. +{store,{"Key","modeled_taint_maps.erl:72"}}. +{store,{"Map","modeled_taint_maps.erl:72"}}. +{store,{"Default","modeled_taint_maps.erl:72"}}. +{try_catch,{try_enter,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{push,{notaint}}. +{restore_capture,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{get,{"Map","modeled_taint_maps.erl:73"}}. +{get,{"Key","modeled_taint_maps.erl:73"}}. +{call_fun,{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}. +{push_scope,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:80"}}. +{store,{"Key","modeled_taint_maps.erl:80"}}. +{store,{"Map","modeled_taint_maps.erl:80"}}. +{get,{"Map","modeled_taint_maps.erl:81"}}. +{duplicate,{}}. +{deconstruct_pattern,{{map,[secret]},"modeled_taint_maps.erl:81"}}. +{store,{"Value","modeled_taint_maps.erl:81"}}. +{store,{"Key","modeled_taint_maps.erl:81"}}. +{pop,{}}. +{get,{"Value","modeled_taint_maps.erl:82"}}. +{func_ret,{get,"modeled_taint_maps.erl:80"}}. +{apply,{{modeled_taint_maps,get,2},"modeled_taint_maps.erl:73"}}. +{func_ret,{dropping_lambda_capture,"modeled_taint_maps.erl:73"}}. +{try_catch,{try_exit,{modeled_taint_maps,73,66716211}},"modeled_taint_maps.erl:73"}. +{store,{"Val","modeled_taint_maps.erl:74"}}. +{get,{"Val","modeled_taint_maps.erl:74"}}. +{func_ret,{get,"modeled_taint_maps.erl:72"}}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}}. +{func_ret,{mget,"modeled_erlang.erl:163"}}. +{apply,{{modeled_erlang,mget,1},"modeled_functions.erl:40"}}. +{sink,{"modeled_functions.erl:40"}}. +{func_ret,{process_dict_main,"modeled_functions.erl:34"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/ps_example.erl b/finer_taint/test/finer_taint_SUITE_data/ps_example.erl new file mode 100644 index 0000000..2e43dbb --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/ps_example.erl @@ -0,0 +1,62 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +-module(ps_example). +-export([ + local_if_clause/0, + ps_local_if_clause/0, + ps_exp_if_clause/0, + ps_if_clause/0 +]). + +local_if_clause() -> + PhoneNumber = finer_taint:source("123@secret.net"), + Result = + if + PhoneNumber =:= "A" -> ok; + true -> PhoneNumber + end, + local_sink(Result). + +local_sink(Result) -> + finer_taint:sink(Result). + +ps_local_if_clause() -> + PhoneNumber = finer_taint:source("123@secret.net"), + Result = + if + PhoneNumber =:= "A" -> ok; + true -> PhoneNumber + end, + power_shell:eval(?MODULE, local_sink, [Result]). + +ps_if_clause() -> + PhoneNumber = finer_taint:source("123@secret.net"), + Result = + if + PhoneNumber =:= "A" -> ok; + true -> PhoneNumber + end, + power_shell:eval(ps_external, local_sink, [Result]). + +ps_exp_if_clause() -> + PhoneNumber = finer_taint:source("123@secret.net"), + Result = + if + PhoneNumber =:= "A" -> ok; + true -> PhoneNumber + end, + power_shell:export(ps_external), + ps_external:local_sink(Result). diff --git a/finer_taint/test/finer_taint_SUITE_data/ps_exp_if_clause_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/ps_exp_if_clause_analysis_instr new file mode 100644 index 0000000..b51e195 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/ps_exp_if_clause_analysis_instr @@ -0,0 +1,32 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{ps_example,ps_exp_if_clause,0},"ps_example.erl:54"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"ps_example.erl:55"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","ps_example.erl:55"}}. +{pop,{}}. +{get,{"PhoneNumber","ps_example.erl:59"}}. +{duplicate,{}}. +{store,{"Result","ps_example.erl:56"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{power_shell,export,1},"ps_example.erl:61"}. +{apply,{{power_shell,export,1},"ps_example.erl:61"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Result","ps_example.erl:62"}}. +{call_fun,{ps_external,local_sink,1},"ps_example.erl:62"}. +{push_scope,{{ps_external,local_sink,1},"ps_external.erl:25"}}. +{store,{"X","ps_external.erl:25"}}. +{get,{"X","ps_external.erl:26"}}. +{sink,{"ps_external.erl:26"}}. +{func_ret,{local_sink,"ps_external.erl:25"}}. +{apply,{{ps_external,local_sink,1},"ps_example.erl:62"}}. +{func_ret,{ps_exp_if_clause,"ps_example.erl:54"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/ps_external.erl b/finer_taint/test/finer_taint_SUITE_data/ps_external.erl new file mode 100644 index 0000000..45319ad --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/ps_external.erl @@ -0,0 +1,26 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +-module(ps_external). + +-export([ + sink/1 +]). + +sink(X) -> + local_sink(X). + +local_sink(X) -> + finer_taint:sink(X). diff --git a/finer_taint/test/finer_taint_SUITE_data/ps_if_clause_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/ps_if_clause_analysis_instr new file mode 100644 index 0000000..7509872 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/ps_if_clause_analysis_instr @@ -0,0 +1,60 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{ps_example,ps_if_clause,0},"ps_example.erl:45"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"ps_example.erl:46"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","ps_example.erl:46"}}. +{pop,{}}. +{get,{"PhoneNumber","ps_example.erl:50"}}. +{duplicate,{}}. +{store,{"Result","ps_example.erl:47"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Result","ps_example.erl:52"}}. +{construct_pattern,{{cons},"ps_example.erl:52"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_power_shell,eval,3},"ps_example.erl:52"}. +{push_scope,{{modeled_power_shell,eval,3},"modeled_power_shell.erl:29"}}. +{store,{"Module","modeled_power_shell.erl:29"}}. +{store,{"Function","modeled_power_shell.erl:29"}}. +{store,{"Args","modeled_power_shell.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Module","modeled_power_shell.erl:30"}}. +{call_fun,{power_shell,export,1},"modeled_power_shell.erl:30"}. +{apply,{{power_shell,export,1},"modeled_power_shell.erl:30"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Args","modeled_power_shell.erl:31"}}. +{get,{"Function","modeled_power_shell.erl:31"}}. +{get,{"Module","modeled_power_shell.erl:31"}}. +{call_fun,{modeled_erlang,mapply,3},"modeled_power_shell.erl:31"}. +{push_scope,{{modeled_erlang,mapply,3},"modeled_erlang.erl:39"}}. +{store,{"M","modeled_erlang.erl:39"}}. +{store,{"F","modeled_erlang.erl:39"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:39"}}. +{store,{"Arg1","modeled_erlang.erl:39"}}. +{pop,{}}. +{get,{"F","modeled_erlang.erl:40"}}. +{pop,{}}. +{get,{"Arg1","modeled_erlang.erl:40"}}. +{call_fun,{ps_external,local_sink,1},"modeled_erlang.erl:40"}. +{push_scope,{{ps_external,local_sink,1},"ps_external.erl:25"}}. +{store,{"X","ps_external.erl:25"}}. +{get,{"X","ps_external.erl:26"}}. +{sink,{"ps_external.erl:26"}}. +{func_ret,{local_sink,"ps_external.erl:25"}}. +{apply,{{ps_external,local_sink,1},"modeled_erlang.erl:40"}}. +{func_ret,{mapply,"modeled_erlang.erl:39"}}. +{apply,{{modeled_erlang,mapply,3},"modeled_power_shell.erl:31"}}. +{func_ret,{eval,"modeled_power_shell.erl:29"}}. +{apply,{{modeled_power_shell,eval,3},"ps_example.erl:52"}}. +{func_ret,{ps_if_clause,"ps_example.erl:45"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/ps_local_if_clause_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/ps_local_if_clause_analysis_instr new file mode 100644 index 0000000..7f28d07 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/ps_local_if_clause_analysis_instr @@ -0,0 +1,60 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{ps_example,ps_local_if_clause,0},"ps_example.erl:36"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"ps_example.erl:37"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","ps_example.erl:37"}}. +{pop,{}}. +{get,{"PhoneNumber","ps_example.erl:41"}}. +{duplicate,{}}. +{store,{"Result","ps_example.erl:38"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Result","ps_example.erl:43"}}. +{construct_pattern,{{cons},"ps_example.erl:43"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_power_shell,eval,3},"ps_example.erl:43"}. +{push_scope,{{modeled_power_shell,eval,3},"modeled_power_shell.erl:29"}}. +{store,{"Module","modeled_power_shell.erl:29"}}. +{store,{"Function","modeled_power_shell.erl:29"}}. +{store,{"Args","modeled_power_shell.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Module","modeled_power_shell.erl:30"}}. +{call_fun,{power_shell,export,1},"modeled_power_shell.erl:30"}. +{apply,{{power_shell,export,1},"modeled_power_shell.erl:30"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Args","modeled_power_shell.erl:31"}}. +{get,{"Function","modeled_power_shell.erl:31"}}. +{get,{"Module","modeled_power_shell.erl:31"}}. +{call_fun,{modeled_erlang,mapply,3},"modeled_power_shell.erl:31"}. +{push_scope,{{modeled_erlang,mapply,3},"modeled_erlang.erl:39"}}. +{store,{"M","modeled_erlang.erl:39"}}. +{store,{"F","modeled_erlang.erl:39"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:39"}}. +{store,{"Arg1","modeled_erlang.erl:39"}}. +{pop,{}}. +{get,{"F","modeled_erlang.erl:40"}}. +{pop,{}}. +{get,{"Arg1","modeled_erlang.erl:40"}}. +{call_fun,{ps_example,local_sink,1},"modeled_erlang.erl:40"}. +{push_scope,{{ps_example,local_sink,1},"ps_example.erl:33"}}. +{store,{"Result","ps_example.erl:33"}}. +{get,{"Result","ps_example.erl:34"}}. +{sink,{"ps_example.erl:34"}}. +{func_ret,{local_sink,"ps_example.erl:33"}}. +{apply,{{ps_example,local_sink,1},"modeled_erlang.erl:40"}}. +{func_ret,{mapply,"modeled_erlang.erl:39"}}. +{apply,{{modeled_erlang,mapply,3},"modeled_power_shell.erl:31"}}. +{func_ret,{eval,"modeled_power_shell.erl:29"}}. +{apply,{{modeled_power_shell,eval,3},"ps_example.erl:43"}}. +{func_ret,{ps_local_if_clause,"ps_example.erl:36"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/record_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/record_main_analysis_instr new file mode 100644 index 0000000..b21ef2c --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/record_main_analysis_instr @@ -0,0 +1,89 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{case_clauses,record_main,0},"case_clauses.erl:43"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"case_clauses.erl:44"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedValue","case_clauses.erl:44"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"TaintedValue","case_clauses.erl:45"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,4},"case_clauses.erl:45"}}. +{duplicate,{}}. +{store,{"NotTaintedRecord","case_clauses.erl:45"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"TaintedValue","case_clauses.erl:46"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,4},"case_clauses.erl:46"}}. +{duplicate,{}}. +{store,{"TaintedRecord","case_clauses.erl:46"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:47"}}. +{get,{"NotTaintedRecord","case_clauses.erl:47"}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:47"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{deconstruct_pattern,{{tuple,4},"case_clauses.erl:58"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{store,{"Hist","case_clauses.erl:58"}}. +{get,{"Hist","case_clauses.erl:58"}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:47"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:47"}}. +{sink,{"case_clauses.erl:47"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:48"}}. +{get,{"TaintedRecord","case_clauses.erl:48"}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:48"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{deconstruct_pattern,{{tuple,4},"case_clauses.erl:59"}}. +{pop,{}}. +{pop,{}}. +{store,{"Val","case_clauses.erl:59"}}. +{pop,{}}. +{get,{"Val","case_clauses.erl:59"}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:48"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:48"}}. +{sink,{"case_clauses.erl:48"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:49"}}. +{push,{notaint}}. +{get,{"TaintedRecord","case_clauses.erl:49"}}. +{construct_pattern,{{cons},"case_clauses.erl:49"}}. +{call_fun,{case_clauses,big_pattern_extractor,1},"case_clauses.erl:49"}. +{push_scope,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:52"}}. +{store,{"X","case_clauses.erl:52"}}. +{get,{"X","case_clauses.erl:53"}}. +{deconstruct_pattern,{{cons},"case_clauses.erl:60"}}. +{store,{"PatternTmpVar_4OH7SYDS","case_clauses.erl:60"}}. +{get,{"PatternTmpVar_4OH7SYDS","case_clauses.erl:60"}}. +{deconstruct_pattern,{{tuple,4},"case_clauses.erl:60"}}. +{pop,{}}. +{pop,{}}. +{store,{"IntVal","case_clauses.erl:60"}}. +{pop,{}}. +{get,{"PatternTmpVar_4OH7SYDS","case_clauses.erl:60"}}. +{store,{"_Record","case_clauses.erl:60"}}. +{pop,{}}. +{get,{"IntVal","case_clauses.erl:60"}}. +{func_ret,{big_pattern_extractor,"case_clauses.erl:52"}}. +{apply,{{case_clauses,big_pattern_extractor,1},"case_clauses.erl:49"}}. +{func_ret,{dropping_lambda_capture,"case_clauses.erl:49"}}. +{sink,{"case_clauses.erl:49"}}. +{func_ret,{record_main,"case_clauses.erl:43"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/recursion_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/recursion_main_analysis_instr new file mode 100644 index 0000000..d7edfd9 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/recursion_main_analysis_instr @@ -0,0 +1,478 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{function_calls,recursion_main,0},"function_calls.erl:41"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:42"}}. +{push,{notaint}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:42"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{store,{"N","function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{get,{"N","function_calls.erl:57"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:57"}. +{apply,{{operators,'-',2},"function_calls.erl:57"}}. +{call_fun,{function_calls,generate_list,1},"function_calls.erl:57"}. +{push_scope,{{function_calls,generate_list,1},"function_calls.erl:56"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{generate_list,"function_calls.erl:56"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:57"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:57"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:57"}}. +{func_ret,{generate_list,"function_calls.erl:57"}}. +{apply,{{function_calls,generate_list,1},"function_calls.erl:42"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:42"}}. +{duplicate,{}}. +{store,{"List","function_calls.erl:42"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"function_calls.erl:43"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedNumber","function_calls.erl:43"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:44"}}. +{get,{"List","function_calls.erl:44"}}. +{get,{"TaintedNumber","function_calls.erl:44"}}. +{push,{notaint}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:44"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:60"}}. +{store,{"N","function_calls.erl:60"}}. +{store,{"Elem","function_calls.erl:60"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:60"}}. +{store,{"Head","function_calls.erl:60"}}. +{store,{"Tail","function_calls.erl:60"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{get,{"Tail","function_calls.erl:61"}}. +{get,{"Elem","function_calls.erl:61"}}. +{get,{"N","function_calls.erl:61"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:61"}. +{apply,{{operators,'-',2},"function_calls.erl:61"}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:61"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:60"}}. +{store,{"N","function_calls.erl:60"}}. +{store,{"Elem","function_calls.erl:60"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:60"}}. +{store,{"Head","function_calls.erl:60"}}. +{store,{"Tail","function_calls.erl:60"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{get,{"Tail","function_calls.erl:61"}}. +{get,{"Elem","function_calls.erl:61"}}. +{get,{"N","function_calls.erl:61"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:61"}. +{apply,{{operators,'-',2},"function_calls.erl:61"}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:61"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:59"}}. +{pop,{}}. +{store,{"Elem","function_calls.erl:59"}}. +{store,{"Tail","function_calls.erl:59"}}. +{get,{"Elem","function_calls.erl:59"}}. +{get,{"Tail","function_calls.erl:59"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:59"}}. +{func_ret,{insert_at,"function_calls.erl:59"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:61"}}. +{duplicate,{}}. +{store,{"NewTail","function_calls.erl:61"}}. +{pop,{}}. +{get,{"Head","function_calls.erl:62"}}. +{get,{"NewTail","function_calls.erl:62"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:62"}}. +{func_ret,{insert_at,"function_calls.erl:60"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:61"}}. +{duplicate,{}}. +{store,{"NewTail","function_calls.erl:61"}}. +{pop,{}}. +{get,{"Head","function_calls.erl:62"}}. +{get,{"NewTail","function_calls.erl:62"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:62"}}. +{func_ret,{insert_at,"function_calls.erl:60"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:44"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:44"}}. +{duplicate,{}}. +{store,{"TaintedList","function_calls.erl:44"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:45"}}. +{get,{"List","function_calls.erl:45"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:45"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:60"}}. +{store,{"N","function_calls.erl:60"}}. +{store,{"Elem","function_calls.erl:60"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:60"}}. +{store,{"Head","function_calls.erl:60"}}. +{store,{"Tail","function_calls.erl:60"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{get,{"Tail","function_calls.erl:61"}}. +{get,{"Elem","function_calls.erl:61"}}. +{get,{"N","function_calls.erl:61"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:61"}. +{apply,{{operators,'-',2},"function_calls.erl:61"}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:61"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:60"}}. +{store,{"N","function_calls.erl:60"}}. +{store,{"Elem","function_calls.erl:60"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:60"}}. +{store,{"Head","function_calls.erl:60"}}. +{store,{"Tail","function_calls.erl:60"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{get,{"Tail","function_calls.erl:61"}}. +{get,{"Elem","function_calls.erl:61"}}. +{get,{"N","function_calls.erl:61"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:61"}. +{apply,{{operators,'-',2},"function_calls.erl:61"}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:61"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:59"}}. +{pop,{}}. +{store,{"Elem","function_calls.erl:59"}}. +{store,{"Tail","function_calls.erl:59"}}. +{get,{"Elem","function_calls.erl:59"}}. +{get,{"Tail","function_calls.erl:59"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:59"}}. +{func_ret,{insert_at,"function_calls.erl:59"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:61"}}. +{duplicate,{}}. +{store,{"NewTail","function_calls.erl:61"}}. +{pop,{}}. +{get,{"Head","function_calls.erl:62"}}. +{get,{"NewTail","function_calls.erl:62"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:62"}}. +{func_ret,{insert_at,"function_calls.erl:60"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:61"}}. +{duplicate,{}}. +{store,{"NewTail","function_calls.erl:61"}}. +{pop,{}}. +{get,{"Head","function_calls.erl:62"}}. +{get,{"NewTail","function_calls.erl:62"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:62"}}. +{func_ret,{insert_at,"function_calls.erl:60"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:45"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:45"}}. +{duplicate,{}}. +{store,{"NotTaintedList","function_calls.erl:45"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:46"}}. +{get,{"List","function_calls.erl:46"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:46"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:60"}}. +{store,{"N","function_calls.erl:60"}}. +{store,{"Elem","function_calls.erl:60"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:60"}}. +{store,{"Head","function_calls.erl:60"}}. +{store,{"Tail","function_calls.erl:60"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{get,{"Tail","function_calls.erl:61"}}. +{get,{"Elem","function_calls.erl:61"}}. +{get,{"N","function_calls.erl:61"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:61"}. +{apply,{{operators,'-',2},"function_calls.erl:61"}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:61"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:60"}}. +{store,{"N","function_calls.erl:60"}}. +{store,{"Elem","function_calls.erl:60"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:60"}}. +{store,{"Head","function_calls.erl:60"}}. +{store,{"Tail","function_calls.erl:60"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{get,{"Tail","function_calls.erl:61"}}. +{get,{"Elem","function_calls.erl:61"}}. +{get,{"N","function_calls.erl:61"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:61"}. +{apply,{{operators,'-',2},"function_calls.erl:61"}}. +{call_fun,{function_calls,insert_at,3},"function_calls.erl:61"}. +{push_scope,{{function_calls,insert_at,3},"function_calls.erl:59"}}. +{pop,{}}. +{store,{"Elem","function_calls.erl:59"}}. +{store,{"Tail","function_calls.erl:59"}}. +{get,{"Elem","function_calls.erl:59"}}. +{get,{"Tail","function_calls.erl:59"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:59"}}. +{func_ret,{insert_at,"function_calls.erl:59"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:61"}}. +{duplicate,{}}. +{store,{"NewTail","function_calls.erl:61"}}. +{pop,{}}. +{get,{"Head","function_calls.erl:62"}}. +{get,{"NewTail","function_calls.erl:62"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:62"}}. +{func_ret,{insert_at,"function_calls.erl:60"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:61"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:61"}}. +{duplicate,{}}. +{store,{"NewTail","function_calls.erl:61"}}. +{pop,{}}. +{get,{"Head","function_calls.erl:62"}}. +{get,{"NewTail","function_calls.erl:62"}}. +{construct_pattern,{{tuple,2},"function_calls.erl:62"}}. +{func_ret,{insert_at,"function_calls.erl:60"}}. +{apply,{{function_calls,insert_at,3},"function_calls.erl:46"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:46"}}. +{duplicate,{}}. +{store,{"SanitizedList","function_calls.erl:46"}}. +{pop,{}}. +{get,{"TaintedList","function_calls.erl:47"}}. +{sink,{"function_calls.erl:47"}}. +{pop,{}}. +{get,{"NotTaintedList","function_calls.erl:48"}}. +{sink,{"function_calls.erl:48"}}. +{pop,{}}. +{get,{"SanitizedList","function_calls.erl:49"}}. +{sink,{"function_calls.erl:49"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,at,2},"function_calls.erl:50"}}. +{get,{"TaintedList","function_calls.erl:50"}}. +{push,{notaint}}. +{call_fun,{function_calls,at,2},"function_calls.erl:50"}. +{push_scope,{{function_calls,at,2},"function_calls.erl:65"}}. +{store,{"N","function_calls.erl:65"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:65"}}. +{store,{"_Head","function_calls.erl:65"}}. +{store,{"Tail","function_calls.erl:65"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,at,2},"function_calls.erl:65"}}. +{get,{"Tail","function_calls.erl:65"}}. +{get,{"N","function_calls.erl:65"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:65"}. +{apply,{{operators,'-',2},"function_calls.erl:65"}}. +{call_fun,{function_calls,at,2},"function_calls.erl:65"}. +{push_scope,{{function_calls,at,2},"function_calls.erl:65"}}. +{store,{"N","function_calls.erl:65"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:65"}}. +{store,{"_Head","function_calls.erl:65"}}. +{store,{"Tail","function_calls.erl:65"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,at,2},"function_calls.erl:65"}}. +{get,{"Tail","function_calls.erl:65"}}. +{get,{"N","function_calls.erl:65"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:65"}. +{apply,{{operators,'-',2},"function_calls.erl:65"}}. +{call_fun,{function_calls,at,2},"function_calls.erl:65"}. +{push_scope,{{function_calls,at,2},"function_calls.erl:64"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:64"}}. +{store,{"E","function_calls.erl:64"}}. +{store,{"_Tail","function_calls.erl:64"}}. +{get,{"E","function_calls.erl:64"}}. +{func_ret,{at,"function_calls.erl:64"}}. +{apply,{{function_calls,at,2},"function_calls.erl:65"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:65"}}. +{func_ret,{at,"function_calls.erl:65"}}. +{apply,{{function_calls,at,2},"function_calls.erl:65"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:65"}}. +{func_ret,{at,"function_calls.erl:65"}}. +{apply,{{function_calls,at,2},"function_calls.erl:50"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:50"}}. +{duplicate,{}}. +{store,{"TaintedElem","function_calls.erl:50"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{function_calls,at,2},"function_calls.erl:51"}}. +{get,{"SanitizedList","function_calls.erl:51"}}. +{push,{notaint}}. +{call_fun,{function_calls,at,2},"function_calls.erl:51"}. +{push_scope,{{function_calls,at,2},"function_calls.erl:65"}}. +{store,{"N","function_calls.erl:65"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:65"}}. +{store,{"_Head","function_calls.erl:65"}}. +{store,{"Tail","function_calls.erl:65"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,at,2},"function_calls.erl:65"}}. +{get,{"Tail","function_calls.erl:65"}}. +{get,{"N","function_calls.erl:65"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:65"}. +{apply,{{operators,'-',2},"function_calls.erl:65"}}. +{call_fun,{function_calls,at,2},"function_calls.erl:65"}. +{push_scope,{{function_calls,at,2},"function_calls.erl:65"}}. +{store,{"N","function_calls.erl:65"}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:65"}}. +{store,{"_Head","function_calls.erl:65"}}. +{store,{"Tail","function_calls.erl:65"}}. +{push,{notaint}}. +{restore_capture,{{function_calls,at,2},"function_calls.erl:65"}}. +{get,{"Tail","function_calls.erl:65"}}. +{get,{"N","function_calls.erl:65"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"function_calls.erl:65"}. +{apply,{{operators,'-',2},"function_calls.erl:65"}}. +{call_fun,{function_calls,at,2},"function_calls.erl:65"}. +{push_scope,{{function_calls,at,2},"function_calls.erl:64"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"function_calls.erl:64"}}. +{store,{"E","function_calls.erl:64"}}. +{store,{"_Tail","function_calls.erl:64"}}. +{get,{"E","function_calls.erl:64"}}. +{func_ret,{at,"function_calls.erl:64"}}. +{apply,{{function_calls,at,2},"function_calls.erl:65"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:65"}}. +{func_ret,{at,"function_calls.erl:65"}}. +{apply,{{function_calls,at,2},"function_calls.erl:65"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:65"}}. +{func_ret,{at,"function_calls.erl:65"}}. +{apply,{{function_calls,at,2},"function_calls.erl:51"}}. +{func_ret,{dropping_lambda_capture,"function_calls.erl:51"}}. +{duplicate,{}}. +{store,{"NotTaintedElem","function_calls.erl:51"}}. +{pop,{}}. +{get,{"TaintedElem","function_calls.erl:52"}}. +{sink,{"function_calls.erl:52"}}. +{pop,{}}. +{get,{"NotTaintedElem","function_calls.erl:53"}}. +{sink,{"function_calls.erl:53"}}. +{func_ret,{recursion_main,"function_calls.erl:41"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/safe_shortcircuit_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/safe_shortcircuit_analysis_instr new file mode 100644 index 0000000..6cf0d17 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/safe_shortcircuit_analysis_instr @@ -0,0 +1,20 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{shortcircuiting,safe_shortcircuit,0},"shortcircuiting.erl:18"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"shortcircuiting.erl:19"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Source","shortcircuiting.erl:19"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{operators,'andalso',2},"shortcircuiting.erl:22"}. +{apply,{{operators,'andalso',2},"shortcircuiting.erl:22"}}. +{func_ret,{safe_shortcircuit,"shortcircuiting.erl:18"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/set_element_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/set_element_main_analysis_instr new file mode 100644 index 0000000..1b2fbb3 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/set_element_main_analysis_instr @@ -0,0 +1,53 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{pattern_to_var,set_element_main,0},"pattern_to_var.erl:48"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:49"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","pattern_to_var.erl:49"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:50"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"ANumber","pattern_to_var.erl:50"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"ANumber","pattern_to_var.erl:51"}}. +{construct_pattern,{{tuple,3},"pattern_to_var.erl:51"}}. +{duplicate,{}}. +{store,{"Tuple","pattern_to_var.erl:51"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"PhoneNumber","pattern_to_var.erl:52"}}. +{get,{"Tuple","pattern_to_var.erl:52"}}. +{push,{notaint}}. +{call_fun,{finer_taint,set_element,3},"pattern_to_var.erl:52"}. +{set_element,{2,3,"pattern_to_var.erl:52"}}. +{apply,{{finer_taint,set_element,3},"pattern_to_var.erl:52"}}. +{duplicate,{}}. +{store,{"Tuple1","pattern_to_var.erl:52"}}. +{pop,{}}. +{get,{"Tuple1","pattern_to_var.erl:53"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,3},"pattern_to_var.erl:53"}}. +{store,{"NotTainted","pattern_to_var.erl:53"}}. +{store,{"Tainted","pattern_to_var.erl:53"}}. +{store,{"Tainted2","pattern_to_var.erl:53"}}. +{pop,{}}. +{get,{"NotTainted","pattern_to_var.erl:54"}}. +{sink,{"pattern_to_var.erl:54"}}. +{pop,{}}. +{get,{"Tainted","pattern_to_var.erl:55"}}. +{sink,{"pattern_to_var.erl:55"}}. +{pop,{}}. +{get,{"Tainted2","pattern_to_var.erl:56"}}. +{sink,{"pattern_to_var.erl:56"}}. +{func_ret,{set_element_main,"pattern_to_var.erl:48"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/shortcircuiting.erl b/finer_taint/test/finer_taint_SUITE_data/shortcircuiting.erl new file mode 100644 index 0000000..c35388f --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/shortcircuiting.erl @@ -0,0 +1,37 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('shortcircuiting'). +-export([safe_shortcircuit/0, unsafe_shortcircuit/0]). + +safe_shortcircuit() -> + Source = finer_taint:source("user-controlled"), + true orelse finer_taint:sink(Source), + false andalso finer_taint:sink(Source), + true andalso ( true orelse finer_taint:sink(Source)). + +unsafe_shortcircuit() -> + Source = finer_taint:source("user-controlled"), + true andalso finer_taint:sink(Source), + Val = false orelse flag_set, + case Val of + flag_set -> finer_taint:sink(Source); + _ -> ok + end, + true andalso (false orelse foo(Source)). + +foo(Source) -> + X = "str" ++ Source, + finer_taint:sink(Source), + true. diff --git a/finer_taint/test/finer_taint_SUITE_data/simple_example.erl b/finer_taint/test/finer_taint_SUITE_data/simple_example.erl new file mode 100644 index 0000000..fa3b8fc --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/simple_example.erl @@ -0,0 +1,56 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('simple_example'). +-export([ + one_clause/0, if_clause/0, lineage_main/0, macro_duplicate_main/0 +]). + +one_clause() -> + PhoneNumber = finer_taint:source("phone_num","123@secret.net"), + AString = "123 os a good start of a string", + X = string:slice(PhoneNumber, 0, 3), + Y = string:slice(AString, 0, 3), + finer_taint:sink(Y), + finer_taint:sink(X). + +if_clause() -> + PhoneNumber = finer_taint:source("123@secret.net"), + Result = if PhoneNumber =:= "A" -> ok; + true -> PhoneNumber + end, + finer_taint:sink(Result). + + +pack(Arg) -> + {Arg, 1}. + +unpack({Arg, 1}) -> + Arg. + +lineage_entry(Arg1, Arg2) -> + PackedArg1 = pack(Arg1), + pack(unpack(PackedArg1) + Arg2). + +lineage_main() -> + lineage_entry(3,4). + + +-define(EXPR_DUPLICATOR(A), begin A,A end). + +macro_duplicate_main() -> + Tainted = finer_taint:source(2), + X = ?EXPR_DUPLICATOR(if is_atom(Tainted) -> 3; + true -> Tainted + 1 end), + finer_taint:sink(X). diff --git a/finer_taint/test/finer_taint_SUITE_data/string_plusplus_pattern_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/string_plusplus_pattern_analysis_instr new file mode 100644 index 0000000..772fd7d --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/string_plusplus_pattern_analysis_instr @@ -0,0 +1,18 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{pattern_to_var,string_plusplus_pattern,0},"pattern_to_var.erl:42"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"pattern_to_var.erl:43"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"PhoneNumber","pattern_to_var.erl:43"}}. +{pop,{}}. +{get,{"PhoneNumber","pattern_to_var.erl:44"}}. +{duplicate,{}}. +{store,{"RestOfNumber","pattern_to_var.erl:44"}}. +{pop,{}}. +{get,{"RestOfNumber","pattern_to_var.erl:45"}}. +{sink,{"pattern_to_var.erl:45"}}. +{func_ret,{string_plusplus_pattern,"pattern_to_var.erl:42"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/try_after_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/try_after_main_analysis_instr new file mode 100644 index 0000000..8456b6f --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/try_after_main_analysis_instr @@ -0,0 +1,18 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{try_catch,try_after_main,0},"try_catch.erl:116"}}. +{try_catch,{try_enter,{try_catch,117,82112316}},"try_catch.erl:117"}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:118"}}. +{push,{notaint}}. +{pop,{}}. +{try_catch,{try_exit,{try_catch,117,82112316}},"try_catch.erl:117"}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Tainted","try_catch.erl:117"}}. +{pop,{}}. +{get,{"Tainted","try_catch.erl:122"}}. +{sink,{"try_catch.erl:122"}}. +{func_ret,{try_after_main,"try_catch.erl:116"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/try_catch.erl b/finer_taint/test/finer_taint_SUITE_data/try_catch.erl new file mode 100644 index 0000000..772ba91 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/try_catch.erl @@ -0,0 +1,122 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('try_catch'). +-export([ + try_main/0, try_after_main/0, + try_catch_nested_main/0, + try_catch_define_main/0, + try_catch_crs_main/0, + try_catch_main2/0 +]). +-include_lib("stdlib/include/ms_transform.hrl"). + + +throwing_function(X) when X =:= 0 -> + throw({foo, X}); +throwing_function(1) -> + 1 / finer_taint:source(0); +throwing_function(X) when X > 0 andalso X rem 2 =:= 1 -> + {odd, X + 1}; +throwing_function(_) -> + {even, 2}. + + +try_main() -> + TaintedOdd = finer_taint:source(5), + NotTainted = try throwing_function(TaintedOdd + 1) of + {even, X} -> X + catch + doesntmatch -> ok + end, + finer_taint:sink(NotTainted), + Tainted = try throwing_function(TaintedOdd) of + {odd, Y} -> Y + catch + doesntmatch -> ok + end, + finer_taint:sink(Tainted), + finer_taint:sink(try throwing_function(1) catch + error:Error -> Error + end), + finer_taint:sink(try throwing_function(TaintedOdd - 5) of + doesntmatch -> ok + catch + {foo, Z} -> Z + end). + +helper_function() -> + TaintedVal = finer_taint:source(42), + Val = try ok of + ok -> TaintedVal + catch + _ -> ok + end. + +try_catch_main2() -> + TaintedValue = helper_function(), + finer_taint:sink(TaintedValue). + + +inner_catch() -> + TaintedVal = finer_taint:source(0), + try throwing_function(TaintedVal) + catch + not_the_throw_value -> neverhappens + end. + +try_catch_nested_main() -> + try inner_catch() + catch + {foo, X} -> finer_taint:sink(X) + end, + Caught = catch inner_catch(), + finer_taint:sink(Caught). + + +-define(TRY_CATCH(Body, Catch), try Body catch Catch -> Catch end). + +try_catch_define_main() -> + TaintedVal = finer_taint:source(0), + try ?TRY_CATCH(?TRY_CATCH(throwing_function(TaintedVal), ok), ok) + catch + {foo, X} -> finer_taint:sink(X) + end. + + +try_catch_crs_main() -> + Tainted = finer_taint:source(0), + try + throwing_function(Tainted) + catch Class:Reason:Stacktrace -> + finer_taint:sink(Class), + finer_taint:sink(Reason), + finer_taint:sink(Stacktrace) + end, + + Tainted1 = try ets:next(nonon_existent, ok) + catch + error:_ -> finer_taint:source(42); + % Needs to be transformed with ms_transform, otherwise it's invalid AST + never_matches -> ets:fun2ms(fun({_, #{ts => Ts}} ) when 1 > Ts -> ok end) + end, + finer_taint:sink(Tainted1). + +try_after_main() -> + Tainted = try + finer_taint:source(0) + after + ok + end, + finer_taint:sink(Tainted). diff --git a/finer_taint/test/finer_taint_SUITE_data/try_catch_crs_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/try_catch_crs_main_analysis_instr new file mode 100644 index 0000000..19005d8 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/try_catch_crs_main_analysis_instr @@ -0,0 +1,56 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{try_catch,try_catch_crs_main,0},"try_catch.erl:98"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:99"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Tainted","try_catch.erl:99"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,100,111021592}},"try_catch.erl:100"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:101"}}. +{get,{"Tainted","try_catch.erl:101"}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:101"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:26"}}. +{store,{"X","try_catch.erl:26"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"X","try_catch.erl:27"}}. +{construct_pattern,{{tuple,2},"try_catch.erl:27"}}. +{call_fun,{erlang,throw,1},"try_catch.erl:27"}. +{try_catch,{catch_enter,{try_catch,100,111021592}},"try_catch.erl:102"}. +{push,{notaint}}. +{store,{"Stacktrace","try_catch.erl:102"}}. +{push,{notaint}}. +{store,{"Class","try_catch.erl:102"}}. +{store,{"Reason","try_catch.erl:102"}}. +{get,{"Class","try_catch.erl:103"}}. +{sink,{"try_catch.erl:103"}}. +{pop,{}}. +{get,{"Reason","try_catch.erl:104"}}. +{sink,{"try_catch.erl:104"}}. +{pop,{}}. +{get,{"Stacktrace","try_catch.erl:105"}}. +{sink,{"try_catch.erl:105"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,108,18268652}},"try_catch.erl:108"}. +{push,{notaint}}. +{try_catch,{catch_enter,{try_catch,108,18268652}},"try_catch.erl:110"}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:110"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Tainted1","try_catch.erl:108"}}. +{pop,{}}. +{get,{"Tainted1","try_catch.erl:114"}}. +{sink,{"try_catch.erl:114"}}. +{func_ret,{try_catch_crs_main,"try_catch.erl:98"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/try_catch_define_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/try_catch_define_main_analysis_instr new file mode 100644 index 0000000..f669232 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/try_catch_define_main_analysis_instr @@ -0,0 +1,33 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{try_catch,try_catch_define_main,0},"try_catch.erl:90"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:91"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","try_catch.erl:91"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,92,22804518}},"try_catch.erl:92"}. +{try_catch,{try_enter,{try_catch,92,25075205}},"try_catch.erl:92"}. +{try_catch,{try_enter,{try_catch,92,27776476}},"try_catch.erl:92"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:92"}}. +{get,{"TaintedVal","try_catch.erl:92"}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:92"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:26"}}. +{store,{"X","try_catch.erl:26"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"X","try_catch.erl:27"}}. +{construct_pattern,{{tuple,2},"try_catch.erl:27"}}. +{call_fun,{erlang,throw,1},"try_catch.erl:27"}. +{try_catch,{catch_enter,{try_catch,92,22804518}},"try_catch.erl:94"}. +{deconstruct_pattern,{{tuple,2},"try_catch.erl:94"}}. +{pop,{}}. +{store,{"X","try_catch.erl:94"}}. +{get,{"X","try_catch.erl:94"}}. +{sink,{"try_catch.erl:94"}}. +{func_ret,{try_catch_define_main,"try_catch.erl:90"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/try_catch_main2_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/try_catch_main2_analysis_instr new file mode 100644 index 0000000..e10bd3f --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/try_catch_main2_analysis_instr @@ -0,0 +1,31 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{try_catch,try_catch_main2,0},"try_catch.erl:67"}}. +{push,{notaint}}. +{restore_capture,{{try_catch,helper_function,0},"try_catch.erl:68"}}. +{call_fun,{try_catch,helper_function,0},"try_catch.erl:68"}. +{push_scope,{{try_catch,helper_function,0},"try_catch.erl:59"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:60"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","try_catch.erl:60"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,61,8318716}},"try_catch.erl:61"}. +{push,{notaint}}. +{try_catch,{try_exit,{try_catch,61,8318716}},"try_catch.erl:61"}. +{pop,{}}. +{get,{"TaintedVal","try_catch.erl:62"}}. +{duplicate,{}}. +{store,{"Val","try_catch.erl:61"}}. +{func_ret,{helper_function,"try_catch.erl:59"}}. +{apply,{{try_catch,helper_function,0},"try_catch.erl:68"}}. +{func_ret,{dropping_lambda_capture,"try_catch.erl:68"}}. +{duplicate,{}}. +{store,{"TaintedValue","try_catch.erl:68"}}. +{pop,{}}. +{get,{"TaintedValue","try_catch.erl:69"}}. +{sink,{"try_catch.erl:69"}}. +{func_ret,{try_catch_main2,"try_catch.erl:67"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/try_catch_nested_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/try_catch_nested_main_analysis_instr new file mode 100644 index 0000000..0d5c995 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/try_catch_nested_main_analysis_instr @@ -0,0 +1,67 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{try_catch,try_catch_nested_main,0},"try_catch.erl:79"}}. +{try_catch,{try_enter,{try_catch,80,45234854}},"try_catch.erl:80"}. +{push,{notaint}}. +{restore_capture,{{try_catch,inner_catch,0},"try_catch.erl:80"}}. +{call_fun,{try_catch,inner_catch,0},"try_catch.erl:80"}. +{push_scope,{{try_catch,inner_catch,0},"try_catch.erl:72"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:73"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","try_catch.erl:73"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,74,93941339}},"try_catch.erl:74"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:74"}}. +{get,{"TaintedVal","try_catch.erl:74"}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:74"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:26"}}. +{store,{"X","try_catch.erl:26"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"X","try_catch.erl:27"}}. +{construct_pattern,{{tuple,2},"try_catch.erl:27"}}. +{call_fun,{erlang,throw,1},"try_catch.erl:27"}. +{try_catch,{catch_enter,{try_catch,80,45234854}},"try_catch.erl:82"}. +{deconstruct_pattern,{{tuple,2},"try_catch.erl:82"}}. +{pop,{}}. +{store,{"X","try_catch.erl:82"}}. +{get,{"X","try_catch.erl:82"}}. +{sink,{"try_catch.erl:82"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{try_catch,inner_catch,0},"try_catch.erl:84"}}. +{call_fun,{try_catch,inner_catch,0},"try_catch.erl:84"}. +{push_scope,{{try_catch,inner_catch,0},"try_catch.erl:72"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:73"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","try_catch.erl:73"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,74,93941339}},"try_catch.erl:74"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:74"}}. +{get,{"TaintedVal","try_catch.erl:74"}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:74"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:26"}}. +{store,{"X","try_catch.erl:26"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"X","try_catch.erl:27"}}. +{construct_pattern,{{tuple,2},"try_catch.erl:27"}}. +{call_fun,{erlang,throw,1},"try_catch.erl:27"}. +{duplicate,{}}. +{store,{"Caught","try_catch.erl:84"}}. +{pop,{}}. +{get,{"Caught","try_catch.erl:85"}}. +{sink,{"try_catch.erl:85"}}. +{func_ret,{try_catch_nested_main,"try_catch.erl:79"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/try_main_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/try_main_analysis_instr new file mode 100644 index 0000000..6d34999 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/try_main_analysis_instr @@ -0,0 +1,109 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{try_catch,try_main,0},"try_catch.erl:36"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:37"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedOdd","try_catch.erl:37"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,38,81539963}},"try_catch.erl:38"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:38"}}. +{get,{"TaintedOdd","try_catch.erl:38"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"try_catch.erl:38"}. +{apply,{{operators,'+',2},"try_catch.erl:38"}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:38"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:32"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"try_catch.erl:33"}}. +{func_ret,{throwing_function,"try_catch.erl:32"}}. +{apply,{{try_catch,throwing_function,1},"try_catch.erl:38"}}. +{func_ret,{dropping_lambda_capture,"try_catch.erl:38"}}. +{try_catch,{try_exit,{try_catch,38,81539963}},"try_catch.erl:38"}. +{deconstruct_pattern,{{tuple,2},"try_catch.erl:39"}}. +{pop,{}}. +{store,{"X","try_catch.erl:39"}}. +{get,{"X","try_catch.erl:39"}}. +{duplicate,{}}. +{store,{"NotTainted","try_catch.erl:38"}}. +{pop,{}}. +{get,{"NotTainted","try_catch.erl:43"}}. +{sink,{"try_catch.erl:43"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,44,39805531}},"try_catch.erl:44"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:44"}}. +{get,{"TaintedOdd","try_catch.erl:44"}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:44"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:30"}}. +{store,{"X","try_catch.erl:30"}}. +{push,{notaint}}. +{get,{"X","try_catch.erl:31"}}. +{push,{notaint}}. +{call_fun,{operators,'+',2},"try_catch.erl:31"}. +{apply,{{operators,'+',2},"try_catch.erl:31"}}. +{construct_pattern,{{tuple,2},"try_catch.erl:31"}}. +{func_ret,{throwing_function,"try_catch.erl:30"}}. +{apply,{{try_catch,throwing_function,1},"try_catch.erl:44"}}. +{func_ret,{dropping_lambda_capture,"try_catch.erl:44"}}. +{try_catch,{try_exit,{try_catch,44,39805531}},"try_catch.erl:44"}. +{deconstruct_pattern,{{tuple,2},"try_catch.erl:45"}}. +{pop,{}}. +{store,{"Y","try_catch.erl:45"}}. +{get,{"Y","try_catch.erl:45"}}. +{duplicate,{}}. +{store,{"Tainted","try_catch.erl:44"}}. +{pop,{}}. +{get,{"Tainted","try_catch.erl:49"}}. +{sink,{"try_catch.erl:49"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,50,57094289}},"try_catch.erl:50"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:50"}}. +{push,{notaint}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:50"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:28"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{push,{"try_catch.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{operators,'/',2},"try_catch.erl:29"}. +{try_catch,{catch_enter,{try_catch,50,57094289}},"try_catch.erl:51"}. +{pop,{}}. +{push,{notaint}}. +{store,{"Error","try_catch.erl:51"}}. +{get,{"Error","try_catch.erl:51"}}. +{sink,{"try_catch.erl:50"}}. +{pop,{}}. +{try_catch,{try_enter,{try_catch,53,29015587}},"try_catch.erl:53"}. +{push,{notaint}}. +{restore_capture,{{try_catch,throwing_function,1},"try_catch.erl:53"}}. +{get,{"TaintedOdd","try_catch.erl:53"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"try_catch.erl:53"}. +{apply,{{operators,'-',2},"try_catch.erl:53"}}. +{call_fun,{try_catch,throwing_function,1},"try_catch.erl:53"}. +{push_scope,{{try_catch,throwing_function,1},"try_catch.erl:26"}}. +{store,{"X","try_catch.erl:26"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"X","try_catch.erl:27"}}. +{construct_pattern,{{tuple,2},"try_catch.erl:27"}}. +{call_fun,{erlang,throw,1},"try_catch.erl:27"}. +{try_catch,{catch_enter,{try_catch,53,29015587}},"try_catch.erl:56"}. +{deconstruct_pattern,{{tuple,2},"try_catch.erl:56"}}. +{pop,{}}. +{store,{"Z","try_catch.erl:56"}}. +{get,{"Z","try_catch.erl:56"}}. +{sink,{"try_catch.erl:53"}}. +{func_ret,{try_main,"try_catch.erl:36"}}. diff --git a/finer_taint/test/finer_taint_SUITE_data/unsafe_shortcircuit_analysis_instr b/finer_taint/test/finer_taint_SUITE_data/unsafe_shortcircuit_analysis_instr new file mode 100644 index 0000000..8874597 --- /dev/null +++ b/finer_taint/test/finer_taint_SUITE_data/unsafe_shortcircuit_analysis_instr @@ -0,0 +1,56 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{shortcircuiting,unsafe_shortcircuit,0},"shortcircuiting.erl:24"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"shortcircuiting.erl:25"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Source","shortcircuiting.erl:25"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Source","shortcircuiting.erl:26"}}. +{sink,{"shortcircuiting.erl:26"}}. +{call_fun,{operators,'andalso',2},"shortcircuiting.erl:26"}. +{apply,{{operators,'andalso',2},"shortcircuiting.erl:26"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{operators,'orelse',2},"shortcircuiting.erl:27"}. +{apply,{{operators,'orelse',2},"shortcircuiting.erl:27"}}. +{duplicate,{}}. +{store,{"Val","shortcircuiting.erl:27"}}. +{pop,{}}. +{get,{"Val","shortcircuiting.erl:28"}}. +{pop,{}}. +{get,{"Source","shortcircuiting.erl:29"}}. +{sink,{"shortcircuiting.erl:29"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{operators,'andalso',2},"shortcircuiting.erl:32"}. +{push,{notaint}}. +{restore_capture,{{shortcircuiting,foo,1},"shortcircuiting.erl:32"}}. +{get,{"Source","shortcircuiting.erl:32"}}. +{call_fun,{shortcircuiting,foo,1},"shortcircuiting.erl:32"}. +{push_scope,{{shortcircuiting,foo,1},"shortcircuiting.erl:34"}}. +{store,{"Source","shortcircuiting.erl:34"}}. +{push,{notaint}}. +{get,{"Source","shortcircuiting.erl:35"}}. +{call_fun,{operators,'++',2},"shortcircuiting.erl:35"}. +{apply,{{operators,'++',2},"shortcircuiting.erl:35"}}. +{duplicate,{}}. +{store,{"X","shortcircuiting.erl:35"}}. +{pop,{}}. +{get,{"Source","shortcircuiting.erl:36"}}. +{sink,{"shortcircuiting.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{foo,"shortcircuiting.erl:34"}}. +{apply,{{shortcircuiting,foo,1},"shortcircuiting.erl:32"}}. +{func_ret,{dropping_lambda_capture,"shortcircuiting.erl:32"}}. +{call_fun,{operators,'orelse',2},"shortcircuiting.erl:32"}. +{apply,{{operators,'orelse',2},"shortcircuiting.erl:32"}}. +{apply,{{operators,'andalso',2},"shortcircuiting.erl:32"}}. +{func_ret,{unsafe_shortcircuit,"shortcircuiting.erl:24"}}. diff --git a/finer_taint/test/parallel_taint_SUITE.erl b/finer_taint/test/parallel_taint_SUITE.erl new file mode 100644 index 0000000..7a9e7b4 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE.erl @@ -0,0 +1,362 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%%------------------------------------------------------------------- +%%% @doc +%%% Tests for finer taint analysis focusing on multi processing abilities +%%% +%%% The tests are similar to finer_taint_SUITE, except that they use +%%% parallel_finer_taint instead pf ct_finer_taint +%%% +%%% @end +%%% ------------------------------------------------------------------- +-module(parallel_taint_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("stdlib/include/assert.hrl"). + +%% Test server callbacks +-export([ + suite/0, + all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + init_per_testcase/2, + end_per_testcase/2 +]). + +%% Test cases +-export([ + two_pids/1, + spawn_taint_transfer/1, + spawn_taint_transfer_via_capture/1, + test_gen_server/1, + test_gen_server_init/1, + scatter_gather/1, + edge_annotations_msg/1, + hibernate/1, + not_instrumented_send/1 +]). + +suite() -> + [{appatic, #{enable_autoclean => true}}]. + +groups() -> + [ + {basic, [], [ + two_pids, + spawn_taint_transfer, + spawn_taint_transfer_via_capture, + test_gen_server, + test_gen_server_init, + scatter_gather, + edge_annotations_msg, + hibernate, + not_instrumented_send + ]} + ]. + +all() -> + [{group, basic}]. + +init_per_suite(Config) -> + ok = logger:add_handler_filter(default, no_progress, {fun logger_filters:progress/2, stop}), + {module, modeled_erlang} = finer_taint_compiler:instrument_loaded_module(modeled_erlang, [ + {finer_taint_module, parallel_finer_taint} + ]), + {module, modeled_gen_server} = finer_taint_compiler:instrument_loaded_module(gen_server, [ + {finer_taint_module, parallel_finer_taint} + ]), + {module, modeled_gen} = finer_taint_compiler:instrument_loaded_module(gen, [ + {finer_taint_module, parallel_finer_taint} + ]), + {module, modeled_lists} = finer_taint_compiler:instrument_loaded_module(lists, [ + {finer_taint_module, parallel_finer_taint} + ]), + {module, modeled_proc_lib} = finer_taint_compiler:instrument_loaded_module(proc_lib, [ + {finer_taint_module, parallel_finer_taint} + ]), + Config. +end_per_suite(_Config) -> + ok. + +init_per_testcase(TestCase, Config) -> + DataDir = ?config(priv_dir, Config), + FileName = filename:join(DataDir, atom_to_list(TestCase) ++ "_analaysis_instr"), + application:set_env(taint_server, instructions_stream_prefix, FileName), + {ok, StartedApps} = application:ensure_all_started(taint_server), + [{statrted_apps, StartedApps}, {analysis_instr, FileName} | Config]. + +end_per_testcase(_TestCase, Config) -> + ToStopApps = proplists:get_value(statrted_apps, Config), + lists:foreach(fun application:stop/1, ToStopApps), + ok. + +compile(Modules, Config) -> + DataDir = ?config(data_dir, Config), + CompileMod = fun(Mod) -> + ModFilename = unicode:characters_to_list(io_lib:format("~p.erl", [Mod])), + ModPath = filename:join([DataDir, ModFilename]), + {ok, Mod, Binary} = compile:file(ModPath, [debug_info, binary]), + {ok, {Mod, [{abstract_code, {_, Forms}} | _]}} = beam_lib:chunks(Binary, [abstract_code, compile_info]), + io:format("~p~n", [Forms]), + {Mod, Forms} + end, + lists:map(CompileMod, Modules) ++ Config. + +replace_undeterministic(Binary) -> + Opts = [{return, binary}, global], + Binary0 = re:replace( + Binary, "{receive_trace,{\"#Ref<[\.0-9]+>\",(\"[^\"]+\")}}", <<"{receive_trace,{==msg_id==,\\1}}">>, Opts + ), + Binary1 = re:replace(Binary0, "{send,{\"#Ref<[\.0-9]+>\",(\"[^\"]+\")}}", <<"{send,{==msg_id==,\\1}}">>, Opts), + Binary2 = re:replace( + Binary1, "\"(gen_server|gen|proc_lib|lists|map).erl:[0-9]+\"", <<"\"\\1.erl:==line==\"">>, Opts + ), + Binary3 = re:replace( + Binary2, "{(store|get),{\"[^\"]+\",", <<"{\\1,{\"==VarName==\",">>, Opts + ), + Binary3. + +compile_and_run_function(Config, Module, Func) -> + [{_, Forms} | _] = compile([Module], Config), + io:format("~p Forms: ~p~n", [Module, Forms]), + InstrumentedForms = finer_taint_compiler:instrument_with_sinks(Forms, [{finer_taint_module, parallel_finer_taint}]), + io:format("~p: ~n~s~n", [Module, erl_prettypr:format(erl_syntax:form_list(InstrumentedForms))]), + {ok, Module, Binary} = compile:forms(InstrumentedForms), + {module, Module} = code:load_binary(Module, "/fake/path/file.erl", Binary), + put(taint_pid, erlang:crc32(io_lib:format("~p~p", [Module, Func]))), + Module:Func(), + application:stop(taint_server), + FilenamePrefix = proplists:get_value(analysis_instr, Config), + AnalysisInstrFiles = filelib:wildcard(FilenamePrefix ++ "-*"), + DataDir = ?config(data_dir, Config), + Verbosity = ct:get_verbosity(default), + {Status, RepoRoot} = file:read_file("/tmp/erlang_taint_root"), + GeneratedHeader = <<"{push,{\"@ge", "nerated\"}}.\n{pop,{}}.\n">>, + HeaderSize = byte_size(GeneratedHeader), + case Module of + % In the scatter_gather case we do not have fixtures because it + % spawns more processes with the spawned process we have no way of + % controling the taint pids of the spawning process and its not worth + % the effort to implement that just for testing + scatter_gather -> + ok; + _ -> + lists:foreach( + fun(AnalysisInstrFile) -> + Basename = filename:basename(AnalysisInstrFile), + io:format("Comparing file ~s ~p~n", [Basename, Verbosity]), + FixtureFilename = filename:join(DataDir, Basename), + {ok, Actual} = file:read_file(AnalysisInstrFile), + Fixture = file:read_file(FixtureFilename), + %% Run with: echo -n `pwd` > /tmp/erlang_taint_root ; buck2 test -c 'erlang.erlang_test_ct_opts=[{verbosity,101}]' + %% to update the local fixture + if + Verbosity =:= 101 andalso Status =:= ok -> + ok = file:write_file( + filename:join([RepoRoot, "finer_taint/test/parallel_taint_SUITE_data", Basename]), + <> + ); + true -> + ok + end, + case {Module, Verbosity =/= 101} of + % In the case of gen_server the fixtures rely on specific OTP version + % and don't work across OTP versions. So we disable the gen_server tests + % unless vebosity is set for local dev. + {example_gen_server, true} -> + ok; + _ -> + {ok, <>} = Fixture, + finer_taint_SUITE:assert_instruction_stream_equal( + replace_undeterministic(ExpectedInstr), replace_undeterministic(Actual) + ) + end + end, + AnalysisInstrFiles + ) + end, + + application:start(taint_server), + % Open Source buck2 test can't handle the amount of stdout tracing spews out + % parallel_abstract_machine:run_tracing(AnalysisInstrFiles). + parallel_abstract_machine:run(AnalysisInstrFiles). + +%%-------------------------------------------------------------------- +%% TEST CASES + +two_pids(Config) -> + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = compile_and_run_function( + Config, two_pids, two_pids_main + ), + ?assertEqual( + [ + {step, "two_pids.erl:36"}, + {step, "two_pids.erl:34"}, + {step, "two_pids.erl:32"}, + {step, "two_pids.erl:28"}, + {message_pass, "two_pids.erl:43"}, + {step, "two_pids.erl:43"}, + {step, "two_pids.erl:43"}, + {source, "two_pids.erl:40"} + ], + FirstHistory + ), + ?assertEqual("two_pids.erl:51", SecondSink), + ?assertEqual(["two_pids.erl:40"], abstract_machine_util:get_sources(SecondHistory)), + ?assertEqual("two_pids.erl:36", FirstSink), + ?assertEqual(["two_pids.erl:40"], abstract_machine_util:get_sources(FirstHistory)). + +edge_annotations_msg(Config) -> + [{leak, _FirstSink, FirstHistory}] = compile_and_run_function( + Config, edge_annot, edge_annotations_main + ), + ?assertEqual( + [ + {step, "edge_annot.erl:26"}, + {step, "edge_annot.erl:25"}, + {call_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:41"}, + {step, "edge_annot.erl:41"}, + {step, "edge_annot.erl:41"}, + {return_site, {edge_annot, to_tuple, 1}, "edge_annot.erl:40"}, + {return_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:21"}, + {step, "edge_annot.erl:28"}, + {step, "edge_annot.erl:27"}, + {call_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:21"}, + {step, "edge_annot.erl:21"}, + {step, "edge_annot.erl:21"}, + {call_site, {edge_annot, to_tuple, 1}, "edge_annot.erl:40"}, + {step, "edge_annot.erl:40"}, + {step, "edge_annot.erl:39"}, + {step, "edge_annot.erl:39"}, + {message_pass, "edge_annot.erl:24"}, + {step, "edge_annot.erl:24"}, + {step, "edge_annot.erl:23"}, + {call_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:37"}, + {step, "edge_annot.erl:37"}, + {step, "edge_annot.erl:37"}, + {return_site, {edge_annot, to_tuple, 1}, "edge_annot.erl:36"}, + {return_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:21"}, + {step, "edge_annot.erl:28"}, + {step, "edge_annot.erl:27"}, + {call_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:21"}, + {step, "edge_annot.erl:21"}, + {step, "edge_annot.erl:21"}, + {call_site, {edge_annot, to_tuple, 1}, "edge_annot.erl:36"}, + {step, "edge_annot.erl:36"}, + {step, "edge_annot.erl:35"}, + {step, "edge_annot.erl:35"}, + {message_pass, "edge_annot.erl:24"}, + {step, "edge_annot.erl:24"}, + {step, "edge_annot.erl:23"}, + {call_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:33"}, + {step, "edge_annot.erl:33"}, + {step, "edge_annot.erl:33"}, + {return_site, {edge_annot, to_tuple, 1}, "edge_annot.erl:32"}, + {return_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:21"}, + {step, "edge_annot.erl:28"}, + {step, "edge_annot.erl:27"}, + {call_site, {edge_annot, from_tuple, 1}, "edge_annot.erl:21"}, + {step, "edge_annot.erl:21"}, + {step, "edge_annot.erl:21"}, + {call_site, {edge_annot, to_tuple, 1}, "edge_annot.erl:32"}, + {step, "edge_annot.erl:32"}, + {source, "edge_annot.erl:31"} + ], + FirstHistory + ). + +scatter_gather(Config) -> + [Leak2, Leak1] = compile_and_run_function(Config, scatter_gather, scatter_gather_main), + {leak, Sink, History} = Leak1, + ?assertEqual("scatter_gather.erl:47", Sink), + ?assertEqual(["scatter_gather.erl:25"], abstract_machine_util:get_sources(History)), + {leak, SecondSink, SecondHistory} = Leak2, + ?assertEqual("scatter_gather.erl:49", SecondSink), + ?assertEqual(["scatter_gather.erl:33"], abstract_machine_util:get_sources(SecondHistory)). + +spawn_taint_transfer(Config) -> + [Leak1, Leak2] = compile_and_run_function(Config, taint_spawn, spawn_info_transfer_main), + {leak, Sink, History} = Leak1, + ?assertEqual("taint_spawn.erl:28", Sink), + ?assertEqual(["taint_spawn.erl:37"], abstract_machine_util:get_sources(History)), + {leak, SecondSink, SecondHistory} = Leak2, + ?assertEqual("taint_spawn.erl:23", SecondSink), + ?assertEqual(["taint_spawn.erl:37"], abstract_machine_util:get_sources(SecondHistory)). + +spawn_taint_transfer_via_capture(Config) -> + [Leak1] = compile_and_run_function(Config, taint_spawn, spawn_info_transfer_via_capture_main), + {leak, Sink, History} = Leak1, + ?assertEqual("taint_spawn.erl:54", Sink), + ?assertEqual(["taint_spawn.erl:50"], abstract_machine_util:get_sources(History)). + +test_gen_server_init(Config) -> + [{leak, Sink, History}] = compile_and_run_function(Config, example_gen_server, gen_server_init_test_main), + ?assertEqual("example_gen_server.erl:76", Sink), + ?assertEqual(["example_gen_server.erl:49"], abstract_machine_util:get_sources(History)). + +test_gen_server(Config) -> + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = compile_and_run_function( + Config, example_gen_server, gen_server_test_main + ), + ?assertEqual("example_gen_server.erl:41", FirstSink), + ?assertEqual(["example_gen_server.erl:40"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("example_gen_server.erl:44", SecondSink), + ?assertEqual(["example_gen_server.erl:40"], abstract_machine_util:get_sources(SecondHistory)), + + % Lineage tests + FilenamePrefix = proplists:get_value(analysis_instr, Config), + AnalysisInstrFiles = filelib:wildcard(FilenamePrefix ++ "-*"), + % Reset the message passing table + application:stop(taint_server), + application:start(taint_server), + ReducedLeaks = parallel_abstract_machine:run_lineage(AnalysisInstrFiles), + ReducedLineage = abstract_machine_util:get_arg_lineage(ReducedLeaks, human_readable), + application:stop(taint_server), + application:start(taint_server), + FullLeaks = parallel_abstract_machine:run_lineage_with_line_history(AnalysisInstrFiles), + FullLineage = abstract_machine_util:get_arg_lineage(FullLeaks, human_readable), + ?assertEqual(FullLineage, ReducedLineage), + Fixture = filename:join(?config(data_dir, Config), "lineage_example_gen_server"), + {ok, <<"@gen", "erated\n", _FixtureLineage/binary>>} = file:read_file(Fixture), + %Note: this assert doesn't work accross OTP versions + %For now let's just check there is some lineage + %?assertEqual(binary_to_list(FixtureLineage), Lineage), + ?assert(length(ReducedLineage) > 100), + application:stop(taint_server), + + % Escript test + EscriptOutputFile = filename:join(?config(data_dir, Config), "escript_output_file.lineage"), + run_finer_taint_escript:main( + ["run-lineage"] ++ AnalysisInstrFiles ++ ["-arg-lineage-hr", "-to-file", EscriptOutputFile] + ), + {ok, EscriptLineage} = file:read_file(EscriptOutputFile), + ?assertEqual(ReducedLineage, binary_to_list(EscriptLineage)). + +hibernate(Config) -> + [{leak, SecondSink, SecondHistory}, {leak, FirstSink, FirstHistory}] = compile_and_run_function( + Config, two_pids, hibernate_main + ), + ?assertEqual("two_pids.erl:67", FirstSink), + ?assertEqual(["two_pids.erl:78"], abstract_machine_util:get_sources(FirstHistory)), + ?assertEqual("two_pids.erl:69", SecondSink), + ?assertEqual(["two_pids.erl:75"], abstract_machine_util:get_sources(SecondHistory)). + +% Ideally this case should detect the leak, but because +% the send is not instrumented, we currently don't detect it +not_instrumented_send(Config) -> + [] = compile_and_run_function(Config, two_pids, not_instrumented_send_main). diff --git a/finer_taint/test/parallel_taint_SUITE_data/edge_annot.erl b/finer_taint/test/parallel_taint_SUITE_data/edge_annot.erl new file mode 100644 index 0000000..af25bc2 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/edge_annot.erl @@ -0,0 +1,44 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('edge_annot'). +-export([ + edge_annotations_main/0 +]). + +to_tuple(Value) -> + from_tuple({Value, 1}). + +from_tuple({Value, {send, Pid}}) -> + Pid ! Value; +from_tuple({Value, sink}) -> + finer_taint:sink(Value); +from_tuple({Value, _}) -> + Value. + +edge_annotations_main() -> + Taint = finer_taint:source(42), + TaintTuple = to_tuple(Taint), + from_tuple({TaintTuple, {send, self()}}), + + Taint1 = receive Val -> Val end, + TaintTuple1 = to_tuple(Taint1), + from_tuple({TaintTuple1, {send, self()}}), + + Taint2 = receive Val1 -> Val1 end, + TaintTuple2 = to_tuple(Taint2), + from_tuple({TaintTuple2, sink}). + + + diff --git a/finer_taint/test/parallel_taint_SUITE_data/edge_annotations_msg_analaysis_instr-2546091552 b/finer_taint/test/parallel_taint_SUITE_data/edge_annotations_msg_analaysis_instr-2546091552 new file mode 100644 index 0000000..6c50a5b --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/edge_annotations_msg_analaysis_instr-2546091552 @@ -0,0 +1,165 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{edge_annot,edge_annotations_main,0},"edge_annot.erl:30"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"edge_annot.erl:31"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Taint","edge_annot.erl:31"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,to_tuple,1},"edge_annot.erl:32"}}. +{get,{"Taint","edge_annot.erl:32"}}. +{call_fun,{edge_annot,to_tuple,1},"edge_annot.erl:32"}. +{push_scope,{{edge_annot,to_tuple,1},"edge_annot.erl:20"}}. +{store,{"Value","edge_annot.erl:20"}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,from_tuple,1},"edge_annot.erl:21"}}. +{get,{"Value","edge_annot.erl:21"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:21"}}. +{call_fun,{edge_annot,from_tuple,1},"edge_annot.erl:21"}. +{push_scope,{{edge_annot,from_tuple,1},"edge_annot.erl:27"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:27"}}. +{store,{"Value","edge_annot.erl:27"}}. +{pop,{}}. +{get,{"Value","edge_annot.erl:28"}}. +{func_ret,{from_tuple,"edge_annot.erl:27"}}. +{apply,{{edge_annot,from_tuple,1},"edge_annot.erl:21"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:21"}}. +{func_ret,{to_tuple,"edge_annot.erl:20"}}. +{apply,{{edge_annot,to_tuple,1},"edge_annot.erl:32"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:32"}}. +{duplicate,{}}. +{store,{"TaintTuple","edge_annot.erl:32"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,from_tuple,1},"edge_annot.erl:33"}}. +{get,{"TaintTuple","edge_annot.erl:33"}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"edge_annot.erl:33"}. +{apply,{{erlang,self,0},"edge_annot.erl:33"}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:33"}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:33"}}. +{call_fun,{edge_annot,from_tuple,1},"edge_annot.erl:33"}. +{push_scope,{{edge_annot,from_tuple,1},"edge_annot.erl:23"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:23"}}. +{store,{"Value","edge_annot.erl:23"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:23"}}. +{pop,{}}. +{store,{"Pid","edge_annot.erl:23"}}. +{get,{"Pid","edge_annot.erl:24"}}. +{get,{"Value","edge_annot.erl:24"}}. +{send,{"#Ref<0.1321034574.60293124.65662>","edge_annot.erl:24"}}. +{func_ret,{from_tuple,"edge_annot.erl:23"}}. +{apply,{{edge_annot,from_tuple,1},"edge_annot.erl:33"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:33"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293124.65662>","edge_annot.erl:35"}}. +{store,{"Val","edge_annot.erl:35"}}. +{get,{"Val","edge_annot.erl:35"}}. +{duplicate,{}}. +{store,{"Taint1","edge_annot.erl:35"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,to_tuple,1},"edge_annot.erl:36"}}. +{get,{"Taint1","edge_annot.erl:36"}}. +{call_fun,{edge_annot,to_tuple,1},"edge_annot.erl:36"}. +{push_scope,{{edge_annot,to_tuple,1},"edge_annot.erl:20"}}. +{store,{"Value","edge_annot.erl:20"}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,from_tuple,1},"edge_annot.erl:21"}}. +{get,{"Value","edge_annot.erl:21"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:21"}}. +{call_fun,{edge_annot,from_tuple,1},"edge_annot.erl:21"}. +{push_scope,{{edge_annot,from_tuple,1},"edge_annot.erl:27"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:27"}}. +{store,{"Value","edge_annot.erl:27"}}. +{pop,{}}. +{get,{"Value","edge_annot.erl:28"}}. +{func_ret,{from_tuple,"edge_annot.erl:27"}}. +{apply,{{edge_annot,from_tuple,1},"edge_annot.erl:21"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:21"}}. +{func_ret,{to_tuple,"edge_annot.erl:20"}}. +{apply,{{edge_annot,to_tuple,1},"edge_annot.erl:36"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:36"}}. +{duplicate,{}}. +{store,{"TaintTuple1","edge_annot.erl:36"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,from_tuple,1},"edge_annot.erl:37"}}. +{get,{"TaintTuple1","edge_annot.erl:37"}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"edge_annot.erl:37"}. +{apply,{{erlang,self,0},"edge_annot.erl:37"}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:37"}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:37"}}. +{call_fun,{edge_annot,from_tuple,1},"edge_annot.erl:37"}. +{push_scope,{{edge_annot,from_tuple,1},"edge_annot.erl:23"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:23"}}. +{store,{"Value","edge_annot.erl:23"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:23"}}. +{pop,{}}. +{store,{"Pid","edge_annot.erl:23"}}. +{get,{"Pid","edge_annot.erl:24"}}. +{get,{"Value","edge_annot.erl:24"}}. +{send,{"#Ref<0.1321034574.60293124.65663>","edge_annot.erl:24"}}. +{func_ret,{from_tuple,"edge_annot.erl:23"}}. +{apply,{{edge_annot,from_tuple,1},"edge_annot.erl:37"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:37"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293124.65663>","edge_annot.erl:39"}}. +{store,{"Val1","edge_annot.erl:39"}}. +{get,{"Val1","edge_annot.erl:39"}}. +{duplicate,{}}. +{store,{"Taint2","edge_annot.erl:39"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,to_tuple,1},"edge_annot.erl:40"}}. +{get,{"Taint2","edge_annot.erl:40"}}. +{call_fun,{edge_annot,to_tuple,1},"edge_annot.erl:40"}. +{push_scope,{{edge_annot,to_tuple,1},"edge_annot.erl:20"}}. +{store,{"Value","edge_annot.erl:20"}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,from_tuple,1},"edge_annot.erl:21"}}. +{get,{"Value","edge_annot.erl:21"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:21"}}. +{call_fun,{edge_annot,from_tuple,1},"edge_annot.erl:21"}. +{push_scope,{{edge_annot,from_tuple,1},"edge_annot.erl:27"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:27"}}. +{store,{"Value","edge_annot.erl:27"}}. +{pop,{}}. +{get,{"Value","edge_annot.erl:28"}}. +{func_ret,{from_tuple,"edge_annot.erl:27"}}. +{apply,{{edge_annot,from_tuple,1},"edge_annot.erl:21"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:21"}}. +{func_ret,{to_tuple,"edge_annot.erl:20"}}. +{apply,{{edge_annot,to_tuple,1},"edge_annot.erl:40"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:40"}}. +{duplicate,{}}. +{store,{"TaintTuple2","edge_annot.erl:40"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{edge_annot,from_tuple,1},"edge_annot.erl:41"}}. +{get,{"TaintTuple2","edge_annot.erl:41"}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"edge_annot.erl:41"}}. +{call_fun,{edge_annot,from_tuple,1},"edge_annot.erl:41"}. +{push_scope,{{edge_annot,from_tuple,1},"edge_annot.erl:25"}}. +{deconstruct_pattern,{{tuple,2},"edge_annot.erl:25"}}. +{store,{"Value","edge_annot.erl:25"}}. +{pop,{}}. +{get,{"Value","edge_annot.erl:26"}}. +{sink,{"edge_annot.erl:26"}}. +{func_ret,{from_tuple,"edge_annot.erl:25"}}. +{apply,{{edge_annot,from_tuple,1},"edge_annot.erl:41"}}. +{func_ret,{dropping_lambda_capture,"edge_annot.erl:41"}}. +{func_ret,{edge_annotations_main,"edge_annot.erl:30"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/example_gen_server.erl b/finer_taint/test/parallel_taint_SUITE_data/example_gen_server.erl new file mode 100644 index 0000000..77b3faf --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/example_gen_server.erl @@ -0,0 +1,89 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module(example_gen_server). +-behaviour(gen_server). + +-export([ + start_link/0, + start_link/2, + store_value/2, + pop/1, + stop/1, + init/1, + handle_call/3, + handle_cast/2 +]). + +-export([gen_server_test_main/0, gen_server_init_test_main/0]). + +% ========= TEST =============== + +id_function(X) -> X. +other_id_function(X) -> X. + +gen_server_test_main() -> + modeled_erlang:real_put(next_taint_pid, [521, 522, 523]), + {ok, Pid} = example_gen_server:start_link(), + modeled_erlang:real_put(next_taint_pid, undefined), + TaintedVal = finer_taint:source(42), + finer_taint:sink(example_gen_server:store_value(Pid, TaintedVal)), + finer_taint:sink(example_gen_server:store_value(Pid, id_function(43))), + finer_taint:sink(other_id_function(example_gen_server:pop(Pid))), + finer_taint:sink(example_gen_server:pop(Pid)), + example_gen_server:stop(Pid). + +% This should test if the taint is propagate through gen_server:start_link +gen_server_init_test_main() -> + TaintedVal = finer_taint:source(42), + modeled_erlang:real_put(next_taint_pid, [521, 522, 523]), + {ok, Pid} = example_gen_server:start_link(TaintedVal, notainted), + modeled_erlang:real_put(next_taint_pid, undefined), + example_gen_server:stop(Pid). + + + +%% ======= GEN SERVER IMPL ============= + +start_link() -> + gen_server:start_link(?MODULE, [], []). + +start_link(TaintedVal, NotTaintedVal) -> + gen_server:start_link(?MODULE, [TaintedVal, NotTaintedVal], []). + +store_value(Pid, Value) -> + gen_server:call(Pid, {store, Value}). + +pop(Pid) -> + gen_server:call(Pid, pop). + +stop(Pid) -> + gen_server:call(Pid, terminate). + +init([]) -> {ok, []}; +init([TaintedVal, NotTaintedVal]) -> + finer_taint:sink(TaintedVal), + finer_taint:sink(NotTaintedVal), + {ok, []}. + + +handle_call({store, Value}, _From, State) -> + {reply, Value, [Value | State]}; +handle_call(pop, _From, [Head | Tail]) -> + {reply, Head, Tail}; +handle_call(terminate, _From, State) -> + {stop, normal, ok, State}. + +handle_cast(_, State) -> + {noreply, State}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-4240823527 b/finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-4240823527 new file mode 100644 index 0000000..ca7b153 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-4240823527 @@ -0,0 +1,64 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{two_pids,hibernate_main,0},"two_pids.erl:73"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"two_pids.erl:74"}. +{apply,{{erlang,self,0},"two_pids.erl:74"}}. +{duplicate,{}}. +{store,{"Parent","two_pids.erl:74"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"two_pids.erl:75"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"ParentTaint","two_pids.erl:75"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"two_pids.erl:76"}. +{apply,{{modeled_erlang,real_put,2},"two_pids.erl:76"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{capture_closure,{["Parent","Tainted"]}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{push,{notaint}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"two_pids.erl:77"}}. +{duplicate,{}}. +{send,{441,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:77"}}. +{pop,{}}. +{call_fun,{erlang,spawn,1},"two_pids.erl:77"}. +{apply,{{erlang,spawn,1},"two_pids.erl:77"}}. +{duplicate,{}}. +{store,{"Child","two_pids.erl:77"}}. +{pop,{}}. +{get,{"Child","two_pids.erl:81"}}. +{push,{notaint}}. +{get,{"ParentTaint","two_pids.erl:81"}}. +{construct_pattern,{{tuple,2},"two_pids.erl:81"}}. +{send,{"#Ref<0.1321034574.60293122.71431>","two_pids.erl:81"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{two_pids,done,0},"two_pids.erl:82"}}. +{call_fun,{two_pids,done,0},"two_pids.erl:82"}. +{push_scope,{{two_pids,done,0},"two_pids.erl:21"}}. +{receive_trace,{"#Ref<0.1321034574.60293122.71432>","two_pids.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"two_pids.erl:21"}}. +{apply,{{two_pids,done,0},"two_pids.erl:82"}}. +{func_ret,{dropping_lambda_capture,"two_pids.erl:82"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{hibernate_main,"two_pids.erl:73"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-441 b/finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-441 new file mode 100644 index 0000000..3459479 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/hibernate_analaysis_instr-441 @@ -0,0 +1,73 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{two_pids,lambda_two_pids_77_17_anon,0},"two_pids.erl:77"}}. +{receive_trace,{441,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:77"}}. +{restore_capture,{{two_pids,lambda_two_pids_77_17_anon,0},"two_pids.erl:77"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"two_pids.erl:78"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Tainted","two_pids.erl:78"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Tainted","two_pids.erl:79"}}. +{construct_pattern,{{cons},"two_pids.erl:79"}}. +{get,{"Parent","two_pids.erl:79"}}. +{construct_pattern,{{cons},"two_pids.erl:79"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mhibernate,3},"two_pids.erl:79"}. +{push_scope,{{modeled_erlang,mhibernate,3},"modeled_erlang.erl:168"}}. +{store,{"M","modeled_erlang.erl:168"}}. +{store,{"F","modeled_erlang.erl:168"}}. +{store,{"Args","modeled_erlang.erl:168"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Args","modeled_erlang.erl:169"}}. +{construct_pattern,{{cons},"modeled_erlang.erl:169"}}. +{get,{"F","modeled_erlang.erl:169"}}. +{construct_pattern,{{cons},"modeled_erlang.erl:169"}}. +{get,{"M","modeled_erlang.erl:169"}}. +{construct_pattern,{{cons},"modeled_erlang.erl:169"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{erlang,hibernate,3},"modeled_erlang.erl:169"}. +{push_scope,{{modeled_erlang,mapply,3},"modeled_erlang.erl:41"}}. +{store,{"M","modeled_erlang.erl:41"}}. +{store,{"F","modeled_erlang.erl:41"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:41"}}. +{store,{"Arg1","modeled_erlang.erl:41"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:41"}}. +{store,{"Arg2","modeled_erlang.erl:41"}}. +{pop,{}}. +{get,{"F","modeled_erlang.erl:42"}}. +{pop,{}}. +{get,{"Arg2","modeled_erlang.erl:42"}}. +{get,{"Arg1","modeled_erlang.erl:42"}}. +{call_fun,{two_pids,hibernate_subproc,2},"modeled_erlang.erl:42"}. +{push_scope,{{two_pids,hibernate_subproc,2},"two_pids.erl:66"}}. +{store,{"Parent","two_pids.erl:66"}}. +{store,{"Tainted","two_pids.erl:66"}}. +{get,{"Tainted","two_pids.erl:67"}}. +{sink,{"two_pids.erl:67"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293122.71431>","two_pids.erl:68"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:69"}}. +{pop,{}}. +{store,{"AlsoTainted","two_pids.erl:69"}}. +{get,{"AlsoTainted","two_pids.erl:69"}}. +{sink,{"two_pids.erl:69"}}. +{pop,{}}. +{get,{"Parent","two_pids.erl:71"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293122.71432>","two_pids.erl:71"}}. +{func_ret,{hibernate_subproc,"two_pids.erl:66"}}. +{apply,{{two_pids,hibernate_subproc,2},"modeled_erlang.erl:42"}}. +{func_ret,{mapply,"modeled_erlang.erl:41"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/lineage_example_gen_server b/finer_taint/test/parallel_taint_SUITE_data/lineage_example_gen_server new file mode 100644 index 0000000..9a68f38 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/lineage_example_gen_server @@ -0,0 +1,671 @@ +@generated +example_gen_server:handle_call/3-Arg1 -> example_gen_server:handle_call/3-Arg3 +example_gen_server:handle_call/3-Arg1 -> example_gen_server:other_id_function/1-Arg1 +example_gen_server:handle_call/3-Arg1 -> gen:reply/2-Arg2 +example_gen_server:handle_call/3-Arg1 -> gen_server:decode_msg/9-Arg4 +example_gen_server:handle_call/3-Arg1 -> gen_server:handle_msg/6-Arg4 +example_gen_server:handle_call/3-Arg1 -> gen_server:loop/7-Arg3 +example_gen_server:handle_call/3-Arg1 -> gen_server:reply/2-Arg2 +example_gen_server:handle_call/3-Arg1 -> gen_server:try_handle_call/4-Arg4 +example_gen_server:handle_call/3-Arg3 -> example_gen_server:handle_call/3-Arg3 +example_gen_server:handle_call/3-Arg3 -> example_gen_server:other_id_function/1-Arg1 +example_gen_server:handle_call/3-Arg3 -> gen:reply/2-Arg2 +example_gen_server:handle_call/3-Arg3 -> gen_server:decode_msg/9-Arg4 +example_gen_server:handle_call/3-Arg3 -> gen_server:handle_msg/6-Arg4 +example_gen_server:handle_call/3-Arg3 -> gen_server:loop/7-Arg3 +example_gen_server:handle_call/3-Arg3 -> gen_server:reply/2-Arg2 +example_gen_server:handle_call/3-Arg3 -> gen_server:terminate/10-Arg9 +example_gen_server:handle_call/3-Arg3 -> gen_server:terminate/8-Arg7 +example_gen_server:handle_call/3-Arg3 -> gen_server:try_handle_call/4-Arg4 +example_gen_server:handle_call/3-Arg3 -> gen_server:try_terminate/3-Arg3 +example_gen_server:id_function/1-Arg1 -> example_gen_server:handle_call/3-Arg1 +example_gen_server:id_function/1-Arg1 -> example_gen_server:handle_call/3-Arg3 +example_gen_server:id_function/1-Arg1 -> example_gen_server:other_id_function/1-Arg1 +example_gen_server:id_function/1-Arg1 -> example_gen_server:store_value/2-Arg2 +example_gen_server:id_function/1-Arg1 -> gen:call/3-Arg3 +example_gen_server:id_function/1-Arg1 -> gen:call/4-Arg3 +example_gen_server:id_function/1-Arg1 -> gen:do_call/4-Arg3 +example_gen_server:id_function/1-Arg1 -> gen:reply/2-Arg2 +example_gen_server:id_function/1-Arg1 -> gen_server:call/2-Arg2 +example_gen_server:id_function/1-Arg1 -> gen_server:decode_msg/9-Arg1 +example_gen_server:id_function/1-Arg1 -> gen_server:decode_msg/9-Arg4 +example_gen_server:id_function/1-Arg1 -> gen_server:handle_msg/6-Arg1 +example_gen_server:id_function/1-Arg1 -> gen_server:handle_msg/6-Arg4 +example_gen_server:id_function/1-Arg1 -> gen_server:loop/7-Arg3 +example_gen_server:id_function/1-Arg1 -> gen_server:reply/2-Arg2 +example_gen_server:id_function/1-Arg1 -> gen_server:try_handle_call/4-Arg2 +example_gen_server:id_function/1-Arg1 -> gen_server:try_handle_call/4-Arg4 +example_gen_server:pop/1-Arg1 -> gen:call/3-Arg1 +example_gen_server:pop/1-Arg1 -> gen:call/4-Arg1 +example_gen_server:pop/1-Arg1 -> gen:do_call/4-Arg1 +example_gen_server:pop/1-Arg1 -> gen_server:call/2-Arg1 +example_gen_server:stop/1-Arg1 -> gen:call/3-Arg1 +example_gen_server:stop/1-Arg1 -> gen:call/4-Arg1 +example_gen_server:stop/1-Arg1 -> gen:do_call/4-Arg1 +example_gen_server:stop/1-Arg1 -> gen_server:call/2-Arg1 +example_gen_server:store_value/2-Arg1 -> gen:call/3-Arg1 +example_gen_server:store_value/2-Arg1 -> gen:call/4-Arg1 +example_gen_server:store_value/2-Arg1 -> gen:do_call/4-Arg1 +example_gen_server:store_value/2-Arg1 -> gen_server:call/2-Arg1 +example_gen_server:store_value/2-Arg2 -> example_gen_server:handle_call/3-Arg1 +example_gen_server:store_value/2-Arg2 -> example_gen_server:handle_call/3-Arg3 +example_gen_server:store_value/2-Arg2 -> example_gen_server:other_id_function/1-Arg1 +example_gen_server:store_value/2-Arg2 -> gen:call/3-Arg3 +example_gen_server:store_value/2-Arg2 -> gen:call/4-Arg3 +example_gen_server:store_value/2-Arg2 -> gen:do_call/4-Arg3 +example_gen_server:store_value/2-Arg2 -> gen:reply/2-Arg2 +example_gen_server:store_value/2-Arg2 -> gen_server:call/2-Arg2 +example_gen_server:store_value/2-Arg2 -> gen_server:decode_msg/9-Arg1 +example_gen_server:store_value/2-Arg2 -> gen_server:decode_msg/9-Arg4 +example_gen_server:store_value/2-Arg2 -> gen_server:handle_msg/6-Arg1 +example_gen_server:store_value/2-Arg2 -> gen_server:handle_msg/6-Arg4 +example_gen_server:store_value/2-Arg2 -> gen_server:loop/7-Arg3 +example_gen_server:store_value/2-Arg2 -> gen_server:reply/2-Arg2 +example_gen_server:store_value/2-Arg2 -> gen_server:try_handle_call/4-Arg2 +example_gen_server:store_value/2-Arg2 -> gen_server:try_handle_call/4-Arg4 +gen:call/3-Arg1 -> gen:call/4-Arg1 +gen:call/3-Arg1 -> gen:do_call/4-Arg1 +gen:call/3-Arg2 -> gen:call/4-Arg2 +gen:call/3-Arg2 -> gen:do_call/4-Arg2 +gen:call/3-Arg2 -> gen_server:decode_msg/9-Arg1 +gen:call/3-Arg2 -> gen_server:handle_msg/6-Arg1 +gen:call/3-Arg3 -> example_gen_server:handle_call/3-Arg1 +gen:call/3-Arg3 -> example_gen_server:handle_call/3-Arg3 +gen:call/3-Arg3 -> example_gen_server:other_id_function/1-Arg1 +gen:call/3-Arg3 -> gen:call/4-Arg3 +gen:call/3-Arg3 -> gen:do_call/4-Arg3 +gen:call/3-Arg3 -> gen:reply/2-Arg2 +gen:call/3-Arg3 -> gen_server:decode_msg/9-Arg1 +gen:call/3-Arg3 -> gen_server:decode_msg/9-Arg4 +gen:call/3-Arg3 -> gen_server:handle_msg/6-Arg1 +gen:call/3-Arg3 -> gen_server:handle_msg/6-Arg4 +gen:call/3-Arg3 -> gen_server:loop/7-Arg3 +gen:call/3-Arg3 -> gen_server:reply/2-Arg2 +gen:call/3-Arg3 -> gen_server:terminate/10-Arg7 +gen:call/3-Arg3 -> gen_server:terminate/8-Arg5 +gen:call/3-Arg3 -> gen_server:try_handle_call/4-Arg2 +gen:call/3-Arg3 -> gen_server:try_handle_call/4-Arg4 +gen:call/4-Arg1 -> gen:do_call/4-Arg1 +gen:call/4-Arg2 -> gen:do_call/4-Arg2 +gen:call/4-Arg2 -> gen_server:decode_msg/9-Arg1 +gen:call/4-Arg2 -> gen_server:handle_msg/6-Arg1 +gen:call/4-Arg3 -> example_gen_server:handle_call/3-Arg1 +gen:call/4-Arg3 -> example_gen_server:handle_call/3-Arg3 +gen:call/4-Arg3 -> example_gen_server:other_id_function/1-Arg1 +gen:call/4-Arg3 -> gen:do_call/4-Arg3 +gen:call/4-Arg3 -> gen:reply/2-Arg2 +gen:call/4-Arg3 -> gen_server:decode_msg/9-Arg1 +gen:call/4-Arg3 -> gen_server:decode_msg/9-Arg4 +gen:call/4-Arg3 -> gen_server:handle_msg/6-Arg1 +gen:call/4-Arg3 -> gen_server:handle_msg/6-Arg4 +gen:call/4-Arg3 -> gen_server:loop/7-Arg3 +gen:call/4-Arg3 -> gen_server:reply/2-Arg2 +gen:call/4-Arg3 -> gen_server:terminate/10-Arg7 +gen:call/4-Arg3 -> gen_server:terminate/8-Arg5 +gen:call/4-Arg3 -> gen_server:try_handle_call/4-Arg2 +gen:call/4-Arg3 -> gen_server:try_handle_call/4-Arg4 +gen:call/4-Arg4 -> gen:do_call/4-Arg4 +gen:do_call/4-Arg2 -> gen_server:decode_msg/9-Arg1 +gen:do_call/4-Arg2 -> gen_server:handle_msg/6-Arg1 +gen:do_call/4-Arg3 -> example_gen_server:handle_call/3-Arg1 +gen:do_call/4-Arg3 -> example_gen_server:handle_call/3-Arg3 +gen:do_call/4-Arg3 -> example_gen_server:other_id_function/1-Arg1 +gen:do_call/4-Arg3 -> gen:reply/2-Arg2 +gen:do_call/4-Arg3 -> gen_server:decode_msg/9-Arg1 +gen:do_call/4-Arg3 -> gen_server:decode_msg/9-Arg4 +gen:do_call/4-Arg3 -> gen_server:handle_msg/6-Arg1 +gen:do_call/4-Arg3 -> gen_server:handle_msg/6-Arg4 +gen:do_call/4-Arg3 -> gen_server:loop/7-Arg3 +gen:do_call/4-Arg3 -> gen_server:reply/2-Arg2 +gen:do_call/4-Arg3 -> gen_server:terminate/10-Arg7 +gen:do_call/4-Arg3 -> gen_server:terminate/8-Arg5 +gen:do_call/4-Arg3 -> gen_server:try_handle_call/4-Arg2 +gen:do_call/4-Arg3 -> gen_server:try_handle_call/4-Arg4 +gen:do_spawn/5-Arg1 -> gen:init_it/6-Arg1 +gen:do_spawn/5-Arg1 -> gen:init_it2/7-Arg1 +gen:do_spawn/5-Arg1 -> modeled_erlang:mapply/3-Arg3 +gen:do_spawn/5-Arg1 -> proc_lib:init_p_do_apply/3-Arg3 +gen:do_spawn/5-Arg1 -> proc_lib:spawn_opt/4-Arg3 +gen:do_spawn/5-Arg1 -> proc_lib:start_link/5-Arg3 +gen:do_spawn/5-Arg1 -> proc_lib:sync_start_link/2-Arg1 +gen:do_spawn/5-Arg1 -> proc_lib:trans_init/3-Arg3 +gen:do_spawn/5-Arg3 -> gen:init_it/6-Arg4 +gen:do_spawn/5-Arg3 -> gen:init_it2/7-Arg5 +gen:do_spawn/5-Arg3 -> gen_server:decode_msg/9-Arg5 +gen:do_spawn/5-Arg3 -> gen_server:handle_msg/6-Arg5 +gen:do_spawn/5-Arg3 -> gen_server:init_it/2-Arg1 +gen:do_spawn/5-Arg3 -> gen_server:init_it/6-Arg4 +gen:do_spawn/5-Arg3 -> gen_server:loop/7-Arg4 +gen:do_spawn/5-Arg3 -> gen_server:terminate/10-Arg8 +gen:do_spawn/5-Arg3 -> gen_server:terminate/8-Arg6 +gen:do_spawn/5-Arg3 -> gen_server:try_handle_call/4-Arg1 +gen:do_spawn/5-Arg3 -> gen_server:try_terminate/3-Arg1 +gen:do_spawn/5-Arg3 -> modeled_erlang:mapply/3-Arg3 +gen:do_spawn/5-Arg3 -> proc_lib:init_p_do_apply/3-Arg3 +gen:do_spawn/5-Arg3 -> proc_lib:spawn_opt/4-Arg3 +gen:do_spawn/5-Arg3 -> proc_lib:start_link/5-Arg3 +gen:do_spawn/5-Arg3 -> proc_lib:sync_start_link/2-Arg1 +gen:do_spawn/5-Arg3 -> proc_lib:trans_init/3-Arg3 +gen:do_spawn/5-Arg4 -> example_gen_server:init/1-Arg1 +gen:do_spawn/5-Arg4 -> gen:init_it/6-Arg5 +gen:do_spawn/5-Arg4 -> gen:init_it2/7-Arg6 +gen:do_spawn/5-Arg4 -> gen_server:init_it/2-Arg2 +gen:do_spawn/5-Arg4 -> gen_server:init_it/6-Arg5 +gen:do_spawn/5-Arg4 -> modeled_erlang:mapply/3-Arg3 +gen:do_spawn/5-Arg4 -> proc_lib:init_p_do_apply/3-Arg3 +gen:do_spawn/5-Arg4 -> proc_lib:spawn_opt/4-Arg3 +gen:do_spawn/5-Arg4 -> proc_lib:start_link/5-Arg3 +gen:do_spawn/5-Arg4 -> proc_lib:sync_start_link/2-Arg1 +gen:do_spawn/5-Arg4 -> proc_lib:trans_init/3-Arg3 +gen:do_spawn/5-Arg5 -> gen:debug_options/2-Arg2 +gen:do_spawn/5-Arg5 -> gen:hibernate_after/1-Arg1 +gen:do_spawn/5-Arg5 -> gen:init_it/6-Arg6 +gen:do_spawn/5-Arg5 -> gen:init_it2/7-Arg7 +gen:do_spawn/5-Arg5 -> gen:spawn_opts/1-Arg1 +gen:do_spawn/5-Arg5 -> gen:timeout/1-Arg1 +gen:do_spawn/5-Arg5 -> gen_server:init_it/6-Arg6 +gen:do_spawn/5-Arg5 -> modeled_erlang:mapply/3-Arg3 +gen:do_spawn/5-Arg5 -> proc_lib:init_p_do_apply/3-Arg3 +gen:do_spawn/5-Arg5 -> proc_lib:spawn_opt/4-Arg3 +gen:do_spawn/5-Arg5 -> proc_lib:start_link/5-Arg3 +gen:do_spawn/5-Arg5 -> proc_lib:sync_start_link/2-Arg1 +gen:do_spawn/5-Arg5 -> proc_lib:trans_init/3-Arg3 +gen:init_it/6-Arg1 -> gen:init_it2/7-Arg1 +gen:init_it/6-Arg2 -> gen:init_it2/7-Arg2 +gen:init_it/6-Arg2 -> gen_server:init_it/6-Arg1 +gen:init_it/6-Arg2 -> proc_lib:init_ack/2-Arg1 +gen:init_it/6-Arg3 -> gen:init_it2/7-Arg3 +gen:init_it/6-Arg3 -> gen_server:decode_msg/9-Arg2 +gen:init_it/6-Arg3 -> gen_server:handle_msg/6-Arg2 +gen:init_it/6-Arg3 -> gen_server:init_it/6-Arg2 +gen:init_it/6-Arg3 -> gen_server:loop/7-Arg1 +gen:init_it/6-Arg4 -> gen:init_it2/7-Arg5 +gen:init_it/6-Arg4 -> gen_server:decode_msg/9-Arg5 +gen:init_it/6-Arg4 -> gen_server:handle_msg/6-Arg5 +gen:init_it/6-Arg4 -> gen_server:init_it/2-Arg1 +gen:init_it/6-Arg4 -> gen_server:init_it/6-Arg4 +gen:init_it/6-Arg4 -> gen_server:loop/7-Arg4 +gen:init_it/6-Arg4 -> gen_server:terminate/10-Arg8 +gen:init_it/6-Arg4 -> gen_server:terminate/8-Arg6 +gen:init_it/6-Arg4 -> gen_server:try_handle_call/4-Arg1 +gen:init_it/6-Arg4 -> gen_server:try_terminate/3-Arg1 +gen:init_it/6-Arg5 -> example_gen_server:init/1-Arg1 +gen:init_it/6-Arg5 -> gen:init_it2/7-Arg6 +gen:init_it/6-Arg5 -> gen_server:init_it/2-Arg2 +gen:init_it/6-Arg5 -> gen_server:init_it/6-Arg5 +gen:init_it/6-Arg6 -> gen:debug_options/2-Arg2 +gen:init_it/6-Arg6 -> gen:hibernate_after/1-Arg1 +gen:init_it/6-Arg6 -> gen:init_it2/7-Arg7 +gen:init_it/6-Arg6 -> gen_server:init_it/6-Arg6 +gen:init_it2/7-Arg2 -> gen_server:init_it/6-Arg1 +gen:init_it2/7-Arg2 -> proc_lib:init_ack/2-Arg1 +gen:init_it2/7-Arg3 -> gen_server:decode_msg/9-Arg2 +gen:init_it2/7-Arg3 -> gen_server:handle_msg/6-Arg2 +gen:init_it2/7-Arg3 -> gen_server:init_it/6-Arg2 +gen:init_it2/7-Arg3 -> gen_server:loop/7-Arg1 +gen:init_it2/7-Arg4 -> gen:debug_options/2-Arg1 +gen:init_it2/7-Arg4 -> gen:name/1-Arg1 +gen:init_it2/7-Arg4 -> gen_server:decode_msg/9-Arg3 +gen:init_it2/7-Arg4 -> gen_server:handle_msg/6-Arg3 +gen:init_it2/7-Arg4 -> gen_server:init_it/6-Arg3 +gen:init_it2/7-Arg4 -> gen_server:loop/7-Arg2 +gen:init_it2/7-Arg4 -> gen_server:terminate/10-Arg5 +gen:init_it2/7-Arg4 -> gen_server:terminate/8-Arg3 +gen:init_it2/7-Arg5 -> gen_server:decode_msg/9-Arg5 +gen:init_it2/7-Arg5 -> gen_server:handle_msg/6-Arg5 +gen:init_it2/7-Arg5 -> gen_server:init_it/2-Arg1 +gen:init_it2/7-Arg5 -> gen_server:init_it/6-Arg4 +gen:init_it2/7-Arg5 -> gen_server:loop/7-Arg4 +gen:init_it2/7-Arg5 -> gen_server:terminate/10-Arg8 +gen:init_it2/7-Arg5 -> gen_server:terminate/8-Arg6 +gen:init_it2/7-Arg5 -> gen_server:try_handle_call/4-Arg1 +gen:init_it2/7-Arg5 -> gen_server:try_terminate/3-Arg1 +gen:init_it2/7-Arg6 -> example_gen_server:init/1-Arg1 +gen:init_it2/7-Arg6 -> gen_server:init_it/2-Arg2 +gen:init_it2/7-Arg6 -> gen_server:init_it/6-Arg5 +gen:init_it2/7-Arg7 -> gen:debug_options/2-Arg2 +gen:init_it2/7-Arg7 -> gen:hibernate_after/1-Arg1 +gen:init_it2/7-Arg7 -> gen_server:init_it/6-Arg6 +gen:name/1-Arg1 -> gen:debug_options/2-Arg1 +gen:name/1-Arg1 -> gen_server:decode_msg/9-Arg3 +gen:name/1-Arg1 -> gen_server:handle_msg/6-Arg3 +gen:name/1-Arg1 -> gen_server:loop/7-Arg2 +gen:name/1-Arg1 -> gen_server:terminate/10-Arg5 +gen:name/1-Arg1 -> gen_server:terminate/8-Arg3 +gen:reply/2-Arg2 -> example_gen_server:other_id_function/1-Arg1 +gen:start/5-Arg1 -> gen:do_spawn/5-Arg1 +gen:start/5-Arg1 -> gen:init_it/6-Arg1 +gen:start/5-Arg1 -> gen:init_it2/7-Arg1 +gen:start/5-Arg1 -> modeled_erlang:mapply/3-Arg3 +gen:start/5-Arg1 -> proc_lib:init_p_do_apply/3-Arg3 +gen:start/5-Arg1 -> proc_lib:spawn_opt/4-Arg3 +gen:start/5-Arg1 -> proc_lib:start_link/5-Arg3 +gen:start/5-Arg1 -> proc_lib:sync_start_link/2-Arg1 +gen:start/5-Arg1 -> proc_lib:trans_init/3-Arg3 +gen:start/5-Arg2 -> gen:do_spawn/5-Arg2 +gen:start/5-Arg3 -> gen:do_spawn/5-Arg3 +gen:start/5-Arg3 -> gen:init_it/6-Arg4 +gen:start/5-Arg3 -> gen:init_it2/7-Arg5 +gen:start/5-Arg3 -> gen_server:decode_msg/9-Arg5 +gen:start/5-Arg3 -> gen_server:handle_msg/6-Arg5 +gen:start/5-Arg3 -> gen_server:init_it/2-Arg1 +gen:start/5-Arg3 -> gen_server:init_it/6-Arg4 +gen:start/5-Arg3 -> gen_server:loop/7-Arg4 +gen:start/5-Arg3 -> gen_server:terminate/10-Arg8 +gen:start/5-Arg3 -> gen_server:terminate/8-Arg6 +gen:start/5-Arg3 -> gen_server:try_handle_call/4-Arg1 +gen:start/5-Arg3 -> gen_server:try_terminate/3-Arg1 +gen:start/5-Arg3 -> modeled_erlang:mapply/3-Arg3 +gen:start/5-Arg3 -> proc_lib:init_p_do_apply/3-Arg3 +gen:start/5-Arg3 -> proc_lib:spawn_opt/4-Arg3 +gen:start/5-Arg3 -> proc_lib:start_link/5-Arg3 +gen:start/5-Arg3 -> proc_lib:sync_start_link/2-Arg1 +gen:start/5-Arg3 -> proc_lib:trans_init/3-Arg3 +gen:start/5-Arg4 -> example_gen_server:init/1-Arg1 +gen:start/5-Arg4 -> gen:do_spawn/5-Arg4 +gen:start/5-Arg4 -> gen:init_it/6-Arg5 +gen:start/5-Arg4 -> gen:init_it2/7-Arg6 +gen:start/5-Arg4 -> gen_server:init_it/2-Arg2 +gen:start/5-Arg4 -> gen_server:init_it/6-Arg5 +gen:start/5-Arg4 -> modeled_erlang:mapply/3-Arg3 +gen:start/5-Arg4 -> proc_lib:init_p_do_apply/3-Arg3 +gen:start/5-Arg4 -> proc_lib:spawn_opt/4-Arg3 +gen:start/5-Arg4 -> proc_lib:start_link/5-Arg3 +gen:start/5-Arg4 -> proc_lib:sync_start_link/2-Arg1 +gen:start/5-Arg4 -> proc_lib:trans_init/3-Arg3 +gen:start/5-Arg5 -> gen:debug_options/2-Arg2 +gen:start/5-Arg5 -> gen:do_spawn/5-Arg5 +gen:start/5-Arg5 -> gen:hibernate_after/1-Arg1 +gen:start/5-Arg5 -> gen:init_it/6-Arg6 +gen:start/5-Arg5 -> gen:init_it2/7-Arg7 +gen:start/5-Arg5 -> gen:spawn_opts/1-Arg1 +gen:start/5-Arg5 -> gen:timeout/1-Arg1 +gen:start/5-Arg5 -> gen_server:init_it/6-Arg6 +gen:start/5-Arg5 -> modeled_erlang:mapply/3-Arg3 +gen:start/5-Arg5 -> proc_lib:init_p_do_apply/3-Arg3 +gen:start/5-Arg5 -> proc_lib:spawn_opt/4-Arg3 +gen:start/5-Arg5 -> proc_lib:start_link/5-Arg3 +gen:start/5-Arg5 -> proc_lib:sync_start_link/2-Arg1 +gen:start/5-Arg5 -> proc_lib:trans_init/3-Arg3 +gen_server:call/2-Arg1 -> gen:call/3-Arg1 +gen_server:call/2-Arg1 -> gen:call/4-Arg1 +gen_server:call/2-Arg1 -> gen:do_call/4-Arg1 +gen_server:call/2-Arg2 -> example_gen_server:handle_call/3-Arg1 +gen_server:call/2-Arg2 -> example_gen_server:handle_call/3-Arg3 +gen_server:call/2-Arg2 -> example_gen_server:other_id_function/1-Arg1 +gen_server:call/2-Arg2 -> gen:call/3-Arg3 +gen_server:call/2-Arg2 -> gen:call/4-Arg3 +gen_server:call/2-Arg2 -> gen:do_call/4-Arg3 +gen_server:call/2-Arg2 -> gen:reply/2-Arg2 +gen_server:call/2-Arg2 -> gen_server:decode_msg/9-Arg1 +gen_server:call/2-Arg2 -> gen_server:decode_msg/9-Arg4 +gen_server:call/2-Arg2 -> gen_server:handle_msg/6-Arg1 +gen_server:call/2-Arg2 -> gen_server:handle_msg/6-Arg4 +gen_server:call/2-Arg2 -> gen_server:loop/7-Arg3 +gen_server:call/2-Arg2 -> gen_server:reply/2-Arg2 +gen_server:call/2-Arg2 -> gen_server:terminate/10-Arg7 +gen_server:call/2-Arg2 -> gen_server:terminate/8-Arg5 +gen_server:call/2-Arg2 -> gen_server:try_handle_call/4-Arg2 +gen_server:call/2-Arg2 -> gen_server:try_handle_call/4-Arg4 +gen_server:decode_msg/9-Arg1 -> example_gen_server:handle_call/3-Arg1 +gen_server:decode_msg/9-Arg1 -> example_gen_server:handle_call/3-Arg3 +gen_server:decode_msg/9-Arg1 -> gen:reply/2-Arg2 +gen_server:decode_msg/9-Arg1 -> gen_server:decode_msg/9-Arg4 +gen_server:decode_msg/9-Arg1 -> gen_server:handle_msg/6-Arg1 +gen_server:decode_msg/9-Arg1 -> gen_server:handle_msg/6-Arg4 +gen_server:decode_msg/9-Arg1 -> gen_server:loop/7-Arg3 +gen_server:decode_msg/9-Arg1 -> gen_server:reply/2-Arg2 +gen_server:decode_msg/9-Arg1 -> gen_server:try_handle_call/4-Arg2 +gen_server:decode_msg/9-Arg1 -> gen_server:try_handle_call/4-Arg4 +gen_server:decode_msg/9-Arg2 -> gen_server:decode_msg/9-Arg2 +gen_server:decode_msg/9-Arg2 -> gen_server:handle_msg/6-Arg2 +gen_server:decode_msg/9-Arg2 -> gen_server:loop/7-Arg1 +gen_server:decode_msg/9-Arg3 -> gen_server:decode_msg/9-Arg3 +gen_server:decode_msg/9-Arg3 -> gen_server:handle_msg/6-Arg3 +gen_server:decode_msg/9-Arg3 -> gen_server:loop/7-Arg2 +gen_server:decode_msg/9-Arg3 -> gen_server:terminate/10-Arg5 +gen_server:decode_msg/9-Arg3 -> gen_server:terminate/8-Arg3 +gen_server:decode_msg/9-Arg4 -> example_gen_server:handle_call/3-Arg3 +gen_server:decode_msg/9-Arg4 -> gen_server:decode_msg/9-Arg4 +gen_server:decode_msg/9-Arg4 -> gen_server:handle_msg/6-Arg4 +gen_server:decode_msg/9-Arg4 -> gen_server:loop/7-Arg3 +gen_server:decode_msg/9-Arg4 -> gen_server:terminate/10-Arg9 +gen_server:decode_msg/9-Arg4 -> gen_server:terminate/8-Arg7 +gen_server:decode_msg/9-Arg4 -> gen_server:try_handle_call/4-Arg4 +gen_server:decode_msg/9-Arg4 -> gen_server:try_terminate/3-Arg3 +gen_server:decode_msg/9-Arg5 -> gen_server:decode_msg/9-Arg5 +gen_server:decode_msg/9-Arg5 -> gen_server:handle_msg/6-Arg5 +gen_server:decode_msg/9-Arg5 -> gen_server:loop/7-Arg4 +gen_server:decode_msg/9-Arg5 -> gen_server:terminate/10-Arg8 +gen_server:decode_msg/9-Arg5 -> gen_server:terminate/8-Arg6 +gen_server:decode_msg/9-Arg5 -> gen_server:try_handle_call/4-Arg1 +gen_server:decode_msg/9-Arg5 -> gen_server:try_terminate/3-Arg1 +gen_server:decode_msg/9-Arg7 -> gen_server:decode_msg/9-Arg7 +gen_server:decode_msg/9-Arg7 -> gen_server:handle_msg/6-Arg6 +gen_server:decode_msg/9-Arg7 -> gen_server:loop/7-Arg6 +gen_server:handle_msg/6-Arg1 -> example_gen_server:handle_call/3-Arg1 +gen_server:handle_msg/6-Arg1 -> example_gen_server:handle_call/3-Arg3 +gen_server:handle_msg/6-Arg1 -> gen:reply/2-Arg2 +gen_server:handle_msg/6-Arg1 -> gen_server:decode_msg/9-Arg4 +gen_server:handle_msg/6-Arg1 -> gen_server:handle_msg/6-Arg4 +gen_server:handle_msg/6-Arg1 -> gen_server:loop/7-Arg3 +gen_server:handle_msg/6-Arg1 -> gen_server:reply/2-Arg2 +gen_server:handle_msg/6-Arg1 -> gen_server:try_handle_call/4-Arg2 +gen_server:handle_msg/6-Arg1 -> gen_server:try_handle_call/4-Arg4 +gen_server:handle_msg/6-Arg2 -> gen_server:decode_msg/9-Arg2 +gen_server:handle_msg/6-Arg2 -> gen_server:handle_msg/6-Arg2 +gen_server:handle_msg/6-Arg2 -> gen_server:loop/7-Arg1 +gen_server:handle_msg/6-Arg3 -> gen_server:decode_msg/9-Arg3 +gen_server:handle_msg/6-Arg3 -> gen_server:handle_msg/6-Arg3 +gen_server:handle_msg/6-Arg3 -> gen_server:loop/7-Arg2 +gen_server:handle_msg/6-Arg3 -> gen_server:terminate/10-Arg5 +gen_server:handle_msg/6-Arg3 -> gen_server:terminate/8-Arg3 +gen_server:handle_msg/6-Arg4 -> example_gen_server:handle_call/3-Arg3 +gen_server:handle_msg/6-Arg4 -> gen_server:decode_msg/9-Arg4 +gen_server:handle_msg/6-Arg4 -> gen_server:handle_msg/6-Arg4 +gen_server:handle_msg/6-Arg4 -> gen_server:loop/7-Arg3 +gen_server:handle_msg/6-Arg4 -> gen_server:terminate/10-Arg9 +gen_server:handle_msg/6-Arg4 -> gen_server:terminate/8-Arg7 +gen_server:handle_msg/6-Arg4 -> gen_server:try_handle_call/4-Arg4 +gen_server:handle_msg/6-Arg4 -> gen_server:try_terminate/3-Arg3 +gen_server:handle_msg/6-Arg5 -> gen_server:decode_msg/9-Arg5 +gen_server:handle_msg/6-Arg5 -> gen_server:handle_msg/6-Arg5 +gen_server:handle_msg/6-Arg5 -> gen_server:loop/7-Arg4 +gen_server:handle_msg/6-Arg5 -> gen_server:terminate/10-Arg8 +gen_server:handle_msg/6-Arg5 -> gen_server:terminate/8-Arg6 +gen_server:handle_msg/6-Arg5 -> gen_server:try_handle_call/4-Arg1 +gen_server:handle_msg/6-Arg5 -> gen_server:try_terminate/3-Arg1 +gen_server:handle_msg/6-Arg6 -> gen_server:decode_msg/9-Arg7 +gen_server:handle_msg/6-Arg6 -> gen_server:handle_msg/6-Arg6 +gen_server:handle_msg/6-Arg6 -> gen_server:loop/7-Arg6 +gen_server:init_it/2-Arg2 -> example_gen_server:init/1-Arg1 +gen_server:init_it/6-Arg1 -> proc_lib:init_ack/2-Arg1 +gen_server:init_it/6-Arg2 -> gen_server:decode_msg/9-Arg2 +gen_server:init_it/6-Arg2 -> gen_server:handle_msg/6-Arg2 +gen_server:init_it/6-Arg2 -> gen_server:loop/7-Arg1 +gen_server:init_it/6-Arg3 -> gen:debug_options/2-Arg1 +gen_server:init_it/6-Arg3 -> gen:name/1-Arg1 +gen_server:init_it/6-Arg3 -> gen_server:decode_msg/9-Arg3 +gen_server:init_it/6-Arg3 -> gen_server:handle_msg/6-Arg3 +gen_server:init_it/6-Arg3 -> gen_server:loop/7-Arg2 +gen_server:init_it/6-Arg3 -> gen_server:terminate/10-Arg5 +gen_server:init_it/6-Arg3 -> gen_server:terminate/8-Arg3 +gen_server:init_it/6-Arg4 -> gen_server:decode_msg/9-Arg5 +gen_server:init_it/6-Arg4 -> gen_server:handle_msg/6-Arg5 +gen_server:init_it/6-Arg4 -> gen_server:init_it/2-Arg1 +gen_server:init_it/6-Arg4 -> gen_server:loop/7-Arg4 +gen_server:init_it/6-Arg4 -> gen_server:terminate/10-Arg8 +gen_server:init_it/6-Arg4 -> gen_server:terminate/8-Arg6 +gen_server:init_it/6-Arg4 -> gen_server:try_handle_call/4-Arg1 +gen_server:init_it/6-Arg4 -> gen_server:try_terminate/3-Arg1 +gen_server:init_it/6-Arg5 -> example_gen_server:init/1-Arg1 +gen_server:init_it/6-Arg5 -> gen_server:init_it/2-Arg2 +gen_server:init_it/6-Arg6 -> gen:debug_options/2-Arg2 +gen_server:init_it/6-Arg6 -> gen:hibernate_after/1-Arg1 +gen_server:loop/7-Arg1 -> gen_server:decode_msg/9-Arg2 +gen_server:loop/7-Arg1 -> gen_server:handle_msg/6-Arg2 +gen_server:loop/7-Arg1 -> gen_server:loop/7-Arg1 +gen_server:loop/7-Arg2 -> gen_server:decode_msg/9-Arg3 +gen_server:loop/7-Arg2 -> gen_server:handle_msg/6-Arg3 +gen_server:loop/7-Arg2 -> gen_server:loop/7-Arg2 +gen_server:loop/7-Arg2 -> gen_server:terminate/10-Arg5 +gen_server:loop/7-Arg2 -> gen_server:terminate/8-Arg3 +gen_server:loop/7-Arg3 -> example_gen_server:handle_call/3-Arg3 +gen_server:loop/7-Arg3 -> gen_server:decode_msg/9-Arg4 +gen_server:loop/7-Arg3 -> gen_server:handle_msg/6-Arg4 +gen_server:loop/7-Arg3 -> gen_server:loop/7-Arg3 +gen_server:loop/7-Arg3 -> gen_server:terminate/10-Arg9 +gen_server:loop/7-Arg3 -> gen_server:terminate/8-Arg7 +gen_server:loop/7-Arg3 -> gen_server:try_handle_call/4-Arg4 +gen_server:loop/7-Arg3 -> gen_server:try_terminate/3-Arg3 +gen_server:loop/7-Arg4 -> gen_server:decode_msg/9-Arg5 +gen_server:loop/7-Arg4 -> gen_server:handle_msg/6-Arg5 +gen_server:loop/7-Arg4 -> gen_server:loop/7-Arg4 +gen_server:loop/7-Arg4 -> gen_server:terminate/10-Arg8 +gen_server:loop/7-Arg4 -> gen_server:terminate/8-Arg6 +gen_server:loop/7-Arg4 -> gen_server:try_handle_call/4-Arg1 +gen_server:loop/7-Arg4 -> gen_server:try_terminate/3-Arg1 +gen_server:loop/7-Arg6 -> gen_server:decode_msg/9-Arg7 +gen_server:loop/7-Arg6 -> gen_server:handle_msg/6-Arg6 +gen_server:loop/7-Arg6 -> gen_server:loop/7-Arg6 +gen_server:loop/7-Arg7 -> gen_server:decode_msg/9-Arg8 +gen_server:reply/2-Arg1 -> gen:reply/2-Arg1 +gen_server:reply/2-Arg2 -> example_gen_server:other_id_function/1-Arg1 +gen_server:reply/2-Arg2 -> gen:reply/2-Arg2 +gen_server:start_link/3-Arg1 -> gen:do_spawn/5-Arg3 +gen_server:start_link/3-Arg1 -> gen:init_it/6-Arg4 +gen_server:start_link/3-Arg1 -> gen:init_it2/7-Arg5 +gen_server:start_link/3-Arg1 -> gen:start/5-Arg3 +gen_server:start_link/3-Arg1 -> gen_server:decode_msg/9-Arg5 +gen_server:start_link/3-Arg1 -> gen_server:handle_msg/6-Arg5 +gen_server:start_link/3-Arg1 -> gen_server:init_it/2-Arg1 +gen_server:start_link/3-Arg1 -> gen_server:init_it/6-Arg4 +gen_server:start_link/3-Arg1 -> gen_server:loop/7-Arg4 +gen_server:start_link/3-Arg1 -> gen_server:terminate/10-Arg8 +gen_server:start_link/3-Arg1 -> gen_server:terminate/8-Arg6 +gen_server:start_link/3-Arg1 -> gen_server:try_handle_call/4-Arg1 +gen_server:start_link/3-Arg1 -> gen_server:try_terminate/3-Arg1 +gen_server:start_link/3-Arg1 -> modeled_erlang:mapply/3-Arg3 +gen_server:start_link/3-Arg1 -> proc_lib:init_p_do_apply/3-Arg3 +gen_server:start_link/3-Arg1 -> proc_lib:spawn_opt/4-Arg3 +gen_server:start_link/3-Arg1 -> proc_lib:start_link/5-Arg3 +gen_server:start_link/3-Arg1 -> proc_lib:sync_start_link/2-Arg1 +gen_server:start_link/3-Arg1 -> proc_lib:trans_init/3-Arg3 +gen_server:start_link/3-Arg2 -> example_gen_server:init/1-Arg1 +gen_server:start_link/3-Arg2 -> gen:do_spawn/5-Arg4 +gen_server:start_link/3-Arg2 -> gen:init_it/6-Arg5 +gen_server:start_link/3-Arg2 -> gen:init_it2/7-Arg6 +gen_server:start_link/3-Arg2 -> gen:start/5-Arg4 +gen_server:start_link/3-Arg2 -> gen_server:init_it/2-Arg2 +gen_server:start_link/3-Arg2 -> gen_server:init_it/6-Arg5 +gen_server:start_link/3-Arg2 -> modeled_erlang:mapply/3-Arg3 +gen_server:start_link/3-Arg2 -> proc_lib:init_p_do_apply/3-Arg3 +gen_server:start_link/3-Arg2 -> proc_lib:spawn_opt/4-Arg3 +gen_server:start_link/3-Arg2 -> proc_lib:start_link/5-Arg3 +gen_server:start_link/3-Arg2 -> proc_lib:sync_start_link/2-Arg1 +gen_server:start_link/3-Arg2 -> proc_lib:trans_init/3-Arg3 +gen_server:start_link/3-Arg3 -> gen:debug_options/2-Arg2 +gen_server:start_link/3-Arg3 -> gen:do_spawn/5-Arg5 +gen_server:start_link/3-Arg3 -> gen:hibernate_after/1-Arg1 +gen_server:start_link/3-Arg3 -> gen:init_it/6-Arg6 +gen_server:start_link/3-Arg3 -> gen:init_it2/7-Arg7 +gen_server:start_link/3-Arg3 -> gen:spawn_opts/1-Arg1 +gen_server:start_link/3-Arg3 -> gen:start/5-Arg5 +gen_server:start_link/3-Arg3 -> gen:timeout/1-Arg1 +gen_server:start_link/3-Arg3 -> gen_server:init_it/6-Arg6 +gen_server:start_link/3-Arg3 -> modeled_erlang:mapply/3-Arg3 +gen_server:start_link/3-Arg3 -> proc_lib:init_p_do_apply/3-Arg3 +gen_server:start_link/3-Arg3 -> proc_lib:spawn_opt/4-Arg3 +gen_server:start_link/3-Arg3 -> proc_lib:start_link/5-Arg3 +gen_server:start_link/3-Arg3 -> proc_lib:sync_start_link/2-Arg1 +gen_server:start_link/3-Arg3 -> proc_lib:trans_init/3-Arg3 +gen_server:terminate/10-Arg1 -> gen_server:terminate_reason/3-Arg1 +gen_server:terminate/10-Arg2 -> gen_server:terminate_reason/3-Arg2 +gen_server:terminate/10-Arg2 -> gen_server:try_terminate/3-Arg2 +gen_server:terminate/10-Arg3 -> gen_server:terminate_reason/3-Arg3 +gen_server:terminate/10-Arg6 -> gen:reply/2-Arg1 +gen_server:terminate/10-Arg6 -> gen_server:reply/2-Arg1 +gen_server:terminate/10-Arg8 -> gen_server:try_terminate/3-Arg1 +gen_server:terminate/10-Arg9 -> gen_server:try_terminate/3-Arg3 +gen_server:terminate/8-Arg1 -> gen_server:terminate/10-Arg2 +gen_server:terminate/8-Arg1 -> gen_server:terminate/10-Arg4 +gen_server:terminate/8-Arg1 -> gen_server:terminate_reason/3-Arg2 +gen_server:terminate/8-Arg1 -> gen_server:try_terminate/3-Arg2 +gen_server:terminate/8-Arg2 -> gen_server:terminate/10-Arg3 +gen_server:terminate/8-Arg2 -> gen_server:terminate_reason/3-Arg3 +gen_server:terminate/8-Arg3 -> gen_server:terminate/10-Arg5 +gen_server:terminate/8-Arg4 -> gen:reply/2-Arg1 +gen_server:terminate/8-Arg4 -> gen_server:reply/2-Arg1 +gen_server:terminate/8-Arg4 -> gen_server:terminate/10-Arg6 +gen_server:terminate/8-Arg5 -> gen_server:terminate/10-Arg7 +gen_server:terminate/8-Arg6 -> gen_server:terminate/10-Arg8 +gen_server:terminate/8-Arg6 -> gen_server:try_terminate/3-Arg1 +gen_server:terminate/8-Arg7 -> gen_server:terminate/10-Arg9 +gen_server:terminate/8-Arg7 -> gen_server:try_terminate/3-Arg3 +gen_server:terminate/8-Arg8 -> gen_server:terminate/10-Arg10 +gen_server:terminate_reason/3-Arg2 -> gen_server:try_terminate/3-Arg2 +gen_server:try_handle_call/4-Arg2 -> example_gen_server:handle_call/3-Arg1 +gen_server:try_handle_call/4-Arg2 -> example_gen_server:handle_call/3-Arg3 +gen_server:try_handle_call/4-Arg2 -> example_gen_server:other_id_function/1-Arg1 +gen_server:try_handle_call/4-Arg2 -> gen:reply/2-Arg2 +gen_server:try_handle_call/4-Arg2 -> gen_server:decode_msg/9-Arg4 +gen_server:try_handle_call/4-Arg2 -> gen_server:handle_msg/6-Arg4 +gen_server:try_handle_call/4-Arg2 -> gen_server:loop/7-Arg3 +gen_server:try_handle_call/4-Arg2 -> gen_server:reply/2-Arg2 +gen_server:try_handle_call/4-Arg2 -> gen_server:try_handle_call/4-Arg4 +gen_server:try_handle_call/4-Arg3 -> example_gen_server:handle_call/3-Arg2 +gen_server:try_handle_call/4-Arg4 -> example_gen_server:handle_call/3-Arg3 +gen_server:try_handle_call/4-Arg4 -> example_gen_server:other_id_function/1-Arg1 +gen_server:try_handle_call/4-Arg4 -> gen:reply/2-Arg2 +gen_server:try_handle_call/4-Arg4 -> gen_server:decode_msg/9-Arg4 +gen_server:try_handle_call/4-Arg4 -> gen_server:handle_msg/6-Arg4 +gen_server:try_handle_call/4-Arg4 -> gen_server:loop/7-Arg3 +gen_server:try_handle_call/4-Arg4 -> gen_server:reply/2-Arg2 +gen_server:try_handle_call/4-Arg4 -> gen_server:terminate/10-Arg9 +gen_server:try_handle_call/4-Arg4 -> gen_server:terminate/8-Arg7 +gen_server:try_handle_call/4-Arg4 -> gen_server:try_handle_call/4-Arg4 +gen_server:try_handle_call/4-Arg4 -> gen_server:try_terminate/3-Arg3 +modeled_erlang:mapply/3-Arg3 -> example_gen_server:init/1-Arg1 +modeled_erlang:mapply/3-Arg3 -> gen:debug_options/2-Arg2 +modeled_erlang:mapply/3-Arg3 -> gen:hibernate_after/1-Arg1 +modeled_erlang:mapply/3-Arg3 -> gen:init_it/6-Arg1 +modeled_erlang:mapply/3-Arg3 -> gen:init_it/6-Arg4 +modeled_erlang:mapply/3-Arg3 -> gen:init_it/6-Arg5 +modeled_erlang:mapply/3-Arg3 -> gen:init_it/6-Arg6 +modeled_erlang:mapply/3-Arg3 -> gen:init_it2/7-Arg1 +modeled_erlang:mapply/3-Arg3 -> gen:init_it2/7-Arg5 +modeled_erlang:mapply/3-Arg3 -> gen:init_it2/7-Arg6 +modeled_erlang:mapply/3-Arg3 -> gen:init_it2/7-Arg7 +modeled_erlang:mapply/3-Arg3 -> gen_server:decode_msg/9-Arg5 +modeled_erlang:mapply/3-Arg3 -> gen_server:handle_msg/6-Arg5 +modeled_erlang:mapply/3-Arg3 -> gen_server:init_it/2-Arg1 +modeled_erlang:mapply/3-Arg3 -> gen_server:init_it/2-Arg2 +modeled_erlang:mapply/3-Arg3 -> gen_server:init_it/6-Arg4 +modeled_erlang:mapply/3-Arg3 -> gen_server:init_it/6-Arg5 +modeled_erlang:mapply/3-Arg3 -> gen_server:init_it/6-Arg6 +modeled_erlang:mapply/3-Arg3 -> gen_server:loop/7-Arg4 +modeled_erlang:mapply/3-Arg3 -> gen_server:terminate/10-Arg8 +modeled_erlang:mapply/3-Arg3 -> gen_server:terminate/8-Arg6 +modeled_erlang:mapply/3-Arg3 -> gen_server:try_handle_call/4-Arg1 +modeled_erlang:mapply/3-Arg3 -> gen_server:try_terminate/3-Arg1 +proc_lib:exit_p/3-Arg1 -> proc_lib:crash_report/4-Arg1 +proc_lib:exit_p/3-Arg1 -> proc_lib:exit_reason/3-Arg1 +proc_lib:exit_p/3-Arg2 -> proc_lib:crash_report/4-Arg2 +proc_lib:exit_p/3-Arg2 -> proc_lib:exit_reason/3-Arg2 +proc_lib:exit_p/3-Arg3 -> proc_lib:crash_report/4-Arg4 +proc_lib:exit_p/3-Arg3 -> proc_lib:exit_reason/3-Arg3 +proc_lib:init_ack/2-Arg2 -> example_gen_server:pop/1-Arg1 +proc_lib:init_ack/2-Arg2 -> example_gen_server:stop/1-Arg1 +proc_lib:init_ack/2-Arg2 -> example_gen_server:store_value/2-Arg1 +proc_lib:init_ack/2-Arg2 -> gen:call/3-Arg1 +proc_lib:init_ack/2-Arg2 -> gen:call/4-Arg1 +proc_lib:init_ack/2-Arg2 -> gen:do_call/4-Arg1 +proc_lib:init_ack/2-Arg2 -> gen_server:call/2-Arg1 +proc_lib:init_p_do_apply/3-Arg1 -> modeled_erlang:mapply/3-Arg1 +proc_lib:init_p_do_apply/3-Arg2 -> modeled_erlang:mapply/3-Arg2 +proc_lib:init_p_do_apply/3-Arg3 -> example_gen_server:init/1-Arg1 +proc_lib:init_p_do_apply/3-Arg3 -> gen:debug_options/2-Arg2 +proc_lib:init_p_do_apply/3-Arg3 -> gen:hibernate_after/1-Arg1 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it/6-Arg1 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it/6-Arg4 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it/6-Arg5 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it/6-Arg6 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it2/7-Arg1 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it2/7-Arg5 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it2/7-Arg6 +proc_lib:init_p_do_apply/3-Arg3 -> gen:init_it2/7-Arg7 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:decode_msg/9-Arg5 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:handle_msg/6-Arg5 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:init_it/2-Arg1 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:init_it/2-Arg2 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:init_it/6-Arg4 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:init_it/6-Arg5 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:init_it/6-Arg6 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:loop/7-Arg4 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:terminate/10-Arg8 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:terminate/8-Arg6 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:try_handle_call/4-Arg1 +proc_lib:init_p_do_apply/3-Arg3 -> gen_server:try_terminate/3-Arg1 +proc_lib:init_p_do_apply/3-Arg3 -> modeled_erlang:mapply/3-Arg3 +proc_lib:make_dummy_args/2-Arg1 -> proc_lib:crash_report/4-Arg3 +proc_lib:make_dummy_args/2-Arg1 -> proc_lib:make_dummy_args/2-Arg1 +proc_lib:make_dummy_args/2-Arg1 -> proc_lib:make_dummy_args/2-Arg2 +proc_lib:make_dummy_args/2-Arg2 -> proc_lib:crash_report/4-Arg3 +proc_lib:make_dummy_args/2-Arg2 -> proc_lib:make_dummy_args/2-Arg2 +proc_lib:spawn_opt/4-Arg1 -> modeled_erlang:mapply/3-Arg1 +proc_lib:spawn_opt/4-Arg1 -> proc_lib:init_p_do_apply/3-Arg1 +proc_lib:spawn_opt/4-Arg1 -> proc_lib:sync_start_link/2-Arg1 +proc_lib:spawn_opt/4-Arg1 -> proc_lib:trans_init/3-Arg1 +proc_lib:spawn_opt/4-Arg2 -> modeled_erlang:mapply/3-Arg2 +proc_lib:spawn_opt/4-Arg2 -> proc_lib:init_p_do_apply/3-Arg2 +proc_lib:spawn_opt/4-Arg2 -> proc_lib:sync_start_link/2-Arg1 +proc_lib:spawn_opt/4-Arg2 -> proc_lib:trans_init/3-Arg2 +proc_lib:spawn_opt/4-Arg3 -> example_gen_server:init/1-Arg1 +proc_lib:spawn_opt/4-Arg3 -> gen:debug_options/2-Arg2 +proc_lib:spawn_opt/4-Arg3 -> gen:hibernate_after/1-Arg1 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it/6-Arg1 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it/6-Arg4 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it/6-Arg5 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it/6-Arg6 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it2/7-Arg1 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it2/7-Arg5 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it2/7-Arg6 +proc_lib:spawn_opt/4-Arg3 -> gen:init_it2/7-Arg7 +proc_lib:spawn_opt/4-Arg3 -> gen_server:decode_msg/9-Arg5 +proc_lib:spawn_opt/4-Arg3 -> gen_server:handle_msg/6-Arg5 +proc_lib:spawn_opt/4-Arg3 -> gen_server:init_it/2-Arg1 +proc_lib:spawn_opt/4-Arg3 -> gen_server:init_it/2-Arg2 +proc_lib:spawn_opt/4-Arg3 -> gen_server:init_it/6-Arg4 +proc_lib:spawn_opt/4-Arg3 -> gen_server:init_it/6-Arg5 +proc_lib:spawn_opt/4-Arg3 -> gen_server:init_it/6-Arg6 +proc_lib:spawn_opt/4-Arg3 -> gen_server:loop/7-Arg4 +proc_lib:spawn_opt/4-Arg3 -> gen_server:terminate/10-Arg8 +proc_lib:spawn_opt/4-Arg3 -> gen_server:terminate/8-Arg6 +proc_lib:spawn_opt/4-Arg3 -> gen_server:try_handle_call/4-Arg1 +proc_lib:spawn_opt/4-Arg3 -> gen_server:try_terminate/3-Arg1 +proc_lib:spawn_opt/4-Arg3 -> modeled_erlang:mapply/3-Arg3 +proc_lib:spawn_opt/4-Arg3 -> proc_lib:init_p_do_apply/3-Arg3 +proc_lib:spawn_opt/4-Arg3 -> proc_lib:sync_start_link/2-Arg1 +proc_lib:spawn_opt/4-Arg3 -> proc_lib:trans_init/3-Arg3 +proc_lib:spawn_opt/4-Arg4 -> proc_lib:sync_start_link/2-Arg1 +proc_lib:start_link/5-Arg1 -> modeled_erlang:mapply/3-Arg1 +proc_lib:start_link/5-Arg1 -> proc_lib:init_p_do_apply/3-Arg1 +proc_lib:start_link/5-Arg1 -> proc_lib:spawn_opt/4-Arg1 +proc_lib:start_link/5-Arg1 -> proc_lib:sync_start_link/2-Arg1 +proc_lib:start_link/5-Arg1 -> proc_lib:trans_init/3-Arg1 +proc_lib:start_link/5-Arg2 -> modeled_erlang:mapply/3-Arg2 +proc_lib:start_link/5-Arg2 -> proc_lib:init_p_do_apply/3-Arg2 +proc_lib:start_link/5-Arg2 -> proc_lib:spawn_opt/4-Arg2 +proc_lib:start_link/5-Arg2 -> proc_lib:sync_start_link/2-Arg1 +proc_lib:start_link/5-Arg2 -> proc_lib:trans_init/3-Arg2 +proc_lib:start_link/5-Arg3 -> example_gen_server:init/1-Arg1 +proc_lib:start_link/5-Arg3 -> gen:debug_options/2-Arg2 +proc_lib:start_link/5-Arg3 -> gen:hibernate_after/1-Arg1 +proc_lib:start_link/5-Arg3 -> gen:init_it/6-Arg1 +proc_lib:start_link/5-Arg3 -> gen:init_it/6-Arg4 +proc_lib:start_link/5-Arg3 -> gen:init_it/6-Arg5 +proc_lib:start_link/5-Arg3 -> gen:init_it/6-Arg6 +proc_lib:start_link/5-Arg3 -> gen:init_it2/7-Arg1 +proc_lib:start_link/5-Arg3 -> gen:init_it2/7-Arg5 +proc_lib:start_link/5-Arg3 -> gen:init_it2/7-Arg6 +proc_lib:start_link/5-Arg3 -> gen:init_it2/7-Arg7 +proc_lib:start_link/5-Arg3 -> gen_server:decode_msg/9-Arg5 +proc_lib:start_link/5-Arg3 -> gen_server:handle_msg/6-Arg5 +proc_lib:start_link/5-Arg3 -> gen_server:init_it/2-Arg1 +proc_lib:start_link/5-Arg3 -> gen_server:init_it/2-Arg2 +proc_lib:start_link/5-Arg3 -> gen_server:init_it/6-Arg4 +proc_lib:start_link/5-Arg3 -> gen_server:init_it/6-Arg5 +proc_lib:start_link/5-Arg3 -> gen_server:init_it/6-Arg6 +proc_lib:start_link/5-Arg3 -> gen_server:loop/7-Arg4 +proc_lib:start_link/5-Arg3 -> gen_server:terminate/10-Arg8 +proc_lib:start_link/5-Arg3 -> gen_server:terminate/8-Arg6 +proc_lib:start_link/5-Arg3 -> gen_server:try_handle_call/4-Arg1 +proc_lib:start_link/5-Arg3 -> gen_server:try_terminate/3-Arg1 +proc_lib:start_link/5-Arg3 -> modeled_erlang:mapply/3-Arg3 +proc_lib:start_link/5-Arg3 -> proc_lib:init_p_do_apply/3-Arg3 +proc_lib:start_link/5-Arg3 -> proc_lib:spawn_opt/4-Arg3 +proc_lib:start_link/5-Arg3 -> proc_lib:sync_start_link/2-Arg1 +proc_lib:start_link/5-Arg3 -> proc_lib:trans_init/3-Arg3 +proc_lib:start_link/5-Arg4 -> proc_lib:sync_start_link/2-Arg2 +proc_lib:start_link/5-Arg5 -> proc_lib:spawn_opt/4-Arg4 +proc_lib:start_link/5-Arg5 -> proc_lib:sync_start_link/2-Arg1 diff --git a/finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-4157287981 b/finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-4157287981 new file mode 100644 index 0000000..2c730ff --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-4157287981 @@ -0,0 +1,87 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{two_pids,not_instrumented_send_main,0},"two_pids.erl:56"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"two_pids.erl:57"}. +{apply,{{erlang,self,0},"two_pids.erl:57"}}. +{duplicate,{}}. +{store,{"Parent","two_pids.erl:57"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"two_pids.erl:58"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Taint","two_pids.erl:58"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"two_pids.erl:59"}. +{apply,{{modeled_erlang,real_put,2},"two_pids.erl:59"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{capture_closure,{["Parent"]}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{push,{notaint}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"two_pids.erl:60"}}. +{duplicate,{}}. +{send,{431,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:60"}}. +{pop,{}}. +{call_fun,{erlang,spawn_link,1},"two_pids.erl:60"}. +{apply,{{erlang,spawn_link,1},"two_pids.erl:60"}}. +{duplicate,{}}. +{store,{"Child","two_pids.erl:60"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"Taint","two_pids.erl:62"}}. +{construct_pattern,{{tuple,2},"two_pids.erl:62"}}. +{construct_pattern,{{cons},"two_pids.erl:62"}}. +{get,{"Child","two_pids.erl:62"}}. +{construct_pattern,{{cons},"two_pids.erl:62"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mapply,3},"two_pids.erl:62"}. +{push_scope,{{modeled_erlang,mapply,3},"modeled_erlang.erl:41"}}. +{store,{"M","modeled_erlang.erl:41"}}. +{store,{"F","modeled_erlang.erl:41"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:41"}}. +{store,{"Arg1","modeled_erlang.erl:41"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:41"}}. +{store,{"Arg2","modeled_erlang.erl:41"}}. +{pop,{}}. +{get,{"F","modeled_erlang.erl:42"}}. +{pop,{}}. +{get,{"Arg2","modeled_erlang.erl:42"}}. +{get,{"Arg1","modeled_erlang.erl:42"}}. +{call_fun,{erlang,send,2},"modeled_erlang.erl:42"}. +{apply,{{erlang,send,2},"modeled_erlang.erl:42"}}. +{func_ret,{mapply,"modeled_erlang.erl:41"}}. +{apply,{{modeled_erlang,mapply,3},"two_pids.erl:62"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{two_pids,done,0},"two_pids.erl:63"}}. +{call_fun,{two_pids,done,0},"two_pids.erl:63"}. +{push_scope,{{two_pids,done,0},"two_pids.erl:21"}}. +{receive_trace,{"#Ref<0.1321034574.60293121.72657>","two_pids.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"two_pids.erl:21"}}. +{apply,{{two_pids,done,0},"two_pids.erl:63"}}. +{func_ret,{dropping_lambda_capture,"two_pids.erl:63"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{not_instrumented_send_main,"two_pids.erl:56"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-431 b/finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-431 new file mode 100644 index 0000000..8f8e089 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/not_instrumented_send_analaysis_instr-431 @@ -0,0 +1,40 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{two_pids,lambda_two_pids_60_22_anon,0},"two_pids.erl:60"}}. +{receive_trace,{431,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:60"}}. +{restore_capture,{{two_pids,lambda_two_pids_60_22_anon,0},"two_pids.erl:60"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{two_pids,sub_proc,1},"two_pids.erl:60"}}. +{get,{"Parent","two_pids.erl:60"}}. +{call_fun,{two_pids,sub_proc,1},"two_pids.erl:60"}. +{push_scope,{{two_pids,sub_proc,1},"two_pids.erl:26"}}. +{store,{"Parent","two_pids.erl:26"}}. +{receive_trace,{nomsg}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:32"}}. +{pop,{}}. +{store,{"T","two_pids.erl:32"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"T","two_pids.erl:33"}}. +{construct_pattern,{{cons},"two_pids.erl:33"}}. +{push,{notaint}}. +{call_fun,{io,format,2},"two_pids.erl:33"}. +{apply,{{io,format,2},"two_pids.erl:33"}}. +{pop,{}}. +{get,{"T","two_pids.erl:34"}}. +{duplicate,{}}. +{store,{"Data","two_pids.erl:27"}}. +{pop,{}}. +{get,{"Data","two_pids.erl:36"}}. +{sink,{"two_pids.erl:36"}}. +{pop,{}}. +{get,{"Parent","two_pids.erl:37"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293121.72657>","two_pids.erl:37"}}. +{func_ret,{sub_proc,"two_pids.erl:26"}}. +{apply,{{two_pids,sub_proc,1},"two_pids.erl:60"}}. +{func_ret,{dropping_lambda_capture,"two_pids.erl:60"}}. +{func_ret,{lambda_two_pids_60_22_anon,"two_pids.erl:60"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/scatter_gather.erl b/finer_taint/test/parallel_taint_SUITE_data/scatter_gather.erl new file mode 100644 index 0000000..5e2b68c --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/scatter_gather.erl @@ -0,0 +1,94 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('scatter_gather'). +-export([ + introduce_taint_function/1, + add_and_add_one/2, + gather_proc/3, + scatter_gather_main/0 +]). + + +introduce_taint_function(_Unused) -> + finer_taint:source("Some val"). + + +add_and_add_one(Value1, Value2) -> + Value1 + Value2 + 1. + +scatter_gather_main() -> + put(next_taint_pid, [621, 622, 623, 624, 625, 626, 627]), + TaintedValue = finer_taint:source(42), + Jobs = [ + {fun ?MODULE:introduce_taint_function/1, [ok]}, + {fun ?MODULE:add_and_add_one/2, [4,5]}, + {fun ?MODULE:add_and_add_one/2, [TaintedValue,5]} + ], + % instrumented_scatter_gather is wa_scatter_gather instrumented with finer_taint + % in the parallel_taint_SUITE for this test + [IntroducedTaint, NotTainted, Tainted] = scatter_gather(Jobs), + if NotTainted =/= 10 -> + throw({invalid_computation, NotTainted}); + Tainted =/= 48 -> + throw({invalid_computation, Tainted}); + true -> + finer_taint:sink(IntroducedTaint), + finer_taint:sink(NotTainted), + finer_taint:sink(Tainted) + end. + +scatter_gather(Jobs) -> + Ref = make_ref(), + GatherPid = spawn(?MODULE, gather_proc, [self(), Ref, length(Jobs)]), + % Spawn individual workers, they will send results to gather_proc + scatter(GatherPid, Jobs), + % Wait for reply from gather_proc + receive + {scatter_gather_result, Ref, List} -> + List + end. + +scatter(ReplyPid, Jobs) -> + [spawn_opt(fun() -> single_job_proc(ReplyPid, Job, Idx) end, []) || Idx <- lists:seq(1, length(Jobs)), Job <- [lists:nth(Idx,Jobs)]]. + +single_job_proc(ReplyPid, Job, Idx) -> + Result = do_single_job(Job), + ReplyPid ! {done, Idx, Result}, + ok. + +do_single_job(Job) -> + {Fun, Args, ExitResult} = normalize_job(Job), + erlang:apply(Fun, Args). + +normalize_job(Fun) when is_function(Fun, 0) -> + {Fun, [], '$no_job_default'}; +normalize_job({Fun, Args}) when is_function(Fun), is_list(Args) -> + {Fun, Args, '$no_job_default'}; +normalize_job({Fun, Args, _ExitResult} = Job) when is_function(Fun), is_list(Args) -> + Job. + +gather_proc(ReplyPid, Ref, NumJobs) -> + List = gather(NumJobs, NumJobs, []), + ReplyPid ! {scatter_gather_result, Ref, List}, + ok. + +gather(0, _TotalJobs, Acc) -> + [Result || {_Idx, Result} <- lists:sort(Acc)]; +gather(NumJobs, TotalJobs, Acc) -> + receive + {done, Idx, Result} -> + gather(NumJobs - 1, TotalJobs, [{Idx, Result} | Acc]) + end. + diff --git a/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-3665323745 b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-3665323745 new file mode 100644 index 0000000..6acc422 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-3665323745 @@ -0,0 +1,149 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{taint_spawn,spawn_info_transfer_main,0},"taint_spawn.erl:36"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"taint_spawn.erl:37"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","taint_spawn.erl:37"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"taint_spawn.erl:38"}. +{apply,{{modeled_erlang,real_put,2},"taint_spawn.erl:38"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"taint_spawn.erl:39"}}. +{get,{"TaintedVal","taint_spawn.erl:39"}}. +{construct_pattern,{{cons},"taint_spawn.erl:39"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"taint_spawn.erl:39"}. +{apply,{{erlang,self,0},"taint_spawn.erl:39"}}. +{construct_pattern,{{cons},"taint_spawn.erl:39"}}. +{push,{notaint}}. +{push,{notaint}}. +{store,{"CfnArg1","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"taint_spawn.erl:39"}}. +{duplicate,{}}. +{send,{421,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:39"}}. +{get,{"CfnArg1","instrumentation"}}. +{call_fun,{erlang,spawn_link,3},"taint_spawn.erl:39"}. +{apply,{{erlang,spawn_link,3},"taint_spawn.erl:39"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"taint_spawn.erl:40"}. +{apply,{{modeled_erlang,real_put,2},"taint_spawn.erl:40"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"taint_spawn.erl:41"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"taint_spawn.erl:41"}. +{apply,{{erlang,self,0},"taint_spawn.erl:41"}}. +{construct_pattern,{{cons},"taint_spawn.erl:41"}}. +{push,{notaint}}. +{push,{notaint}}. +{store,{"CfnArg1","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"taint_spawn.erl:41"}}. +{duplicate,{}}. +{send,{422,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:41"}}. +{get,{"CfnArg1","instrumentation"}}. +{call_fun,{erlang,spawn_link,3},"taint_spawn.erl:41"}. +{apply,{{erlang,spawn_link,3},"taint_spawn.erl:41"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"taint_spawn.erl:42"}. +{apply,{{modeled_erlang,real_put,2},"taint_spawn.erl:42"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"TaintedVal","taint_spawn.erl:43"}}. +{construct_pattern,{{cons},"taint_spawn.erl:43"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"taint_spawn.erl:43"}. +{apply,{{erlang,self,0},"taint_spawn.erl:43"}}. +{construct_pattern,{{cons},"taint_spawn.erl:43"}}. +{push,{notaint}}. +{push,{notaint}}. +{store,{"CfnArg1","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"taint_spawn.erl:43"}}. +{duplicate,{}}. +{send,{423,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:43"}}. +{get,{"CfnArg1","instrumentation"}}. +{call_fun,{erlang,spawn_link,3},"taint_spawn.erl:43"}. +{apply,{{erlang,spawn_link,3},"taint_spawn.erl:43"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"taint_spawn.erl:44"}. +{apply,{{modeled_erlang,real_put,2},"taint_spawn.erl:44"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{taint_spawn,done,0},"taint_spawn.erl:45"}}. +{call_fun,{taint_spawn,done,0},"taint_spawn.erl:45"}. +{push_scope,{{taint_spawn,done,0},"taint_spawn.erl:31"}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64032>","taint_spawn.erl:32"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"taint_spawn.erl:31"}}. +{apply,{{taint_spawn,done,0},"taint_spawn.erl:45"}}. +{func_ret,{dropping_lambda_capture,"taint_spawn.erl:45"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{taint_spawn,done,0},"taint_spawn.erl:46"}}. +{call_fun,{taint_spawn,done,0},"taint_spawn.erl:46"}. +{push_scope,{{taint_spawn,done,0},"taint_spawn.erl:31"}}. +{receive_trace,{"#Ref<0.1321034574.60293122.65949>","taint_spawn.erl:32"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"taint_spawn.erl:31"}}. +{apply,{{taint_spawn,done,0},"taint_spawn.erl:46"}}. +{func_ret,{dropping_lambda_capture,"taint_spawn.erl:46"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{taint_spawn,done,0},"taint_spawn.erl:47"}}. +{call_fun,{taint_spawn,done,0},"taint_spawn.erl:47"}. +{push_scope,{{taint_spawn,done,0},"taint_spawn.erl:31"}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64033>","taint_spawn.erl:32"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"taint_spawn.erl:31"}}. +{apply,{{taint_spawn,done,0},"taint_spawn.erl:47"}}. +{func_ret,{dropping_lambda_capture,"taint_spawn.erl:47"}}. +{func_ret,{spawn_info_transfer_main,"taint_spawn.erl:36"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-421 b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-421 new file mode 100644 index 0000000..77e1137 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-421 @@ -0,0 +1,41 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{taint_spawn,sub_proc,3},"taint_spawn.erl:22"}}. +{receive_trace,{421,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:22"}}. +{restore_capture,{{taint_spawn,sub_proc,3},"taint_spawn.erl:22"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{pop,{}}. +{store,{"PushScopeTmp-1","instrumentation"}}. +{store,{"PushScopeTmp-2","instrumentation"}}. +{store,{"PushScopeTmp-3","instrumentation"}}. +{get,{"PushScopeTmp-1","instrumentation"}}. +{get,{"PushScopeTmp-2","instrumentation"}}. +{get,{"PushScopeTmp-3","instrumentation"}}. +{store,{"Parent","taint_spawn.erl:22"}}. +{store,{"TaintedArg","taint_spawn.erl:22"}}. +{store,{"NotTaintedArg","taint_spawn.erl:22"}}. +{get,{"TaintedArg","taint_spawn.erl:23"}}. +{sink,{"taint_spawn.erl:23"}}. +{pop,{}}. +{get,{"NotTaintedArg","taint_spawn.erl:24"}}. +{sink,{"taint_spawn.erl:24"}}. +{pop,{}}. +{get,{"Parent","taint_spawn.erl:25"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293122.65949>","taint_spawn.erl:25"}}. +{func_ret,{sub_proc,"taint_spawn.erl:22"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-422 b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-422 new file mode 100644 index 0000000..be3fd83 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-422 @@ -0,0 +1,30 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{taint_spawn,sub_proc1arg,2},"taint_spawn.erl:27"}}. +{receive_trace,{422,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:27"}}. +{restore_capture,{{taint_spawn,sub_proc1arg,2},"taint_spawn.erl:27"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{pop,{}}. +{store,{"PushScopeTmp-1","instrumentation"}}. +{store,{"PushScopeTmp-2","instrumentation"}}. +{get,{"PushScopeTmp-1","instrumentation"}}. +{get,{"PushScopeTmp-2","instrumentation"}}. +{store,{"Parent","taint_spawn.erl:27"}}. +{store,{"Arg","taint_spawn.erl:27"}}. +{get,{"Arg","taint_spawn.erl:28"}}. +{sink,{"taint_spawn.erl:28"}}. +{pop,{}}. +{get,{"Parent","taint_spawn.erl:29"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293123.64032>","taint_spawn.erl:29"}}. +{func_ret,{sub_proc1arg,"taint_spawn.erl:27"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-423 b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-423 new file mode 100644 index 0000000..c843afb --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_analaysis_instr-423 @@ -0,0 +1,30 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{taint_spawn,sub_proc1arg,2},"taint_spawn.erl:27"}}. +{receive_trace,{423,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:27"}}. +{restore_capture,{{taint_spawn,sub_proc1arg,2},"taint_spawn.erl:27"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{pop,{}}. +{store,{"PushScopeTmp-1","instrumentation"}}. +{store,{"PushScopeTmp-2","instrumentation"}}. +{get,{"PushScopeTmp-1","instrumentation"}}. +{get,{"PushScopeTmp-2","instrumentation"}}. +{store,{"Parent","taint_spawn.erl:27"}}. +{store,{"Arg","taint_spawn.erl:27"}}. +{get,{"Arg","taint_spawn.erl:28"}}. +{sink,{"taint_spawn.erl:28"}}. +{pop,{}}. +{get,{"Parent","taint_spawn.erl:29"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293123.64033>","taint_spawn.erl:29"}}. +{func_ret,{sub_proc1arg,"taint_spawn.erl:27"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-2553365084 b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-2553365084 new file mode 100644 index 0000000..763e5b6 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-2553365084 @@ -0,0 +1,55 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{taint_spawn,spawn_info_transfer_via_capture_main,0}, + "taint_spawn.erl:49"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"taint_spawn.erl:50"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","taint_spawn.erl:50"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"taint_spawn.erl:51"}. +{apply,{{erlang,self,0},"taint_spawn.erl:51"}}. +{duplicate,{}}. +{store,{"Parent","taint_spawn.erl:51"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"taint_spawn.erl:52"}. +{apply,{{modeled_erlang,real_put,2},"taint_spawn.erl:52"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{capture_closure,{["Parent","TaintedVal"]}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{push,{notaint}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"taint_spawn.erl:53"}}. +{duplicate,{}}. +{send,{421,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:53"}}. +{pop,{}}. +{call_fun,{erlang,spawn_link,1},"taint_spawn.erl:53"}. +{apply,{{erlang,spawn_link,1},"taint_spawn.erl:53"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{taint_spawn,done,0},"taint_spawn.erl:57"}}. +{call_fun,{taint_spawn,done,0},"taint_spawn.erl:57"}. +{push_scope,{{taint_spawn,done,0},"taint_spawn.erl:31"}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64552>","taint_spawn.erl:32"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"taint_spawn.erl:31"}}. +{apply,{{taint_spawn,done,0},"taint_spawn.erl:57"}}. +{func_ret,{dropping_lambda_capture,"taint_spawn.erl:57"}}. +{func_ret,{spawn_info_transfer_via_capture_main,"taint_spawn.erl:49"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-421 b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-421 new file mode 100644 index 0000000..e8ebaa6 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/spawn_taint_transfer_via_capture_analaysis_instr-421 @@ -0,0 +1,16 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{taint_spawn,lambda_taint_spawn_53_16_anon,0}, + "taint_spawn.erl:53"}}. +{receive_trace,{421,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"taint_spawn.erl:53"}}. +{restore_capture,{{taint_spawn,lambda_taint_spawn_53_16_anon,0}, + "taint_spawn.erl:53"}}. +{pop,{}}. +{get,{"TaintedVal","taint_spawn.erl:54"}}. +{sink,{"taint_spawn.erl:54"}}. +{pop,{}}. +{get,{"Parent","taint_spawn.erl:55"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293123.64552>","taint_spawn.erl:55"}}. +{func_ret,{lambda_taint_spawn_53_16_anon,"taint_spawn.erl:53"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/taint_spawn.erl b/finer_taint/test/parallel_taint_SUITE_data/taint_spawn.erl new file mode 100644 index 0000000..73cc892 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/taint_spawn.erl @@ -0,0 +1,57 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('taint_spawn'). +-export([ + spawn_info_transfer_main/0, spawn_info_transfer_via_capture_main/0, + sub_proc1arg/2, + sub_proc/3 +]). + +sub_proc(Parent, TaintedArg, NotTaintedArg) -> + finer_taint:sink(TaintedArg), + finer_taint:sink(NotTaintedArg), + Parent ! done. + +sub_proc1arg(Parent, Arg) -> + finer_taint:sink(Arg), + Parent ! done. + +done() -> + receive + done -> ok + end. + +spawn_info_transfer_main() -> + TaintedVal = finer_taint:source(42), + modeled_erlang:real_put(next_taint_pid, 421), + spawn_link(taint_spawn, sub_proc, [self(), TaintedVal, notainted]), + modeled_erlang:real_put(next_taint_pid, 422), + spawn_link(taint_spawn, sub_proc1arg, [self(), notainted]), + modeled_erlang:real_put(next_taint_pid, 423), + spawn_link(taint_spawn, sub_proc1arg, [self(), TaintedVal]), + modeled_erlang:real_put(next_taint_pid, undefined), + done(), + done(), + done(). + +spawn_info_transfer_via_capture_main() -> + TaintedVal = finer_taint:source(42), + Parent = self(), + modeled_erlang:real_put(next_taint_pid, 421), + spawn_link(fun() -> + finer_taint:sink(TaintedVal), + Parent ! done + end), + done(). diff --git a/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-2935128038 b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-2935128038 new file mode 100644 index 0000000..d10bfcc --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-2935128038 @@ -0,0 +1,960 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{example_gen_server,gen_server_test_main,0}, + "example_gen_server.erl:36"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"example_gen_server.erl:37"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"example_gen_server.erl:37"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"example_gen_server.erl:37"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"example_gen_server.erl:37"}. +{apply,{{modeled_erlang,real_put,2},"example_gen_server.erl:37"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{example_gen_server,start_link,0},"example_gen_server.erl:38"}. +{push_scope,{{example_gen_server,start_link,0},"example_gen_server.erl:59"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_gen_server,start_link,3},"example_gen_server.erl:60"}. +{push_scope,{{gen_server,start_link,3},"gen_server.erl:309"}}. +{store,{"Module","gen_server.erl:309"}}. +{store,{"Args","gen_server.erl:309"}}. +{store,{"Options","gen_server.erl:309"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen_server.erl:310"}}. +{get,{"Args","gen_server.erl:310"}}. +{get,{"Module","gen_server.erl:310"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_gen,start,5},"gen_server.erl:310"}. +{push_scope,{{gen,start,5},"gen.erl:116"}}. +{store,{"GenMod","gen.erl:116"}}. +{store,{"LinkP","gen.erl:116"}}. +{store,{"Mod","gen.erl:116"}}. +{store,{"Args","gen.erl:116"}}. +{store,{"Options","gen.erl:116"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_spawn,5},"gen.erl:117"}}. +{get,{"Options","gen.erl:117"}}. +{get,{"Args","gen.erl:117"}}. +{get,{"Mod","gen.erl:117"}}. +{get,{"LinkP","gen.erl:117"}}. +{get,{"GenMod","gen.erl:117"}}. +{call_fun,{gen,do_spawn,5},"gen.erl:117"}. +{push_scope,{{gen,do_spawn,5},"gen.erl:123"}}. +{store,{"GenMod","gen.erl:123"}}. +{pop,{}}. +{store,{"Mod","gen.erl:123"}}. +{store,{"Args","gen.erl:123"}}. +{store,{"Options","gen.erl:123"}}. +{push,{notaint}}. +{restore_capture,{{gen,timeout,1},"gen.erl:124"}}. +{get,{"Options","gen.erl:124"}}. +{call_fun,{gen,timeout,1},"gen.erl:124"}. +{push_scope,{{gen,timeout,1},"gen.erl:713"}}. +{store,{"Options","gen.erl:713"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:714"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:714"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:714"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{timeout,"gen.erl:713"}}. +{apply,{{gen,timeout,1},"gen.erl:124"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:124"}}. +{duplicate,{}}. +{store,{"Time","gen.erl:124"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen,spawn_opts,1},"gen.erl:128"}}. +{get,{"Options","gen.erl:128"}}. +{call_fun,{gen,spawn_opts,1},"gen.erl:128"}. +{push_scope,{{gen,spawn_opts,1},"gen.erl:721"}}. +{store,{"Options","gen.erl:721"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:722"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:722"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:722"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{spawn_opts,"gen.erl:721"}}. +{apply,{{gen,spawn_opts,1},"gen.erl:128"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:128"}}. +{get,{"Time","gen.erl:127"}}. +{push,{notaint}}. +{get,{"Options","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{get,{"Args","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{get,{"Mod","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:126"}. +{apply,{{erlang,self,0},"gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:126"}. +{apply,{{erlang,self,0},"gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{get,{"GenMod","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_proc_lib,start_link,5},"gen.erl:125"}. +{push_scope,{{proc_lib,start_link,5},"proc_lib.erl:355"}}. +{store,{"M","proc_lib.erl:355"}}. +{store,{"F","proc_lib.erl:355"}}. +{store,{"A","proc_lib.erl:355"}}. +{store,{"Timeout","proc_lib.erl:355"}}. +{store,{"SpawnOpts","proc_lib.erl:355"}}. +{push,{notaint}}. +{duplicate,{}}. +{store,{"Monitor","proc_lib.erl:356"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"SpawnOpts","proc_lib.erl:356"}}. +{get,{"Monitor","proc_lib.erl:356"}}. +{call_fun,{modeled_taint_lists,member,2},"proc_lib.erl:356"}. +{apply,{{modeled_taint_lists,member,2},"proc_lib.erl:356"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"SpawnOpts","proc_lib.erl:356"}}. +{push,{notaint}}. +{get,{"Monitor","proc_lib.erl:356"}}. +{call_fun,{modeled_taint_lists,keyfind,3},"proc_lib.erl:356"}. +{apply,{{modeled_taint_lists,keyfind,3},"proc_lib.erl:356"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,sync_start,2},"proc_lib.erl:357"}}. +{get,{"Timeout","proc_lib.erl:358"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"SpawnOpts","proc_lib.erl:358"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"proc_lib.erl:358"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"proc_lib.erl:358"}}. +{get,{"A","proc_lib.erl:358"}}. +{get,{"F","proc_lib.erl:358"}}. +{get,{"M","proc_lib.erl:358"}}. +{call_fun,{modeled_proc_lib,spawn_opt,4},"proc_lib.erl:358"}. +{push_scope,{{proc_lib,spawn_opt,4},"proc_lib.erl:189"}}. +{store,{"M","proc_lib.erl:189"}}. +{store,{"F","proc_lib.erl:189"}}. +{store,{"A","proc_lib.erl:189"}}. +{store,{"Opts","proc_lib.erl:189"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,get_my_name,0},"proc_lib.erl:190"}}. +{call_fun,{proc_lib,get_my_name,0},"proc_lib.erl:190"}. +{push_scope,{{proc_lib,get_my_name,0},"proc_lib.erl:797"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,proc_info,2},"proc_lib.erl:798"}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"proc_lib.erl:798"}. +{apply,{{erlang,self,0},"proc_lib.erl:798"}}. +{call_fun,{proc_lib,proc_info,2},"proc_lib.erl:798"}. +{push_scope,{{proc_lib,proc_info,2},"proc_lib.erl:811"}}. +{store,{"Pid","proc_lib.erl:811"}}. +{store,{"Item","proc_lib.erl:811"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Item","proc_lib.erl:812"}}. +{get,{"Pid","proc_lib.erl:812"}}. +{call_fun,{erlang,process_info,2},"proc_lib.erl:812"}. +{apply,{{erlang,process_info,2},"proc_lib.erl:812"}}. +{func_ret,{proc_info,"proc_lib.erl:811"}}. +{apply,{{proc_lib,proc_info,2},"proc_lib.erl:798"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:798"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"proc_lib.erl:800"}. +{apply,{{erlang,self,0},"proc_lib.erl:800"}}. +{func_ret,{get_my_name,"proc_lib.erl:797"}}. +{apply,{{proc_lib,get_my_name,0},"proc_lib.erl:190"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:190"}}. +{duplicate,{}}. +{store,{"Parent","proc_lib.erl:190"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,get_ancestors,0},"proc_lib.erl:191"}}. +{call_fun,{proc_lib,get_ancestors,0},"proc_lib.erl:191"}. +{push_scope,{{proc_lib,get_ancestors,0},"proc_lib.erl:805"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mget,1},"proc_lib.erl:806"}. +{push_scope,{{modeled_erlang,mget,1},"modeled_erlang.erl:163"}}. +{store,{"Key","modeled_erlang.erl:163"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:164"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:164"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:165"}}. +{get,{"Key","modeled_erlang.erl:165"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}}. +{func_ret,{mget,"modeled_erlang.erl:163"}}. +{apply,{{modeled_erlang,mget,1},"proc_lib.erl:806"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{get_ancestors,"proc_lib.erl:805"}}. +{apply,{{proc_lib,get_ancestors,0},"proc_lib.erl:191"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:191"}}. +{duplicate,{}}. +{store,{"Ancestors","proc_lib.erl:191"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Opts","proc_lib.erl:192"}}. +{push,{notaint}}. +{get,{"A","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"F","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"M","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"Ancestors","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"Parent","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{push,{notaint}}. +{push,{notaint}}. +{store,{"CfnArg1","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"proc_lib.erl:192"}}. +{duplicate,{}}. +{send,{521,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"proc_lib.erl:192"}}. +{get,{"CfnArg1","instrumentation"}}. +{call_fun,{erlang,spawn_opt,4},"proc_lib.erl:192"}. +{apply,{{erlang,spawn_opt,4},"proc_lib.erl:192"}}. +{func_ret,{spawn_opt,"proc_lib.erl:189"}}. +{apply,{{modeled_proc_lib,spawn_opt,4},"proc_lib.erl:358"}}. +{call_fun,{proc_lib,sync_start,2},"proc_lib.erl:357"}. +{push_scope,{{proc_lib,sync_start,2},"proc_lib.erl:309"}}. +{deconstruct_pattern,{{tuple,2},"proc_lib.erl:309"}}. +{store,{"Pid","proc_lib.erl:309"}}. +{store,{"Ref","proc_lib.erl:309"}}. +{store,{"Timeout","proc_lib.erl:309"}}. +{get,{"Timeout","proc_lib.erl:321"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293121.66553>","proc_lib.erl:310"}}. +{deconstruct_pattern,{{tuple,3},"proc_lib.erl:311"}}. +{pop,{}}. +{store,{"Pid","proc_lib.erl:311"}}. +{store,{"Return","proc_lib.erl:311"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"proc_lib.erl:312"}}. +{get,{"Ref","proc_lib.erl:312"}}. +{call_fun,{erlang,demonitor,2},"proc_lib.erl:312"}. +{apply,{{erlang,demonitor,2},"proc_lib.erl:312"}}. +{pop,{}}. +{get,{"Return","proc_lib.erl:313"}}. +{func_ret,{sync_start,"proc_lib.erl:309"}}. +{apply,{{proc_lib,sync_start,2},"proc_lib.erl:357"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:357"}}. +{func_ret,{start_link,"proc_lib.erl:355"}}. +{apply,{{modeled_proc_lib,start_link,5},"gen.erl:125"}}. +{func_ret,{do_spawn,"gen.erl:123"}}. +{apply,{{gen,do_spawn,5},"gen.erl:117"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:117"}}. +{func_ret,{start,"gen.erl:116"}}. +{apply,{{modeled_gen,start,5},"gen_server.erl:310"}}. +{func_ret,{start_link,"gen_server.erl:309"}}. +{apply,{{modeled_gen_server,start_link,3},"example_gen_server.erl:60"}}. +{func_ret,{start_link,"example_gen_server.erl:59"}}. +{apply,{{example_gen_server,start_link,0},"example_gen_server.erl:38"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"example_gen_server.erl:38"}}. +{pop,{}}. +{store,{"Pid","example_gen_server.erl:38"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"example_gen_server.erl:39"}. +{apply,{{modeled_erlang,real_put,2},"example_gen_server.erl:39"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{"example_gen_server.erl:40"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","example_gen_server.erl:40"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"TaintedVal","example_gen_server.erl:41"}}. +{get,{"Pid","example_gen_server.erl:41"}}. +{call_fun,{example_gen_server,store_value,2},"example_gen_server.erl:41"}. +{push_scope,{{example_gen_server,store_value,2},"example_gen_server.erl:65"}}. +{store,{"Pid","example_gen_server.erl:65"}}. +{store,{"Value","example_gen_server.erl:65"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Value","example_gen_server.erl:66"}}. +{construct_pattern,{{tuple,2},"example_gen_server.erl:66"}}. +{get,{"Pid","example_gen_server.erl:66"}}. +{call_fun,{modeled_gen_server,call,2},"example_gen_server.erl:66"}. +{push_scope,{{gen_server,call,2},"gen_server.erl:381"}}. +{store,{"ServerRef","gen_server.erl:381"}}. +{store,{"Request","gen_server.erl:381"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Request","gen_server.erl:382"}}. +{push,{notaint}}. +{get,{"ServerRef","gen_server.erl:382"}}. +{call_fun,{modeled_gen,call,3},"gen_server.erl:382"}. +{push_scope,{{gen,call,3},"gen.erl:214"}}. +{store,{"Process","gen.erl:214"}}. +{store,{"Label","gen.erl:214"}}. +{store,{"Request","gen.erl:214"}}. +{push,{notaint}}. +{restore_capture,{{gen,call,4},"gen.erl:215"}}. +{push,{notaint}}. +{get,{"Request","gen.erl:215"}}. +{get,{"Label","gen.erl:215"}}. +{get,{"Process","gen.erl:215"}}. +{call_fun,{gen,call,4},"gen.erl:215"}. +{push_scope,{{gen,call,4},"gen.erl:218"}}. +{store,{"Process","gen.erl:218"}}. +{store,{"Label","gen.erl:218"}}. +{store,{"Request","gen.erl:218"}}. +{store,{"Timeout","gen.erl:218"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_call,4},"gen.erl:220"}}. +{get,{"Timeout","gen.erl:220"}}. +{get,{"Request","gen.erl:220"}}. +{get,{"Label","gen.erl:220"}}. +{get,{"Process","gen.erl:220"}}. +{call_fun,{gen,do_call,4},"gen.erl:220"}. +{push_scope,{{gen,do_call,4},"gen.erl:248"}}. +{store,{"Process","gen.erl:248"}}. +{store,{"Label","gen.erl:248"}}. +{store,{"Request","gen.erl:248"}}. +{store,{"Timeout","gen.erl:248"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen.erl:249"}}. +{construct_pattern,{{cons},"gen.erl:249"}}. +{get,{"Process","gen.erl:249"}}. +{push,{notaint}}. +{call_fun,{erlang,monitor,3},"gen.erl:249"}. +{apply,{{erlang,monitor,3},"gen.erl:249"}}. +{duplicate,{}}. +{store,{"Mref","gen.erl:249"}}. +{pop,{}}. +{get,{"Mref","gen.erl:251"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:251"}}. +{duplicate,{}}. +{store,{"Tag","gen.erl:251"}}. +{pop,{}}. +{get,{"Process","gen.erl:259"}}. +{get,{"Label","gen.erl:259"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:259"}. +{apply,{{erlang,self,0},"gen.erl:259"}}. +{get,{"Tag","gen.erl:259"}}. +{construct_pattern,{{tuple,2},"gen.erl:259"}}. +{get,{"Request","gen.erl:259"}}. +{construct_pattern,{{tuple,3},"gen.erl:259"}}. +{send,{"#Ref<0.1321034574.60293123.64957>","gen.erl:259"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:259"}}. +{pop,{}}. +{get,{"Timeout","gen.erl:270"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64958>","gen.erl:261"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:262"}}. +{deconstruct_pattern,{{cons},"gen.erl:262"}}. +{pop,{}}. +{store,{"Mref","gen.erl:262"}}. +{store,{"Reply","gen.erl:262"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:263"}}. +{get,{"Mref","gen.erl:263"}}. +{call_fun,{erlang,demonitor,2},"gen.erl:263"}. +{apply,{{erlang,demonitor,2},"gen.erl:263"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Reply","gen.erl:264"}}. +{construct_pattern,{{tuple,2},"gen.erl:264"}}. +{func_ret,{do_call,"gen.erl:248"}}. +{apply,{{gen,do_call,4},"gen.erl:220"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:220"}}. +{func_ret,{call,"gen.erl:218"}}. +{apply,{{gen,call,4},"gen.erl:215"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:215"}}. +{func_ret,{call,"gen.erl:214"}}. +{apply,{{modeled_gen,call,3},"gen_server.erl:382"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:383"}}. +{pop,{}}. +{store,{"Res","gen_server.erl:383"}}. +{get,{"Res","gen_server.erl:384"}}. +{func_ret,{call,"gen_server.erl:381"}}. +{apply,{{modeled_gen_server,call,2},"example_gen_server.erl:66"}}. +{func_ret,{store_value,"example_gen_server.erl:65"}}. +{apply,{{example_gen_server,store_value,2},"example_gen_server.erl:41"}}. +{sink,{"example_gen_server.erl:41"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{example_gen_server,id_function,1}, + "example_gen_server.erl:42"}}. +{push,{notaint}}. +{call_fun,{example_gen_server,id_function,1},"example_gen_server.erl:42"}. +{push_scope,{{example_gen_server,id_function,1},"example_gen_server.erl:33"}}. +{store,{"X","example_gen_server.erl:33"}}. +{get,{"X","example_gen_server.erl:33"}}. +{func_ret,{id_function,"example_gen_server.erl:33"}}. +{apply,{{example_gen_server,id_function,1},"example_gen_server.erl:42"}}. +{func_ret,{dropping_lambda_capture,"example_gen_server.erl:42"}}. +{get,{"Pid","example_gen_server.erl:42"}}. +{call_fun,{example_gen_server,store_value,2},"example_gen_server.erl:42"}. +{push_scope,{{example_gen_server,store_value,2},"example_gen_server.erl:65"}}. +{store,{"Pid","example_gen_server.erl:65"}}. +{store,{"Value","example_gen_server.erl:65"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Value","example_gen_server.erl:66"}}. +{construct_pattern,{{tuple,2},"example_gen_server.erl:66"}}. +{get,{"Pid","example_gen_server.erl:66"}}. +{call_fun,{modeled_gen_server,call,2},"example_gen_server.erl:66"}. +{push_scope,{{gen_server,call,2},"gen_server.erl:381"}}. +{store,{"ServerRef","gen_server.erl:381"}}. +{store,{"Request","gen_server.erl:381"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Request","gen_server.erl:382"}}. +{push,{notaint}}. +{get,{"ServerRef","gen_server.erl:382"}}. +{call_fun,{modeled_gen,call,3},"gen_server.erl:382"}. +{push_scope,{{gen,call,3},"gen.erl:214"}}. +{store,{"Process","gen.erl:214"}}. +{store,{"Label","gen.erl:214"}}. +{store,{"Request","gen.erl:214"}}. +{push,{notaint}}. +{restore_capture,{{gen,call,4},"gen.erl:215"}}. +{push,{notaint}}. +{get,{"Request","gen.erl:215"}}. +{get,{"Label","gen.erl:215"}}. +{get,{"Process","gen.erl:215"}}. +{call_fun,{gen,call,4},"gen.erl:215"}. +{push_scope,{{gen,call,4},"gen.erl:218"}}. +{store,{"Process","gen.erl:218"}}. +{store,{"Label","gen.erl:218"}}. +{store,{"Request","gen.erl:218"}}. +{store,{"Timeout","gen.erl:218"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_call,4},"gen.erl:220"}}. +{get,{"Timeout","gen.erl:220"}}. +{get,{"Request","gen.erl:220"}}. +{get,{"Label","gen.erl:220"}}. +{get,{"Process","gen.erl:220"}}. +{call_fun,{gen,do_call,4},"gen.erl:220"}. +{push_scope,{{gen,do_call,4},"gen.erl:248"}}. +{store,{"Process","gen.erl:248"}}. +{store,{"Label","gen.erl:248"}}. +{store,{"Request","gen.erl:248"}}. +{store,{"Timeout","gen.erl:248"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen.erl:249"}}. +{construct_pattern,{{cons},"gen.erl:249"}}. +{get,{"Process","gen.erl:249"}}. +{push,{notaint}}. +{call_fun,{erlang,monitor,3},"gen.erl:249"}. +{apply,{{erlang,monitor,3},"gen.erl:249"}}. +{duplicate,{}}. +{store,{"Mref","gen.erl:249"}}. +{pop,{}}. +{get,{"Mref","gen.erl:251"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:251"}}. +{duplicate,{}}. +{store,{"Tag","gen.erl:251"}}. +{pop,{}}. +{get,{"Process","gen.erl:259"}}. +{get,{"Label","gen.erl:259"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:259"}. +{apply,{{erlang,self,0},"gen.erl:259"}}. +{get,{"Tag","gen.erl:259"}}. +{construct_pattern,{{tuple,2},"gen.erl:259"}}. +{get,{"Request","gen.erl:259"}}. +{construct_pattern,{{tuple,3},"gen.erl:259"}}. +{send,{"#Ref<0.1321034574.60293121.66555>","gen.erl:259"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:259"}}. +{pop,{}}. +{get,{"Timeout","gen.erl:270"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293121.66556>","gen.erl:261"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:262"}}. +{deconstruct_pattern,{{cons},"gen.erl:262"}}. +{pop,{}}. +{store,{"Mref","gen.erl:262"}}. +{store,{"Reply","gen.erl:262"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:263"}}. +{get,{"Mref","gen.erl:263"}}. +{call_fun,{erlang,demonitor,2},"gen.erl:263"}. +{apply,{{erlang,demonitor,2},"gen.erl:263"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Reply","gen.erl:264"}}. +{construct_pattern,{{tuple,2},"gen.erl:264"}}. +{func_ret,{do_call,"gen.erl:248"}}. +{apply,{{gen,do_call,4},"gen.erl:220"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:220"}}. +{func_ret,{call,"gen.erl:218"}}. +{apply,{{gen,call,4},"gen.erl:215"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:215"}}. +{func_ret,{call,"gen.erl:214"}}. +{apply,{{modeled_gen,call,3},"gen_server.erl:382"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:383"}}. +{pop,{}}. +{store,{"Res","gen_server.erl:383"}}. +{get,{"Res","gen_server.erl:384"}}. +{func_ret,{call,"gen_server.erl:381"}}. +{apply,{{modeled_gen_server,call,2},"example_gen_server.erl:66"}}. +{func_ret,{store_value,"example_gen_server.erl:65"}}. +{apply,{{example_gen_server,store_value,2},"example_gen_server.erl:42"}}. +{sink,{"example_gen_server.erl:42"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{example_gen_server,other_id_function,1}, + "example_gen_server.erl:43"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Pid","example_gen_server.erl:43"}}. +{call_fun,{example_gen_server,pop,1},"example_gen_server.erl:43"}. +{push_scope,{{example_gen_server,pop,1},"example_gen_server.erl:68"}}. +{store,{"Pid","example_gen_server.erl:68"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pid","example_gen_server.erl:69"}}. +{call_fun,{modeled_gen_server,call,2},"example_gen_server.erl:69"}. +{push_scope,{{gen_server,call,2},"gen_server.erl:381"}}. +{store,{"ServerRef","gen_server.erl:381"}}. +{store,{"Request","gen_server.erl:381"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Request","gen_server.erl:382"}}. +{push,{notaint}}. +{get,{"ServerRef","gen_server.erl:382"}}. +{call_fun,{modeled_gen,call,3},"gen_server.erl:382"}. +{push_scope,{{gen,call,3},"gen.erl:214"}}. +{store,{"Process","gen.erl:214"}}. +{store,{"Label","gen.erl:214"}}. +{store,{"Request","gen.erl:214"}}. +{push,{notaint}}. +{restore_capture,{{gen,call,4},"gen.erl:215"}}. +{push,{notaint}}. +{get,{"Request","gen.erl:215"}}. +{get,{"Label","gen.erl:215"}}. +{get,{"Process","gen.erl:215"}}. +{call_fun,{gen,call,4},"gen.erl:215"}. +{push_scope,{{gen,call,4},"gen.erl:218"}}. +{store,{"Process","gen.erl:218"}}. +{store,{"Label","gen.erl:218"}}. +{store,{"Request","gen.erl:218"}}. +{store,{"Timeout","gen.erl:218"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_call,4},"gen.erl:220"}}. +{get,{"Timeout","gen.erl:220"}}. +{get,{"Request","gen.erl:220"}}. +{get,{"Label","gen.erl:220"}}. +{get,{"Process","gen.erl:220"}}. +{call_fun,{gen,do_call,4},"gen.erl:220"}. +{push_scope,{{gen,do_call,4},"gen.erl:248"}}. +{store,{"Process","gen.erl:248"}}. +{store,{"Label","gen.erl:248"}}. +{store,{"Request","gen.erl:248"}}. +{store,{"Timeout","gen.erl:248"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen.erl:249"}}. +{construct_pattern,{{cons},"gen.erl:249"}}. +{get,{"Process","gen.erl:249"}}. +{push,{notaint}}. +{call_fun,{erlang,monitor,3},"gen.erl:249"}. +{apply,{{erlang,monitor,3},"gen.erl:249"}}. +{duplicate,{}}. +{store,{"Mref","gen.erl:249"}}. +{pop,{}}. +{get,{"Mref","gen.erl:251"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:251"}}. +{duplicate,{}}. +{store,{"Tag","gen.erl:251"}}. +{pop,{}}. +{get,{"Process","gen.erl:259"}}. +{get,{"Label","gen.erl:259"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:259"}. +{apply,{{erlang,self,0},"gen.erl:259"}}. +{get,{"Tag","gen.erl:259"}}. +{construct_pattern,{{tuple,2},"gen.erl:259"}}. +{get,{"Request","gen.erl:259"}}. +{construct_pattern,{{tuple,3},"gen.erl:259"}}. +{send,{"#Ref<0.1321034574.60293123.64960>","gen.erl:259"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:259"}}. +{pop,{}}. +{get,{"Timeout","gen.erl:270"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64961>","gen.erl:261"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:262"}}. +{deconstruct_pattern,{{cons},"gen.erl:262"}}. +{pop,{}}. +{store,{"Mref","gen.erl:262"}}. +{store,{"Reply","gen.erl:262"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:263"}}. +{get,{"Mref","gen.erl:263"}}. +{call_fun,{erlang,demonitor,2},"gen.erl:263"}. +{apply,{{erlang,demonitor,2},"gen.erl:263"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Reply","gen.erl:264"}}. +{construct_pattern,{{tuple,2},"gen.erl:264"}}. +{func_ret,{do_call,"gen.erl:248"}}. +{apply,{{gen,do_call,4},"gen.erl:220"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:220"}}. +{func_ret,{call,"gen.erl:218"}}. +{apply,{{gen,call,4},"gen.erl:215"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:215"}}. +{func_ret,{call,"gen.erl:214"}}. +{apply,{{modeled_gen,call,3},"gen_server.erl:382"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:383"}}. +{pop,{}}. +{store,{"Res","gen_server.erl:383"}}. +{get,{"Res","gen_server.erl:384"}}. +{func_ret,{call,"gen_server.erl:381"}}. +{apply,{{modeled_gen_server,call,2},"example_gen_server.erl:69"}}. +{func_ret,{pop,"example_gen_server.erl:68"}}. +{apply,{{example_gen_server,pop,1},"example_gen_server.erl:43"}}. +{call_fun,{example_gen_server,other_id_function,1}, + "example_gen_server.erl:43"}. +{push_scope,{{example_gen_server,other_id_function,1}, + "example_gen_server.erl:34"}}. +{store,{"X","example_gen_server.erl:34"}}. +{get,{"X","example_gen_server.erl:34"}}. +{func_ret,{other_id_function,"example_gen_server.erl:34"}}. +{apply,{{example_gen_server,other_id_function,1},"example_gen_server.erl:43"}}. +{func_ret,{dropping_lambda_capture,"example_gen_server.erl:43"}}. +{sink,{"example_gen_server.erl:43"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Pid","example_gen_server.erl:44"}}. +{call_fun,{example_gen_server,pop,1},"example_gen_server.erl:44"}. +{push_scope,{{example_gen_server,pop,1},"example_gen_server.erl:68"}}. +{store,{"Pid","example_gen_server.erl:68"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pid","example_gen_server.erl:69"}}. +{call_fun,{modeled_gen_server,call,2},"example_gen_server.erl:69"}. +{push_scope,{{gen_server,call,2},"gen_server.erl:381"}}. +{store,{"ServerRef","gen_server.erl:381"}}. +{store,{"Request","gen_server.erl:381"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Request","gen_server.erl:382"}}. +{push,{notaint}}. +{get,{"ServerRef","gen_server.erl:382"}}. +{call_fun,{modeled_gen,call,3},"gen_server.erl:382"}. +{push_scope,{{gen,call,3},"gen.erl:214"}}. +{store,{"Process","gen.erl:214"}}. +{store,{"Label","gen.erl:214"}}. +{store,{"Request","gen.erl:214"}}. +{push,{notaint}}. +{restore_capture,{{gen,call,4},"gen.erl:215"}}. +{push,{notaint}}. +{get,{"Request","gen.erl:215"}}. +{get,{"Label","gen.erl:215"}}. +{get,{"Process","gen.erl:215"}}. +{call_fun,{gen,call,4},"gen.erl:215"}. +{push_scope,{{gen,call,4},"gen.erl:218"}}. +{store,{"Process","gen.erl:218"}}. +{store,{"Label","gen.erl:218"}}. +{store,{"Request","gen.erl:218"}}. +{store,{"Timeout","gen.erl:218"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_call,4},"gen.erl:220"}}. +{get,{"Timeout","gen.erl:220"}}. +{get,{"Request","gen.erl:220"}}. +{get,{"Label","gen.erl:220"}}. +{get,{"Process","gen.erl:220"}}. +{call_fun,{gen,do_call,4},"gen.erl:220"}. +{push_scope,{{gen,do_call,4},"gen.erl:248"}}. +{store,{"Process","gen.erl:248"}}. +{store,{"Label","gen.erl:248"}}. +{store,{"Request","gen.erl:248"}}. +{store,{"Timeout","gen.erl:248"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen.erl:249"}}. +{construct_pattern,{{cons},"gen.erl:249"}}. +{get,{"Process","gen.erl:249"}}. +{push,{notaint}}. +{call_fun,{erlang,monitor,3},"gen.erl:249"}. +{apply,{{erlang,monitor,3},"gen.erl:249"}}. +{duplicate,{}}. +{store,{"Mref","gen.erl:249"}}. +{pop,{}}. +{get,{"Mref","gen.erl:251"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:251"}}. +{duplicate,{}}. +{store,{"Tag","gen.erl:251"}}. +{pop,{}}. +{get,{"Process","gen.erl:259"}}. +{get,{"Label","gen.erl:259"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:259"}. +{apply,{{erlang,self,0},"gen.erl:259"}}. +{get,{"Tag","gen.erl:259"}}. +{construct_pattern,{{tuple,2},"gen.erl:259"}}. +{get,{"Request","gen.erl:259"}}. +{construct_pattern,{{tuple,3},"gen.erl:259"}}. +{send,{"#Ref<0.1321034574.60293123.64963>","gen.erl:259"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:259"}}. +{pop,{}}. +{get,{"Timeout","gen.erl:270"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64964>","gen.erl:261"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:262"}}. +{deconstruct_pattern,{{cons},"gen.erl:262"}}. +{pop,{}}. +{store,{"Mref","gen.erl:262"}}. +{store,{"Reply","gen.erl:262"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:263"}}. +{get,{"Mref","gen.erl:263"}}. +{call_fun,{erlang,demonitor,2},"gen.erl:263"}. +{apply,{{erlang,demonitor,2},"gen.erl:263"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Reply","gen.erl:264"}}. +{construct_pattern,{{tuple,2},"gen.erl:264"}}. +{func_ret,{do_call,"gen.erl:248"}}. +{apply,{{gen,do_call,4},"gen.erl:220"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:220"}}. +{func_ret,{call,"gen.erl:218"}}. +{apply,{{gen,call,4},"gen.erl:215"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:215"}}. +{func_ret,{call,"gen.erl:214"}}. +{apply,{{modeled_gen,call,3},"gen_server.erl:382"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:383"}}. +{pop,{}}. +{store,{"Res","gen_server.erl:383"}}. +{get,{"Res","gen_server.erl:384"}}. +{func_ret,{call,"gen_server.erl:381"}}. +{apply,{{modeled_gen_server,call,2},"example_gen_server.erl:69"}}. +{func_ret,{pop,"example_gen_server.erl:68"}}. +{apply,{{example_gen_server,pop,1},"example_gen_server.erl:44"}}. +{sink,{"example_gen_server.erl:44"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Pid","example_gen_server.erl:45"}}. +{call_fun,{example_gen_server,stop,1},"example_gen_server.erl:45"}. +{push_scope,{{example_gen_server,stop,1},"example_gen_server.erl:71"}}. +{store,{"Pid","example_gen_server.erl:71"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pid","example_gen_server.erl:72"}}. +{call_fun,{modeled_gen_server,call,2},"example_gen_server.erl:72"}. +{push_scope,{{gen_server,call,2},"gen_server.erl:381"}}. +{store,{"ServerRef","gen_server.erl:381"}}. +{store,{"Request","gen_server.erl:381"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Request","gen_server.erl:382"}}. +{push,{notaint}}. +{get,{"ServerRef","gen_server.erl:382"}}. +{call_fun,{modeled_gen,call,3},"gen_server.erl:382"}. +{push_scope,{{gen,call,3},"gen.erl:214"}}. +{store,{"Process","gen.erl:214"}}. +{store,{"Label","gen.erl:214"}}. +{store,{"Request","gen.erl:214"}}. +{push,{notaint}}. +{restore_capture,{{gen,call,4},"gen.erl:215"}}. +{push,{notaint}}. +{get,{"Request","gen.erl:215"}}. +{get,{"Label","gen.erl:215"}}. +{get,{"Process","gen.erl:215"}}. +{call_fun,{gen,call,4},"gen.erl:215"}. +{push_scope,{{gen,call,4},"gen.erl:218"}}. +{store,{"Process","gen.erl:218"}}. +{store,{"Label","gen.erl:218"}}. +{store,{"Request","gen.erl:218"}}. +{store,{"Timeout","gen.erl:218"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_call,4},"gen.erl:220"}}. +{get,{"Timeout","gen.erl:220"}}. +{get,{"Request","gen.erl:220"}}. +{get,{"Label","gen.erl:220"}}. +{get,{"Process","gen.erl:220"}}. +{call_fun,{gen,do_call,4},"gen.erl:220"}. +{push_scope,{{gen,do_call,4},"gen.erl:248"}}. +{store,{"Process","gen.erl:248"}}. +{store,{"Label","gen.erl:248"}}. +{store,{"Request","gen.erl:248"}}. +{store,{"Timeout","gen.erl:248"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen.erl:249"}}. +{construct_pattern,{{cons},"gen.erl:249"}}. +{get,{"Process","gen.erl:249"}}. +{push,{notaint}}. +{call_fun,{erlang,monitor,3},"gen.erl:249"}. +{apply,{{erlang,monitor,3},"gen.erl:249"}}. +{duplicate,{}}. +{store,{"Mref","gen.erl:249"}}. +{pop,{}}. +{get,{"Mref","gen.erl:251"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:251"}}. +{duplicate,{}}. +{store,{"Tag","gen.erl:251"}}. +{pop,{}}. +{get,{"Process","gen.erl:259"}}. +{get,{"Label","gen.erl:259"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:259"}. +{apply,{{erlang,self,0},"gen.erl:259"}}. +{get,{"Tag","gen.erl:259"}}. +{construct_pattern,{{tuple,2},"gen.erl:259"}}. +{get,{"Request","gen.erl:259"}}. +{construct_pattern,{{tuple,3},"gen.erl:259"}}. +{send,{"#Ref<0.1321034574.60293123.64966>","gen.erl:259"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:259"}}. +{pop,{}}. +{get,{"Timeout","gen.erl:270"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64967>","gen.erl:261"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:262"}}. +{deconstruct_pattern,{{cons},"gen.erl:262"}}. +{pop,{}}. +{store,{"Mref","gen.erl:262"}}. +{store,{"Reply","gen.erl:262"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:263"}}. +{get,{"Mref","gen.erl:263"}}. +{call_fun,{erlang,demonitor,2},"gen.erl:263"}. +{apply,{{erlang,demonitor,2},"gen.erl:263"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Reply","gen.erl:264"}}. +{construct_pattern,{{tuple,2},"gen.erl:264"}}. +{func_ret,{do_call,"gen.erl:248"}}. +{apply,{{gen,do_call,4},"gen.erl:220"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:220"}}. +{func_ret,{call,"gen.erl:218"}}. +{apply,{{gen,call,4},"gen.erl:215"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:215"}}. +{func_ret,{call,"gen.erl:214"}}. +{apply,{{modeled_gen,call,3},"gen_server.erl:382"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:383"}}. +{pop,{}}. +{store,{"Res","gen_server.erl:383"}}. +{get,{"Res","gen_server.erl:384"}}. +{func_ret,{call,"gen_server.erl:381"}}. +{apply,{{modeled_gen_server,call,2},"example_gen_server.erl:72"}}. +{func_ret,{stop,"example_gen_server.erl:71"}}. +{apply,{{example_gen_server,stop,1},"example_gen_server.erl:45"}}. +{func_ret,{gen_server_test_main,"example_gen_server.erl:36"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-521 b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-521 new file mode 100644 index 0000000..cffc549 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_analaysis_instr-521 @@ -0,0 +1,1569 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{proc_lib,init_p,5},"proc_lib.erl:234"}}. +{receive_trace,{521,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"proc_lib.erl:234"}}. +{restore_capture,{{proc_lib,init_p,5},"proc_lib.erl:234"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{pop,{}}. +{store,{"PushScopeTmp-1","instrumentation"}}. +{store,{"PushScopeTmp-2","instrumentation"}}. +{store,{"PushScopeTmp-3","instrumentation"}}. +{store,{"PushScopeTmp-4","instrumentation"}}. +{store,{"PushScopeTmp-5","instrumentation"}}. +{get,{"PushScopeTmp-1","instrumentation"}}. +{get,{"PushScopeTmp-2","instrumentation"}}. +{get,{"PushScopeTmp-3","instrumentation"}}. +{get,{"PushScopeTmp-4","instrumentation"}}. +{get,{"PushScopeTmp-5","instrumentation"}}. +{store,{"Parent","proc_lib.erl:234"}}. +{store,{"Ancestors","proc_lib.erl:234"}}. +{store,{"M","proc_lib.erl:234"}}. +{store,{"F","proc_lib.erl:234"}}. +{store,{"A","proc_lib.erl:234"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Ancestors","proc_lib.erl:235"}}. +{get,{"Parent","proc_lib.erl:235"}}. +{construct_pattern,{{cons},"proc_lib.erl:235"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mput,2},"proc_lib.erl:235"}. +{push_scope,{{modeled_erlang,mput,2},"modeled_erlang.erl:155"}}. +{store,{"Key","modeled_erlang.erl:155"}}. +{store,{"Value","modeled_erlang.erl:155"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:156"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:156"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:157"}}. +{get,{"Key","modeled_erlang.erl:157"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}}. +{duplicate,{}}. +{store,{"ReturnValue","modeled_erlang.erl:157"}}. +{pop,{}}. +{get,{"Key","modeled_erlang.erl:158"}}. +{get,{"Value","modeled_erlang.erl:158"}}. +{get,{"Pd","modeled_erlang.erl:158"}}. +{construct_pattern,{{map,['$ancestors']},"modeled_erlang.erl:158"}}. +{duplicate,{}}. +{store,{"NewDict","modeled_erlang.erl:158"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{get,{"NewDict","modeled_erlang.erl:159"}}. +{call_fun,{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}. +{apply,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:159"}}. +{pop,{}}. +{get,{"ReturnValue","modeled_erlang.erl:160"}}. +{func_ret,{mput,"modeled_erlang.erl:155"}}. +{apply,{{modeled_erlang,mput,2},"proc_lib.erl:235"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,trans_init,3},"proc_lib.erl:236"}}. +{get,{"A","proc_lib.erl:236"}}. +{get,{"F","proc_lib.erl:236"}}. +{get,{"M","proc_lib.erl:236"}}. +{call_fun,{proc_lib,trans_init,3},"proc_lib.erl:236"}. +{push_scope,{{proc_lib,trans_init,3},"proc_lib.erl:573"}}. +{store,{"M","proc_lib.erl:573"}}. +{store,{"F","proc_lib.erl:573"}}. +{store,{"A","proc_lib.erl:573"}}. +{get,{"M","proc_lib.erl:574"}}. +{get,{"F","proc_lib.erl:574"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"A","proc_lib.erl:574"}}. +{call_fun,{erlang,length,1},"proc_lib.erl:574"}. +{apply,{{erlang,length,1},"proc_lib.erl:574"}}. +{construct_pattern,{{tuple,3},"proc_lib.erl:574"}}. +{func_ret,{trans_init,"proc_lib.erl:573"}}. +{apply,{{proc_lib,trans_init,3},"proc_lib.erl:236"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:236"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mput,2},"proc_lib.erl:236"}. +{push_scope,{{modeled_erlang,mput,2},"modeled_erlang.erl:155"}}. +{store,{"Key","modeled_erlang.erl:155"}}. +{store,{"Value","modeled_erlang.erl:155"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:156"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:156"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:157"}}. +{get,{"Key","modeled_erlang.erl:157"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}}. +{duplicate,{}}. +{store,{"ReturnValue","modeled_erlang.erl:157"}}. +{pop,{}}. +{get,{"Key","modeled_erlang.erl:158"}}. +{get,{"Value","modeled_erlang.erl:158"}}. +{get,{"Pd","modeled_erlang.erl:158"}}. +{construct_pattern,{{map,['$initial_call']},"modeled_erlang.erl:158"}}. +{duplicate,{}}. +{store,{"NewDict","modeled_erlang.erl:158"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{get,{"NewDict","modeled_erlang.erl:159"}}. +{call_fun,{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}. +{apply,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:159"}}. +{pop,{}}. +{get,{"ReturnValue","modeled_erlang.erl:160"}}. +{func_ret,{mput,"modeled_erlang.erl:155"}}. +{apply,{{modeled_erlang,mput,2},"proc_lib.erl:236"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,init_p_do_apply,3},"proc_lib.erl:237"}}. +{get,{"A","proc_lib.erl:237"}}. +{get,{"F","proc_lib.erl:237"}}. +{get,{"M","proc_lib.erl:237"}}. +{call_fun,{proc_lib,init_p_do_apply,3},"proc_lib.erl:237"}. +{push_scope,{{proc_lib,init_p_do_apply,3},"proc_lib.erl:239"}}. +{store,{"M","proc_lib.erl:239"}}. +{store,{"F","proc_lib.erl:239"}}. +{store,{"A","proc_lib.erl:239"}}. +{try_catch,{try_enter,{proc_lib,240,87462718}},"proc_lib.erl:240"}. +{push,{notaint}}. +{pop,{}}. +{get,{"A","proc_lib.erl:241"}}. +{get,{"F","proc_lib.erl:241"}}. +{get,{"M","proc_lib.erl:241"}}. +{call_fun,{modeled_erlang,mapply,3},"proc_lib.erl:241"}. +{push_scope,{{modeled_erlang,mapply,3},"modeled_erlang.erl:49"}}. +{store,{"M","modeled_erlang.erl:49"}}. +{store,{"F","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg1","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg2","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg3","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg4","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg5","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg6","modeled_erlang.erl:49"}}. +{pop,{}}. +{get,{"F","modeled_erlang.erl:50"}}. +{pop,{}}. +{get,{"Arg6","modeled_erlang.erl:50"}}. +{get,{"Arg5","modeled_erlang.erl:50"}}. +{get,{"Arg4","modeled_erlang.erl:50"}}. +{get,{"Arg3","modeled_erlang.erl:50"}}. +{get,{"Arg2","modeled_erlang.erl:50"}}. +{get,{"Arg1","modeled_erlang.erl:50"}}. +{call_fun,{modeled_gen,init_it,6},"modeled_erlang.erl:50"}. +{push_scope,{{gen,init_it,6},"gen.erl:190"}}. +{store,{"GenMod","gen.erl:190"}}. +{store,{"Starter","gen.erl:190"}}. +{store,{"Parent","gen.erl:190"}}. +{store,{"Mod","gen.erl:190"}}. +{store,{"Args","gen.erl:190"}}. +{store,{"Options","gen.erl:190"}}. +{push,{notaint}}. +{restore_capture,{{gen,init_it2,7},"gen.erl:191"}}. +{get,{"Options","gen.erl:191"}}. +{get,{"Args","gen.erl:191"}}. +{get,{"Mod","gen.erl:191"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:191"}. +{apply,{{erlang,self,0},"gen.erl:191"}}. +{get,{"Parent","gen.erl:191"}}. +{get,{"Starter","gen.erl:191"}}. +{get,{"GenMod","gen.erl:191"}}. +{call_fun,{gen,init_it2,7},"gen.erl:191"}. +{push_scope,{{gen,init_it2,7},"gen.erl:202"}}. +{store,{"GenMod","gen.erl:202"}}. +{store,{"Starter","gen.erl:202"}}. +{store,{"Parent","gen.erl:202"}}. +{store,{"Name","gen.erl:202"}}. +{store,{"Mod","gen.erl:202"}}. +{store,{"Args","gen.erl:202"}}. +{store,{"Options","gen.erl:202"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:203"}}. +{get,{"Args","gen.erl:203"}}. +{get,{"Mod","gen.erl:203"}}. +{get,{"Name","gen.erl:203"}}. +{get,{"Parent","gen.erl:203"}}. +{get,{"Starter","gen.erl:203"}}. +{call_fun,{modeled_gen_server,init_it,6},"gen.erl:203"}. +{push_scope,{{gen_server,init_it,6},"gen_server.erl:912"}}. +{store,{"Starter","gen_server.erl:912"}}. +{store,{"Parent","gen_server.erl:912"}}. +{store,{"Name0","gen_server.erl:912"}}. +{store,{"Mod","gen_server.erl:912"}}. +{store,{"Args","gen_server.erl:912"}}. +{store,{"Options","gen_server.erl:912"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Name0","gen_server.erl:913"}}. +{call_fun,{modeled_gen,name,1},"gen_server.erl:913"}. +{push_scope,{{gen,name,1},"gen.erl:642"}}. +{store,{"Pid","gen.erl:642"}}. +{get,{"Pid","gen.erl:642"}}. +{func_ret,{name,"gen.erl:642"}}. +{apply,{{modeled_gen,name,1},"gen_server.erl:913"}}. +{duplicate,{}}. +{store,{"Name","gen_server.erl:913"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen_server.erl:914"}}. +{get,{"Name","gen_server.erl:914"}}. +{call_fun,{modeled_gen,debug_options,2},"gen_server.erl:914"}. +{push_scope,{{gen,debug_options,2},"gen.erl:737"}}. +{store,{"Name","gen.erl:737"}}. +{store,{"Opts","gen.erl:737"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Opts","gen.erl:738"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:738"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:738"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{debug_options,"gen.erl:737"}}. +{apply,{{modeled_gen,debug_options,2},"gen_server.erl:914"}}. +{duplicate,{}}. +{store,{"Debug","gen_server.erl:914"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen_server.erl:915"}}. +{call_fun,{modeled_gen,hibernate_after,1},"gen_server.erl:915"}. +{push_scope,{{gen,hibernate_after,1},"gen.erl:729"}}. +{store,{"Options","gen.erl:729"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:730"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:730"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:730"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{hibernate_after,"gen.erl:729"}}. +{apply,{{modeled_gen,hibernate_after,1},"gen_server.erl:915"}}. +{duplicate,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:915"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,create_callback_cache,1},"gen_server.erl:916"}}. +{get,{"Mod","gen_server.erl:916"}}. +{call_fun,{gen_server,create_callback_cache,1},"gen_server.erl:916"}. +{push_scope,{{gen_server,create_callback_cache,1},"gen_server.erl:1010"}}. +{store,{"Mod","gen_server.erl:1010"}}. +{push,{notaint}}. +{get,{"Mod","gen_server.erl:1011"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,5},"gen_server.erl:1011"}}. +{func_ret,{create_callback_cache,"gen_server.erl:1010"}}. +{apply,{{gen_server,create_callback_cache,1},"gen_server.erl:916"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:916"}}. +{duplicate,{}}. +{store,{"CbCache","gen_server.erl:916"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,init_it,2},"gen_server.erl:917"}}. +{get,{"Args","gen_server.erl:917"}}. +{get,{"Mod","gen_server.erl:917"}}. +{call_fun,{gen_server,init_it,2},"gen_server.erl:917"}. +{push_scope,{{gen_server,init_it,2},"gen_server.erl:960"}}. +{store,{"Mod","gen_server.erl:960"}}. +{store,{"Args","gen_server.erl:960"}}. +{try_catch,{try_enter,{gen_server,961,46082404}},"gen_server.erl:961"}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Args","gen_server.erl:962"}}. +{call_fun,{example_gen_server,init,1},"gen_server.erl:962"}. +{push_scope,{{example_gen_server,init,1},"example_gen_server.erl:74"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"example_gen_server.erl:74"}}. +{func_ret,{init,"example_gen_server.erl:74"}}. +{apply,{{example_gen_server,init,1},"gen_server.erl:962"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:962"}}. +{try_catch,{try_exit,{gen_server,961,46082404}},"gen_server.erl:961"}. +{func_ret,{init_it,"gen_server.erl:960"}}. +{apply,{{gen_server,init_it,2},"gen_server.erl:917"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:917"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:918"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:918"}}. +{pop,{}}. +{store,{"State","gen_server.erl:918"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen_server.erl:919"}. +{apply,{{erlang,self,0},"gen_server.erl:919"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:919"}}. +{get,{"Starter","gen_server.erl:919"}}. +{call_fun,{modeled_proc_lib,init_ack,2},"gen_server.erl:919"}. +{push_scope,{{proc_lib,init_ack,2},"proc_lib.erl:448"}}. +{store,{"Parent","proc_lib.erl:448"}}. +{store,{"Return","proc_lib.erl:448"}}. +{get,{"Parent","proc_lib.erl:449"}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"proc_lib.erl:449"}. +{apply,{{erlang,self,0},"proc_lib.erl:449"}}. +{get,{"Return","proc_lib.erl:449"}}. +{construct_pattern,{{tuple,3},"proc_lib.erl:449"}}. +{send,{"#Ref<0.1321034574.60293121.66553>","proc_lib.erl:449"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{init_ack,"proc_lib.erl:448"}}. +{apply,{{modeled_proc_lib,init_ack,2},"gen_server.erl:919"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,loop,7},"gen_server.erl:920"}}. +{get,{"Debug","gen_server.erl:922"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:922"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:921"}}. +{get,{"State","gen_server.erl:921"}}. +{get,{"Name","gen_server.erl:921"}}. +{get,{"Parent","gen_server.erl:921"}}. +{call_fun,{gen_server,loop,7},"gen_server.erl:920"}. +{push_scope,{{gen_server,loop,7},"gen_server.erl:992"}}. +{store,{"Parent","gen_server.erl:992"}}. +{store,{"Name","gen_server.erl:992"}}. +{store,{"State","gen_server.erl:992"}}. +{store,{"CbCache","gen_server.erl:992"}}. +{pop,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:992"}}. +{store,{"Debug","gen_server.erl:992"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:996"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64957>","gen_server.erl:993"}}. +{store,{"Msg","gen_server.erl:994"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,decode_msg,9},"gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"Debug","gen_server.erl:995"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:995"}}. +{get,{"State","gen_server.erl:995"}}. +{get,{"Name","gen_server.erl:995"}}. +{get,{"Parent","gen_server.erl:995"}}. +{get,{"Msg","gen_server.erl:995"}}. +{call_fun,{gen_server,decode_msg,9},"gen_server.erl:995"}. +{push_scope,{{gen_server,decode_msg,9},"gen_server.erl:1024"}}. +{store,{"Msg","gen_server.erl:1024"}}. +{store,{"Parent","gen_server.erl:1024"}}. +{store,{"Name","gen_server.erl:1024"}}. +{store,{"State","gen_server.erl:1024"}}. +{store,{"CbCache","gen_server.erl:1024"}}. +{store,{"Time","gen_server.erl:1024"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1024"}}. +{store,{"Debug","gen_server.erl:1024"}}. +{store,{"Hib","gen_server.erl:1024"}}. +{get,{"Msg","gen_server.erl:1025"}}. +{store,{"_Msg","gen_server.erl:1032"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,handle_msg,6},"gen_server.erl:1033"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1033"}}. +{get,{"CbCache","gen_server.erl:1033"}}. +{get,{"State","gen_server.erl:1033"}}. +{get,{"Name","gen_server.erl:1033"}}. +{get,{"Parent","gen_server.erl:1033"}}. +{get,{"Msg","gen_server.erl:1033"}}. +{call_fun,{gen_server,handle_msg,6},"gen_server.erl:1033"}. +{push_scope,{{gen_server,handle_msg,6},"gen_server.erl:1141"}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1141"}}. +{pop,{}}. +{store,{"From","gen_server.erl:1141"}}. +{store,{"Msg","gen_server.erl:1141"}}. +{store,{"Parent","gen_server.erl:1141"}}. +{store,{"Name","gen_server.erl:1141"}}. +{store,{"State","gen_server.erl:1141"}}. +{store,{"CbCache","gen_server.erl:1141"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1141"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{get,{"State","gen_server.erl:1142"}}. +{get,{"From","gen_server.erl:1142"}}. +{get,{"Msg","gen_server.erl:1142"}}. +{get,{"CbCache","gen_server.erl:1142"}}. +{call_fun,{gen_server,try_handle_call,4},"gen_server.erl:1142"}. +{push_scope,{{gen_server,try_handle_call,4},"gen_server.erl:1111"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"HandleCall","gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"Msg","gen_server.erl:1111"}}. +{store,{"From","gen_server.erl:1111"}}. +{store,{"State","gen_server.erl:1111"}}. +{try_catch,{try_enter,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{push,{notaint}}. +{get,{"HandleCall","gen_server.erl:1113"}}. +{restore_capture,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{get,{"State","gen_server.erl:1113"}}. +{get,{"From","gen_server.erl:1113"}}. +{get,{"Msg","gen_server.erl:1113"}}. +{call_fun,{gen_server,variable_func,3},"gen_server.erl:1113"}. +{push_scope,{{example_gen_server,handle_call,3},"example_gen_server.erl:81"}}. +{deconstruct_pattern,{{tuple,2},"example_gen_server.erl:81"}}. +{pop,{}}. +{store,{"Value","example_gen_server.erl:81"}}. +{store,{"_From","example_gen_server.erl:81"}}. +{store,{"State","example_gen_server.erl:81"}}. +{push,{notaint}}. +{get,{"Value","example_gen_server.erl:82"}}. +{get,{"State","example_gen_server.erl:82"}}. +{get,{"Value","example_gen_server.erl:82"}}. +{construct_pattern,{{cons},"example_gen_server.erl:82"}}. +{construct_pattern,{{tuple,3},"example_gen_server.erl:82"}}. +{func_ret,{handle_call,"example_gen_server.erl:81"}}. +{apply,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1113"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1113"}}. +{try_catch,{try_exit,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{func_ret,{try_handle_call,"gen_server.erl:1111"}}. +{apply,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1142"}}. +{duplicate,{}}. +{store,{"Result","gen_server.erl:1142"}}. +{pop,{}}. +{get,{"Result","gen_server.erl:1143"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1144"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1144"}}. +{pop,{}}. +{store,{"Reply","gen_server.erl:1144"}}. +{store,{"NState","gen_server.erl:1144"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{get,{"Reply","gen_server.erl:1145"}}. +{get,{"From","gen_server.erl:1145"}}. +{call_fun,{gen_server,reply,2},"gen_server.erl:1145"}. +{push_scope,{{gen_server,reply,2},"gen_server.erl:615"}}. +{store,{"Client","gen_server.erl:615"}}. +{store,{"Reply","gen_server.erl:615"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:616"}}. +{get,{"Client","gen_server.erl:616"}}. +{call_fun,{modeled_gen,reply,2},"gen_server.erl:616"}. +{push_scope,{{gen,reply,2},"gen.erl:546"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:546"}}. +{store,{"_To","gen.erl:546"}}. +{store,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{store,{"Tag","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{deconstruct_pattern,{{cons},"gen.erl:546"}}. +{pop,{}}. +{store,{"Alias","gen.erl:546"}}. +{store,{"Reply","gen.erl:546"}}. +{get,{"Alias","gen.erl:547"}}. +{get,{"Tag","gen.erl:547"}}. +{get,{"Reply","gen.erl:547"}}. +{construct_pattern,{{tuple,2},"gen.erl:547"}}. +{send,{"#Ref<0.1321034574.60293123.64958>","gen.erl:547"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{reply,"gen.erl:546"}}. +{apply,{{modeled_gen,reply,2},"gen_server.erl:616"}}. +{func_ret,{reply,"gen_server.erl:615"}}. +{apply,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1145"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,loop,7},"gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:1146"}}. +{get,{"NState","gen_server.erl:1146"}}. +{get,{"Name","gen_server.erl:1146"}}. +{get,{"Parent","gen_server.erl:1146"}}. +{call_fun,{gen_server,loop,7},"gen_server.erl:1146"}. +{push_scope,{{gen_server,loop,7},"gen_server.erl:992"}}. +{store,{"Parent","gen_server.erl:992"}}. +{store,{"Name","gen_server.erl:992"}}. +{store,{"State","gen_server.erl:992"}}. +{store,{"CbCache","gen_server.erl:992"}}. +{pop,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:992"}}. +{store,{"Debug","gen_server.erl:992"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:996"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293121.66555>","gen_server.erl:993"}}. +{store,{"Msg","gen_server.erl:994"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,decode_msg,9},"gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"Debug","gen_server.erl:995"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:995"}}. +{get,{"State","gen_server.erl:995"}}. +{get,{"Name","gen_server.erl:995"}}. +{get,{"Parent","gen_server.erl:995"}}. +{get,{"Msg","gen_server.erl:995"}}. +{call_fun,{gen_server,decode_msg,9},"gen_server.erl:995"}. +{push_scope,{{gen_server,decode_msg,9},"gen_server.erl:1024"}}. +{store,{"Msg","gen_server.erl:1024"}}. +{store,{"Parent","gen_server.erl:1024"}}. +{store,{"Name","gen_server.erl:1024"}}. +{store,{"State","gen_server.erl:1024"}}. +{store,{"CbCache","gen_server.erl:1024"}}. +{store,{"Time","gen_server.erl:1024"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1024"}}. +{store,{"Debug","gen_server.erl:1024"}}. +{store,{"Hib","gen_server.erl:1024"}}. +{get,{"Msg","gen_server.erl:1025"}}. +{store,{"_Msg","gen_server.erl:1032"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,handle_msg,6},"gen_server.erl:1033"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1033"}}. +{get,{"CbCache","gen_server.erl:1033"}}. +{get,{"State","gen_server.erl:1033"}}. +{get,{"Name","gen_server.erl:1033"}}. +{get,{"Parent","gen_server.erl:1033"}}. +{get,{"Msg","gen_server.erl:1033"}}. +{call_fun,{gen_server,handle_msg,6},"gen_server.erl:1033"}. +{push_scope,{{gen_server,handle_msg,6},"gen_server.erl:1141"}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1141"}}. +{pop,{}}. +{store,{"From","gen_server.erl:1141"}}. +{store,{"Msg","gen_server.erl:1141"}}. +{store,{"Parent","gen_server.erl:1141"}}. +{store,{"Name","gen_server.erl:1141"}}. +{store,{"State","gen_server.erl:1141"}}. +{store,{"CbCache","gen_server.erl:1141"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1141"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{get,{"State","gen_server.erl:1142"}}. +{get,{"From","gen_server.erl:1142"}}. +{get,{"Msg","gen_server.erl:1142"}}. +{get,{"CbCache","gen_server.erl:1142"}}. +{call_fun,{gen_server,try_handle_call,4},"gen_server.erl:1142"}. +{push_scope,{{gen_server,try_handle_call,4},"gen_server.erl:1111"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"HandleCall","gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"Msg","gen_server.erl:1111"}}. +{store,{"From","gen_server.erl:1111"}}. +{store,{"State","gen_server.erl:1111"}}. +{try_catch,{try_enter,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{push,{notaint}}. +{get,{"HandleCall","gen_server.erl:1113"}}. +{restore_capture,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{get,{"State","gen_server.erl:1113"}}. +{get,{"From","gen_server.erl:1113"}}. +{get,{"Msg","gen_server.erl:1113"}}. +{call_fun,{gen_server,variable_func,3},"gen_server.erl:1113"}. +{push_scope,{{example_gen_server,handle_call,3},"example_gen_server.erl:81"}}. +{deconstruct_pattern,{{tuple,2},"example_gen_server.erl:81"}}. +{pop,{}}. +{store,{"Value","example_gen_server.erl:81"}}. +{store,{"_From","example_gen_server.erl:81"}}. +{store,{"State","example_gen_server.erl:81"}}. +{push,{notaint}}. +{get,{"Value","example_gen_server.erl:82"}}. +{get,{"State","example_gen_server.erl:82"}}. +{get,{"Value","example_gen_server.erl:82"}}. +{construct_pattern,{{cons},"example_gen_server.erl:82"}}. +{construct_pattern,{{tuple,3},"example_gen_server.erl:82"}}. +{func_ret,{handle_call,"example_gen_server.erl:81"}}. +{apply,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1113"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1113"}}. +{try_catch,{try_exit,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{func_ret,{try_handle_call,"gen_server.erl:1111"}}. +{apply,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1142"}}. +{duplicate,{}}. +{store,{"Result","gen_server.erl:1142"}}. +{pop,{}}. +{get,{"Result","gen_server.erl:1143"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1144"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1144"}}. +{pop,{}}. +{store,{"Reply","gen_server.erl:1144"}}. +{store,{"NState","gen_server.erl:1144"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{get,{"Reply","gen_server.erl:1145"}}. +{get,{"From","gen_server.erl:1145"}}. +{call_fun,{gen_server,reply,2},"gen_server.erl:1145"}. +{push_scope,{{gen_server,reply,2},"gen_server.erl:615"}}. +{store,{"Client","gen_server.erl:615"}}. +{store,{"Reply","gen_server.erl:615"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:616"}}. +{get,{"Client","gen_server.erl:616"}}. +{call_fun,{modeled_gen,reply,2},"gen_server.erl:616"}. +{push_scope,{{gen,reply,2},"gen.erl:546"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:546"}}. +{store,{"_To","gen.erl:546"}}. +{store,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{store,{"Tag","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{deconstruct_pattern,{{cons},"gen.erl:546"}}. +{pop,{}}. +{store,{"Alias","gen.erl:546"}}. +{store,{"Reply","gen.erl:546"}}. +{get,{"Alias","gen.erl:547"}}. +{get,{"Tag","gen.erl:547"}}. +{get,{"Reply","gen.erl:547"}}. +{construct_pattern,{{tuple,2},"gen.erl:547"}}. +{send,{"#Ref<0.1321034574.60293121.66556>","gen.erl:547"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{reply,"gen.erl:546"}}. +{apply,{{modeled_gen,reply,2},"gen_server.erl:616"}}. +{func_ret,{reply,"gen_server.erl:615"}}. +{apply,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1145"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,loop,7},"gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:1146"}}. +{get,{"NState","gen_server.erl:1146"}}. +{get,{"Name","gen_server.erl:1146"}}. +{get,{"Parent","gen_server.erl:1146"}}. +{call_fun,{gen_server,loop,7},"gen_server.erl:1146"}. +{push_scope,{{gen_server,loop,7},"gen_server.erl:992"}}. +{store,{"Parent","gen_server.erl:992"}}. +{store,{"Name","gen_server.erl:992"}}. +{store,{"State","gen_server.erl:992"}}. +{store,{"CbCache","gen_server.erl:992"}}. +{pop,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:992"}}. +{store,{"Debug","gen_server.erl:992"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:996"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64960>","gen_server.erl:993"}}. +{store,{"Msg","gen_server.erl:994"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,decode_msg,9},"gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"Debug","gen_server.erl:995"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:995"}}. +{get,{"State","gen_server.erl:995"}}. +{get,{"Name","gen_server.erl:995"}}. +{get,{"Parent","gen_server.erl:995"}}. +{get,{"Msg","gen_server.erl:995"}}. +{call_fun,{gen_server,decode_msg,9},"gen_server.erl:995"}. +{push_scope,{{gen_server,decode_msg,9},"gen_server.erl:1024"}}. +{store,{"Msg","gen_server.erl:1024"}}. +{store,{"Parent","gen_server.erl:1024"}}. +{store,{"Name","gen_server.erl:1024"}}. +{store,{"State","gen_server.erl:1024"}}. +{store,{"CbCache","gen_server.erl:1024"}}. +{store,{"Time","gen_server.erl:1024"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1024"}}. +{store,{"Debug","gen_server.erl:1024"}}. +{store,{"Hib","gen_server.erl:1024"}}. +{get,{"Msg","gen_server.erl:1025"}}. +{store,{"_Msg","gen_server.erl:1032"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,handle_msg,6},"gen_server.erl:1033"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1033"}}. +{get,{"CbCache","gen_server.erl:1033"}}. +{get,{"State","gen_server.erl:1033"}}. +{get,{"Name","gen_server.erl:1033"}}. +{get,{"Parent","gen_server.erl:1033"}}. +{get,{"Msg","gen_server.erl:1033"}}. +{call_fun,{gen_server,handle_msg,6},"gen_server.erl:1033"}. +{push_scope,{{gen_server,handle_msg,6},"gen_server.erl:1141"}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1141"}}. +{pop,{}}. +{store,{"From","gen_server.erl:1141"}}. +{store,{"Msg","gen_server.erl:1141"}}. +{store,{"Parent","gen_server.erl:1141"}}. +{store,{"Name","gen_server.erl:1141"}}. +{store,{"State","gen_server.erl:1141"}}. +{store,{"CbCache","gen_server.erl:1141"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1141"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{get,{"State","gen_server.erl:1142"}}. +{get,{"From","gen_server.erl:1142"}}. +{get,{"Msg","gen_server.erl:1142"}}. +{get,{"CbCache","gen_server.erl:1142"}}. +{call_fun,{gen_server,try_handle_call,4},"gen_server.erl:1142"}. +{push_scope,{{gen_server,try_handle_call,4},"gen_server.erl:1111"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"HandleCall","gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"Msg","gen_server.erl:1111"}}. +{store,{"From","gen_server.erl:1111"}}. +{store,{"State","gen_server.erl:1111"}}. +{try_catch,{try_enter,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{push,{notaint}}. +{get,{"HandleCall","gen_server.erl:1113"}}. +{restore_capture,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{get,{"State","gen_server.erl:1113"}}. +{get,{"From","gen_server.erl:1113"}}. +{get,{"Msg","gen_server.erl:1113"}}. +{call_fun,{gen_server,variable_func,3},"gen_server.erl:1113"}. +{push_scope,{{example_gen_server,handle_call,3},"example_gen_server.erl:83"}}. +{pop,{}}. +{store,{"_From","example_gen_server.erl:83"}}. +{deconstruct_pattern,{{cons},"example_gen_server.erl:83"}}. +{store,{"Head","example_gen_server.erl:83"}}. +{store,{"Tail","example_gen_server.erl:83"}}. +{push,{notaint}}. +{get,{"Head","example_gen_server.erl:84"}}. +{get,{"Tail","example_gen_server.erl:84"}}. +{construct_pattern,{{tuple,3},"example_gen_server.erl:84"}}. +{func_ret,{handle_call,"example_gen_server.erl:83"}}. +{apply,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1113"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1113"}}. +{try_catch,{try_exit,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{func_ret,{try_handle_call,"gen_server.erl:1111"}}. +{apply,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1142"}}. +{duplicate,{}}. +{store,{"Result","gen_server.erl:1142"}}. +{pop,{}}. +{get,{"Result","gen_server.erl:1143"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1144"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1144"}}. +{pop,{}}. +{store,{"Reply","gen_server.erl:1144"}}. +{store,{"NState","gen_server.erl:1144"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{get,{"Reply","gen_server.erl:1145"}}. +{get,{"From","gen_server.erl:1145"}}. +{call_fun,{gen_server,reply,2},"gen_server.erl:1145"}. +{push_scope,{{gen_server,reply,2},"gen_server.erl:615"}}. +{store,{"Client","gen_server.erl:615"}}. +{store,{"Reply","gen_server.erl:615"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:616"}}. +{get,{"Client","gen_server.erl:616"}}. +{call_fun,{modeled_gen,reply,2},"gen_server.erl:616"}. +{push_scope,{{gen,reply,2},"gen.erl:546"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:546"}}. +{store,{"_To","gen.erl:546"}}. +{store,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{store,{"Tag","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{deconstruct_pattern,{{cons},"gen.erl:546"}}. +{pop,{}}. +{store,{"Alias","gen.erl:546"}}. +{store,{"Reply","gen.erl:546"}}. +{get,{"Alias","gen.erl:547"}}. +{get,{"Tag","gen.erl:547"}}. +{get,{"Reply","gen.erl:547"}}. +{construct_pattern,{{tuple,2},"gen.erl:547"}}. +{send,{"#Ref<0.1321034574.60293123.64961>","gen.erl:547"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{reply,"gen.erl:546"}}. +{apply,{{modeled_gen,reply,2},"gen_server.erl:616"}}. +{func_ret,{reply,"gen_server.erl:615"}}. +{apply,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1145"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,loop,7},"gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:1146"}}. +{get,{"NState","gen_server.erl:1146"}}. +{get,{"Name","gen_server.erl:1146"}}. +{get,{"Parent","gen_server.erl:1146"}}. +{call_fun,{gen_server,loop,7},"gen_server.erl:1146"}. +{push_scope,{{gen_server,loop,7},"gen_server.erl:992"}}. +{store,{"Parent","gen_server.erl:992"}}. +{store,{"Name","gen_server.erl:992"}}. +{store,{"State","gen_server.erl:992"}}. +{store,{"CbCache","gen_server.erl:992"}}. +{pop,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:992"}}. +{store,{"Debug","gen_server.erl:992"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:996"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64963>","gen_server.erl:993"}}. +{store,{"Msg","gen_server.erl:994"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,decode_msg,9},"gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"Debug","gen_server.erl:995"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:995"}}. +{get,{"State","gen_server.erl:995"}}. +{get,{"Name","gen_server.erl:995"}}. +{get,{"Parent","gen_server.erl:995"}}. +{get,{"Msg","gen_server.erl:995"}}. +{call_fun,{gen_server,decode_msg,9},"gen_server.erl:995"}. +{push_scope,{{gen_server,decode_msg,9},"gen_server.erl:1024"}}. +{store,{"Msg","gen_server.erl:1024"}}. +{store,{"Parent","gen_server.erl:1024"}}. +{store,{"Name","gen_server.erl:1024"}}. +{store,{"State","gen_server.erl:1024"}}. +{store,{"CbCache","gen_server.erl:1024"}}. +{store,{"Time","gen_server.erl:1024"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1024"}}. +{store,{"Debug","gen_server.erl:1024"}}. +{store,{"Hib","gen_server.erl:1024"}}. +{get,{"Msg","gen_server.erl:1025"}}. +{store,{"_Msg","gen_server.erl:1032"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,handle_msg,6},"gen_server.erl:1033"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1033"}}. +{get,{"CbCache","gen_server.erl:1033"}}. +{get,{"State","gen_server.erl:1033"}}. +{get,{"Name","gen_server.erl:1033"}}. +{get,{"Parent","gen_server.erl:1033"}}. +{get,{"Msg","gen_server.erl:1033"}}. +{call_fun,{gen_server,handle_msg,6},"gen_server.erl:1033"}. +{push_scope,{{gen_server,handle_msg,6},"gen_server.erl:1141"}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1141"}}. +{pop,{}}. +{store,{"From","gen_server.erl:1141"}}. +{store,{"Msg","gen_server.erl:1141"}}. +{store,{"Parent","gen_server.erl:1141"}}. +{store,{"Name","gen_server.erl:1141"}}. +{store,{"State","gen_server.erl:1141"}}. +{store,{"CbCache","gen_server.erl:1141"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1141"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{get,{"State","gen_server.erl:1142"}}. +{get,{"From","gen_server.erl:1142"}}. +{get,{"Msg","gen_server.erl:1142"}}. +{get,{"CbCache","gen_server.erl:1142"}}. +{call_fun,{gen_server,try_handle_call,4},"gen_server.erl:1142"}. +{push_scope,{{gen_server,try_handle_call,4},"gen_server.erl:1111"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"HandleCall","gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"Msg","gen_server.erl:1111"}}. +{store,{"From","gen_server.erl:1111"}}. +{store,{"State","gen_server.erl:1111"}}. +{try_catch,{try_enter,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{push,{notaint}}. +{get,{"HandleCall","gen_server.erl:1113"}}. +{restore_capture,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{get,{"State","gen_server.erl:1113"}}. +{get,{"From","gen_server.erl:1113"}}. +{get,{"Msg","gen_server.erl:1113"}}. +{call_fun,{gen_server,variable_func,3},"gen_server.erl:1113"}. +{push_scope,{{example_gen_server,handle_call,3},"example_gen_server.erl:83"}}. +{pop,{}}. +{store,{"_From","example_gen_server.erl:83"}}. +{deconstruct_pattern,{{cons},"example_gen_server.erl:83"}}. +{store,{"Head","example_gen_server.erl:83"}}. +{store,{"Tail","example_gen_server.erl:83"}}. +{push,{notaint}}. +{get,{"Head","example_gen_server.erl:84"}}. +{get,{"Tail","example_gen_server.erl:84"}}. +{construct_pattern,{{tuple,3},"example_gen_server.erl:84"}}. +{func_ret,{handle_call,"example_gen_server.erl:83"}}. +{apply,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1113"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1113"}}. +{try_catch,{try_exit,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{func_ret,{try_handle_call,"gen_server.erl:1111"}}. +{apply,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1142"}}. +{duplicate,{}}. +{store,{"Result","gen_server.erl:1142"}}. +{pop,{}}. +{get,{"Result","gen_server.erl:1143"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1144"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1144"}}. +{pop,{}}. +{store,{"Reply","gen_server.erl:1144"}}. +{store,{"NState","gen_server.erl:1144"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{get,{"Reply","gen_server.erl:1145"}}. +{get,{"From","gen_server.erl:1145"}}. +{call_fun,{gen_server,reply,2},"gen_server.erl:1145"}. +{push_scope,{{gen_server,reply,2},"gen_server.erl:615"}}. +{store,{"Client","gen_server.erl:615"}}. +{store,{"Reply","gen_server.erl:615"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:616"}}. +{get,{"Client","gen_server.erl:616"}}. +{call_fun,{modeled_gen,reply,2},"gen_server.erl:616"}. +{push_scope,{{gen,reply,2},"gen.erl:546"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:546"}}. +{store,{"_To","gen.erl:546"}}. +{store,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{store,{"Tag","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{deconstruct_pattern,{{cons},"gen.erl:546"}}. +{pop,{}}. +{store,{"Alias","gen.erl:546"}}. +{store,{"Reply","gen.erl:546"}}. +{get,{"Alias","gen.erl:547"}}. +{get,{"Tag","gen.erl:547"}}. +{get,{"Reply","gen.erl:547"}}. +{construct_pattern,{{tuple,2},"gen.erl:547"}}. +{send,{"#Ref<0.1321034574.60293123.64964>","gen.erl:547"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{reply,"gen.erl:546"}}. +{apply,{{modeled_gen,reply,2},"gen_server.erl:616"}}. +{func_ret,{reply,"gen_server.erl:615"}}. +{apply,{{gen_server,reply,2},"gen_server.erl:1145"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1145"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,loop,7},"gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1146"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:1146"}}. +{get,{"NState","gen_server.erl:1146"}}. +{get,{"Name","gen_server.erl:1146"}}. +{get,{"Parent","gen_server.erl:1146"}}. +{call_fun,{gen_server,loop,7},"gen_server.erl:1146"}. +{push_scope,{{gen_server,loop,7},"gen_server.erl:992"}}. +{store,{"Parent","gen_server.erl:992"}}. +{store,{"Name","gen_server.erl:992"}}. +{store,{"State","gen_server.erl:992"}}. +{store,{"CbCache","gen_server.erl:992"}}. +{pop,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:992"}}. +{store,{"Debug","gen_server.erl:992"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:996"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293123.64966>","gen_server.erl:993"}}. +{store,{"Msg","gen_server.erl:994"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,decode_msg,9},"gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"Debug","gen_server.erl:995"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:995"}}. +{get,{"State","gen_server.erl:995"}}. +{get,{"Name","gen_server.erl:995"}}. +{get,{"Parent","gen_server.erl:995"}}. +{get,{"Msg","gen_server.erl:995"}}. +{call_fun,{gen_server,decode_msg,9},"gen_server.erl:995"}. +{push_scope,{{gen_server,decode_msg,9},"gen_server.erl:1024"}}. +{store,{"Msg","gen_server.erl:1024"}}. +{store,{"Parent","gen_server.erl:1024"}}. +{store,{"Name","gen_server.erl:1024"}}. +{store,{"State","gen_server.erl:1024"}}. +{store,{"CbCache","gen_server.erl:1024"}}. +{store,{"Time","gen_server.erl:1024"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1024"}}. +{store,{"Debug","gen_server.erl:1024"}}. +{store,{"Hib","gen_server.erl:1024"}}. +{get,{"Msg","gen_server.erl:1025"}}. +{store,{"_Msg","gen_server.erl:1032"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,handle_msg,6},"gen_server.erl:1033"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1033"}}. +{get,{"CbCache","gen_server.erl:1033"}}. +{get,{"State","gen_server.erl:1033"}}. +{get,{"Name","gen_server.erl:1033"}}. +{get,{"Parent","gen_server.erl:1033"}}. +{get,{"Msg","gen_server.erl:1033"}}. +{call_fun,{gen_server,handle_msg,6},"gen_server.erl:1033"}. +{push_scope,{{gen_server,handle_msg,6},"gen_server.erl:1141"}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1141"}}. +{pop,{}}. +{store,{"From","gen_server.erl:1141"}}. +{store,{"Msg","gen_server.erl:1141"}}. +{store,{"Parent","gen_server.erl:1141"}}. +{store,{"Name","gen_server.erl:1141"}}. +{store,{"State","gen_server.erl:1141"}}. +{store,{"CbCache","gen_server.erl:1141"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1141"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{get,{"State","gen_server.erl:1142"}}. +{get,{"From","gen_server.erl:1142"}}. +{get,{"Msg","gen_server.erl:1142"}}. +{get,{"CbCache","gen_server.erl:1142"}}. +{call_fun,{gen_server,try_handle_call,4},"gen_server.erl:1142"}. +{push_scope,{{gen_server,try_handle_call,4},"gen_server.erl:1111"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"HandleCall","gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"Msg","gen_server.erl:1111"}}. +{store,{"From","gen_server.erl:1111"}}. +{store,{"State","gen_server.erl:1111"}}. +{try_catch,{try_enter,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{push,{notaint}}. +{get,{"HandleCall","gen_server.erl:1113"}}. +{restore_capture,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{get,{"State","gen_server.erl:1113"}}. +{get,{"From","gen_server.erl:1113"}}. +{get,{"Msg","gen_server.erl:1113"}}. +{call_fun,{gen_server,variable_func,3},"gen_server.erl:1113"}. +{push_scope,{{example_gen_server,handle_call,3},"example_gen_server.erl:85"}}. +{pop,{}}. +{store,{"_From","example_gen_server.erl:85"}}. +{store,{"State","example_gen_server.erl:85"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"State","example_gen_server.erl:86"}}. +{construct_pattern,{{tuple,4},"example_gen_server.erl:86"}}. +{func_ret,{handle_call,"example_gen_server.erl:85"}}. +{apply,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1113"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1113"}}. +{try_catch,{try_exit,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{func_ret,{try_handle_call,"gen_server.erl:1111"}}. +{apply,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1142"}}. +{duplicate,{}}. +{store,{"Result","gen_server.erl:1142"}}. +{pop,{}}. +{get,{"Result","gen_server.erl:1143"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1155"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,4},"gen_server.erl:1155"}}. +{pop,{}}. +{store,{"Reason","gen_server.erl:1155"}}. +{store,{"Reply","gen_server.erl:1155"}}. +{store,{"NState","gen_server.erl:1155"}}. +{try_catch,{try_enter,{gen_server,1156,19367316}},"gen_server.erl:1156"}. +{get,{"CbCache","gen_server.erl:1157"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1157"}}. +{pop,{}}. +{store,{"rec1","gen_server.erl:1157"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{get,{"rec1","gen_server.erl:1157"}}. +{duplicate,{}}. +{store,{"Mod","gen_server.erl:1157"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,terminate,8},"gen_server.erl:1158"}}. +{push,{notaint}}. +{get,{"NState","gen_server.erl:1158"}}. +{get,{"Mod","gen_server.erl:1158"}}. +{get,{"Msg","gen_server.erl:1158"}}. +{get,{"From","gen_server.erl:1158"}}. +{get,{"Name","gen_server.erl:1158"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen_server.erl:1158"}. +{apply,{{erlang,self,0},"gen_server.erl:1158"}}. +{call_fun,{erlang,process_info,2},"gen_server.erl:1158"}. +{apply,{{erlang,process_info,2},"gen_server.erl:1158"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,elemnt,2},"gen_server.erl:1158"}. +{push_scope,{{modeled_erlang,elemnt,2},"modeled_erlang.erl:79"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"modeled_erlang.erl:79"}}. +{store,{"_A1","modeled_erlang.erl:79"}}. +{store,{"A2","modeled_erlang.erl:79"}}. +{get,{"A2","modeled_erlang.erl:80"}}. +{func_ret,{elemnt,"modeled_erlang.erl:79"}}. +{apply,{{modeled_erlang,elemnt,2},"gen_server.erl:1158"}}. +{get,{"Reason","gen_server.erl:1158"}}. +{call_fun,{gen_server,terminate,8},"gen_server.erl:1158"}. +{push_scope,{{gen_server,terminate,8},"gen_server.erl:1312"}}. +{store,{"Reason","gen_server.erl:1312"}}. +{store,{"Stacktrace","gen_server.erl:1312"}}. +{store,{"Name","gen_server.erl:1312"}}. +{store,{"From","gen_server.erl:1312"}}. +{store,{"Msg","gen_server.erl:1312"}}. +{store,{"Mod","gen_server.erl:1312"}}. +{store,{"State","gen_server.erl:1312"}}. +{store,{"Debug","gen_server.erl:1312"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,terminate,10},"gen_server.erl:1313"}}. +{get,{"Debug","gen_server.erl:1313"}}. +{get,{"State","gen_server.erl:1313"}}. +{get,{"Mod","gen_server.erl:1313"}}. +{get,{"Msg","gen_server.erl:1313"}}. +{get,{"From","gen_server.erl:1313"}}. +{get,{"Name","gen_server.erl:1313"}}. +{push,{notaint}}. +{get,{"Stacktrace","gen_server.erl:1313"}}. +{get,{"Reason","gen_server.erl:1313"}}. +{push,{notaint}}. +{call_fun,{gen_server,terminate,10},"gen_server.erl:1313"}. +{push_scope,{{gen_server,terminate,10},"gen_server.erl:1320"}}. +{store,{"Class","gen_server.erl:1320"}}. +{store,{"Reason","gen_server.erl:1320"}}. +{store,{"Stacktrace","gen_server.erl:1320"}}. +{store,{"ReportStacktrace","gen_server.erl:1320"}}. +{store,{"Name","gen_server.erl:1320"}}. +{store,{"From","gen_server.erl:1320"}}. +{store,{"Msg","gen_server.erl:1320"}}. +{store,{"Mod","gen_server.erl:1320"}}. +{store,{"State","gen_server.erl:1320"}}. +{store,{"Debug","gen_server.erl:1320"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_terminate,3},"gen_server.erl:1321"}}. +{get,{"State","gen_server.erl:1321"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,catch_result,3},"gen_server.erl:1321"}}. +{get,{"Stacktrace","gen_server.erl:1321"}}. +{get,{"Reason","gen_server.erl:1321"}}. +{get,{"Class","gen_server.erl:1321"}}. +{call_fun,{gen_server,catch_result,3},"gen_server.erl:1321"}. +{push_scope,{{gen_server,catch_result,3},"gen_server.erl:1346"}}. +{pop,{}}. +{store,{"Reason","gen_server.erl:1346"}}. +{store,{"_Stacktrace","gen_server.erl:1346"}}. +{get,{"Reason","gen_server.erl:1346"}}. +{func_ret,{catch_result,"gen_server.erl:1346"}}. +{apply,{{gen_server,catch_result,3},"gen_server.erl:1321"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1321"}}. +{get,{"Mod","gen_server.erl:1321"}}. +{call_fun,{gen_server,try_terminate,3},"gen_server.erl:1321"}. +{push_scope,{{gen_server,try_terminate,3},"gen_server.erl:1121"}}. +{store,{"Mod","gen_server.erl:1121"}}. +{store,{"Reason","gen_server.erl:1121"}}. +{store,{"State","gen_server.erl:1121"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"Mod","gen_server.erl:1122"}}. +{call_fun,{erlang,function_exported,3},"gen_server.erl:1122"}. +{apply,{{erlang,function_exported,3},"gen_server.erl:1122"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1133"}}. +{func_ret,{try_terminate,"gen_server.erl:1121"}}. +{apply,{{gen_server,try_terminate,3},"gen_server.erl:1321"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1321"}}. +{duplicate,{}}. +{store,{"Reply","gen_server.erl:1321"}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:1322"}}. +{pop,{}}. +{get,{"Class","gen_server.erl:1327"}}. +{get,{"Reason","gen_server.erl:1327"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1327"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1328"}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Stacktrace","gen_server.erl:1337"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Stacktrace","gen_server.erl:1341"}}. +{get,{"Reason","gen_server.erl:1341"}}. +{get,{"Class","gen_server.erl:1341"}}. +{call_fun,{erlang,raise,3},"gen_server.erl:1341"}. +{push,{notaint}}. +{restore_capture,{{gen_server,reply,2},"gen_server.erl:1160"}}. +{get,{"Reply","gen_server.erl:1160"}}. +{get,{"From","gen_server.erl:1160"}}. +{call_fun,{gen_server,reply,2},"gen_server.erl:1160"}. +{push_scope,{{gen_server,reply,2},"gen_server.erl:615"}}. +{store,{"Client","gen_server.erl:615"}}. +{store,{"Reply","gen_server.erl:615"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:616"}}. +{get,{"Client","gen_server.erl:616"}}. +{call_fun,{modeled_gen,reply,2},"gen_server.erl:616"}. +{push_scope,{{gen,reply,2},"gen.erl:546"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:546"}}. +{store,{"_To","gen.erl:546"}}. +{store,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{store,{"Tag","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{deconstruct_pattern,{{cons},"gen.erl:546"}}. +{pop,{}}. +{store,{"Alias","gen.erl:546"}}. +{store,{"Reply","gen.erl:546"}}. +{get,{"Alias","gen.erl:547"}}. +{get,{"Tag","gen.erl:547"}}. +{get,{"Reply","gen.erl:547"}}. +{construct_pattern,{{tuple,2},"gen.erl:547"}}. +{send,{"#Ref<0.1321034574.60293123.64967>","gen.erl:547"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{reply,"gen.erl:546"}}. +{apply,{{modeled_gen,reply,2},"gen_server.erl:616"}}. +{func_ret,{reply,"gen_server.erl:615"}}. +{apply,{{gen_server,reply,2},"gen_server.erl:1160"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1160"}}. +{pop,{}}. +{try_catch,{catch_enter,{proc_lib,240,87462718}},"proc_lib.erl:243"}. +{push,{notaint}}. +{store,{"Stacktrace","proc_lib.erl:243"}}. +{push,{notaint}}. +{store,{"Class","proc_lib.erl:243"}}. +{store,{"Reason","proc_lib.erl:243"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,exit_p,3},"proc_lib.erl:244"}}. +{get,{"Stacktrace","proc_lib.erl:244"}}. +{get,{"Reason","proc_lib.erl:244"}}. +{get,{"Class","proc_lib.erl:244"}}. +{call_fun,{proc_lib,exit_p,3},"proc_lib.erl:244"}. +{push_scope,{{proc_lib,exit_p,3},"proc_lib.erl:257"}}. +{store,{"Class","proc_lib.erl:257"}}. +{store,{"Reason","proc_lib.erl:257"}}. +{store,{"Stacktrace","proc_lib.erl:257"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mget,1},"proc_lib.erl:258"}. +{push_scope,{{modeled_erlang,mget,1},"modeled_erlang.erl:163"}}. +{store,{"Key","modeled_erlang.erl:163"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:164"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:164"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:165"}}. +{get,{"Key","modeled_erlang.erl:165"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}}. +{func_ret,{mget,"modeled_erlang.erl:163"}}. +{apply,{{modeled_erlang,mget,1},"proc_lib.erl:258"}}. +{deconstruct_pattern,{{tuple,3},"proc_lib.erl:259"}}. +{store,{"M","proc_lib.erl:259"}}. +{store,{"F","proc_lib.erl:259"}}. +{store,{"A","proc_lib.erl:259"}}. +{get,{"M","proc_lib.erl:260"}}. +{get,{"F","proc_lib.erl:260"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:260"}}. +{push,{notaint}}. +{get,{"A","proc_lib.erl:260"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:260"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:498"}}. +{pop,{}}. +{store,{"Acc","proc_lib.erl:498"}}. +{get,{"Acc","proc_lib.erl:499"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:498"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:260"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:260"}}. +{construct_pattern,{{tuple,3},"proc_lib.erl:260"}}. +{duplicate,{}}. +{store,{"MFA","proc_lib.erl:260"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,crash_report,4},"proc_lib.erl:261"}}. +{get,{"Stacktrace","proc_lib.erl:261"}}. +{get,{"MFA","proc_lib.erl:261"}}. +{get,{"Reason","proc_lib.erl:261"}}. +{get,{"Class","proc_lib.erl:261"}}. +{call_fun,{proc_lib,crash_report,4},"proc_lib.erl:261"}. +{push_scope,{{proc_lib,crash_report,4},"proc_lib.erl:580"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{crash_report,"proc_lib.erl:580"}}. +{apply,{{proc_lib,crash_report,4},"proc_lib.erl:261"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:261"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Stacktrace","proc_lib.erl:262"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,exit_reason,3},"proc_lib.erl:262"}}. +{get,{"Stacktrace","proc_lib.erl:262"}}. +{get,{"Reason","proc_lib.erl:262"}}. +{get,{"Class","proc_lib.erl:262"}}. +{call_fun,{proc_lib,exit_reason,3},"proc_lib.erl:262"}. +{push_scope,{{proc_lib,exit_reason,3},"proc_lib.erl:272"}}. +{pop,{}}. +{store,{"Reason","proc_lib.erl:272"}}. +{store,{"_Stacktrace","proc_lib.erl:272"}}. +{get,{"Reason","proc_lib.erl:273"}}. +{func_ret,{exit_reason,"proc_lib.erl:272"}}. +{apply,{{proc_lib,exit_reason,3},"proc_lib.erl:262"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:262"}}. +{push,{notaint}}. +{call_fun,{erlang,raise,3},"proc_lib.erl:262"}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-3311790007 b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-3311790007 new file mode 100644 index 0000000..bd7921c --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-3311790007 @@ -0,0 +1,454 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{example_gen_server,gen_server_init_test_main,0}, + "example_gen_server.erl:48"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"example_gen_server.erl:49"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"TaintedVal","example_gen_server.erl:49"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"example_gen_server.erl:50"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"example_gen_server.erl:50"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"example_gen_server.erl:50"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"example_gen_server.erl:50"}. +{apply,{{modeled_erlang,real_put,2},"example_gen_server.erl:50"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"TaintedVal","example_gen_server.erl:51"}}. +{call_fun,{example_gen_server,start_link,2},"example_gen_server.erl:51"}. +{push_scope,{{example_gen_server,start_link,2},"example_gen_server.erl:62"}}. +{store,{"TaintedVal","example_gen_server.erl:62"}}. +{store,{"NotTaintedVal","example_gen_server.erl:62"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"NotTaintedVal","example_gen_server.erl:63"}}. +{construct_pattern,{{cons},"example_gen_server.erl:63"}}. +{get,{"TaintedVal","example_gen_server.erl:63"}}. +{construct_pattern,{{cons},"example_gen_server.erl:63"}}. +{push,{notaint}}. +{call_fun,{modeled_gen_server,start_link,3},"example_gen_server.erl:63"}. +{push_scope,{{gen_server,start_link,3},"gen_server.erl:309"}}. +{store,{"Module","gen_server.erl:309"}}. +{store,{"Args","gen_server.erl:309"}}. +{store,{"Options","gen_server.erl:309"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen_server.erl:310"}}. +{get,{"Args","gen_server.erl:310"}}. +{get,{"Module","gen_server.erl:310"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_gen,start,5},"gen_server.erl:310"}. +{push_scope,{{gen,start,5},"gen.erl:116"}}. +{store,{"GenMod","gen.erl:116"}}. +{store,{"LinkP","gen.erl:116"}}. +{store,{"Mod","gen.erl:116"}}. +{store,{"Args","gen.erl:116"}}. +{store,{"Options","gen.erl:116"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_spawn,5},"gen.erl:117"}}. +{get,{"Options","gen.erl:117"}}. +{get,{"Args","gen.erl:117"}}. +{get,{"Mod","gen.erl:117"}}. +{get,{"LinkP","gen.erl:117"}}. +{get,{"GenMod","gen.erl:117"}}. +{call_fun,{gen,do_spawn,5},"gen.erl:117"}. +{push_scope,{{gen,do_spawn,5},"gen.erl:123"}}. +{store,{"GenMod","gen.erl:123"}}. +{pop,{}}. +{store,{"Mod","gen.erl:123"}}. +{store,{"Args","gen.erl:123"}}. +{store,{"Options","gen.erl:123"}}. +{push,{notaint}}. +{restore_capture,{{gen,timeout,1},"gen.erl:124"}}. +{get,{"Options","gen.erl:124"}}. +{call_fun,{gen,timeout,1},"gen.erl:124"}. +{push_scope,{{gen,timeout,1},"gen.erl:713"}}. +{store,{"Options","gen.erl:713"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:714"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:714"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:714"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{timeout,"gen.erl:713"}}. +{apply,{{gen,timeout,1},"gen.erl:124"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:124"}}. +{duplicate,{}}. +{store,{"Time","gen.erl:124"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen,spawn_opts,1},"gen.erl:128"}}. +{get,{"Options","gen.erl:128"}}. +{call_fun,{gen,spawn_opts,1},"gen.erl:128"}. +{push_scope,{{gen,spawn_opts,1},"gen.erl:721"}}. +{store,{"Options","gen.erl:721"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:722"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:722"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:722"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{spawn_opts,"gen.erl:721"}}. +{apply,{{gen,spawn_opts,1},"gen.erl:128"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:128"}}. +{get,{"Time","gen.erl:127"}}. +{push,{notaint}}. +{get,{"Options","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{get,{"Args","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{get,{"Mod","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:126"}. +{apply,{{erlang,self,0},"gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:126"}. +{apply,{{erlang,self,0},"gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{get,{"GenMod","gen.erl:126"}}. +{construct_pattern,{{cons},"gen.erl:126"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_proc_lib,start_link,5},"gen.erl:125"}. +{push_scope,{{proc_lib,start_link,5},"proc_lib.erl:355"}}. +{store,{"M","proc_lib.erl:355"}}. +{store,{"F","proc_lib.erl:355"}}. +{store,{"A","proc_lib.erl:355"}}. +{store,{"Timeout","proc_lib.erl:355"}}. +{store,{"SpawnOpts","proc_lib.erl:355"}}. +{push,{notaint}}. +{duplicate,{}}. +{store,{"Monitor","proc_lib.erl:356"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"SpawnOpts","proc_lib.erl:356"}}. +{get,{"Monitor","proc_lib.erl:356"}}. +{call_fun,{modeled_taint_lists,member,2},"proc_lib.erl:356"}. +{apply,{{modeled_taint_lists,member,2},"proc_lib.erl:356"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"SpawnOpts","proc_lib.erl:356"}}. +{push,{notaint}}. +{get,{"Monitor","proc_lib.erl:356"}}. +{call_fun,{modeled_taint_lists,keyfind,3},"proc_lib.erl:356"}. +{apply,{{modeled_taint_lists,keyfind,3},"proc_lib.erl:356"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,sync_start,2},"proc_lib.erl:357"}}. +{get,{"Timeout","proc_lib.erl:358"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"SpawnOpts","proc_lib.erl:358"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"proc_lib.erl:358"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"proc_lib.erl:358"}}. +{get,{"A","proc_lib.erl:358"}}. +{get,{"F","proc_lib.erl:358"}}. +{get,{"M","proc_lib.erl:358"}}. +{call_fun,{modeled_proc_lib,spawn_opt,4},"proc_lib.erl:358"}. +{push_scope,{{proc_lib,spawn_opt,4},"proc_lib.erl:189"}}. +{store,{"M","proc_lib.erl:189"}}. +{store,{"F","proc_lib.erl:189"}}. +{store,{"A","proc_lib.erl:189"}}. +{store,{"Opts","proc_lib.erl:189"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,get_my_name,0},"proc_lib.erl:190"}}. +{call_fun,{proc_lib,get_my_name,0},"proc_lib.erl:190"}. +{push_scope,{{proc_lib,get_my_name,0},"proc_lib.erl:797"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,proc_info,2},"proc_lib.erl:798"}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"proc_lib.erl:798"}. +{apply,{{erlang,self,0},"proc_lib.erl:798"}}. +{call_fun,{proc_lib,proc_info,2},"proc_lib.erl:798"}. +{push_scope,{{proc_lib,proc_info,2},"proc_lib.erl:811"}}. +{store,{"Pid","proc_lib.erl:811"}}. +{store,{"Item","proc_lib.erl:811"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Item","proc_lib.erl:812"}}. +{get,{"Pid","proc_lib.erl:812"}}. +{call_fun,{erlang,process_info,2},"proc_lib.erl:812"}. +{apply,{{erlang,process_info,2},"proc_lib.erl:812"}}. +{func_ret,{proc_info,"proc_lib.erl:811"}}. +{apply,{{proc_lib,proc_info,2},"proc_lib.erl:798"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:798"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"proc_lib.erl:800"}. +{apply,{{erlang,self,0},"proc_lib.erl:800"}}. +{func_ret,{get_my_name,"proc_lib.erl:797"}}. +{apply,{{proc_lib,get_my_name,0},"proc_lib.erl:190"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:190"}}. +{duplicate,{}}. +{store,{"Parent","proc_lib.erl:190"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,get_ancestors,0},"proc_lib.erl:191"}}. +{call_fun,{proc_lib,get_ancestors,0},"proc_lib.erl:191"}. +{push_scope,{{proc_lib,get_ancestors,0},"proc_lib.erl:805"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mget,1},"proc_lib.erl:806"}. +{push_scope,{{modeled_erlang,mget,1},"modeled_erlang.erl:163"}}. +{store,{"Key","modeled_erlang.erl:163"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:164"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:164"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:165"}}. +{get,{"Key","modeled_erlang.erl:165"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}}. +{func_ret,{mget,"modeled_erlang.erl:163"}}. +{apply,{{modeled_erlang,mget,1},"proc_lib.erl:806"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{get_ancestors,"proc_lib.erl:805"}}. +{apply,{{proc_lib,get_ancestors,0},"proc_lib.erl:191"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:191"}}. +{duplicate,{}}. +{store,{"Ancestors","proc_lib.erl:191"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Opts","proc_lib.erl:192"}}. +{push,{notaint}}. +{get,{"A","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"F","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"M","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"Ancestors","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{get,{"Parent","proc_lib.erl:192"}}. +{construct_pattern,{{cons},"proc_lib.erl:192"}}. +{push,{notaint}}. +{push,{notaint}}. +{store,{"CfnArg1","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"proc_lib.erl:192"}}. +{duplicate,{}}. +{send,{521,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"proc_lib.erl:192"}}. +{get,{"CfnArg1","instrumentation"}}. +{call_fun,{erlang,spawn_opt,4},"proc_lib.erl:192"}. +{apply,{{erlang,spawn_opt,4},"proc_lib.erl:192"}}. +{func_ret,{spawn_opt,"proc_lib.erl:189"}}. +{apply,{{modeled_proc_lib,spawn_opt,4},"proc_lib.erl:358"}}. +{call_fun,{proc_lib,sync_start,2},"proc_lib.erl:357"}. +{push_scope,{{proc_lib,sync_start,2},"proc_lib.erl:309"}}. +{deconstruct_pattern,{{tuple,2},"proc_lib.erl:309"}}. +{store,{"Pid","proc_lib.erl:309"}}. +{store,{"Ref","proc_lib.erl:309"}}. +{store,{"Timeout","proc_lib.erl:309"}}. +{get,{"Timeout","proc_lib.erl:321"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293122.70247>","proc_lib.erl:310"}}. +{deconstruct_pattern,{{tuple,3},"proc_lib.erl:311"}}. +{pop,{}}. +{store,{"Pid","proc_lib.erl:311"}}. +{store,{"Return","proc_lib.erl:311"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"proc_lib.erl:312"}}. +{get,{"Ref","proc_lib.erl:312"}}. +{call_fun,{erlang,demonitor,2},"proc_lib.erl:312"}. +{apply,{{erlang,demonitor,2},"proc_lib.erl:312"}}. +{pop,{}}. +{get,{"Return","proc_lib.erl:313"}}. +{func_ret,{sync_start,"proc_lib.erl:309"}}. +{apply,{{proc_lib,sync_start,2},"proc_lib.erl:357"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:357"}}. +{func_ret,{start_link,"proc_lib.erl:355"}}. +{apply,{{modeled_proc_lib,start_link,5},"gen.erl:125"}}. +{func_ret,{do_spawn,"gen.erl:123"}}. +{apply,{{gen,do_spawn,5},"gen.erl:117"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:117"}}. +{func_ret,{start,"gen.erl:116"}}. +{apply,{{modeled_gen,start,5},"gen_server.erl:310"}}. +{func_ret,{start_link,"gen_server.erl:309"}}. +{apply,{{modeled_gen_server,start_link,3},"example_gen_server.erl:63"}}. +{func_ret,{start_link,"example_gen_server.erl:62"}}. +{apply,{{example_gen_server,start_link,2},"example_gen_server.erl:51"}}. +{duplicate,{}}. +{deconstruct_pattern,{{tuple,2},"example_gen_server.erl:51"}}. +{pop,{}}. +{store,{"Pid","example_gen_server.erl:51"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"example_gen_server.erl:52"}. +{apply,{{modeled_erlang,real_put,2},"example_gen_server.erl:52"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Pid","example_gen_server.erl:53"}}. +{call_fun,{example_gen_server,stop,1},"example_gen_server.erl:53"}. +{push_scope,{{example_gen_server,stop,1},"example_gen_server.erl:71"}}. +{store,{"Pid","example_gen_server.erl:71"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pid","example_gen_server.erl:72"}}. +{call_fun,{modeled_gen_server,call,2},"example_gen_server.erl:72"}. +{push_scope,{{gen_server,call,2},"gen_server.erl:381"}}. +{store,{"ServerRef","gen_server.erl:381"}}. +{store,{"Request","gen_server.erl:381"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Request","gen_server.erl:382"}}. +{push,{notaint}}. +{get,{"ServerRef","gen_server.erl:382"}}. +{call_fun,{modeled_gen,call,3},"gen_server.erl:382"}. +{push_scope,{{gen,call,3},"gen.erl:214"}}. +{store,{"Process","gen.erl:214"}}. +{store,{"Label","gen.erl:214"}}. +{store,{"Request","gen.erl:214"}}. +{push,{notaint}}. +{restore_capture,{{gen,call,4},"gen.erl:215"}}. +{push,{notaint}}. +{get,{"Request","gen.erl:215"}}. +{get,{"Label","gen.erl:215"}}. +{get,{"Process","gen.erl:215"}}. +{call_fun,{gen,call,4},"gen.erl:215"}. +{push_scope,{{gen,call,4},"gen.erl:218"}}. +{store,{"Process","gen.erl:218"}}. +{store,{"Label","gen.erl:218"}}. +{store,{"Request","gen.erl:218"}}. +{store,{"Timeout","gen.erl:218"}}. +{push,{notaint}}. +{restore_capture,{{gen,do_call,4},"gen.erl:220"}}. +{get,{"Timeout","gen.erl:220"}}. +{get,{"Request","gen.erl:220"}}. +{get,{"Label","gen.erl:220"}}. +{get,{"Process","gen.erl:220"}}. +{call_fun,{gen,do_call,4},"gen.erl:220"}. +{push_scope,{{gen,do_call,4},"gen.erl:248"}}. +{store,{"Process","gen.erl:248"}}. +{store,{"Label","gen.erl:248"}}. +{store,{"Request","gen.erl:248"}}. +{store,{"Timeout","gen.erl:248"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen.erl:249"}}. +{construct_pattern,{{cons},"gen.erl:249"}}. +{get,{"Process","gen.erl:249"}}. +{push,{notaint}}. +{call_fun,{erlang,monitor,3},"gen.erl:249"}. +{apply,{{erlang,monitor,3},"gen.erl:249"}}. +{duplicate,{}}. +{store,{"Mref","gen.erl:249"}}. +{pop,{}}. +{get,{"Mref","gen.erl:251"}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:251"}}. +{duplicate,{}}. +{store,{"Tag","gen.erl:251"}}. +{pop,{}}. +{get,{"Process","gen.erl:259"}}. +{get,{"Label","gen.erl:259"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:259"}. +{apply,{{erlang,self,0},"gen.erl:259"}}. +{get,{"Tag","gen.erl:259"}}. +{construct_pattern,{{tuple,2},"gen.erl:259"}}. +{get,{"Request","gen.erl:259"}}. +{construct_pattern,{{tuple,3},"gen.erl:259"}}. +{send,{"#Ref<0.1321034574.60293121.70804>","gen.erl:259"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:259"}}. +{pop,{}}. +{get,{"Timeout","gen.erl:270"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293122.70248>","gen.erl:261"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:262"}}. +{deconstruct_pattern,{{cons},"gen.erl:262"}}. +{pop,{}}. +{store,{"Mref","gen.erl:262"}}. +{store,{"Reply","gen.erl:262"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{cons},"gen.erl:263"}}. +{get,{"Mref","gen.erl:263"}}. +{call_fun,{erlang,demonitor,2},"gen.erl:263"}. +{apply,{{erlang,demonitor,2},"gen.erl:263"}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Reply","gen.erl:264"}}. +{construct_pattern,{{tuple,2},"gen.erl:264"}}. +{func_ret,{do_call,"gen.erl:248"}}. +{apply,{{gen,do_call,4},"gen.erl:220"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:220"}}. +{func_ret,{call,"gen.erl:218"}}. +{apply,{{gen,call,4},"gen.erl:215"}}. +{func_ret,{dropping_lambda_capture,"gen.erl:215"}}. +{func_ret,{call,"gen.erl:214"}}. +{apply,{{modeled_gen,call,3},"gen_server.erl:382"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:383"}}. +{pop,{}}. +{store,{"Res","gen_server.erl:383"}}. +{get,{"Res","gen_server.erl:384"}}. +{func_ret,{call,"gen_server.erl:381"}}. +{apply,{{modeled_gen_server,call,2},"example_gen_server.erl:72"}}. +{func_ret,{stop,"example_gen_server.erl:71"}}. +{apply,{{example_gen_server,stop,1},"example_gen_server.erl:53"}}. +{func_ret,{gen_server_init_test_main,"example_gen_server.erl:48"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-521 b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-521 new file mode 100644 index 0000000..ead75ee --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/test_gen_server_init_analaysis_instr-521 @@ -0,0 +1,955 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{proc_lib,init_p,5},"proc_lib.erl:234"}}. +{receive_trace,{521,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"proc_lib.erl:234"}}. +{restore_capture,{{proc_lib,init_p,5},"proc_lib.erl:234"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{deconstruct_pattern,{{cons},"instrumentation"}}. +{store,{"PushScopeTmpHead","instrumentation"}}. +{store,{"PushScopeTmpTail","instrumentation"}}. +{get,{"PushScopeTmpHead","instrumentation"}}. +{get,{"PushScopeTmpTail","instrumentation"}}. +{pop,{}}. +{store,{"PushScopeTmp-1","instrumentation"}}. +{store,{"PushScopeTmp-2","instrumentation"}}. +{store,{"PushScopeTmp-3","instrumentation"}}. +{store,{"PushScopeTmp-4","instrumentation"}}. +{store,{"PushScopeTmp-5","instrumentation"}}. +{get,{"PushScopeTmp-1","instrumentation"}}. +{get,{"PushScopeTmp-2","instrumentation"}}. +{get,{"PushScopeTmp-3","instrumentation"}}. +{get,{"PushScopeTmp-4","instrumentation"}}. +{get,{"PushScopeTmp-5","instrumentation"}}. +{store,{"Parent","proc_lib.erl:234"}}. +{store,{"Ancestors","proc_lib.erl:234"}}. +{store,{"M","proc_lib.erl:234"}}. +{store,{"F","proc_lib.erl:234"}}. +{store,{"A","proc_lib.erl:234"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Ancestors","proc_lib.erl:235"}}. +{get,{"Parent","proc_lib.erl:235"}}. +{construct_pattern,{{cons},"proc_lib.erl:235"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mput,2},"proc_lib.erl:235"}. +{push_scope,{{modeled_erlang,mput,2},"modeled_erlang.erl:155"}}. +{store,{"Key","modeled_erlang.erl:155"}}. +{store,{"Value","modeled_erlang.erl:155"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:156"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:156"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:157"}}. +{get,{"Key","modeled_erlang.erl:157"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}}. +{duplicate,{}}. +{store,{"ReturnValue","modeled_erlang.erl:157"}}. +{pop,{}}. +{get,{"Key","modeled_erlang.erl:158"}}. +{get,{"Value","modeled_erlang.erl:158"}}. +{get,{"Pd","modeled_erlang.erl:158"}}. +{construct_pattern,{{map,['$ancestors']},"modeled_erlang.erl:158"}}. +{duplicate,{}}. +{store,{"NewDict","modeled_erlang.erl:158"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{get,{"NewDict","modeled_erlang.erl:159"}}. +{call_fun,{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}. +{apply,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:159"}}. +{pop,{}}. +{get,{"ReturnValue","modeled_erlang.erl:160"}}. +{func_ret,{mput,"modeled_erlang.erl:155"}}. +{apply,{{modeled_erlang,mput,2},"proc_lib.erl:235"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,trans_init,3},"proc_lib.erl:236"}}. +{get,{"A","proc_lib.erl:236"}}. +{get,{"F","proc_lib.erl:236"}}. +{get,{"M","proc_lib.erl:236"}}. +{call_fun,{proc_lib,trans_init,3},"proc_lib.erl:236"}. +{push_scope,{{proc_lib,trans_init,3},"proc_lib.erl:573"}}. +{store,{"M","proc_lib.erl:573"}}. +{store,{"F","proc_lib.erl:573"}}. +{store,{"A","proc_lib.erl:573"}}. +{get,{"M","proc_lib.erl:574"}}. +{get,{"F","proc_lib.erl:574"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"A","proc_lib.erl:574"}}. +{call_fun,{erlang,length,1},"proc_lib.erl:574"}. +{apply,{{erlang,length,1},"proc_lib.erl:574"}}. +{construct_pattern,{{tuple,3},"proc_lib.erl:574"}}. +{func_ret,{trans_init,"proc_lib.erl:573"}}. +{apply,{{proc_lib,trans_init,3},"proc_lib.erl:236"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:236"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mput,2},"proc_lib.erl:236"}. +{push_scope,{{modeled_erlang,mput,2},"modeled_erlang.erl:155"}}. +{store,{"Key","modeled_erlang.erl:155"}}. +{store,{"Value","modeled_erlang.erl:155"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:156"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:156"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:156"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:157"}}. +{get,{"Key","modeled_erlang.erl:157"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:157"}}. +{duplicate,{}}. +{store,{"ReturnValue","modeled_erlang.erl:157"}}. +{pop,{}}. +{get,{"Key","modeled_erlang.erl:158"}}. +{get,{"Value","modeled_erlang.erl:158"}}. +{get,{"Pd","modeled_erlang.erl:158"}}. +{construct_pattern,{{map,['$initial_call']},"modeled_erlang.erl:158"}}. +{duplicate,{}}. +{store,{"NewDict","modeled_erlang.erl:158"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{get,{"NewDict","modeled_erlang.erl:159"}}. +{call_fun,{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}. +{apply,{{modeled_erlang,process_dict,1},"modeled_erlang.erl:159"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:159"}}. +{pop,{}}. +{get,{"ReturnValue","modeled_erlang.erl:160"}}. +{func_ret,{mput,"modeled_erlang.erl:155"}}. +{apply,{{modeled_erlang,mput,2},"proc_lib.erl:236"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,init_p_do_apply,3},"proc_lib.erl:237"}}. +{get,{"A","proc_lib.erl:237"}}. +{get,{"F","proc_lib.erl:237"}}. +{get,{"M","proc_lib.erl:237"}}. +{call_fun,{proc_lib,init_p_do_apply,3},"proc_lib.erl:237"}. +{push_scope,{{proc_lib,init_p_do_apply,3},"proc_lib.erl:239"}}. +{store,{"M","proc_lib.erl:239"}}. +{store,{"F","proc_lib.erl:239"}}. +{store,{"A","proc_lib.erl:239"}}. +{try_catch,{try_enter,{proc_lib,240,87462718}},"proc_lib.erl:240"}. +{push,{notaint}}. +{pop,{}}. +{get,{"A","proc_lib.erl:241"}}. +{get,{"F","proc_lib.erl:241"}}. +{get,{"M","proc_lib.erl:241"}}. +{call_fun,{modeled_erlang,mapply,3},"proc_lib.erl:241"}. +{push_scope,{{modeled_erlang,mapply,3},"modeled_erlang.erl:49"}}. +{store,{"M","modeled_erlang.erl:49"}}. +{store,{"F","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg1","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg2","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg3","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg4","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg5","modeled_erlang.erl:49"}}. +{deconstruct_pattern,{{cons},"modeled_erlang.erl:49"}}. +{store,{"Arg6","modeled_erlang.erl:49"}}. +{pop,{}}. +{get,{"F","modeled_erlang.erl:50"}}. +{pop,{}}. +{get,{"Arg6","modeled_erlang.erl:50"}}. +{get,{"Arg5","modeled_erlang.erl:50"}}. +{get,{"Arg4","modeled_erlang.erl:50"}}. +{get,{"Arg3","modeled_erlang.erl:50"}}. +{get,{"Arg2","modeled_erlang.erl:50"}}. +{get,{"Arg1","modeled_erlang.erl:50"}}. +{call_fun,{modeled_gen,init_it,6},"modeled_erlang.erl:50"}. +{push_scope,{{gen,init_it,6},"gen.erl:190"}}. +{store,{"GenMod","gen.erl:190"}}. +{store,{"Starter","gen.erl:190"}}. +{store,{"Parent","gen.erl:190"}}. +{store,{"Mod","gen.erl:190"}}. +{store,{"Args","gen.erl:190"}}. +{store,{"Options","gen.erl:190"}}. +{push,{notaint}}. +{restore_capture,{{gen,init_it2,7},"gen.erl:191"}}. +{get,{"Options","gen.erl:191"}}. +{get,{"Args","gen.erl:191"}}. +{get,{"Mod","gen.erl:191"}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen.erl:191"}. +{apply,{{erlang,self,0},"gen.erl:191"}}. +{get,{"Parent","gen.erl:191"}}. +{get,{"Starter","gen.erl:191"}}. +{get,{"GenMod","gen.erl:191"}}. +{call_fun,{gen,init_it2,7},"gen.erl:191"}. +{push_scope,{{gen,init_it2,7},"gen.erl:202"}}. +{store,{"GenMod","gen.erl:202"}}. +{store,{"Starter","gen.erl:202"}}. +{store,{"Parent","gen.erl:202"}}. +{store,{"Name","gen.erl:202"}}. +{store,{"Mod","gen.erl:202"}}. +{store,{"Args","gen.erl:202"}}. +{store,{"Options","gen.erl:202"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:203"}}. +{get,{"Args","gen.erl:203"}}. +{get,{"Mod","gen.erl:203"}}. +{get,{"Name","gen.erl:203"}}. +{get,{"Parent","gen.erl:203"}}. +{get,{"Starter","gen.erl:203"}}. +{call_fun,{modeled_gen_server,init_it,6},"gen.erl:203"}. +{push_scope,{{gen_server,init_it,6},"gen_server.erl:912"}}. +{store,{"Starter","gen_server.erl:912"}}. +{store,{"Parent","gen_server.erl:912"}}. +{store,{"Name0","gen_server.erl:912"}}. +{store,{"Mod","gen_server.erl:912"}}. +{store,{"Args","gen_server.erl:912"}}. +{store,{"Options","gen_server.erl:912"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Name0","gen_server.erl:913"}}. +{call_fun,{modeled_gen,name,1},"gen_server.erl:913"}. +{push_scope,{{gen,name,1},"gen.erl:642"}}. +{store,{"Pid","gen.erl:642"}}. +{get,{"Pid","gen.erl:642"}}. +{func_ret,{name,"gen.erl:642"}}. +{apply,{{modeled_gen,name,1},"gen_server.erl:913"}}. +{duplicate,{}}. +{store,{"Name","gen_server.erl:913"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen_server.erl:914"}}. +{get,{"Name","gen_server.erl:914"}}. +{call_fun,{modeled_gen,debug_options,2},"gen_server.erl:914"}. +{push_scope,{{gen,debug_options,2},"gen.erl:737"}}. +{store,{"Name","gen.erl:737"}}. +{store,{"Opts","gen.erl:737"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Opts","gen.erl:738"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:738"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:738"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{debug_options,"gen.erl:737"}}. +{apply,{{modeled_gen,debug_options,2},"gen_server.erl:914"}}. +{duplicate,{}}. +{store,{"Debug","gen_server.erl:914"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen_server.erl:915"}}. +{call_fun,{modeled_gen,hibernate_after,1},"gen_server.erl:915"}. +{push_scope,{{gen,hibernate_after,1},"gen.erl:729"}}. +{store,{"Options","gen.erl:729"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Options","gen.erl:730"}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_taint_lists,keyfind,3},"gen.erl:730"}. +{apply,{{modeled_taint_lists,keyfind,3},"gen.erl:730"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{hibernate_after,"gen.erl:729"}}. +{apply,{{modeled_gen,hibernate_after,1},"gen_server.erl:915"}}. +{duplicate,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:915"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,create_callback_cache,1},"gen_server.erl:916"}}. +{get,{"Mod","gen_server.erl:916"}}. +{call_fun,{gen_server,create_callback_cache,1},"gen_server.erl:916"}. +{push_scope,{{gen_server,create_callback_cache,1},"gen_server.erl:1010"}}. +{store,{"Mod","gen_server.erl:1010"}}. +{push,{notaint}}. +{get,{"Mod","gen_server.erl:1011"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,5},"gen_server.erl:1011"}}. +{func_ret,{create_callback_cache,"gen_server.erl:1010"}}. +{apply,{{gen_server,create_callback_cache,1},"gen_server.erl:916"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:916"}}. +{duplicate,{}}. +{store,{"CbCache","gen_server.erl:916"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,init_it,2},"gen_server.erl:917"}}. +{get,{"Args","gen_server.erl:917"}}. +{get,{"Mod","gen_server.erl:917"}}. +{call_fun,{gen_server,init_it,2},"gen_server.erl:917"}. +{push_scope,{{gen_server,init_it,2},"gen_server.erl:960"}}. +{store,{"Mod","gen_server.erl:960"}}. +{store,{"Args","gen_server.erl:960"}}. +{try_catch,{try_enter,{gen_server,961,46082404}},"gen_server.erl:961"}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Args","gen_server.erl:962"}}. +{call_fun,{example_gen_server,init,1},"gen_server.erl:962"}. +{push_scope,{{example_gen_server,init,1},"example_gen_server.erl:75"}}. +{deconstruct_pattern,{{cons},"example_gen_server.erl:75"}}. +{store,{"TaintedVal","example_gen_server.erl:75"}}. +{deconstruct_pattern,{{cons},"example_gen_server.erl:75"}}. +{store,{"NotTaintedVal","example_gen_server.erl:75"}}. +{pop,{}}. +{get,{"TaintedVal","example_gen_server.erl:76"}}. +{sink,{"example_gen_server.erl:76"}}. +{pop,{}}. +{get,{"NotTaintedVal","example_gen_server.erl:77"}}. +{sink,{"example_gen_server.erl:77"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"example_gen_server.erl:78"}}. +{func_ret,{init,"example_gen_server.erl:75"}}. +{apply,{{example_gen_server,init,1},"gen_server.erl:962"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:962"}}. +{try_catch,{try_exit,{gen_server,961,46082404}},"gen_server.erl:961"}. +{func_ret,{init_it,"gen_server.erl:960"}}. +{apply,{{gen_server,init_it,2},"gen_server.erl:917"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:917"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:918"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:918"}}. +{pop,{}}. +{store,{"State","gen_server.erl:918"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen_server.erl:919"}. +{apply,{{erlang,self,0},"gen_server.erl:919"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:919"}}. +{get,{"Starter","gen_server.erl:919"}}. +{call_fun,{modeled_proc_lib,init_ack,2},"gen_server.erl:919"}. +{push_scope,{{proc_lib,init_ack,2},"proc_lib.erl:448"}}. +{store,{"Parent","proc_lib.erl:448"}}. +{store,{"Return","proc_lib.erl:448"}}. +{get,{"Parent","proc_lib.erl:449"}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"proc_lib.erl:449"}. +{apply,{{erlang,self,0},"proc_lib.erl:449"}}. +{get,{"Return","proc_lib.erl:449"}}. +{construct_pattern,{{tuple,3},"proc_lib.erl:449"}}. +{send,{"#Ref<0.1321034574.60293122.70247>","proc_lib.erl:449"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{init_ack,"proc_lib.erl:448"}}. +{apply,{{modeled_proc_lib,init_ack,2},"gen_server.erl:919"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,loop,7},"gen_server.erl:920"}}. +{get,{"Debug","gen_server.erl:922"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:922"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:921"}}. +{get,{"State","gen_server.erl:921"}}. +{get,{"Name","gen_server.erl:921"}}. +{get,{"Parent","gen_server.erl:921"}}. +{call_fun,{gen_server,loop,7},"gen_server.erl:920"}. +{push_scope,{{gen_server,loop,7},"gen_server.erl:992"}}. +{store,{"Parent","gen_server.erl:992"}}. +{store,{"Name","gen_server.erl:992"}}. +{store,{"State","gen_server.erl:992"}}. +{store,{"CbCache","gen_server.erl:992"}}. +{pop,{}}. +{store,{"HibernateAfterTimeout","gen_server.erl:992"}}. +{store,{"Debug","gen_server.erl:992"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:996"}}. +{pop,{}}. +{receive_trace,{"#Ref<0.1321034574.60293121.70804>","gen_server.erl:993"}}. +{store,{"Msg","gen_server.erl:994"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,decode_msg,9},"gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"Debug","gen_server.erl:995"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:995"}}. +{push,{notaint}}. +{get,{"CbCache","gen_server.erl:995"}}. +{get,{"State","gen_server.erl:995"}}. +{get,{"Name","gen_server.erl:995"}}. +{get,{"Parent","gen_server.erl:995"}}. +{get,{"Msg","gen_server.erl:995"}}. +{call_fun,{gen_server,decode_msg,9},"gen_server.erl:995"}. +{push_scope,{{gen_server,decode_msg,9},"gen_server.erl:1024"}}. +{store,{"Msg","gen_server.erl:1024"}}. +{store,{"Parent","gen_server.erl:1024"}}. +{store,{"Name","gen_server.erl:1024"}}. +{store,{"State","gen_server.erl:1024"}}. +{store,{"CbCache","gen_server.erl:1024"}}. +{store,{"Time","gen_server.erl:1024"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1024"}}. +{store,{"Debug","gen_server.erl:1024"}}. +{store,{"Hib","gen_server.erl:1024"}}. +{get,{"Msg","gen_server.erl:1025"}}. +{store,{"_Msg","gen_server.erl:1032"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,handle_msg,6},"gen_server.erl:1033"}}. +{get,{"HibernateAfterTimeout","gen_server.erl:1033"}}. +{get,{"CbCache","gen_server.erl:1033"}}. +{get,{"State","gen_server.erl:1033"}}. +{get,{"Name","gen_server.erl:1033"}}. +{get,{"Parent","gen_server.erl:1033"}}. +{get,{"Msg","gen_server.erl:1033"}}. +{call_fun,{gen_server,handle_msg,6},"gen_server.erl:1033"}. +{push_scope,{{gen_server,handle_msg,6},"gen_server.erl:1141"}}. +{deconstruct_pattern,{{tuple,3},"gen_server.erl:1141"}}. +{pop,{}}. +{store,{"From","gen_server.erl:1141"}}. +{store,{"Msg","gen_server.erl:1141"}}. +{store,{"Parent","gen_server.erl:1141"}}. +{store,{"Name","gen_server.erl:1141"}}. +{store,{"State","gen_server.erl:1141"}}. +{store,{"CbCache","gen_server.erl:1141"}}. +{store,{"HibernateAfterTimeout","gen_server.erl:1141"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{get,{"State","gen_server.erl:1142"}}. +{get,{"From","gen_server.erl:1142"}}. +{get,{"Msg","gen_server.erl:1142"}}. +{get,{"CbCache","gen_server.erl:1142"}}. +{call_fun,{gen_server,try_handle_call,4},"gen_server.erl:1142"}. +{push_scope,{{gen_server,try_handle_call,4},"gen_server.erl:1111"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"HandleCall","gen_server.erl:1111"}}. +{pop,{}}. +{pop,{}}. +{store,{"Msg","gen_server.erl:1111"}}. +{store,{"From","gen_server.erl:1111"}}. +{store,{"State","gen_server.erl:1111"}}. +{try_catch,{try_enter,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{push,{notaint}}. +{get,{"HandleCall","gen_server.erl:1113"}}. +{restore_capture,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{get,{"State","gen_server.erl:1113"}}. +{get,{"From","gen_server.erl:1113"}}. +{get,{"Msg","gen_server.erl:1113"}}. +{call_fun,{gen_server,variable_func,3},"gen_server.erl:1113"}. +{push_scope,{{example_gen_server,handle_call,3},"example_gen_server.erl:85"}}. +{pop,{}}. +{store,{"_From","example_gen_server.erl:85"}}. +{store,{"State","example_gen_server.erl:85"}}. +{push,{notaint}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"State","example_gen_server.erl:86"}}. +{construct_pattern,{{tuple,4},"example_gen_server.erl:86"}}. +{func_ret,{handle_call,"example_gen_server.erl:85"}}. +{apply,{{gen_server,variable_func,3},"gen_server.erl:1113"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1113"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1113"}}. +{try_catch,{try_exit,{gen_server,1112,39984533}},"gen_server.erl:1112"}. +{func_ret,{try_handle_call,"gen_server.erl:1111"}}. +{apply,{{gen_server,try_handle_call,4},"gen_server.erl:1142"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1142"}}. +{duplicate,{}}. +{store,{"Result","gen_server.erl:1142"}}. +{pop,{}}. +{get,{"Result","gen_server.erl:1143"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1155"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,4},"gen_server.erl:1155"}}. +{pop,{}}. +{store,{"Reason","gen_server.erl:1155"}}. +{store,{"Reply","gen_server.erl:1155"}}. +{store,{"NState","gen_server.erl:1155"}}. +{try_catch,{try_enter,{gen_server,1156,19367316}},"gen_server.erl:1156"}. +{get,{"CbCache","gen_server.erl:1157"}}. +{deconstruct_pattern,{{tuple,5},"gen_server.erl:1157"}}. +{pop,{}}. +{store,{"rec1","gen_server.erl:1157"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{get,{"rec1","gen_server.erl:1157"}}. +{duplicate,{}}. +{store,{"Mod","gen_server.erl:1157"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{gen_server,terminate,8},"gen_server.erl:1158"}}. +{push,{notaint}}. +{get,{"NState","gen_server.erl:1158"}}. +{get,{"Mod","gen_server.erl:1158"}}. +{get,{"Msg","gen_server.erl:1158"}}. +{get,{"From","gen_server.erl:1158"}}. +{get,{"Name","gen_server.erl:1158"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"gen_server.erl:1158"}. +{apply,{{erlang,self,0},"gen_server.erl:1158"}}. +{call_fun,{erlang,process_info,2},"gen_server.erl:1158"}. +{apply,{{erlang,process_info,2},"gen_server.erl:1158"}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,elemnt,2},"gen_server.erl:1158"}. +{push_scope,{{modeled_erlang,elemnt,2},"modeled_erlang.erl:79"}}. +{pop,{}}. +{deconstruct_pattern,{{tuple,2},"modeled_erlang.erl:79"}}. +{store,{"_A1","modeled_erlang.erl:79"}}. +{store,{"A2","modeled_erlang.erl:79"}}. +{get,{"A2","modeled_erlang.erl:80"}}. +{func_ret,{elemnt,"modeled_erlang.erl:79"}}. +{apply,{{modeled_erlang,elemnt,2},"gen_server.erl:1158"}}. +{get,{"Reason","gen_server.erl:1158"}}. +{call_fun,{gen_server,terminate,8},"gen_server.erl:1158"}. +{push_scope,{{gen_server,terminate,8},"gen_server.erl:1312"}}. +{store,{"Reason","gen_server.erl:1312"}}. +{store,{"Stacktrace","gen_server.erl:1312"}}. +{store,{"Name","gen_server.erl:1312"}}. +{store,{"From","gen_server.erl:1312"}}. +{store,{"Msg","gen_server.erl:1312"}}. +{store,{"Mod","gen_server.erl:1312"}}. +{store,{"State","gen_server.erl:1312"}}. +{store,{"Debug","gen_server.erl:1312"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,terminate,10},"gen_server.erl:1313"}}. +{get,{"Debug","gen_server.erl:1313"}}. +{get,{"State","gen_server.erl:1313"}}. +{get,{"Mod","gen_server.erl:1313"}}. +{get,{"Msg","gen_server.erl:1313"}}. +{get,{"From","gen_server.erl:1313"}}. +{get,{"Name","gen_server.erl:1313"}}. +{push,{notaint}}. +{get,{"Stacktrace","gen_server.erl:1313"}}. +{get,{"Reason","gen_server.erl:1313"}}. +{push,{notaint}}. +{call_fun,{gen_server,terminate,10},"gen_server.erl:1313"}. +{push_scope,{{gen_server,terminate,10},"gen_server.erl:1320"}}. +{store,{"Class","gen_server.erl:1320"}}. +{store,{"Reason","gen_server.erl:1320"}}. +{store,{"Stacktrace","gen_server.erl:1320"}}. +{store,{"ReportStacktrace","gen_server.erl:1320"}}. +{store,{"Name","gen_server.erl:1320"}}. +{store,{"From","gen_server.erl:1320"}}. +{store,{"Msg","gen_server.erl:1320"}}. +{store,{"Mod","gen_server.erl:1320"}}. +{store,{"State","gen_server.erl:1320"}}. +{store,{"Debug","gen_server.erl:1320"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,try_terminate,3},"gen_server.erl:1321"}}. +{get,{"State","gen_server.erl:1321"}}. +{push,{notaint}}. +{restore_capture,{{gen_server,catch_result,3},"gen_server.erl:1321"}}. +{get,{"Stacktrace","gen_server.erl:1321"}}. +{get,{"Reason","gen_server.erl:1321"}}. +{get,{"Class","gen_server.erl:1321"}}. +{call_fun,{gen_server,catch_result,3},"gen_server.erl:1321"}. +{push_scope,{{gen_server,catch_result,3},"gen_server.erl:1346"}}. +{pop,{}}. +{store,{"Reason","gen_server.erl:1346"}}. +{store,{"_Stacktrace","gen_server.erl:1346"}}. +{get,{"Reason","gen_server.erl:1346"}}. +{func_ret,{catch_result,"gen_server.erl:1346"}}. +{apply,{{gen_server,catch_result,3},"gen_server.erl:1321"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1321"}}. +{get,{"Mod","gen_server.erl:1321"}}. +{call_fun,{gen_server,try_terminate,3},"gen_server.erl:1321"}. +{push_scope,{{gen_server,try_terminate,3},"gen_server.erl:1121"}}. +{store,{"Mod","gen_server.erl:1121"}}. +{store,{"Reason","gen_server.erl:1121"}}. +{store,{"State","gen_server.erl:1121"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{get,{"Mod","gen_server.erl:1122"}}. +{call_fun,{erlang,function_exported,3},"gen_server.erl:1122"}. +{apply,{{erlang,function_exported,3},"gen_server.erl:1122"}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1133"}}. +{func_ret,{try_terminate,"gen_server.erl:1121"}}. +{apply,{{gen_server,try_terminate,3},"gen_server.erl:1321"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1321"}}. +{duplicate,{}}. +{store,{"Reply","gen_server.erl:1321"}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:1322"}}. +{pop,{}}. +{get,{"Class","gen_server.erl:1327"}}. +{get,{"Reason","gen_server.erl:1327"}}. +{construct_pattern,{{tuple,2},"gen_server.erl:1327"}}. +{deconstruct_pattern,{{tuple,2},"gen_server.erl:1328"}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Stacktrace","gen_server.erl:1337"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Stacktrace","gen_server.erl:1341"}}. +{get,{"Reason","gen_server.erl:1341"}}. +{get,{"Class","gen_server.erl:1341"}}. +{call_fun,{erlang,raise,3},"gen_server.erl:1341"}. +{push,{notaint}}. +{restore_capture,{{gen_server,reply,2},"gen_server.erl:1160"}}. +{get,{"Reply","gen_server.erl:1160"}}. +{get,{"From","gen_server.erl:1160"}}. +{call_fun,{gen_server,reply,2},"gen_server.erl:1160"}. +{push_scope,{{gen_server,reply,2},"gen_server.erl:615"}}. +{store,{"Client","gen_server.erl:615"}}. +{store,{"Reply","gen_server.erl:615"}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Reply","gen_server.erl:616"}}. +{get,{"Client","gen_server.erl:616"}}. +{call_fun,{modeled_gen,reply,2},"gen_server.erl:616"}. +{push_scope,{{gen,reply,2},"gen.erl:546"}}. +{deconstruct_pattern,{{tuple,2},"gen.erl:546"}}. +{store,{"_To","gen.erl:546"}}. +{store,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{store,{"Tag","gen.erl:546"}}. +{get,{"PatternTmpVar_TSBDXBCT","gen.erl:546"}}. +{deconstruct_pattern,{{cons},"gen.erl:546"}}. +{pop,{}}. +{store,{"Alias","gen.erl:546"}}. +{store,{"Reply","gen.erl:546"}}. +{get,{"Alias","gen.erl:547"}}. +{get,{"Tag","gen.erl:547"}}. +{get,{"Reply","gen.erl:547"}}. +{construct_pattern,{{tuple,2},"gen.erl:547"}}. +{send,{"#Ref<0.1321034574.60293122.70248>","gen.erl:547"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{reply,"gen.erl:546"}}. +{apply,{{modeled_gen,reply,2},"gen_server.erl:616"}}. +{func_ret,{reply,"gen_server.erl:615"}}. +{apply,{{gen_server,reply,2},"gen_server.erl:1160"}}. +{func_ret,{dropping_lambda_capture,"gen_server.erl:1160"}}. +{pop,{}}. +{try_catch,{catch_enter,{proc_lib,240,87462718}},"proc_lib.erl:243"}. +{push,{notaint}}. +{store,{"Stacktrace","proc_lib.erl:243"}}. +{push,{notaint}}. +{store,{"Class","proc_lib.erl:243"}}. +{store,{"Reason","proc_lib.erl:243"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,exit_p,3},"proc_lib.erl:244"}}. +{get,{"Stacktrace","proc_lib.erl:244"}}. +{get,{"Reason","proc_lib.erl:244"}}. +{get,{"Class","proc_lib.erl:244"}}. +{call_fun,{proc_lib,exit_p,3},"proc_lib.erl:244"}. +{push_scope,{{proc_lib,exit_p,3},"proc_lib.erl:257"}}. +{store,{"Class","proc_lib.erl:257"}}. +{store,{"Reason","proc_lib.erl:257"}}. +{store,{"Stacktrace","proc_lib.erl:257"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,mget,1},"proc_lib.erl:258"}. +{push_scope,{{modeled_erlang,mget,1},"modeled_erlang.erl:163"}}. +{store,{"Key","modeled_erlang.erl:163"}}. +{push,{notaint}}. +{restore_capture,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{call_fun,{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}. +{apply,{{modeled_erlang,process_dict,0},"modeled_erlang.erl:164"}}. +{func_ret,{dropping_lambda_capture,"modeled_erlang.erl:164"}}. +{duplicate,{}}. +{store,{"Pd","modeled_erlang.erl:164"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Pd","modeled_erlang.erl:165"}}. +{get,{"Key","modeled_erlang.erl:165"}}. +{call_fun,{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}. +{apply,{{modeled_taint_maps,get,3},"modeled_erlang.erl:165"}}. +{func_ret,{mget,"modeled_erlang.erl:163"}}. +{apply,{{modeled_erlang,mget,1},"proc_lib.erl:258"}}. +{deconstruct_pattern,{{tuple,3},"proc_lib.erl:259"}}. +{store,{"M","proc_lib.erl:259"}}. +{store,{"F","proc_lib.erl:259"}}. +{store,{"A","proc_lib.erl:259"}}. +{get,{"M","proc_lib.erl:260"}}. +{get,{"F","proc_lib.erl:260"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:260"}}. +{push,{notaint}}. +{get,{"A","proc_lib.erl:260"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:260"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:500"}}. +{store,{"N","proc_lib.erl:500"}}. +{store,{"Acc","proc_lib.erl:500"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{pop,{}}. +{get,{"N","proc_lib.erl:501"}}. +{call_fun,{erlang,integer_to_list,1},"proc_lib.erl:501"}. +{apply,{{erlang,integer_to_list,1},"proc_lib.erl:501"}}. +{call_fun,{operators,'++',2},"proc_lib.erl:501"}. +{apply,{{operators,'++',2},"proc_lib.erl:501"}}. +{call_fun,{erlang,list_to_atom,1},"proc_lib.erl:501"}. +{apply,{{erlang,list_to_atom,1},"proc_lib.erl:501"}}. +{duplicate,{}}. +{store,{"Arg","proc_lib.erl:501"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{get,{"Acc","proc_lib.erl:502"}}. +{get,{"Arg","proc_lib.erl:502"}}. +{construct_pattern,{{cons},"proc_lib.erl:502"}}. +{get,{"N","proc_lib.erl:502"}}. +{push,{notaint}}. +{call_fun,{operators,'-',2},"proc_lib.erl:502"}. +{apply,{{operators,'-',2},"proc_lib.erl:502"}}. +{call_fun,{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}. +{push_scope,{{proc_lib,make_dummy_args,2},"proc_lib.erl:498"}}. +{pop,{}}. +{store,{"Acc","proc_lib.erl:498"}}. +{get,{"Acc","proc_lib.erl:499"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:498"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:502"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:502"}}. +{func_ret,{make_dummy_args,"proc_lib.erl:500"}}. +{apply,{{proc_lib,make_dummy_args,2},"proc_lib.erl:260"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:260"}}. +{construct_pattern,{{tuple,3},"proc_lib.erl:260"}}. +{duplicate,{}}. +{store,{"MFA","proc_lib.erl:260"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,crash_report,4},"proc_lib.erl:261"}}. +{get,{"Stacktrace","proc_lib.erl:261"}}. +{get,{"MFA","proc_lib.erl:261"}}. +{get,{"Reason","proc_lib.erl:261"}}. +{get,{"Class","proc_lib.erl:261"}}. +{call_fun,{proc_lib,crash_report,4},"proc_lib.erl:261"}. +{push_scope,{{proc_lib,crash_report,4},"proc_lib.erl:580"}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{crash_report,"proc_lib.erl:580"}}. +{apply,{{proc_lib,crash_report,4},"proc_lib.erl:261"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:261"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Stacktrace","proc_lib.erl:262"}}. +{push,{notaint}}. +{restore_capture,{{proc_lib,exit_reason,3},"proc_lib.erl:262"}}. +{get,{"Stacktrace","proc_lib.erl:262"}}. +{get,{"Reason","proc_lib.erl:262"}}. +{get,{"Class","proc_lib.erl:262"}}. +{call_fun,{proc_lib,exit_reason,3},"proc_lib.erl:262"}. +{push_scope,{{proc_lib,exit_reason,3},"proc_lib.erl:272"}}. +{pop,{}}. +{store,{"Reason","proc_lib.erl:272"}}. +{store,{"_Stacktrace","proc_lib.erl:272"}}. +{get,{"Reason","proc_lib.erl:273"}}. +{func_ret,{exit_reason,"proc_lib.erl:272"}}. +{apply,{{proc_lib,exit_reason,3},"proc_lib.erl:262"}}. +{func_ret,{dropping_lambda_capture,"proc_lib.erl:262"}}. +{push,{notaint}}. +{call_fun,{erlang,raise,3},"proc_lib.erl:262"}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/two_pids.erl b/finer_taint/test/parallel_taint_SUITE_data/two_pids.erl new file mode 100644 index 0000000..93fed09 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/two_pids.erl @@ -0,0 +1,83 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +-module('two_pids'). +-export([ + two_pids_main/0, hibernate_subproc/2, hibernate_main/0, + not_instrumented_send_main/0 +]). + +done() -> + receive + done -> ok + end. + +sub_proc(Parent) -> + Data = + receive + {some_info, Var} -> + io:format("got some info ~p~n", [Var]), + Var; + {some_tainted_info, T} -> + io:format("got some other info~p~n", [T]), + T + end, + finer_taint:sink(Data), + Parent ! done. + +two_pids_main() -> + Taint = finer_taint:source(42), + Parent = self(), + modeled_erlang:real_put(next_taint_pid, 421), + spawn_link(fun() -> sub_proc(Parent) end) ! {some_tainted_info, Taint}, + modeled_erlang:real_put(next_taint_pid, 422), + spawn_link(fun() -> sub_proc(Parent) end) ! {some_info, ha}, + done(), + done(), + receive + never_get_this_msg -> ok + after + 200 -> finer_taint:sink(Taint) + end, + ok. + + +not_instrumented_send_main() -> + Parent = self(), + Taint = finer_taint:source(42), + modeled_erlang:real_put(next_taint_pid, 431), + Child = spawn_link(fun() -> sub_proc(Parent) end), + % This simulates sending a message from uninstrumented code + erlang:apply(erlang, send, [Child,{some_tainted_info, Taint}]), + done(), + ok. + +hibernate_subproc(Parent, Tainted) -> + finer_taint:sink(Tainted), + receive + {a_msg, AlsoTainted} -> finer_taint:sink(AlsoTainted) + end, + Parent ! done. + +hibernate_main() -> + Parent = self(), + ParentTaint = finer_taint:source("12344555@phone.net"), + modeled_erlang:real_put(next_taint_pid, 441), + Child = spawn(fun() -> + Tainted = finer_taint:source(42), + erlang:hibernate(?MODULE, hibernate_subproc, [Parent, Tainted]) + end), + Child ! {a_msg, ParentTaint}, + done(), + ok. diff --git a/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-2315115876 b/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-2315115876 new file mode 100644 index 0000000..b4536d2 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-2315115876 @@ -0,0 +1,105 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{two_pids,two_pids_main,0},"two_pids.erl:39"}}. +{push,{notaint}}. +{pop,{}}. +{push,{"two_pids.erl:40"}}. +{push,{notaint}}. +{pop,{}}. +{duplicate,{}}. +{store,{"Taint","two_pids.erl:40"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{call_fun,{erlang,self,0},"two_pids.erl:41"}. +{apply,{{erlang,self,0},"two_pids.erl:41"}}. +{duplicate,{}}. +{store,{"Parent","two_pids.erl:41"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"two_pids.erl:42"}. +{apply,{{modeled_erlang,real_put,2},"two_pids.erl:42"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{capture_closure,{["Parent"]}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{push,{notaint}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"two_pids.erl:43"}}. +{duplicate,{}}. +{send,{421,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:43"}}. +{pop,{}}. +{call_fun,{erlang,spawn_link,1},"two_pids.erl:43"}. +{apply,{{erlang,spawn_link,1},"two_pids.erl:43"}}. +{push,{notaint}}. +{get,{"Taint","two_pids.erl:43"}}. +{construct_pattern,{{tuple,2},"two_pids.erl:43"}}. +{send,{"#Ref<0.1321034574.60293121.64623>","two_pids.erl:43"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{push,{notaint}}. +{call_fun,{modeled_erlang,real_put,2},"two_pids.erl:44"}. +{apply,{{modeled_erlang,real_put,2},"two_pids.erl:44"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{capture_closure,{["Parent"]}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{push,{notaint}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg0","instrumentation"}}. +{store,{"Cfn1Arg1","instrumentation"}}. +{get,{"Cfn1Arg0","instrumentation"}}. +{get,{"Cfn1Arg1","instrumentation"}}. +{construct_pattern,{{tuple,2},"two_pids.erl:45"}}. +{duplicate,{}}. +{send,{422,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:45"}}. +{pop,{}}. +{call_fun,{erlang,spawn_link,1},"two_pids.erl:45"}. +{apply,{{erlang,spawn_link,1},"two_pids.erl:45"}}. +{push,{notaint}}. +{push,{notaint}}. +{construct_pattern,{{tuple,2},"two_pids.erl:45"}}. +{send,{"#Ref<0.1321034574.60293121.64625>","two_pids.erl:45"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{two_pids,done,0},"two_pids.erl:46"}}. +{call_fun,{two_pids,done,0},"two_pids.erl:46"}. +{push_scope,{{two_pids,done,0},"two_pids.erl:21"}}. +{receive_trace,{"#Ref<0.1321034574.60293122.65291>","two_pids.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"two_pids.erl:21"}}. +{apply,{{two_pids,done,0},"two_pids.erl:46"}}. +{func_ret,{dropping_lambda_capture,"two_pids.erl:46"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{two_pids,done,0},"two_pids.erl:47"}}. +{call_fun,{two_pids,done,0},"two_pids.erl:47"}. +{push_scope,{{two_pids,done,0},"two_pids.erl:21"}}. +{receive_trace,{"#Ref<0.1321034574.60293121.64637>","two_pids.erl:22"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{done,"two_pids.erl:21"}}. +{apply,{{two_pids,done,0},"two_pids.erl:47"}}. +{func_ret,{dropping_lambda_capture,"two_pids.erl:47"}}. +{pop,{}}. +{push,{notaint}}. +{pop,{}}. +{get,{"Taint","two_pids.erl:51"}}. +{sink,{"two_pids.erl:51"}}. +{pop,{}}. +{push,{notaint}}. +{func_ret,{two_pids_main,"two_pids.erl:39"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-421 b/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-421 new file mode 100644 index 0000000..6bcb5ee --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-421 @@ -0,0 +1,40 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{two_pids,lambda_two_pids_43_16_anon,0},"two_pids.erl:43"}}. +{receive_trace,{421,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:43"}}. +{restore_capture,{{two_pids,lambda_two_pids_43_16_anon,0},"two_pids.erl:43"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{two_pids,sub_proc,1},"two_pids.erl:43"}}. +{get,{"Parent","two_pids.erl:43"}}. +{call_fun,{two_pids,sub_proc,1},"two_pids.erl:43"}. +{push_scope,{{two_pids,sub_proc,1},"two_pids.erl:26"}}. +{store,{"Parent","two_pids.erl:26"}}. +{receive_trace,{"#Ref<0.1321034574.60293121.64623>","two_pids.erl:28"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:32"}}. +{pop,{}}. +{store,{"T","two_pids.erl:32"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"T","two_pids.erl:33"}}. +{construct_pattern,{{cons},"two_pids.erl:33"}}. +{push,{notaint}}. +{call_fun,{io,format,2},"two_pids.erl:33"}. +{apply,{{io,format,2},"two_pids.erl:33"}}. +{pop,{}}. +{get,{"T","two_pids.erl:34"}}. +{duplicate,{}}. +{store,{"Data","two_pids.erl:27"}}. +{pop,{}}. +{get,{"Data","two_pids.erl:36"}}. +{sink,{"two_pids.erl:36"}}. +{pop,{}}. +{get,{"Parent","two_pids.erl:37"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293122.65291>","two_pids.erl:37"}}. +{func_ret,{sub_proc,"two_pids.erl:26"}}. +{apply,{{two_pids,sub_proc,1},"two_pids.erl:43"}}. +{func_ret,{dropping_lambda_capture,"two_pids.erl:43"}}. +{func_ret,{lambda_two_pids_43_16_anon,"two_pids.erl:43"}}. diff --git a/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-422 b/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-422 new file mode 100644 index 0000000..34a17d5 --- /dev/null +++ b/finer_taint/test/parallel_taint_SUITE_data/two_pids_analaysis_instr-422 @@ -0,0 +1,40 @@ +{push,{"@generated"}}. +{pop,{}}. +{push_scope,{{two_pids,lambda_two_pids_45_16_anon,0},"two_pids.erl:45"}}. +{receive_trace,{422,"instrumentation"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:45"}}. +{restore_capture,{{two_pids,lambda_two_pids_45_16_anon,0},"two_pids.erl:45"}}. +{pop,{}}. +{push,{notaint}}. +{restore_capture,{{two_pids,sub_proc,1},"two_pids.erl:45"}}. +{get,{"Parent","two_pids.erl:45"}}. +{call_fun,{two_pids,sub_proc,1},"two_pids.erl:45"}. +{push_scope,{{two_pids,sub_proc,1},"two_pids.erl:26"}}. +{store,{"Parent","two_pids.erl:26"}}. +{receive_trace,{"#Ref<0.1321034574.60293121.64625>","two_pids.erl:28"}}. +{deconstruct_pattern,{{tuple,2},"two_pids.erl:29"}}. +{pop,{}}. +{store,{"Var","two_pids.erl:29"}}. +{push,{notaint}}. +{pop,{}}. +{push,{notaint}}. +{get,{"Var","two_pids.erl:30"}}. +{construct_pattern,{{cons},"two_pids.erl:30"}}. +{push,{notaint}}. +{call_fun,{io,format,2},"two_pids.erl:30"}. +{apply,{{io,format,2},"two_pids.erl:30"}}. +{pop,{}}. +{get,{"Var","two_pids.erl:31"}}. +{duplicate,{}}. +{store,{"Data","two_pids.erl:27"}}. +{pop,{}}. +{get,{"Data","two_pids.erl:36"}}. +{sink,{"two_pids.erl:36"}}. +{pop,{}}. +{get,{"Parent","two_pids.erl:37"}}. +{push,{notaint}}. +{send,{"#Ref<0.1321034574.60293121.64637>","two_pids.erl:37"}}. +{func_ret,{sub_proc,"two_pids.erl:26"}}. +{apply,{{two_pids,sub_proc,1},"two_pids.erl:45"}}. +{func_ret,{dropping_lambda_capture,"two_pids.erl:45"}}. +{func_ret,{lambda_two_pids_45_16_anon,"two_pids.erl:45"}}. diff --git a/finer_taint/test/taint_gatherer_SUITE.erl b/finer_taint/test/taint_gatherer_SUITE.erl new file mode 100644 index 0000000..e89f0e1 --- /dev/null +++ b/finer_taint/test/taint_gatherer_SUITE.erl @@ -0,0 +1,80 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%%------------------------------------------------------------------- +%%% @doc +%%% Tests taint_gatherer +%%% @end +%%% ------------------------------------------------------------------- +-module(taint_gatherer_SUITE). + +-include_lib("stdlib/include/assert.hrl"). + +%% Test server callbacks +-export([ + all/0, + groups/0 +]). + +%% Test cases +-export([ + can_gather_leaks/1, + waits_for_leaks/1 +]). + +groups() -> + [ + {basic, [sequence], [ + can_gather_leaks, + waits_for_leaks + ]} + ]. + +all() -> + [{group, basic}]. + +%%-------------------------------------------------------------------- +%% TEST CASES + +can_gather_leaks(_Config) -> + {ok, Pid} = taint_gatherer:start_link(), + taint_gatherer:add_leaks(Pid, #{{leak, "loc", []} => ok}), + taint_gatherer:add_leaks(Pid, #{{leak, "loc1", []} => ok}), + % All pids have added leaks, should return immediately. + Leaks = taint_gatherer:get_gathered_leaks(Pid, 0, [self(), self()]), + gen_server:stop(Pid), + ?assertEqual(#{{leak, "loc", []} => ok, {leak, "loc1", []} => ok}, Leaks). + +waits_for_leaks(_Config) -> + Parent = self(), + GatherLeakTimeoutMs = 1000, + {ok, Pid} = taint_gatherer:start_link(), + spawn(fun() -> + Parent ! started, + Leaks = taint_gatherer:get_gathered_leaks(Pid, GatherLeakTimeoutMs, [Parent]), + Parent ! {got_leaks, Leaks} + end), + receive + started -> ok + end, + taint_gatherer:add_leaks(Pid, #{{leak, "loc", []} => ok}), + RecLeaks = + receive + {got_leaks, L} -> L + after 3 * GatherLeakTimeoutMs -> + #{rec_leak_timeout => ok} + end, + gen_server:stop(Pid), + ?assertEqual(#{{leak, "loc", []} => ok}, RecLeaks). diff --git a/mode/finer_taint b/mode/finer_taint new file mode 100644 index 0000000..1eace49 --- /dev/null +++ b/mode/finer_taint @@ -0,0 +1 @@ +--target-platforms=toolchains//:finer-taint-mode diff --git a/mode/online_finer_taint b/mode/online_finer_taint new file mode 100644 index 0000000..5bc0286 --- /dev/null +++ b/mode/online_finer_taint @@ -0,0 +1 @@ +--target-platforms=toolchains//:online-finer-taint-mode diff --git a/prelude b/prelude new file mode 160000 index 0000000..1f204b9 --- /dev/null +++ b/prelude @@ -0,0 +1 @@ +Subproject commit 1f204b9429de2e36b9579ae73bfeef06b5310a60 diff --git a/taint_server/README.md b/taint_server/README.md new file mode 100644 index 0000000..514b40a --- /dev/null +++ b/taint_server/README.md @@ -0,0 +1,14 @@ +# A server supporting the taint analysis + +This app is responsible for writing instructions for the abstract machine to a +file during the execution of the program under test. + + +### Important env variables + +* `instructions_stream_prefix` - Filepath prefix where the instruction traces + will be written too. For example `/tmp/default-`. The actual files written + will have thread ids appended to the end. Defaults to + `instructions_stream_prefix_default` (`/tmp/default_instr_prefix`). + + diff --git a/taint_server/include/taint_server.hrl b/taint_server/include/taint_server.hrl new file mode 100644 index 0000000..5b03d15 --- /dev/null +++ b/taint_server/include/taint_server.hrl @@ -0,0 +1,17 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format + +-define(NUM_WORKERS, 20). diff --git a/taint_server/src/abstract_machine_server.erl b/taint_server/src/abstract_machine_server.erl new file mode 100644 index 0000000..3b0fe14 --- /dev/null +++ b/taint_server/src/abstract_machine_server.erl @@ -0,0 +1,113 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +-module(abstract_machine_server). +-compile(warn_missing_spec_all). +-behaviour(gen_server). + +% ======== gen_server / internal ====== + +-export([init/1, handle_cast/2, handle_call/3, terminate/2]). + +-include_lib("taint_server/include/taint_server.hrl"). + +-record(abstract_machine_server_state, { + filename :: string(), + tid_state_map :: #{tid() => per_tid_state()} +}). + +%% tid() is similar to pid(), but it represent the id of the abstract machine executing +%% an instruction stream for a particular process. In other words, where real processes +%% are indentfied with pid(), "taint processes" are identfied with tid() +-type tid() :: integer(). + +-type per_tid_state() :: file:io_device(). +-type state() :: #abstract_machine_server_state{}. + +-export_type([tid/0]). + +% ======== PUBLIC API ============== +-export([write_instruction/2, start_link/2, write_instruction_sync/2]). + +-spec write_instruction(tid(), taint_abstract_machine:instruction()) -> ok. +write_instruction(Tid, Instruction) -> + gen_server:cast(tid_to_global_name(Tid), {write_instruction, Tid, Instruction}). + +% Mainly useful for testing, otherwise it is likely too expensive +-spec write_instruction_sync(tid(), taint_abstract_machine:instruction()) -> ok. +write_instruction_sync(Tid, Instruction) -> + gen_server:call(tid_to_global_name(Tid), {write_instruction, Tid, Instruction}). + +-spec start_link(WorkerIdx :: integer(), InstructionsFilePrefix :: string()) -> term(). +start_link(WorkerIdx, InstructionsFilePrefix) -> + gen_server:start_link(tid_to_global_name(WorkerIdx), ?MODULE, [InstructionsFilePrefix], []). + +% ========== gen_server / internal =============== +% +-spec init([InstructionsFilePrefix :: string()]) -> {ok, state()}. +init([InstructionsFilePrefix]) -> + % We trap the exit to ensure the messages in the queue get processed and not discarded + % when an exit is initiated + process_flag(trap_exit, true), + % This process is expected to receive a lot of messages, so this flag is improtant + % to keep good performance + process_flag(message_queue_data, off_heap), + {ok, #abstract_machine_server_state{filename = InstructionsFilePrefix, tid_state_map = #{}}}. + +-spec terminate(term(), state()) -> ok. +terminate(_Reason, #abstract_machine_server_state{tid_state_map = Map}) -> + maps:fold( + fun(_, Fd, ok) -> + file:close(Fd), + ok + end, + ok, + Map + ). + +-spec get_or_create_state_for_tid(tid(), state()) -> {state(), per_tid_state()}. +get_or_create_state_for_tid( + Tid, State = #abstract_machine_server_state{filename = Prefix, tid_state_map = TidMap} +) -> + case maps:get(Tid, TidMap, undefined) of + undefined -> + {ok, Fd} = file:open(io_lib:format("~s-~p", [Prefix, Tid]), [append, raw]), + {State#abstract_machine_server_state{tid_state_map = TidMap#{Tid => Fd}}, Fd}; + TidState -> + {State, TidState} + end. + +-spec handle_call( + {write_instruction, tid(), taint_abstract_machine:instruction()}, gen_server:from(), state() +) -> + {reply, ok, state()} | {stop, normal, ok, state()}. +handle_call({write_instruction, Tid, Instruction}, _From, State) -> + {reply, ok, write_instruction(Tid, Instruction, State)}. + +-spec handle_cast( + {write_instruction, Tid :: tid(), Instruction :: taint_abstract_machine:instruction()}, state() +) -> {noreply, state()}. +handle_cast({write_instruction, Tid, Instruction}, State) -> + {noreply, write_instruction(Tid, Instruction, State)}. + +-spec write_instruction(tid(), taint_abstract_machine:instruction(), state()) -> state(). +write_instruction(Tid, Instruction, State) -> + {State1, TidState} = get_or_create_state_for_tid(Tid, State), + ok = file:write(TidState, io_lib:format("~p.~n", [Instruction])), + State1. + +-spec tid_to_global_name(tid() | integer()) -> gen_server:server_name(). +tid_to_global_name(Tid) -> + {global, {?MODULE, Tid rem ?NUM_WORKERS}}. diff --git a/taint_server/src/taint_message_passer.erl b/taint_server/src/taint_message_passer.erl new file mode 100644 index 0000000..c1ba0ba --- /dev/null +++ b/taint_server/src/taint_message_passer.erl @@ -0,0 +1,70 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format +%% Implements some sort of message queue for passing taint messages. +%% The main idea is to block the get() operation untill the message becomes available +-module(taint_message_passer). +-compile(warn_missing_spec_all). + +-export([init/0, uninit/0, blocking_get/1, set/2, blocking_get/2]). + +-spec init() -> ok. +init() -> + %% taint_messages stores tuples: + %% % MessageId has this taint value + %% {MessageId :: string(), taint_value(), nopid} + %% % Tell pid() the message once it is set + %% | {MessageId :: string(), nomsg, pid()} + ets:new(taint_messages, [set, public, named_table]), + ok. + +-spec uninit() -> ok. +uninit() -> + ets:delete(taint_messages), + ok. + +-spec blocking_get(string()) -> taint_abstract_machine:taint_value(). +blocking_get(MessageId) -> + blocking_get(MessageId, 12000). + +-spec blocking_get(string(), number()) -> taint_abstract_machine:taint_value(). +blocking_get(MessageId, Timeout) -> + case ets:insert_new(taint_messages, {MessageId, nomsg, self()}) of + false -> + %Note: we could delete the ETS record at this point + [{MessageId, Val, nopid}] = ets:lookup(taint_messages, MessageId), + io:format("deliver msg ~p~n", [MessageId]), + Val; + true -> + receive + {got_key, MessageId, Message} -> + io:format("deliver msg ~p~n", [MessageId]), + Message + after Timeout -> + io:format("Skipping MessageId ~p~n", [MessageId]), + {notaint, []} + end + end. + +-spec set(string(), taint_abstract_machine:taint_value()) -> ok. +set(MessageId, Message) -> + case ets:insert_new(taint_messages, {MessageId, Message, nopid}) of + true -> + ok; + false -> + [{MessageId, nomsg, Pid}] = ets:lookup(taint_messages, MessageId), + Pid ! {got_key, MessageId, Message} + end, + ok. diff --git a/taint_server/src/taint_server.app.src b/taint_server/src/taint_server.app.src new file mode 100644 index 0000000..9dd3be3 --- /dev/null +++ b/taint_server/src/taint_server.app.src @@ -0,0 +1,29 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +{application, taint_server, [ + {description, + "App containing servers for the taint app that need an external dependency. They cannot be part of the taint app, because the taint app needs to compile as a parse transform, which can't have dependencies"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {taint_server_app, []}}, + %% NOTE: Remember to sync changes to `applications` to + %% the BUCK file in the application's base folder + {applications, [ + kernel, + stdlib + ]}, + {modules, []}, + {env, [{instructions_stream_prefix_default, "/tmp/default_instr_prefix"}]} +]}. diff --git a/taint_server/src/taint_server_app.erl b/taint_server/src/taint_server_app.erl new file mode 100644 index 0000000..efd2eef --- /dev/null +++ b/taint_server/src/taint_server_app.erl @@ -0,0 +1,33 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format + +-module(taint_server_app). +-compile(warn_missing_spec_all). + +-behaviour(application). + +-export([start/2, stop/1]). + +-spec start(application:start_type(), term()) -> {ok, pid()}. +start(_StartType, _StartArgs) -> + taint_message_passer:init(), + {ok, Supervisor} = taint_server_sup:start_link(), + {ok, Supervisor}. + +-spec stop(term()) -> ok. +stop(_State) -> + taint_message_passer:uninit(), + ok. diff --git a/taint_server/src/taint_server_sup.erl b/taint_server/src/taint_server_sup.erl new file mode 100644 index 0000000..b17e97e --- /dev/null +++ b/taint_server/src/taint_server_sup.erl @@ -0,0 +1,74 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%% % @format + +-module(taint_server_sup). + +-behaviour(supervisor). +-compile(warn_missing_spec_all). + +-export([start_link/0]). + +-export([init/1]). +-include_lib("taint_server/include/taint_server.hrl"). + +-define(SERVER, ?MODULE). + +-spec start_link() -> supervisor:startlink_ret(). +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +-spec init(term()) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. +init([]) -> + SupFlags = #{ + strategy => one_for_one, + intensity => 0, + period => 1 + }, + InstructionsStreamsPrefix = + case application:get_env(taint_server, instructions_stream_prefix) of + {ok, Prefix} -> + Prefix; + undefined -> + {ok, Prefix} = application:get_env(taint_server, instructions_stream_prefix_default), + Prefix + end, + ChildSpecs = + [ + #{ + id => Id, + restart => transient, + % The workers might take a long time to finish processing all messages + % and writting instructions to disk, so give a generous timeout to + % prevent forcefully killing them + shutdown => 50000, + start => + {abstract_machine_server, start_link, [ + Id, + InstructionsStreamsPrefix + ]} + } + || Id <- lists:seq(0, ?NUM_WORKERS - 1) + ], + {ok, {SupFlags, ChildSpecs}}. diff --git a/taint_server/test/message_passer_SUITE.erl b/taint_server/test/message_passer_SUITE.erl new file mode 100644 index 0000000..e5f031a --- /dev/null +++ b/taint_server/test/message_passer_SUITE.erl @@ -0,0 +1,149 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%%------------------------------------------------------------------- +%%% @doc +%%% Tests for taint message_passer +%%% @end +%%% ------------------------------------------------------------------- +-module(message_passer_SUITE). + +-include_lib("stdlib/include/assert.hrl"). + +%% Test server callbacks +-export([ + all/0, + suite/0, + init_per_testcase/2, + init_per_suite/1, + end_per_suite/1, + end_per_testcase/2, + groups/0 +]). + +%% Test cases +-export([ + first_set_then_get/1, + get_twice_and_crash/1, + message_after_timeout/1, + first_get_then_set/1 +]). + +suite() -> + % The tests in this suite shouldn't take a long time, + % but if there is a bug, they could block forever, so + % we set a generous but short timeout + [{timetrap, {seconds, 10}}]. + +groups() -> + [ + {basic, [sequence], [ + first_set_then_get, + first_get_then_set, + message_after_timeout, + get_twice_and_crash + ]} + ]. + +all() -> + [{group, basic}]. + +init_per_testcase(_TestCase, Config) -> + taint_message_passer:init(), + Config. + +end_per_testcase(_TestCase, Config) -> + taint_message_passer:uninit(), + Config. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% TEST CASES + +first_set_then_get(_Config) -> + MsgId = ref_to_list(make_ref()), + taint_message_passer:set(MsgId, msg1), + ?assertEqual(msg1, taint_message_passer:blocking_get(MsgId)). + +first_get_then_set(_Config) -> + MsgId = ref_to_list(make_ref()), + ReplyTo = self(), + spawn_link(fun() -> + ReplyTo ! started, + Msg = taint_message_passer:blocking_get(MsgId), + ReplyTo ! {got_msg, Msg} + end), + receive + started -> ok + end, + taint_message_passer:set(MsgId, msg2), + Message = + receive + {got_msg, Msg} -> Msg + after 5000 -> timeout + end, + ?assertEqual(msg2, Message). + +message_after_timeout(_Config) -> + MsgId = ref_to_list(make_ref()), + MsgId1 = ref_to_list(make_ref()), + ReplyTo = self(), + spawn_link(fun() -> + % This needs to timeout, so we set a short timeout + {notaint, []} = taint_message_passer:blocking_get(MsgId, 10), + ReplyTo ! started, + % This is expected to receive a message in this test, so + % infinite timeout + GotMsg = taint_message_passer:blocking_get(MsgId1, infinity), + ReplyTo ! {got_msg, GotMsg} + end), + receive + started -> ok + end, + + % This message timed-out so we shouldn't receive it + taint_message_passer:set(MsgId, msg), + % This is the message we want + taint_message_passer:set(MsgId1, msg2), + Message = + receive + {got_msg, RecMsg} -> RecMsg + after 4000 -> timeout + end, + ?assertEqual(msg2, Message). + +% Getting the same message twice shouldn't +% happen so we expect to crash if it does +get_twice_and_crash(_Config) -> + MsgId = ref_to_list(make_ref()), + ReplyTo = self(), + spawn_link(fun() -> + ReplyTo ! started, + taint_message_passer:blocking_get(MsgId), + ?assertException(error, {badmatch, _}, taint_message_passer:blocking_get(MsgId)), + ReplyTo ! ok + end), + receive + started -> ok + end, + taint_message_passer:set(MsgId, msg2), + receive + ok -> ok + end. diff --git a/taint_server/test/taint_server_SUITE.erl b/taint_server/test/taint_server_SUITE.erl new file mode 100644 index 0000000..9be31e3 --- /dev/null +++ b/taint_server/test/taint_server_SUITE.erl @@ -0,0 +1,80 @@ +% Copyright (c) Meta Platforms, Inc. and affiliates. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + +%%% % @format +%%%------------------------------------------------------------------- +%%% @doc +%%% Tests taint_writer +%%% @end +%%% ------------------------------------------------------------------- +-module(taint_server_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("stdlib/include/assert.hrl"). + +%% Test server callbacks +-export([ + suite/0, + all/0, + groups/0 +]). + +%% Test cases +-export([ + can_write_parallel_instructions_to_file/1 +]). + +suite() -> + [{appatic, #{enable_autoclean => true}}]. + +groups() -> + [ + {basic, [sequence], [ + can_write_parallel_instructions_to_file + ]} + ]. + +all() -> + [{group, basic}]. + +%%-------------------------------------------------------------------- +%% TEST CASES + +can_write_parallel_instructions_to_file(Config) -> + PrivDir = ?config(priv_dir, Config), + AbstrInstructionPrefix = filename:join(PrivDir, "abstr-instr-tpid"), + application:set_env(taint_server, instructions_stream_prefix, AbstrInstructionPrefix), + application:start(taint_server), + Parent = self(), + abstract_machine_server:write_instruction(123, {pop, {}}), + abstract_machine_server:write_instruction(42, {duplicate, {}}), + spawn(fun() -> + abstract_machine_server:write_instruction(43, {pop, {}}), + Parent ! done + end), + done(), + application:stop(taint_server), + {ok, Data123} = file:read_file(AbstrInstructionPrefix ++ "-123"), + ?assertEqual(<<"{pop,{}}.\n">>, Data123), + {ok, Data43} = file:read_file(AbstrInstructionPrefix ++ "-43"), + ?assertEqual(<<"{pop,{}}.\n">>, Data43), + {ok, Data42} = file:read_file(AbstrInstructionPrefix ++ "-42"), + ?assertEqual(<<"{duplicate,{}}.\n">>, Data42). + +done() -> + receive + done -> ok + after 500 -> + throw(timeout) + end. diff --git a/third-party/BUCK b/third-party/BUCK new file mode 100644 index 0000000..fb0f634 --- /dev/null +++ b/third-party/BUCK @@ -0,0 +1,42 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +erlang_application( + name="jsone", + srcs=glob(["jsone/src/*.erl"]), + app_src="jsone/src/jsone.app.src", + erl_opts=["+debug_info", "+inline"], + shell_libs=[], + use_global_parse_transforms=False, + build_edoc_chunks=False, + version="1.8.1", + visibility=["PUBLIC"], +) + +erlang_application( + name="power_shell", + srcs=glob(["power_shell/src/*.erl"]), + app_src="power_shell/src/power_shell.app.src", + erl_opts=["+debug_info", "+inline"], + shell_libs=[], + use_global_parse_transforms=False, + build_edoc_chunks=False, + version="1.3.0", + visibility=["PUBLIC"], + applications=[ + "kernel", + "stdlib", + "compiler", + ], +) diff --git a/third-party/jsone b/third-party/jsone new file mode 160000 index 0000000..fd911a2 --- /dev/null +++ b/third-party/jsone @@ -0,0 +1 @@ +Subproject commit fd911a20ae0bd7454962733668812ebee2379abe diff --git a/third-party/power_shell b/third-party/power_shell new file mode 160000 index 0000000..4791638 --- /dev/null +++ b/third-party/power_shell @@ -0,0 +1 @@ +Subproject commit 47916380d40445b9e38e2eba4860e55c86a98f0e diff --git a/toolchains/.buckroot b/toolchains/.buckroot new file mode 100644 index 0000000..3824d3f --- /dev/null +++ b/toolchains/.buckroot @@ -0,0 +1,14 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License". +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/toolchains/BUCK b/toolchains/BUCK new file mode 100644 index 0000000..f4506e2 --- /dev/null +++ b/toolchains/BUCK @@ -0,0 +1,98 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain") +load( + "@prelude//erlang:erlang_toolchain.bzl", + "config_erlang_toolchain_rule", + "get_primary_tools", + "multi_version_toolchain_rule", + _toolchain_resources="toolchain_resources", +) + +system_genrule_toolchain( + name="genrule", + visibility=["PUBLIC"], +) + +erlang_otp_binaries( + name="local", + erl="local/erl", + erlc="local/erlc", + escript="local/escript", + visibility=["PUBLIC"], +) + + +config_erlang_toolchain_rule( + name="erlang-local", + otp_binaries=":local", + visibility=["PUBLIC"], + erl_opts="+nowarn_underscore_match +debug_info +warnings_as_errors -DA_MACRO", + emu_flags="+sbwt very_short +sbwtdcpu very_short +sbwtdio very_short", + parse_transforms_filters={}, + parse_transforms=[], +) + +config_erlang_toolchain_rule( + name="erlang-taint", + otp_binaries=":local", + visibility=["PUBLIC"], + erl_opts="+nowarn_underscore_match +debug_info +warnings_as_errors -DA_MACRO +{finer_taint_module,parallel_finer_taint} +{finer_taint_do_checks,false} +{finer_taint_sinks,true}", + emu_flags="+sbwt very_short +sbwtdcpu very_short +sbwtdio very_short", + parse_transforms_filters={}, + parse_transforms=["root//:finer_taint_compiler"], +) + +config_erlang_toolchain_rule( + name="erlang-online-taint", + otp_binaries=":local", + visibility=["PUBLIC"], + erl_opts="+nowarn_underscore_match +debug_info +warnings_as_errors -DA_MACRO +{finer_taint_module,online_finer_taint} +{finer_taint_do_checks,false} +{finer_taint_sinks,true}", + emu_flags="+sbwt very_short +sbwtdcpu very_short +sbwtdio very_short", + parse_transforms_filters={}, + parse_transforms=["root//:finer_taint_compiler"], +) + +constraint_setting(name="taint_mode") + +constraint_value(name="off", constraint_setting=":taint_mode") +constraint_value(name="finer_taint", constraint_setting=":taint_mode") +constraint_value(name="online_finer_taint", constraint_setting=":taint_mode") + +config_setting(name="online-finer-taint-set", constraint_values=[":online_finer_taint"]) +config_setting(name="finer-taint-set", constraint_values=[":finer_taint"]) +config_setting(name="notaint-set", constraint_values=[":off"]) + +platform(name="default", constraint_values=[":no"]) +platform(name="finer-taint-mode", constraint_values=[":finer_taint"]) +platform(name="online-finer-taint-mode", constraint_values=[":online_finer_taint"]) + +multi_version_toolchain_rule( + name="erlang-default", + targets=select( + { + ":notaint-set": [":erlang-local", ":erlang-taint"], + ":finer-taint-set": [":erlang-taint"], + ":online-finer-taint-set": [":erlang-online-taint"], + "DEFAULT": [":erlang-local"], + } + ), + visibility=["PUBLIC"], +) +_toolchain_resources( + name="erlang-resources", + jsone="root//third-party:jsone", + visibility=["PUBLIC"], +) diff --git a/toolchains/local/erl b/toolchains/local/erl new file mode 100755 index 0000000..e3d4cba --- /dev/null +++ b/toolchains/local/erl @@ -0,0 +1,19 @@ +#! /bin/bash + +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License". +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eu + +exec erl "${@:1}" diff --git a/toolchains/local/erlc b/toolchains/local/erlc new file mode 100755 index 0000000..a46fb72 --- /dev/null +++ b/toolchains/local/erlc @@ -0,0 +1,19 @@ +#! /bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License". +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +set -eu + +exec erlc "${@:1}" diff --git a/toolchains/local/escript b/toolchains/local/escript new file mode 100755 index 0000000..1cc1d1c --- /dev/null +++ b/toolchains/local/escript @@ -0,0 +1,19 @@ +#! /bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License". +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +set -eu + +exec escript "${@:1}"