├── CODEOWNERS ├── .gitmodules ├── .gitignore ├── example ├── Makefile ├── README.md └── test.c ├── src ├── taint_semantics.h ├── visit_counter.h ├── MyPinTool.h ├── makefile ├── tat_constants.h ├── visit_counter.cpp ├── taint.h ├── tat_instr.h ├── makefile.rules ├── tracer.h ├── taint.cpp ├── taint_semantics.cpp ├── post_process.py ├── tracer.cpp └── CECTraceTool.cpp ├── SECURITY.md ├── .github ├── dependabot.yml └── workflows │ └── scorecard.yml ├── include └── pin_based_cec.h ├── CONTRIBUTING.md ├── LICENSE ├── CODE_OF_CONDUCT.md └── README.md /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # The maintainers team owns the whole set of files 2 | * @intel/pin-based-cec-maintain 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/CRCpp"] 2 | path = vendor/CRCpp 3 | url = https://github.com/d-bahr/CRCpp 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | pin.log 3 | src/obj-intel64 4 | example/pin.log 5 | example/image_map 6 | example/iptrace 7 | example/keyexp-test 8 | example/memtrace 9 | example/results.txt 10 | example/summary.log 11 | example/taint -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | all: keyexp-test 2 | 3 | keyexp-test: test.c 4 | gcc -g -Wall -o keyexp-test test.c 5 | 6 | clean: 7 | rm -f keyexp-test 8 | 9 | run: 10 | $(PIN_ROOT)/pin -t ../src/obj-intel64/CECTraceTool.so -f aes128_key_expansion -s summary.log -- ./keyexp-test 11 | -------------------------------------------------------------------------------- /src/taint_semantics.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #ifndef __TAINT_SEMANTICS_H__ 7 | #define __TAINT_SEMANTICS_H__ 8 | 9 | #include "pin.H" 10 | 11 | void TaintInstruction(INS ins, void* v); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | ## Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). 6 | -------------------------------------------------------------------------------- /src/visit_counter.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #ifndef __VISIT_COUNTER_H__ 7 | #define __VISIT_COUNTER_H__ 8 | 9 | #include 10 | #include 11 | 12 | class IpVisitCounter { 13 | public: 14 | IpVisitCounter(); 15 | uint32_t getCount(uint64_t ip); 16 | void incrementCount(uint64_t ip); 17 | void enable(void); 18 | void disable(void); 19 | void reset(void); 20 | 21 | private: 22 | bool enabled; 23 | std::map ipVisitCountMap; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /include/pin_based_cec.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #ifndef __PIN_BASED_CEC_H__ 7 | #define __PIN_BASED_CEC_H__ 8 | 9 | #include 10 | 11 | __attribute__((noinline)) void PinBasedCEC_MarkSecret(uint64_t secret, 12 | uint64_t size) {} 13 | 14 | __attribute__((noinline)) void PinBasedCEC_ClearSecret(uint64_t secret, 15 | uint64_t size) {} 16 | 17 | __attribute__((noinline)) void PinBasedCEC_ClearSecrets(void) {} 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/MyPinTool.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #ifndef __MY_PIN_TOOL_H__ 7 | #define __MY_PIN_TOOL_H__ 8 | 9 | #include "pin.H" 10 | 11 | #include "tat_instr.h" 12 | 13 | ADDRINT PIN_FAST_ANALYSIS_CALL InstructionCanBeTainted(); 14 | ADDRINT PIN_FAST_ANALYSIS_CALL MemoryIsTainted(); 15 | void PIN_FAST_ANALYSIS_CALL PremarshallMemoryOperand(tat_instr_t* instr, 16 | uint32_t memop_idx, 17 | ADDRINT memea); 18 | void PIN_FAST_ANALYSIS_CALL AnalyzeInstructionForTaint(tat_instr_t* instr); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/makefile: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # DO NOT EDIT THIS FILE! 4 | # 5 | ############################################################## 6 | 7 | # If the tool is built out of the kit, PIN_ROOT must be specified in the make invocation and point to the kit root. 8 | ifdef PIN_ROOT 9 | CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config 10 | else 11 | CONFIG_ROOT := ../Config 12 | endif 13 | include $(CONFIG_ROOT)/makefile.config 14 | include makefile.rules 15 | include $(TOOLS_ROOT)/Config/makefile.default.rules 16 | 17 | ############################################################## 18 | # 19 | # DO NOT EDIT THIS FILE! 20 | # 21 | ############################################################## 22 | -------------------------------------------------------------------------------- /src/tat_constants.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #if !defined(_TAT_CONSTANTS_H_) 7 | # define _TAT_CONSTANTS_H_ 8 | 9 | #define TAT_MIN_SIGNED_SHORT (-32768) 10 | #define TAT_MAX_SIGNED_SHORT (32767) 11 | 12 | #define TAT_MIN_SIGNED_BYTE (-128) 13 | #define TAT_MAX_SIGNED_BYTE (127) 14 | 15 | #define TAT_MIN_UNSIGNED_SHORT (0) 16 | #define TAT_MAX_UNSIGNED_SHORT (65535) 17 | 18 | #define TAT_MIN_UNSIGNED_BYTE (0) 19 | #define TAT_MAX_UNSIGNED_BYTE (255) 20 | 21 | 22 | #define TAT_BYTES_PER_WORD 2 23 | #define TAT_BYTES_PER_DWORD 4 24 | #define TAT_BYTES_PER_QWORD 8 25 | #define TAT_BYTES_PER_XWORD 16 26 | #define TAT_BYTES_PER_YWORD 32 27 | #define TAT_BYTES_PER_ZWORD 64 28 | 29 | #define TAT_MAX_MEMOPS_PER_INST 8 30 | #define TAT_MEMORY_GRANULARITY 8 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Pin-Based Constant Execution Checker - Software Contribution Guide 2 | 3 | Pin-based Constant Execution Checker (Pin-based CEC) welcomes all contributors to improve the project. 4 | 5 | Before contributing, please review and abide by the [Code of Conduct](CODE_OF_CONDUCT.md). 6 | 7 | ## Contributing features or fixes 8 | 9 | All submissions should be made in the form of a pull request related to an open issue. Please limit the content of a pull request to the subject of a single issue. 10 | 11 | All commits must have a valid `Signed-off-by` header to indicate you have the rights to contribute the code. 12 | 13 | ## Reporting Bugs 14 | 15 | Bugs or issues related to Pin-based CEC may be submitted here: [pin-based-cec issues](https://github.com/intel/pin-based-cec/issues) 16 | 17 | ## Reporting Security Concerns 18 | 19 | Visit [security reporting policies](SECURITY.md) for security issue reporting instructions. 20 | -------------------------------------------------------------------------------- /src/visit_counter.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #include "visit_counter.h" 7 | #include 8 | #include 9 | #include 10 | 11 | IpVisitCounter::IpVisitCounter() { enabled = false; } 12 | 13 | uint32_t IpVisitCounter::getCount(uint64_t ip) { 14 | if (enabled) { 15 | std::map::iterator it = ipVisitCountMap.find(ip); 16 | 17 | if (it == ipVisitCountMap.end()) { 18 | return 0; 19 | } else { 20 | return it->second; 21 | } 22 | } else { 23 | return 0; 24 | } 25 | } 26 | 27 | void IpVisitCounter::incrementCount(uint64_t ip) { 28 | if (enabled) { 29 | std::map::iterator it = ipVisitCountMap.find(ip); 30 | 31 | if (it == ipVisitCountMap.end()) { 32 | ipVisitCountMap.insert(std::pair(ip, 1)); 33 | } else { 34 | uint32_t x = it->second + 1; 35 | it->second = x; 36 | } 37 | } 38 | } 39 | 40 | void IpVisitCounter::enable(void) { enabled = true; } 41 | 42 | void IpVisitCounter::disable(void) { enabled = false; } 43 | 44 | void IpVisitCounter::reset(void) { ipVisitCountMap.clear(); } 45 | -------------------------------------------------------------------------------- /src/taint.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #ifndef __TAINT_H__ 7 | #define __TAINT_H__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "pin.H" 14 | #include "tracer.h" 15 | #include "visit_counter.h" 16 | 17 | #include "tat_instr.h" 18 | 19 | class Taint { 20 | public: 21 | Taint(Tracer* _tracer, IpVisitCounter* _ipVisitCounter, bool _ignoreRIP); 22 | ~Taint(); 23 | void printSummary(void); 24 | void markTaint(uint64_t, bool); 25 | void clearTaint(uint64_t, bool); 26 | void reset(void); 27 | void printTaintedSet(std::ostream& stream); 28 | bool hasTaintedState(); 29 | bool hasTaintedMemory(); 30 | bool hasTaintedFlags(); 31 | void analyzeAndPropagate(tat_instr_t*); 32 | 33 | private: 34 | bool isTainted(uint64_t, bool); 35 | void markRegTainted(REG reg); 36 | void clearRegTainted(REG reg); 37 | void markMemTainted(uint64_t); 38 | void clearMemTainted(uint64_t); 39 | bool regIsTainted(REG reg); 40 | bool memIsTainted(uint64_t); 41 | std::set taintedRegs; 42 | std::set taintedMem; 43 | Tracer* tracer; 44 | IpVisitCounter* ipVisitCounter; 45 | bool ignoreRIP; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Intel Corporation 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/tat_instr.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #if !defined(_TAT_INSTR_H_) 7 | # define _TAT_INSTR_H_ 8 | 9 | #include "tat_constants.h" 10 | extern "C" { 11 | #include "xed-interface.h" 12 | } 13 | #include 14 | #include 15 | typedef enum { 16 | TAT_VL_XMM=0, 17 | TAT_VL_YMM=1, 18 | TAT_VL_ZMM=2, 19 | TAT_VL_LAST 20 | } tat_vector_length_t; 21 | 22 | typedef enum { 23 | TAT_FLAG_NONE=0, 24 | TAT_FLAG_REG=1, 25 | TAT_FLAG_MEM=2, 26 | TAT_FLAG_IMM=4, 27 | TAT_FLAG_SPARSE=8, 28 | TAT_FLAG_ZERO_REG=16, 29 | TAT_FLAG_AGEN=32 30 | } tat_ins_flags_t; 31 | 32 | typedef enum { 33 | TAT_OTYPE_OTHER=0, 34 | TAT_OTYPE_REG=1, 35 | TAT_OTYPE_MEM=2, 36 | TAT_OTYPE_IMM=3, 37 | TAT_OTYPE_RELBR=4, 38 | TAT_OTYPE_AGEN=5 39 | } tat_operand_type_t; 40 | 41 | typedef enum { 42 | TAT_OPERAND_NONE = 0, 43 | TAT_OPERAND_READ = 1, 44 | TAT_OPERAND_WRITTEN = 2, 45 | TAT_OPERAND_READ_AND_WRITTEN = 3 46 | } tat_operand_access_t; 47 | 48 | #define TAT_INSTR_MAX_REGS_PER_INST 10 49 | #define TAT_INSTR_MAX_OPERANDS_PER_INST 8 50 | #define TAT_INSTR_MAX_REG_PER_MEMOP 2 51 | 52 | typedef struct { 53 | 54 | // The ip of the instruction 55 | uint64_t pc; 56 | 57 | // Operand counts 58 | uint32_t operand_count; // Number of operands 59 | uint32_t reg_operand_count; // Number of register operands 60 | uint32_t mem_operand_count; // Number of memory operands 61 | 62 | // Flags to define instruction 63 | uint32_t instruction_flags; 64 | 65 | // Operand characteristics arrays 66 | tat_operand_type_t operand_type[TAT_INSTR_MAX_OPERANDS_PER_INST]; 67 | tat_operand_access_t operand_access[TAT_INSTR_MAX_OPERANDS_PER_INST]; 68 | 69 | // Registers operand data 70 | REG operand_register[TAT_INSTR_MAX_OPERANDS_PER_INST]; 71 | xed_reg_enum_t operand_xed_register[TAT_INSTR_MAX_OPERANDS_PER_INST]; 72 | 73 | // Memory operands data 74 | uint32_t memops_bytes[TAT_MAX_MEMOPS_PER_INST]; 75 | bool memops_is_write[TAT_MAX_MEMOPS_PER_INST]; 76 | REG base_register[TAT_MAX_MEMOPS_PER_INST]; 77 | REG index_register[TAT_MAX_MEMOPS_PER_INST]; 78 | uint64_t memops_memea[TAT_MAX_MEMOPS_PER_INST]; 79 | 80 | // Instruction immediate value 81 | uint16_t imm0; 82 | 83 | // gather or scatter instructions 84 | bool gather_or_scatter; 85 | 86 | // target address for mask based conditional branch 87 | uint64_t target; 88 | // fall through address for mask based conditional branch 89 | uint64_t fallthru; 90 | 91 | } tat_instr_t; 92 | 93 | void tat_instr_init(tat_instr_t* s); 94 | void tat_instr_print(tat_instr_t* s, FILE* f); 95 | tat_instr_t* tat_instr_alloc(); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: '17 17 * * 2' 14 | push: 15 | branches: [ "main" ] 16 | 17 | # Declare default permissions as read only. 18 | permissions: read-all 19 | 20 | jobs: 21 | analysis: 22 | name: Scorecard analysis 23 | runs-on: ubuntu-latest 24 | permissions: 25 | # Needed to upload the results to code-scanning dashboard. 26 | security-events: write 27 | # Needed to publish results and get a badge (see publish_results below). 28 | id-token: write 29 | # Uncomment the permissions below if installing in a private repository. 30 | # contents: read 31 | # actions: read 32 | 33 | steps: 34 | - name: "Checkout code" 35 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 36 | with: 37 | persist-credentials: false 38 | 39 | - name: "Run analysis" 40 | uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 41 | with: 42 | results_file: results.sarif 43 | results_format: sarif 44 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 45 | # - you want to enable the Branch-Protection check on a *public* repository, or 46 | # - you are installing Scorecard on a *private* repository 47 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. 48 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 49 | 50 | # Public repositories: 51 | # - Publish results to OpenSSF REST API for easy access by consumers 52 | # - Allows the repository to include the Scorecard badge. 53 | # - See https://github.com/ossf/scorecard-action#publishing-results. 54 | # For private repositories: 55 | # - `publish_results` will always be set to `false`, regardless 56 | # of the value entered here. 57 | publish_results: true 58 | 59 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 60 | # format to the repository Actions tab. 61 | # - name: "Upload artifact" 62 | # uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20 63 | # with: 64 | # name: SARIF file 65 | # path: results.sarif 66 | # retention-days: 5 67 | 68 | # Upload the results to GitHub's code scanning dashboard (optional). 69 | # Commenting out will disable upload of results to your repo's Code Scanning dashboard 70 | - name: "Upload to code-scanning" 71 | uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 72 | with: 73 | sarif_file: results.sarif 74 | -------------------------------------------------------------------------------- /src/makefile.rules: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # This file includes all the test targets as well as all the 4 | # non-default build rules and test recipes. 5 | # 6 | ############################################################## 7 | 8 | 9 | ############################################################## 10 | # 11 | # Test targets 12 | # 13 | ############################################################## 14 | 15 | ###### Place all generic definitions here ###### 16 | 17 | # This defines tests which run tools of the same name. This is simply for convenience to avoid 18 | # defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS). 19 | # Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS. 20 | TEST_TOOL_ROOTS := CECTraceTool 21 | 22 | # This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS. 23 | TEST_ROOTS := 24 | 25 | # This defines the tools which will be run during the the tests, and were not already defined in 26 | # TEST_TOOL_ROOTS. 27 | TOOL_ROOTS := 28 | 29 | # This defines the static analysis tools which will be run during the the tests. They should not 30 | # be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in 31 | # TEST_ROOTS. 32 | # Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library. 33 | # This library provides a subset of the Pin APIs which allows the tool to perform static analysis 34 | # of an application or dll. Pin itself is not used when this tool runs. 35 | SA_TOOL_ROOTS := 36 | 37 | # This defines all the applications that will be run during the tests. 38 | APP_ROOTS := 39 | 40 | # This defines any additional object files that need to be compiled. 41 | OBJECT_ROOTS := tracer taint taint_semantics visit_counter 42 | 43 | # This defines any additional dlls (shared objects), other than the pintools, that need to be compiled. 44 | DLL_ROOTS := 45 | 46 | # This defines any static libraries (archives), that need to be built. 47 | LIB_ROOTS := 48 | 49 | ###### Define the sanity subset ###### 50 | 51 | # This defines the list of tests that should run in sanity. It should include all the tests listed in 52 | # TEST_TOOL_ROOTS and TEST_ROOTS excluding only unstable tests. 53 | SANITY_SUBSET := $(TEST_TOOL_ROOTS) $(TEST_ROOTS) 54 | 55 | 56 | ############################################################## 57 | # 58 | # Test recipes 59 | # 60 | ############################################################## 61 | 62 | # This section contains recipes for tests other than the default. 63 | # See makefile.default.rules for the default test rules. 64 | # All tests in this section should adhere to the naming convention: .test 65 | 66 | 67 | ############################################################## 68 | # 69 | # Build rules 70 | # 71 | ############################################################## 72 | 73 | # This section contains the build rules for all binaries that have special build rules. 74 | # See makefile.default.rules for the default build rules. 75 | 76 | $(OBJDIR)CECTraceTool$(PINTOOL_SUFFIX): $(OBJDIR)CECTraceTool$(OBJ_SUFFIX) $(OBJDIR)tracer$(OBJ_SUFFIX) $(OBJDIR)taint$(OBJ_SUFFIX) $(OBJDIR)taint_semantics$(OBJ_SUFFIX) $(OBJDIR)visit_counter$(OBJ_SUFFIX) 77 | $(LINKER) $(TOOL_LDFLAGS_NOOPT) $(LINK_EXE)$@ $< $(OBJDIR)tracer$(OBJ_SUFFIX) $(OBJDIR)taint$(OBJ_SUFFIX) $(OBJDIR)taint_semantics$(OBJ_SUFFIX) $(OBJDIR)visit_counter$(OBJ_SUFFIX) $(TOOL_LPATHS) $(TOOL_LIBS) 78 | -------------------------------------------------------------------------------- /src/tracer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #ifndef __TRACER_H__ 7 | #define __TRACER_H__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "visit_counter.h" 16 | 17 | #define MEM_TAG_POS_RSP_OFFSET 0 18 | #define MEM_TAG_NEG_RSP_OFFSET 1 19 | #define MEM_TAG_ALLOC 2 20 | #define MEM_TAG_SECTION 3 21 | #define MEM_TAG_ABSOLUTE 4 22 | 23 | #define ALIGN64(x) (((x + 63) >> 6) << 6) 24 | 25 | #define VERDICT_PASS 1 26 | #define VERDICT_FAIL 2 27 | #define VERDICT_INCONCLUSIVE 3 28 | 29 | struct TaggedMemoryAddress { 30 | uint32_t type; 31 | std::string label; 32 | std::string localLabel; 33 | uint64_t value; 34 | uint64_t start; 35 | }; 36 | 37 | struct AllocationRecord { 38 | uint64_t addr; 39 | uint64_t size; 40 | std::string label; 41 | }; 42 | 43 | class Tracer { 44 | public: 45 | Tracer(std::string, std::string, std::string, uint32_t, bool, uint32_t, 46 | uint32_t, IpVisitCounter*); 47 | ~Tracer(); 48 | void updateInstructionPointer(uint64_t); 49 | void updateMemoryRead(uint64_t, uint64_t); 50 | void updateMemoryWrite(uint64_t, uint64_t); 51 | void enterTargetRoutine(uint32_t, uint64_t, uint64_t); 52 | void exitTargetRoutine(uint32_t); 53 | void addGlobalAllocation(uint64_t, uint64_t); 54 | void removeGlobalAllocation(uint64_t); 55 | void addSection(uint64_t, uint64_t, const std::string&); 56 | void markLocalAllocationAsAligned(std::string&); 57 | uint32_t summarize(const std::string&); 58 | void setTaintFlag(void); 59 | void clearTaintFlag(void); 60 | bool isEnabled(void); 61 | std::ofstream& getTaintFile(void); 62 | 63 | private: 64 | bool enabled; 65 | bool taintFlag; 66 | bool onlyConsumeTaintedInstructions; 67 | uint32_t instructionPointerDigest; 68 | uint32_t memoryAccessDigest; 69 | std::ofstream traceMemFile; 70 | std::ofstream traceIPFile; 71 | std::ofstream taintFile; 72 | std::string traceMemFilePath; 73 | std::string traceIPFilePath; 74 | std::string taintFilePath; 75 | std::string traceMemFileName; 76 | std::string traceIPFileName; 77 | std::string taintFileName; 78 | uint32_t currentTargetRoutineMask; 79 | uint32_t refTargetRoutineMask; 80 | uint32_t nextLocalLabelId; 81 | uint32_t nextGlobalLabelId; 82 | uint64_t savedRSP; 83 | uint32_t exitCount; 84 | bool markLut; 85 | uint64_t lutAddress; 86 | uint32_t lutSize; 87 | uint32_t lutTargetRoutineIndex; 88 | std::map allocationMap; 89 | std::map sectionMap; 90 | std::map globalToLocalAllocationLabelMap; 91 | std::vector instructionPointerDigestHistory; 92 | std::vector memoryAccessDigestHistory; 93 | std::set alignedLocalAllocations; 94 | IpVisitCounter* ipVisitCounter; 95 | void updateInstructionPointerDigest(uint64_t); 96 | void updateMemoryAccessDigest(uint64_t, TaggedMemoryAddress*, bool, bool); 97 | void convertMemoryAddressToTag(uint64_t, TaggedMemoryAddress*); 98 | std::string localizeAllocationLabel(std::string&); 99 | AllocationRecord* findAllocation(uint64_t); 100 | AllocationRecord* findSection(uint64_t); 101 | bool isLutAddr(uint64_t); 102 | bool isLocalAllocationAligned(std::string&); 103 | void openTraceMemFile(void); 104 | void openTraceIPFile(void); 105 | void openTaintFile(void); 106 | void finalizeTraceMemFile(void); 107 | void finalizeTraceIPFile(void); 108 | void finalizeTaintFile(void); 109 | }; 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Pin-based Constant Execution Checker (Pin-based CEC) Usage Example 2 | ================================================================== 3 | 4 | This example demonstrates how to use Pin-based CEC to check for secret-dependent execution in a naive implementation of AES key expansion that uses a lookup-table-based Sbox. The key expansion is implemented as three functions in [`test.c`](test.c): `aes128_key_expansion()`, which fills out the Rijndael key schedule, `rot_word()`, which left-rotates a dword by 8 bits, and `sub_word()`, which replaces the bytes of a dword with the results of an Sbox transformation. Because `sub_word()` uses the secret-dependent input as an index into a lookup table in memory to compute the Sbox transformation, the input into the Sbox operation may be recovered via a cache-based side channel attack. This is a side channel vulnerability that can leak an AES key. 5 | 6 | Dependencies 7 | ------------ 8 | 9 | - Linux 10 | - python 3.5 or greater 11 | - gcc/g++/make 12 | - diff 13 | 14 | Building and Running 15 | -------------------- 16 | 17 | 1. Build the Pin-based CEC by following instructions in the [main README](../README.md). 18 | 19 | 2. Move to the `example` directory and build the example program: 20 | 21 | ```bash 22 | cd example 23 | make 24 | ``` 25 | 26 | The example program does AES key expansion 10 times with random keys. Typically, the subroutine of interest should be exercised with a sufficient number of unique inputs to ensure a high confidence in the results. The Pin-based CEC test could be performed in conjunction with a code-coverage tool to ensure that all relevant sections of the code have been exercised sufficiently. 27 | 28 | 3. Run the example program and observe that it works as expected: 29 | 30 | ```bash 31 | ./keyexp-test 32 | ``` 33 | 34 | 4. Run the example program through Pin-based CEC: 35 | 36 | ```bash 37 | make run PIN_ROOT= 38 | ``` 39 | 40 | This command executes the `keyexp-test` program and instructs Pin-based CEC to instrument and track the `aes128_key_expansion()` function. 41 | 42 | 5. Post-process the logs to identify any secret-dependent differences: 43 | 44 | ```bash 45 | python ../post_process.py results.txt 46 | ``` 47 | 48 | This script applies the taint information to the execution traces and diffs them to check for secret-dependent non-constant execution or memory accesses. 49 | 50 | 6. The script should output some text on stdout and in `results.txt` that looks like the following (but with more lines): 51 | 52 | ```text 53 | Addresses with tainted memory access differences: 54 | 55B0A4453214 --> sub_word (keyexp-test @ 1214) 55 | 55B0A4453228 --> sub_word (keyexp-test @ 1228) 56 | 55B0A445323C --> sub_word (keyexp-test @ 123C) 57 | 55B0A4453250 --> sub_word (keyexp-test @ 1250) 58 | ``` 59 | 60 | These are the addresses that had execution differences and were also identified as operating on secret data by the taint analysis. On the left of the arrow is the address, and on the right is the symbol name in the binary that contains that address (or "???" if no corresponding symbol could be found), the image name, and the offset within that image where the instruction is. In this case, 4 instructions in the `sub_word()` function are flagged. These are the 4 instructions that perform lookups into the Sbox table in memory. We can quickly verify that is the case by looking at the annotated disassembly of `sub_word()`: 61 | 62 | ```text 63 | uint8_t s0 = AES_SBOX[b0]; 64 | 1207: 0f b6 45 f8 movzx eax,BYTE PTR [rbp-0x8] 65 | 120b: 48 98 cdqe 66 | 120d: 48 8d 15 ac 2e 00 00 lea rdx,[rip+0x2eac] # 40c0 67 | 1214: 0f b6 04 10 movzx eax,BYTE PTR [rax+rdx*1] # Secret dependent memory access here! 68 | 1218: 88 45 fc mov BYTE PTR [rbp-0x4],al 69 | uint8_t s1 = AES_SBOX[b1]; 70 | 121b: 0f b6 45 f9 movzx eax,BYTE PTR [rbp-0x7] 71 | 121f: 48 98 cdqe 72 | 1221: 48 8d 15 98 2e 00 00 lea rdx,[rip+0x2e98] # 40c0 73 | 1228: 0f b6 04 10 movzx eax,BYTE PTR [rax+rdx*1] # Secret dependent memory access here! 74 | 122c: 88 45 fd mov BYTE PTR [rbp-0x3],al 75 | uint8_t s2 = AES_SBOX[b2]; 76 | 122f: 0f b6 45 fa movzx eax,BYTE PTR [rbp-0x6] 77 | 1233: 48 98 cdqe 78 | 1235: 48 8d 15 84 2e 00 00 lea rdx,[rip+0x2e84] # 40c0 79 | 123c: 0f b6 04 10 movzx eax,BYTE PTR [rax+rdx*1] # Secret dependent memory access here! 80 | 1240: 88 45 fe mov BYTE PTR [rbp-0x2],al 81 | uint8_t s3 = AES_SBOX[b3]; 82 | 1243: 0f b6 45 fb movzx eax,BYTE PTR [rbp-0x5] 83 | 1247: 48 98 cdqe 84 | 1249: 48 8d 15 70 2e 00 00 lea rdx,[rip+0x2e70] # 40c0 85 | 1250: 0f b6 04 10 movzx eax,BYTE PTR [rax+rdx*1] # Secret dependent memory access here! 86 | 1254: 88 45 ff mov BYTE PTR [rbp-0x1],al 87 | ``` 88 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | * Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or advances of any kind 22 | * Trolling, insulting or derogatory comments, and personal or political attacks 23 | * Public or private harassment 24 | * Publishing others’ private information, such as a physical or email address, without their explicit permission 25 | * Other conduct which could reasonably be considered inappropriate in a professional setting 26 | 27 | ## Enforcement Responsibilities 28 | 29 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | Community leaders 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, and will communicate reasons for moderation decisions when appropriate. 32 | 33 | ## Scope 34 | 35 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 36 | 37 | ## Enforcement 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at CommunityCodeofConduct@intel.com All complaints will be reviewed and investigated promptly and fairly. 40 | 41 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 42 | 43 | ## Enforcement Guidelines 44 | 45 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action reported or observed in the course of community activities they deem in violation of this Code of Conduct: 46 | 47 | ### 1. Correction 48 | 49 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 50 | 51 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 52 | 53 | ### 2. Warning 54 | 55 | **Community Impact**: A violation through a single incident or series of actions. 56 | 57 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 58 | 59 | ### 3. Temporary Ban 60 | 61 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 62 | 63 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 64 | 65 | ### 4. Permanent Ban 66 | 67 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 68 | 69 | **Consequence**: A permanent ban from any sort of public interaction within the community. 70 | 71 | ## Attribution 72 | 73 | This Code of Conduct is adapted from the [Contributor Covenant Code of Conduct, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct). Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/inclusion/blob/master/code-of-conduct-enforcement/consequence-ladder.md). For answers to common questions about this code of conduct, see the [FAQ](https://www.contributor-covenant.org/faq). 74 | -------------------------------------------------------------------------------- /example/test.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "../include/pin_based_cec.h" 10 | 11 | #define AES128_KEY_SIZE_IN_DWORDS 4 12 | #define AES128_KEY_SIZE_IN_BYTES (AES128_KEY_SIZE_IN_DWORDS * sizeof(uint32_t)) 13 | #define AES128_NUM_ROUNDS 11 14 | 15 | static uint32_t AES128_RCON[10] = { 16 | 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 17 | 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 18 | }; 19 | 20 | static uint8_t AES_SBOX[256] = { 21 | 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 22 | 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 23 | 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 24 | 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 25 | 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 26 | 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 27 | 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 28 | 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 29 | 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 30 | 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 31 | 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 32 | 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 33 | 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 34 | 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 35 | 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 36 | 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 37 | }; 38 | 39 | uint32_t rot_word(uint32_t word) { 40 | uint8_t b0 = (word & 0xFF000000) >> 24; 41 | return (word << 8) | b0; 42 | } 43 | 44 | uint32_t sub_word(uint32_t word) { 45 | uint8_t b0 = (word >> 24) & 0xFF; 46 | uint8_t b1 = (word >> 16) & 0xFF; 47 | uint8_t b2 = (word >> 8) & 0xFF; 48 | uint8_t b3 = (word ) & 0xFF; 49 | 50 | uint8_t s0 = AES_SBOX[b0]; 51 | uint8_t s1 = AES_SBOX[b1]; 52 | uint8_t s2 = AES_SBOX[b2]; 53 | uint8_t s3 = AES_SBOX[b3]; 54 | 55 | return ((uint32_t)s0 << 24) | ((uint32_t)s1 << 16) | ((uint32_t)s2 << 8) | s3; 56 | } 57 | 58 | // Naive implementation of AES-128 key expansion. 59 | void aes128_key_expansion(const uint32_t* key, uint32_t* key_schedule) { 60 | for (uint32_t i = 0; i < 4 * AES128_NUM_ROUNDS; i++) { 61 | if (i < AES128_KEY_SIZE_IN_DWORDS) { 62 | key_schedule[i] = key[i]; 63 | } else if ((i >= AES128_KEY_SIZE_IN_DWORDS) && (i % AES128_KEY_SIZE_IN_DWORDS == 0)) { 64 | key_schedule[i] = key_schedule[i - AES128_KEY_SIZE_IN_DWORDS] ^ sub_word(rot_word(key_schedule[i - 1])) ^ AES128_RCON[i / AES128_KEY_SIZE_IN_DWORDS - 1]; 65 | } else { 66 | key_schedule[i] = key_schedule[i - AES128_KEY_SIZE_IN_DWORDS] ^ key_schedule[i - 1]; 67 | } 68 | } 69 | } 70 | 71 | 72 | int main(void) { 73 | for (uint32_t iter = 0; iter < 10; iter++) { 74 | 75 | // Allocate memory for our secret key. 76 | uint32_t* key = (uint32_t*)malloc(AES128_KEY_SIZE_IN_BYTES); 77 | if (key == NULL) { 78 | printf("Error: couldn't allocate memory.\n"); 79 | return 1; 80 | } 81 | 82 | // Allocate some memory that will hold our key schedule (the result of key expansion). 83 | uint32_t* key_schedule = (uint32_t*)malloc(4 * AES128_NUM_ROUNDS * sizeof(uint32_t)); 84 | if (key_schedule == NULL) { 85 | printf("Error: couldn't allcoate memory.\n"); 86 | return 1; 87 | } 88 | 89 | // Generate a random key. 90 | FILE* rng = fopen("/dev/urandom", "r"); 91 | if (rng == NULL) { 92 | printf("Error: couldn't open /dev/urandom.\n"); 93 | return 1; 94 | } 95 | 96 | size_t num_bytes = fread(key, sizeof(uint8_t), AES128_KEY_SIZE_IN_BYTES, rng); 97 | fclose(rng); 98 | if (num_bytes != AES128_KEY_SIZE_IN_BYTES) { 99 | printf("Error: couldn't get random data.\n"); 100 | return 1; 101 | } 102 | 103 | // Print the generated key. 104 | printf("Key: "); 105 | for (uint32_t i = 0; i < AES128_KEY_SIZE_IN_DWORDS; i++) { 106 | printf("%08X ", key[i]); 107 | } 108 | printf("\n"); 109 | 110 | // Mark the memory location of the key as a secret, so that Pin-based CEC can track it. 111 | PinBasedCEC_MarkSecret((uint64_t)key, AES128_KEY_SIZE_IN_BYTES); 112 | 113 | // Call the function to expand the key to fill the key schedule. 114 | aes128_key_expansion((const uint32_t*)key, key_schedule); 115 | 116 | // Tell Pin-based CEC that we are done using all the secrets we marked earlier, so it can stop tracking them. 117 | PinBasedCEC_ClearSecrets(); 118 | 119 | // Print the expanded key. 120 | printf("Key schedule:\n"); 121 | for (uint32_t i = 0; i < AES128_NUM_ROUNDS; i++) { 122 | printf("\t"); 123 | for (uint32_t j = 0; j < AES128_KEY_SIZE_IN_DWORDS; j++) { 124 | printf("%08X ", key_schedule[i*AES128_KEY_SIZE_IN_DWORDS + j]); 125 | } 126 | printf("\n"); 127 | } 128 | printf("\n"); 129 | 130 | // Cleanup our allocations. 131 | free(key); 132 | free(key_schedule); 133 | } 134 | 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## PROJECT NOT UNDER ACTIVE MANAGEMENT 2 | 3 | This project will no longer be maintained by Intel. 4 | 5 | Intel has ceased development and contributions including, but not limited to, maintenance, bug fixes, new releases, or updates, to this project. 6 | 7 | Intel no longer accepts patches to this project. 8 | 9 | If you have an ongoing need to use this project, are interested in independently developing it, or would like to maintain patches for the open source software community, please create your own fork of this project. 10 | 11 | Contact: webadmin@linux.intel.com 12 | Pin-based Constant Execution Checker (Pin-based CEC) 13 | ==================================================== 14 | 15 | Pin-based Constant Execution Checker (Pin-based CEC) is a dynamic binary instrumentation tool that checks for non-constant execution and memory-access patterns while a program is running. It does this by using the [Intel Pin framework](https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html) to trace every instruction that a targeted subroutine executes, logging all instruction pointers and memory addresses that get accessed, and comparing logs across subroutine invocations to ensure a constant execution profile. The tool uses taint analysis to determine if the execution differences are secret-dependent, to cut down on false positives. 16 | 17 | Development model 18 | ----------------- 19 | 20 | We use a "tagged release" development model. This essentially means that only commits explicitly tagged as releases are recommended for use in production. Full list of releases is available here: [Pin-based CEC releases](https://github.com/intel/pin-based-cec/releases). All other commits are development-quality and should only be used for development process (e.g., contributions). 21 | 22 | Dependencies 23 | ------------ 24 | 25 | - Linux 26 | - python 3.5 or greater 27 | - gcc/g++/make 28 | - diff 29 | 30 | Building 31 | -------- 32 | 33 | 1. Download Intel Pin from https://software.intel.com/en-us/articles/pin-a-binary-instrumentation-tool-downloads and extract it somewhere. 34 | 35 | 2. Get the sources 36 | 37 | - Download and unpack a released version from the [Releases section](https://github.com/intel/pin-based-cec/releases). 38 | 39 | - Alternatively, clone the repository while making sure git submodules are fetched: 40 | 41 | ```bash 42 | git clone --recurse-submodules https://github.com/intel/pin-based-cec 43 | ``` 44 | 45 | or 46 | 47 | ```bash 48 | git clone https://github.com/intel/pin-based-cec 49 | git submodule init 50 | git submodule update 51 | ``` 52 | 53 | - Check out a tag corresponding to the release you are interested in (full list is in the [Releases section](https://github.com/intel/pin-based-cec/releases)) 54 | 55 | 3. Use make to build the pintool 56 | 57 | ```bash 58 | cd src 59 | make PIN_ROOT= 60 | ``` 61 | 62 | Usage 63 | ----- 64 | 65 | 1. Create a test program that exercises the Function Under Test. For example, if you want to test a modular 66 | exponentiation implementation, do RSA signatures in a loop. 67 | 68 | 1. Include [`include/pin_based_cec.h`](include/pin_based_cec.h) file in your test program. 69 | 70 | 1. Add calls to `PinBasedCEC_MarkSecret(uint64_t addr, uint64_t size)` to mark your secret data for taint analysis. It is recommended to mark the data as close to the Function Under Test as you can. See [`example/test.c`](example/test.c) in the repo for an example. 71 | 72 | 1. Add calls to `PinBasedCEC_ClearSecrets()` when you are done processing your secret data. 73 | 74 | 1. Run the test program instrumented with Pin-based CEC. See the OPTIONS section. Any traces of the Function Under Test that showed execution or memory-access differences will have logs stored in the `trace/` folder, and a corresponding taint analysis in the `taint/` folder. See [`example/Makefile`](example/Makefile) for an example of how to run the Pin-based CEC pintool. 75 | 76 | 1. Run the [`src/post_process.py`](src/post_process.py) script to apply the taint analysis results to the traces. The script will output the addresses of execution differences that are tainted with secret data. 77 | 78 | Example 79 | ------- 80 | 81 | See the [`example/`](example/) for steps on how to use Pin-based CEC to detect a potential side-channel vulnerability in an AES key expansion implementation. 82 | 83 | Options 84 | ------- 85 | 86 | ```text 87 | -t src/obj-intel64/CECTraceTool.so -f -l -A -s -- 88 | 89 | Options: 90 | -s 91 | Name of summary output file containing overall pass/fail results. 92 | 93 | -f 94 | Target routine to instrument. Multiple target routines can be specified by repeating the -f option and 95 | tracing will only begin once all target routines have been entered. 96 | 97 | Experimental Options: 98 | -A 99 | Treat all the allocation labels (such as "lmem3") in the list as 64-byte aligned. This is useful to 100 | reduce false-positives when a program makes cacheline-aligned accesses into a buffer that itself might 101 | not be aligned to a cacheline. 102 | 103 | -m 104 | If yes, enable LUT marking in the trace log. 105 | 106 | -n 107 | If LUT marking is enabled, use the target routine specified by the given index to get the LUT address. 108 | Which argument of this function is treated as the LUT address is specified by -l. 109 | 110 | -l 111 | If LUT marking is enabled, treat the argument with the given index as the LUT address. The function 112 | that this is applied to is specified by -n. 113 | ``` 114 | 115 | Post Processing 116 | --------------- 117 | 118 | To post process results, run the [`src/post_process.py`](src/post_process.py) script in the directory containing `trace/` and `taint/` folders: 119 | 120 | ```bash 121 | python3 post_process.py [--verbose] [--branch] 122 | ``` 123 | 124 | `--verbose` enables verbose output. 125 | 126 | `--branch` enables tainted branch checking (experimental). Any time RIP becomes tainted, the address of the tainting instruction is flagged and output. This can be used to detect scenarios where a conditional branch depends on secret data even if no execution differences are observed in the traces (e.g., when only one side of the branch is taken during the observed executions of a program). 127 | 128 | The results (address that have been flagged as non-constant between executions) will be stored in the result file. 129 | 130 | Notes and caveats 131 | ----------------- 132 | 133 | - __Tail call elimination__ 134 | If the Function Under Test has been optimized with tail call elimination (and therefore does not have a closing `RET` instruction), Pin will not be able to detect the end of the function, and so the analysis will not function correctly. This caveat only applies to the Function Under Test itself (the function passed as a parameter to Pin-based CEC), not subfunctions or other functions called during the execution of the Function Under Test. 135 | 136 | Code of Conduct 137 | --------------- 138 | 139 | Intel has adopted the Contributor Covenant as the Code of Conduct for all of its open source projects. 140 | 141 | See [CODE OF CONDUCT](CODE_OF_CONDUCT.md) for more information. 142 | 143 | Contributing 144 | ------------ 145 | 146 | Contributions to Pin-based CEC are welcome in the form of issues and pull requests. 147 | 148 | See [CONTRIBUTING](CONTRIBUTING.md) for more information. 149 | -------------------------------------------------------------------------------- /src/taint.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #include "taint.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "pin.H" 12 | #include "visit_counter.h" 13 | 14 | #include "tat_instr.h" 15 | 16 | Taint::Taint(Tracer* _tracer, IpVisitCounter* _ipVisitCounter, 17 | bool _ignoreRIP) { 18 | tracer = _tracer; 19 | ipVisitCounter = _ipVisitCounter; 20 | ignoreRIP = _ignoreRIP; 21 | } 22 | 23 | Taint::~Taint() {} 24 | 25 | bool Taint::regIsTainted(REG reg) { 26 | std::set::iterator it = taintedRegs.find(reg); 27 | return (it != taintedRegs.end()); 28 | } 29 | 30 | bool Taint::memIsTainted(uint64_t addr) { 31 | std::set::iterator it = taintedMem.find(addr); 32 | return (it != taintedMem.end()); 33 | } 34 | 35 | bool Taint::isTainted(uint64_t op, bool isMem) { 36 | if (isMem) { 37 | return memIsTainted(op); 38 | } else { 39 | return regIsTainted((REG)op); 40 | } 41 | } 42 | 43 | void Taint::markTaint(uint64_t op, bool isMem) { 44 | if (isMem) { 45 | markMemTainted(op); 46 | } else { 47 | markRegTainted((REG)op); 48 | } 49 | } 50 | 51 | void Taint::clearTaint(uint64_t op, bool isMem) { 52 | if (isMem) { 53 | clearMemTainted(op); 54 | } else { 55 | clearRegTainted((REG)op); 56 | } 57 | } 58 | 59 | void Taint::markRegTainted(REG reg) { 60 | taintedRegs.insert(reg); 61 | } 62 | 63 | void Taint::clearRegTainted(REG reg) { 64 | taintedRegs.erase(reg); 65 | } 66 | 67 | void Taint::markMemTainted(uint64_t addr) { 68 | taintedMem.insert(addr); 69 | } 70 | 71 | void Taint::clearMemTainted(uint64_t addr) { 72 | taintedMem.erase(addr); 73 | } 74 | 75 | void Taint::printSummary(void) { 76 | std::cout << std::hex; 77 | for (std::set::iterator it = taintedRegs.begin(); 78 | it != taintedRegs.end(); ++it) { 79 | std::cout << "Reg: " << *it << std::endl; 80 | } 81 | std::cout << std::dec; 82 | 83 | std::cout << "Mem set size: " << taintedMem.size() << std::endl; 84 | } 85 | 86 | void Taint::reset(void) { 87 | taintedRegs.clear(); 88 | taintedMem.clear(); 89 | } 90 | 91 | void Taint::printTaintedSet(std::ostream& stream) { 92 | for (std::set::iterator it = taintedRegs.begin(); 93 | it != taintedRegs.end(); ++it) { 94 | stream << "REG(" << REG_StringShort((REG)*it) << ")" << std::endl; 95 | } 96 | for (std::set::iterator it = taintedMem.begin(); 97 | it != taintedMem.end(); ++it) { 98 | stream << "MEM(" << std::hex << *it << std::dec << ")" << std::endl; 99 | } 100 | } 101 | 102 | bool Taint::hasTaintedState() { 103 | return ((taintedRegs.size() > 0) || (taintedMem.size() > 0)); 104 | } 105 | 106 | bool Taint::hasTaintedFlags() { 107 | return (taintedRegs.find(REG_RFLAGS) != taintedRegs.end()); 108 | } 109 | 110 | bool Taint::hasTaintedMemory() { return (taintedMem.size() > 0); } 111 | 112 | void Taint::analyzeAndPropagate(tat_instr_t* instr) { 113 | bool anyReadOperandIsTainted = false; 114 | bool anyWriteOperandIsTainted = false; 115 | uint32_t memop_index = 0; 116 | 117 | std::stringstream ss; 118 | ss << "["; 119 | 120 | // Check if any read operand is tainted 121 | for (uint32_t i = 0; i < instr->operand_count; i++) { 122 | tat_operand_access_t access = instr->operand_access[i]; 123 | tat_operand_type_t type = instr->operand_type[i]; 124 | if ((access == TAT_OPERAND_READ) || 125 | (access == TAT_OPERAND_READ_AND_WRITTEN)) { 126 | if (type == TAT_OTYPE_REG) { 127 | REG reg = instr->operand_register[i]; 128 | ss << "REG(" << REG_StringShort(reg) << ")"; 129 | if (regIsTainted(reg)) { 130 | anyReadOperandIsTainted = true; 131 | ss << ".T"; 132 | } 133 | ss << " "; 134 | } else if (type == TAT_OTYPE_MEM) { 135 | uint64_t memea = instr->memops_memea[memop_index]; 136 | uint32_t mem_size = instr->memops_bytes[memop_index]; 137 | REG base_reg = instr->base_register[memop_index]; 138 | REG index_reg = instr->index_register[memop_index]; 139 | ss << "MEM(" << std::hex << memea << std::dec << ", " << mem_size 140 | << " = " << REG_StringShort(base_reg) << " + " 141 | << REG_StringShort(index_reg) << ")"; 142 | if (regIsTainted(base_reg) || regIsTainted(index_reg)) { 143 | anyReadOperandIsTainted = true; 144 | ss << ".T"; 145 | } else { 146 | for (uint32_t j = 0; j < mem_size; j++) { 147 | if (memIsTainted(memea + j)) { 148 | anyReadOperandIsTainted = true; 149 | ss << ".T"; 150 | break; 151 | } 152 | } 153 | } 154 | ss << " "; 155 | } else if (type == TAT_OTYPE_AGEN) { 156 | REG index_reg = instr->base_register[0]; 157 | REG base_reg = instr->index_register[0]; 158 | 159 | if (index_reg != REG_INVALID()) { 160 | ss << "REG(" << REG_StringShort(index_reg) << ")"; 161 | if (regIsTainted(index_reg)) { 162 | anyReadOperandIsTainted = true; 163 | ss << ".T"; 164 | } 165 | ss << " "; 166 | } 167 | 168 | if (base_reg != REG_INVALID()) { 169 | ss << "REG(" << REG_StringShort(base_reg) << ")"; 170 | if (regIsTainted(base_reg)) { 171 | anyReadOperandIsTainted = true; 172 | ss << ".T"; 173 | } 174 | ss << " "; 175 | } 176 | } 177 | } 178 | if (type == TAT_OTYPE_MEM) { 179 | memop_index++; 180 | } 181 | } 182 | 183 | ss << "] --> ["; 184 | memop_index = 0; 185 | 186 | // Check if any write operand is tainted 187 | for (uint32_t i = 0; i < instr->operand_count; i++) { 188 | tat_operand_access_t access = instr->operand_access[i]; 189 | tat_operand_type_t type = instr->operand_type[i]; 190 | if ((access == TAT_OPERAND_WRITTEN) || 191 | (access == TAT_OPERAND_READ_AND_WRITTEN)) { 192 | if (type == TAT_OTYPE_REG) { 193 | REG reg = instr->operand_register[i]; 194 | ss << "REG(" << REG_StringShort(reg) << ")"; 195 | if (regIsTainted(reg)) { 196 | anyWriteOperandIsTainted = true; 197 | ss << ".T"; 198 | } 199 | ss << " "; 200 | } else if (type == TAT_OTYPE_MEM) { 201 | uint64_t memea = instr->memops_memea[memop_index]; 202 | uint32_t mem_size = instr->memops_bytes[memop_index]; 203 | ss << "MEM(" << std::hex << memea << std::dec << ", " << mem_size 204 | << ")"; 205 | for (uint32_t j = 0; j < mem_size; j++) { 206 | if (memIsTainted(memea + j)) { 207 | anyWriteOperandIsTainted = true; 208 | ss << ".T"; 209 | break; 210 | } 211 | } 212 | ss << " "; 213 | } 214 | } 215 | if (type == TAT_OTYPE_MEM) { 216 | memop_index++; 217 | } 218 | } 219 | 220 | ss << "]"; 221 | 222 | // Apply the taint propagation 223 | if (anyReadOperandIsTainted) { 224 | memop_index = 0; 225 | for (uint32_t i = 0; i < instr->operand_count; i++) { 226 | tat_operand_access_t access = instr->operand_access[i]; 227 | tat_operand_type_t type = instr->operand_type[i]; 228 | if ((access == TAT_OPERAND_WRITTEN) || 229 | (access == TAT_OPERAND_READ_AND_WRITTEN)) { 230 | if (type == TAT_OTYPE_REG) { 231 | REG reg = instr->operand_register[i]; 232 | markRegTainted(reg); 233 | } else if (type == TAT_OTYPE_MEM) { 234 | uint64_t memea = instr->memops_memea[memop_index]; 235 | uint32_t mem_size = instr->memops_bytes[memop_index]; 236 | for (uint32_t j = 0; j < mem_size; j++) { 237 | markMemTainted(memea + j); 238 | } 239 | } 240 | } 241 | if (type == TAT_OTYPE_MEM) { 242 | memop_index++; 243 | } 244 | } 245 | } else { 246 | memop_index = 0; 247 | for (uint32_t i = 0; i < instr->operand_count; i++) { 248 | tat_operand_access_t access = instr->operand_access[i]; 249 | tat_operand_type_t type = instr->operand_type[i]; 250 | if ((access == TAT_OPERAND_WRITTEN) || 251 | (access == TAT_OPERAND_READ_AND_WRITTEN)) { 252 | if (type == TAT_OTYPE_REG) { 253 | REG reg = instr->operand_register[i]; 254 | clearRegTainted(reg); 255 | } else if (type == TAT_OTYPE_MEM) { 256 | uint64_t memea = instr->memops_memea[memop_index]; 257 | uint32_t mem_size = instr->memops_bytes[memop_index]; 258 | for (uint32_t j = 0; j < mem_size; j++) { 259 | clearMemTainted(memea + j); 260 | } 261 | } 262 | } 263 | if (type == TAT_OTYPE_MEM) { 264 | memop_index++; 265 | } 266 | } 267 | } 268 | 269 | // If anything was tainted, log it 270 | if ((anyReadOperandIsTainted || anyWriteOperandIsTainted) && 271 | tracer->isEnabled()) { 272 | uint32_t ipVisitCount = ipVisitCounter->getCount(instr->pc); 273 | tracer->getTaintFile() << std::hex << instr->pc << "." << ipVisitCount 274 | << std::dec << ": " << ss.str() << std::endl; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/taint_semantics.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #include "MyPinTool.h" 10 | #include "pin.H" 11 | #include "taint_semantics.h" 12 | //#include "xed-decoded-inst-api.h" 13 | 14 | extern "C" { 15 | #include "xed-interface.h" 16 | } 17 | 18 | #include "tat_instr.h" 19 | 20 | // Call order for TAT 21 | typedef enum { 22 | 23 | // pre-marshall memory 24 | TAT_CALL_ORDER_PREMARSHALL = CALL_ORDER_LAST - 10, 25 | 26 | // taint analysis 27 | TAT_CALL_ORDER_TAINT = CALL_ORDER_LAST - 5, 28 | 29 | } tat_call_order_t; 30 | 31 | void tat_instr_init(tat_instr_t* s) { memset(s, 0, sizeof(tat_instr_t)); } 32 | 33 | tat_instr_t* tat_instr_alloc() { 34 | tat_instr_t* p = (tat_instr_t*)(malloc(sizeof(tat_instr_t))); 35 | tat_instr_init(p); 36 | return p; 37 | } 38 | 39 | REG tat_xed_exact_map_to_pin_reg(xed_reg_enum_t r) { 40 | return INS_XedExactMapToPinReg(r); 41 | } 42 | 43 | static xed_reg_enum_t tat_get_largest_enclosing_register( 44 | xed_reg_enum_t xedreg) { 45 | return xed_get_largest_enclosing_register(xedreg); 46 | } 47 | 48 | /* Function to instrument mem0 components : 49 | * Get base register and index registers 50 | * This is done to check if those registers are tainted 51 | * and caused tainted memory address 52 | * */ 53 | static void tat_instrument_mem_components(tat_instr_t* instr, 54 | xed_decoded_inst_t const* const xedd, 55 | uint32_t memop) { 56 | // Get base register 57 | xed_reg_enum_t xed_base = xed_decoded_inst_get_base_reg(xedd, memop); 58 | if (xed_base == XED_REG_INVALID) { 59 | instr->base_register[memop] = REG_INVALID(); 60 | } else { 61 | xed_reg_enum_t fullreg = tat_get_largest_enclosing_register(xed_base); 62 | instr->base_register[memop] = tat_xed_exact_map_to_pin_reg(fullreg); 63 | } 64 | 65 | // Get index register 66 | xed_reg_enum_t xed_index = xed_decoded_inst_get_index_reg(xedd, memop); 67 | if (xed_index == XED_REG_INVALID) { 68 | instr->index_register[memop] = REG_INVALID(); 69 | } else { 70 | xed_reg_enum_t fullreg = tat_get_largest_enclosing_register(xed_index); 71 | instr->index_register[memop] = tat_xed_exact_map_to_pin_reg(fullreg); 72 | } 73 | } 74 | 75 | // Check if this is zeroing instruction for registers 76 | // like - xor rax,rax 77 | static void tat_instrument_check_if_zero_register(tat_instr_t* instr, 78 | xed_iclass_enum_t iclass) { 79 | // Only relevant for registers 80 | if (instr->instruction_flags != TAT_FLAG_REG) return; 81 | 82 | uint32_t operands_to_compare; 83 | 84 | switch (iclass) { 85 | case XED_ICLASS_PXOR: 86 | case XED_ICLASS_XOR: 87 | case XED_ICLASS_XORPD: 88 | case XED_ICLASS_XORPS: 89 | operands_to_compare = 2; 90 | break; 91 | case XED_ICLASS_VPXOR: 92 | case XED_ICLASS_VXORPD: 93 | case XED_ICLASS_VXORPS: 94 | operands_to_compare = 3; 95 | break; 96 | default: 97 | // This instruction is not relevant to zeroing register 98 | return; 99 | } 100 | 101 | xed_reg_enum_t first_register = instr->operand_xed_register[0]; 102 | for (uint32_t op_ind = 1; op_ind < operands_to_compare; op_ind++) { 103 | // If those are not exactly the same registers then 104 | // this is no zeroing register instruction 105 | if (first_register != instr->operand_xed_register[op_ind]) return; 106 | } 107 | 108 | // If we got here then 109 | // all operands are the same then we are zeroing the register 110 | instr->instruction_flags |= TAT_FLAG_ZERO_REG; 111 | return; 112 | } 113 | 114 | // Get operand access 115 | tat_operand_access_t get_access(const xed_operand_t* operand) { 116 | // In conditional write we also need to read the register 117 | if (xed_operand_conditional_write(operand)) 118 | return TAT_OPERAND_READ_AND_WRITTEN; 119 | else if (xed_operand_read_only(operand)) 120 | return TAT_OPERAND_READ; 121 | else if (xed_operand_written_only(operand)) 122 | return TAT_OPERAND_WRITTEN; 123 | else 124 | return TAT_OPERAND_READ_AND_WRITTEN; 125 | } 126 | 127 | // This function instruments the operand of the instruction 128 | void tat_instrument_operand(INS ins, xed_decoded_inst_t const* const xedd, 129 | xed_inst_t const* const xedi, 130 | uint32_t operand_index, tat_instr_t* instr) { 131 | const xed_operand_t* operand = xed_inst_operand(xedi, operand_index); 132 | const xed_operand_enum_t operand_name = xed_operand_name(operand); 133 | const xed_category_enum_t cat = xed_decoded_inst_get_category(xedd); 134 | tat_operand_access_t access; 135 | 136 | // Instrument register operands 137 | if (xed_operand_is_register(operand_name)) { 138 | xed_reg_enum_t xedreg = xed_decoded_inst_get_reg(xedd, operand_name); 139 | xed_reg_enum_t fullreg = tat_get_largest_enclosing_register(xedreg); 140 | xed_reg_class_enum_t reg_class = xed_reg_class(xedreg); 141 | 142 | // Handler pop and push register 143 | if (fullreg == XED_REG_STACKPOP || fullreg == XED_REG_STACKPUSH) { 144 | fullreg = XED_REG_RSP; 145 | access = TAT_OPERAND_READ_AND_WRITTEN; 146 | } 147 | // Un-handled register classes 148 | else if (reg_class == XED_REG_CLASS_PSEUDO || 149 | reg_class == XED_REG_CLASS_PSEUDOX87 || 150 | reg_class == XED_REG_CLASS_XCR) { 151 | return; 152 | } 153 | // Get operand access 154 | else { 155 | access = get_access(operand); 156 | } 157 | 158 | // Get PIN register 159 | REG pinreg = tat_xed_exact_map_to_pin_reg(fullreg); 160 | 161 | // Direct branches or calls or Conditional branches can not taint registers 162 | if ((INS_IsDirectBranch(ins) || INS_IsDirectCall(ins) || cat == XED_CATEGORY_COND_BR) && 163 | (access == TAT_OPERAND_WRITTEN || 164 | access == TAT_OPERAND_READ_AND_WRITTEN)) { 165 | return; 166 | } 167 | 168 | // Set instr fields 169 | instr->instruction_flags |= TAT_FLAG_REG; 170 | instr->reg_operand_count++; 171 | instr->operand_type[operand_index] = TAT_OTYPE_REG; 172 | instr->operand_access[operand_index] = access; 173 | instr->operand_register[operand_index] = pinreg; 174 | instr->operand_xed_register[operand_index] = xedreg; 175 | } 176 | 177 | // Handle memory operands 178 | else if (operand_name == XED_OPERAND_MEM0 || 179 | operand_name == XED_OPERAND_MEM1) { 180 | // Set instr fields 181 | instr->instruction_flags |= TAT_FLAG_MEM; 182 | instr->operand_type[operand_index] = TAT_OTYPE_MEM; 183 | // Determine access 184 | if (xed_decoded_inst_mem_written(xedd, 0) && 185 | xed_decoded_inst_mem_read(xedd, 0)) { 186 | instr->operand_access[operand_index] = TAT_OPERAND_READ_AND_WRITTEN; 187 | } else if (xed_decoded_inst_mem_written(xedd, 0)) { 188 | instr->operand_access[operand_index] = TAT_OPERAND_WRITTEN; 189 | } else if (xed_decoded_inst_mem_read(xedd, 0)) { 190 | instr->operand_access[operand_index] = TAT_OPERAND_READ; 191 | } else { 192 | std::cout << "Illegal access type in memory operand" << std::endl; 193 | PIN_ExitProcess(1); 194 | } 195 | 196 | // Determine memop index 197 | uint32_t memop = 0; 198 | if (operand_name == XED_OPERAND_MEM1) memop = 1; 199 | 200 | // Set base and index registers 201 | if (instr->gather_or_scatter) { 202 | // Support memory operands of gather instructions 203 | for (memop = 0; memop < instr->mem_operand_count; memop++) { 204 | tat_instrument_mem_components(instr, xedd, memop); 205 | } 206 | } else { 207 | // Normal memory operand 208 | tat_instrument_mem_components(instr, xedd, memop); 209 | } 210 | } 211 | 212 | // Handle immediate operands 213 | else if (operand_name == XED_OPERAND_IMM0) { 214 | const xed_operand_values_t* ov; 215 | ov = xed_decoded_inst_operands_const(xedd); 216 | instr->imm0 = (uint16_t)xed_operand_values_get_immediate_uint64(ov); 217 | 218 | // Set instr fields 219 | instr->instruction_flags |= TAT_FLAG_IMM; 220 | instr->operand_type[operand_index] = TAT_OTYPE_IMM; 221 | instr->operand_access[operand_index] = TAT_OPERAND_NONE; 222 | } 223 | 224 | // Set RELBR operands 225 | else if (operand_name == XED_OPERAND_RELBR) { 226 | // Set instr fields 227 | instr->operand_type[operand_index] = TAT_OTYPE_RELBR; 228 | instr->operand_access[operand_index] = TAT_OPERAND_NONE; 229 | 230 | // Set fall through and target fields for conditional branches 231 | instr->fallthru = instr->pc + xed_decoded_inst_get_length(xedd); 232 | instr->target = 233 | instr->fallthru + xed_decoded_inst_get_branch_displacement(xedd); 234 | } 235 | 236 | // We do not need to do anything in base register 237 | // cause PIN will handle as memory operands 238 | else if (xed_operand_is_memory_addressing_register(operand_name)) { 239 | } 240 | 241 | // Handle AGEN operand like in lea instructions 242 | else if (operand_name == XED_OPERAND_AGEN) { 243 | // Set instr fields 244 | instr->instruction_flags |= TAT_FLAG_AGEN; 245 | instr->operand_type[operand_index] = TAT_OTYPE_AGEN; 246 | instr->operand_access[operand_index] = TAT_OPERAND_READ; 247 | 248 | // Set base and index registers 249 | tat_instrument_mem_components(instr, xedd, 0); 250 | } 251 | 252 | else { 253 | std::cout << "Unhandled operand " << operand_index << " " 254 | << xed_operand_enum_t2str(operand_name) 255 | << " in Instruction: " << INS_Disassemble(ins) 256 | << " memops: " << INS_MemoryOperandCount(ins) << std::endl; 257 | PIN_ExitProcess(1); 258 | } 259 | } 260 | 261 | // This is the main instrumentation function 262 | void tat_instrument(INS ins, void* v) { 263 | // Get XED data 264 | xed_decoded_inst_t const* const xedd = INS_XedDec(ins); 265 | const xed_inst_t* xedi = xed_decoded_inst_inst(xedd); 266 | const xed_iclass_enum_t iclass = xed_decoded_inst_get_iclass(xedd); 267 | uint32_t memop_idx; 268 | 269 | // Allocate and initialize instr 270 | tat_instr_t* instr = tat_instr_alloc(); 271 | instr->pc = INS_Address(ins); 272 | instr->gather_or_scatter = 273 | xed_decoded_inst_get_attribute(xedd, XED_ATTRIBUTE_GATHER); 274 | 275 | // Instrument operands 276 | instr->operand_count = xed_inst_noperands(xedi); 277 | instr->mem_operand_count = INS_MemoryOperandCount(ins); 278 | for (uint32_t i = 0; i < instr->operand_count; i++) 279 | tat_instrument_operand(ins, xedd, xedi, i, instr); 280 | 281 | // Check if this is zeroing instruction for registers 282 | // like - xor rax,rax 283 | tat_instrument_check_if_zero_register(instr, iclass); 284 | 285 | // Taint analysis routine 286 | // Call it only when registers or memory are affected 287 | if (instr->instruction_flags & (TAT_FLAG_REG | TAT_FLAG_MEM | TAT_FLAG_AGEN)) { 288 | INS_InsertIfCall(ins, IPOINT_BEFORE, (AFUNPTR)InstructionCanBeTainted, 289 | IARG_FAST_ANALYSIS_CALL, IARG_CALL_ORDER, 290 | TAT_CALL_ORDER_TAINT, 291 | // IARG_REG_VALUE, virtual_reg_tdata, 292 | IARG_END); 293 | 294 | INS_InsertThenCall(ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeInstructionForTaint, 295 | IARG_FAST_ANALYSIS_CALL, IARG_CALL_ORDER, 296 | TAT_CALL_ORDER_TAINT, 297 | // IARG_REG_VALUE, virtual_reg_tdata, 298 | IARG_PTR, instr, IARG_END); 299 | } 300 | 301 | // Memory taint pre-marshalling 302 | for (memop_idx = 0; memop_idx < instr->mem_operand_count; memop_idx++) { 303 | // Update memop data 304 | instr->memops_bytes[memop_idx] = 305 | (uint32_t)INS_MemoryOperandSize(ins, memop_idx); 306 | instr->memops_is_write[memop_idx] = 307 | INS_MemoryOperandIsWritten(ins, memop_idx); 308 | 309 | // Prepare pre-marshalling for data of memory operands 310 | INS_InsertIfCall(ins, IPOINT_BEFORE, (AFUNPTR)MemoryIsTainted, 311 | IARG_FAST_ANALYSIS_CALL, IARG_CALL_ORDER, 312 | TAT_CALL_ORDER_PREMARSHALL, 313 | // IARG_REG_VALUE, virtual_reg_tdata, 314 | IARG_END); 315 | INS_InsertThenCall(ins, IPOINT_BEFORE, (AFUNPTR)PremarshallMemoryOperand, 316 | IARG_FAST_ANALYSIS_CALL, 317 | // IARG_REG_VALUE, virtual_reg_tdata, 318 | IARG_PTR, instr, IARG_UINT32, memop_idx, IARG_MEMORYOP_EA, 319 | memop_idx, IARG_CALL_ORDER, TAT_CALL_ORDER_PREMARSHALL, 320 | IARG_END); 321 | } 322 | 323 | } 324 | 325 | void TaintInstruction(INS ins, void* v) { tat_instrument(ins, v); } 326 | -------------------------------------------------------------------------------- /src/post_process.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Intel Corporation 2 | # 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | import subprocess 6 | import os 7 | import os.path 8 | import re 9 | import argparse 10 | import json 11 | import operator 12 | 13 | 14 | def load_tainted_address_set(filename): 15 | tainted_addr_set = set() 16 | with open(filename, 'r') as f: 17 | for full_line in f: 18 | line = full_line.strip() 19 | if line == "TraceStart" or line == "TraceEnd": 20 | pass 21 | else: 22 | colon_index = line.find(':') 23 | addr = line[0:colon_index] 24 | operands = line[colon_index:] 25 | arrow_index = operands.find('-->') 26 | read_operands = operands[0:arrow_index] 27 | # write_operands = operands[arrow_index:] 28 | if (read_operands.find(".T") >= 0): 29 | tainted_addr_set.add(addr) 30 | return tainted_addr_set 31 | 32 | 33 | def parse_line(line): 34 | if line.startswith("IP:"): 35 | return line[4:] 36 | elif line.startswith("MemRead:") or line.startswith("MemWrite:"): 37 | first_space_index = line.find(' ') 38 | second_space_index = line.find(' ', first_space_index + 1) 39 | return line[first_space_index+1: second_space_index] 40 | elif line == "TraceMemStart": 41 | return None 42 | elif line == "TraceMemEnd": 43 | return None 44 | elif line == "TraceIPStart": 45 | return None 46 | elif line == "TraceIPEnd": 47 | return None 48 | else: 49 | print("Unknown line: %s" % line) 50 | return None 51 | 52 | 53 | def parse_diff_for_mem(diff_str): 54 | left_addr_list = [] 55 | right_addr_list = [] 56 | start_index = 0 57 | last_line = False 58 | while not last_line: 59 | end_index = diff_str.find("\n", start_index) 60 | if end_index == -1: 61 | line = diff_str[start_index:] 62 | last_line = True 63 | else: 64 | line = diff_str[start_index: end_index] 65 | start_index = end_index + 1 66 | 67 | if line == "": 68 | pass 69 | elif line.startswith('@@') or line.startswith('++') or line.startswith('--'): 70 | pass 71 | elif line.startswith('-'): 72 | left_addr_list.append(parse_line(line[1:])) 73 | elif line.startswith('+'): 74 | right_addr_list.append(parse_line(line[1:])) 75 | 76 | return (left_addr_list, right_addr_list) 77 | 78 | 79 | def parse_diff_for_ip(diff_str): 80 | left_addr_list = [] 81 | right_addr_list = [] 82 | start_index = 0 83 | last_line = False 84 | top_context = True 85 | while not last_line: 86 | end_index = diff_str.find("\n", start_index) 87 | if end_index == -1: 88 | line = diff_str[start_index:] 89 | last_line = True 90 | else: 91 | line = diff_str[start_index: end_index] 92 | start_index = end_index + 1 93 | 94 | if line == "": 95 | pass 96 | elif line.startswith('@@') or line.startswith('++') or line.startswith('--'): 97 | top_context = True 98 | elif line.startswith('-'): 99 | left_addr_list.append(parse_line(line[1:])) 100 | top_context = False 101 | elif line.startswith('+'): 102 | right_addr_list.append(parse_line(line[1:])) 103 | top_context = False 104 | else: 105 | if top_context: 106 | left_addr_list.append(parse_line(line[1:])) 107 | right_addr_list.append(parse_line(line[1:])) 108 | 109 | return (left_addr_list, right_addr_list) 110 | 111 | 112 | def diff_files_for_mem(filename1, filename2): 113 | cmd = ["diff", "--speed-large-files", "-U0", filename1, filename2] 114 | proc = subprocess.run(cmd, shell=False, stdout=subprocess.PIPE) 115 | return parse_diff_for_mem(proc.stdout.decode("utf-8")) 116 | 117 | 118 | def diff_files_for_ip(filename1, filename2): 119 | cmd = ["diff", "--speed-large-files", "-U1", filename1, filename2] 120 | proc = subprocess.run(cmd, shell=False, stdout=subprocess.PIPE) 121 | return parse_diff_for_ip(proc.stdout.decode("utf-8")) 122 | 123 | 124 | def lookup_symbol(symtab, addr): 125 | for (start, size, name, image_address, image_name) in symtab: 126 | if (addr >= start) and (addr < start + size): 127 | return (name, addr - image_address, image_name) 128 | return ("???", 0, "") 129 | 130 | 131 | def filter_trace(trace_filename, taint_filename, out_filename): 132 | tainted_addr_set = load_tainted_address_set(taint_filename) 133 | with open(out_filename, 'w') as f_out: 134 | with open(trace_filename, 'r') as f_in: 135 | for line in f_in: 136 | addr = parse_line(line.strip()) 137 | if addr is not None and addr in tainted_addr_set: 138 | f_out.write(line) 139 | 140 | 141 | def analyze_all_files_for_mem(trace_path, taint_path, symtab, 142 | verbose, result_file): 143 | ref_filename = "trace0" 144 | ref_filepath = os.path.join(trace_path, ref_filename) 145 | ref_taint_filename = "taint0" 146 | ref_taint_filepath = os.path.join(taint_path, ref_taint_filename) 147 | 148 | if verbose: 149 | print("Filtering memory reference trace...") 150 | 151 | ref_temp_filepath = os.path.join(trace_path, "temp0") 152 | filter_trace(ref_filepath, ref_taint_filepath, ref_temp_filepath) 153 | 154 | addr_set = set() 155 | temp_filepath = os.path.join(trace_path, "tempX") 156 | 157 | with os.scandir(trace_path) as scanner: 158 | for item in scanner: 159 | if item.is_file() and item.name != ref_filename and item.name.startswith("trace"): 160 | if verbose: 161 | print("Filtering memory %s..." % item.name) 162 | trace_filepath = os.path.join(trace_path, item.name) 163 | trace_index = int(item.name[5:]) 164 | taint_filename = "taint%d" % trace_index 165 | taint_filepath = os.path.join(taint_path, taint_filename) 166 | filter_trace(trace_filepath, taint_filepath, temp_filepath) 167 | 168 | if verbose: 169 | print("Diffing memory %s..." % item.name) 170 | 171 | (left_tainted_addr_list, 172 | right_tainted_addr_list) = diff_files_for_mem(ref_temp_filepath, temp_filepath) 173 | 174 | if len(left_tainted_addr_list) > 0 or len(right_tainted_addr_list) > 0: 175 | for addr in left_tainted_addr_list: 176 | dot_index = addr.find('.') 177 | addr_set.add(int(addr[0:dot_index], 16)) 178 | for addr in right_tainted_addr_list: 179 | dot_index = addr.find('.') 180 | addr_set.add(int(addr[0:dot_index], 16)) 181 | 182 | print() 183 | 184 | # Cleanup temp files (temp0 and tempX) 185 | if os.path.isfile(ref_temp_filepath): 186 | os.remove(ref_temp_filepath) 187 | if os.path.isfile(temp_filepath): 188 | os.remove(temp_filepath) 189 | 190 | if len(addr_set) > 0: 191 | print("Addresses with tainted memory access differences:") 192 | result_file.write("Addresses with tainted memory access differences:\n") 193 | for addr in sorted(addr_set): 194 | (sym_name, offset, image_name) = lookup_symbol(symtab, addr) 195 | print("%X --> %s (%s @ %X)" % (addr, sym_name, image_name, offset)) 196 | result_file.write("%X --> %s (%s @ %X)\n" % (addr, sym_name, image_name, offset)) 197 | else: 198 | print("No addresses with tainted memory access differences.") 199 | result_file.write("No addresses with tainted memory access differences.\n") 200 | print() 201 | 202 | 203 | def analyze_all_files_for_ip(trace_path, taint_path, symtab, verbose, result_file): 204 | ref_filename = "trace0" 205 | ref_filepath = os.path.join(trace_path, ref_filename) 206 | ref_taint_filename = "taint0" 207 | ref_taint_filepath = os.path.join(taint_path, ref_taint_filename) 208 | 209 | if verbose: 210 | print("Filtering IP reference trace...") 211 | 212 | ref_temp_filepath = os.path.join(trace_path, "temp0") 213 | filter_trace(ref_filepath, ref_taint_filepath, ref_temp_filepath) 214 | 215 | addr_set = set() 216 | temp_filepath = os.path.join(trace_path, "tempX") 217 | 218 | with os.scandir(trace_path) as scanner: 219 | for item in scanner: 220 | if item.is_file() and item.name != ref_filename and item.name.startswith("trace"): 221 | if verbose: 222 | print("Filtering IP %s..." % item.name) 223 | trace_filepath = os.path.join(trace_path, item.name) 224 | trace_index = int(item.name[5:]) 225 | taint_filename = "taint%d" % trace_index 226 | taint_filepath = os.path.join(taint_path, taint_filename) 227 | filter_trace(trace_filepath, taint_filepath, temp_filepath) 228 | 229 | if verbose: 230 | print("Diffing IP %s..." % item.name) 231 | 232 | (left_tainted_addr_list, 233 | right_tainted_addr_list) = diff_files_for_ip(ref_temp_filepath, temp_filepath) 234 | 235 | if len(left_tainted_addr_list) > 0 or len(right_tainted_addr_list) > 0: 236 | for addr in left_tainted_addr_list: 237 | dot_index = addr.find('.') 238 | addr_set.add(int(addr[0:dot_index], 16)) 239 | for addr in right_tainted_addr_list: 240 | dot_index = addr.find('.') 241 | addr_set.add(int(addr[0:dot_index], 16)) 242 | 243 | print() 244 | 245 | # Cleanup temp files (temp0 and tempX) 246 | if os.path.isfile(ref_temp_filepath): 247 | os.remove(ref_temp_filepath) 248 | if os.path.isfile(temp_filepath): 249 | os.remove(temp_filepath) 250 | 251 | if len(addr_set) > 0: 252 | print("Addresses with tainted execution differences:") 253 | result_file.write("Addresses with tainted execution differences:\n") 254 | for addr in sorted(addr_set): 255 | (sym_name, offset, image_name) = lookup_symbol(symtab, addr) 256 | print("%X --> %s (%s @ %X)" % (addr, sym_name, image_name, offset)) 257 | result_file.write("%X --> %s (%s @ %X)\n" % (addr, sym_name, image_name, offset)) 258 | else: 259 | print("No addresses with tainted execution differences.") 260 | result_file.write("No addresses with tainted execution differences.\n") 261 | print() 262 | 263 | 264 | def parse_image_map(image_map_filename, verbose=False): 265 | global_symtab = [] 266 | with open(image_map_filename, 'r') as image_map_file: 267 | image_map = json.load(image_map_file) 268 | for (image_name, image_offset_str) in image_map.items(): 269 | image_offset = int(image_offset_str, 16) 270 | symtab = parse_symbols(image_name, offset=image_offset, verbose=verbose) 271 | global_symtab.extend(symtab) 272 | global_symtab.sort(key=operator.itemgetter(0)) 273 | return global_symtab 274 | 275 | 276 | def parse_symbols(image_name, offset=0, verbose=False): 277 | cmd = ["nm", "-nS", "--defined-only", image_name] 278 | proc = subprocess.run(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 279 | nm_output = proc.stdout.decode("utf-8") 280 | 281 | if verbose: 282 | print(proc.stderr.decode("utf-8")) 283 | 284 | line_re = re.compile("([0-9a-f]+) ([0-9a-f]+) . (.+)") 285 | symtab = [] 286 | for line in nm_output.split("\n"): 287 | m = line_re.match(line) 288 | if m is not None: 289 | start = int(m.group(1), 16) + offset 290 | size = int(m.group(2), 16) 291 | name = m.group(3) 292 | symtab.append((start, size, name, offset, image_name)) 293 | return symtab 294 | 295 | 296 | def branch_check_file(filename): 297 | addr_set = set() 298 | line_re = re.compile(r"([0-9a-f]+)\.[0-9a-f]+\: \[(.+)\] --> \[(.+)\]") 299 | with open(filename, 'r') as f_in: 300 | for line in f_in: 301 | m = line_re.match(line.strip()) 302 | if m is not None: 303 | addr = int(m.group(1), 16) 304 | read_operands = m.group(2) 305 | write_operands = m.group(3) 306 | if read_operands.find('.T') != -1 and write_operands.find('REG(rip)') != -1: 307 | addr_set.add(addr) 308 | 309 | return addr_set 310 | 311 | 312 | def branch_check_all_files(taint_path, symtab, verbose): 313 | addr_set = set() 314 | 315 | with os.scandir(taint_path) as scanner: 316 | for item in scanner: 317 | if item.is_file(): 318 | file_addr_set = branch_check_file(os.path.join(taint_path, item.name)) 319 | addr_set.update(file_addr_set) 320 | 321 | if len(addr_set) > 0: 322 | print("Tainted branches:") 323 | for addr in sorted(addr_set): 324 | (sym_name, offset, image_name) = lookup_symbol(symtab, addr) 325 | print("%X --> %s (%s @ %X)" % (addr, sym_name, image_name, offset)) 326 | else: 327 | print("No tainted branches.") 328 | 329 | print() 330 | 331 | 332 | def main(verbose, branch_check, result_filename): 333 | symtab = parse_image_map("image_map", verbose=verbose) 334 | 335 | with open(result_filename, 'w') as result_file: 336 | analyze_all_files_for_mem("memtrace", "taint", symtab, verbose, result_file) 337 | analyze_all_files_for_ip("iptrace", "taint", symtab, verbose, result_file) 338 | 339 | if branch_check: 340 | branch_check_all_files("taint", symtab, verbose) 341 | 342 | 343 | if __name__ == '__main__': 344 | parser = argparse.ArgumentParser(description="Process some traces.") 345 | parser.add_argument("result_file", help="Name of output file to print to (as well as stdout).") 346 | parser.add_argument("--verbose", default=False, action='store_true') 347 | parser.add_argument("--branch", default=False, action='store_true', 348 | help="Report any conditional branches that depend on secret data (experimental).") 349 | 350 | args = parser.parse_args() 351 | main(args.verbose, args.branch, args.result_file) 352 | -------------------------------------------------------------------------------- /src/tracer.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../vendor/CRCpp/inc/CRC.h" 17 | #include "tracer.h" 18 | 19 | Tracer::Tracer(std::string _traceMemFilePath, std::string _traceIPFilePath, 20 | std::string _taintFilePath, uint32_t numTargetRoutines, 21 | bool _markLut, uint32_t _lutTargetRoutineIndex, 22 | uint32_t _lutSize, IpVisitCounter *_ipVisitCounter) { 23 | nextLocalLabelId = 0; 24 | nextGlobalLabelId = 0; 25 | savedRSP = 0; 26 | exitCount = 0; 27 | enabled = false; 28 | instructionPointerDigest = 0xFFFFFFFF; 29 | memoryAccessDigest = 0xFFFFFFFF; 30 | currentTargetRoutineMask = 0; 31 | refTargetRoutineMask = (1 << numTargetRoutines) - 1; 32 | traceMemFilePath = _traceMemFilePath; 33 | traceIPFilePath = _traceIPFilePath; 34 | taintFilePath = _taintFilePath; 35 | markLut = _markLut; 36 | lutAddress = 0; 37 | lutSize = _lutSize; 38 | lutTargetRoutineIndex = _lutTargetRoutineIndex; 39 | taintFlag = false; 40 | onlyConsumeTaintedInstructions = false; 41 | ipVisitCounter = _ipVisitCounter; 42 | } 43 | 44 | Tracer::~Tracer() { 45 | traceMemFile.close(); 46 | traceIPFile.close(); 47 | taintFile.close(); 48 | 49 | for (std::map::iterator it = 50 | allocationMap.begin(); 51 | it != allocationMap.end(); ++it) { 52 | free(it->second); 53 | } 54 | 55 | for (std::map::iterator it = sectionMap.begin(); 56 | it != sectionMap.end(); ++it) { 57 | free(it->second); 58 | } 59 | } 60 | 61 | void Tracer::updateInstructionPointer(uint64_t ip) { 62 | if (enabled) { 63 | uint32_t ipVisitCount = ipVisitCounter->getCount(ip); 64 | 65 | traceIPFile << "IP: " << ip << "." << ipVisitCount << std::endl; 66 | 67 | updateInstructionPointerDigest(ip); 68 | } 69 | 70 | taintFlag = false; 71 | } 72 | 73 | void Tracer::updateMemoryRead(uint64_t ip, uint64_t addr) { 74 | if (enabled) { 75 | uint32_t ipVisitCount = ipVisitCounter->getCount(ip); 76 | 77 | TaggedMemoryAddress taggedAddress; 78 | convertMemoryAddressToTag(addr, &taggedAddress); 79 | 80 | traceMemFile << "MemRead: " << ip << "." << ipVisitCount << " ("; 81 | 82 | if (taggedAddress.type == MEM_TAG_POS_RSP_OFFSET) { 83 | traceMemFile << "rsp + " << taggedAddress.value; 84 | } else if (taggedAddress.type == MEM_TAG_NEG_RSP_OFFSET) { 85 | traceMemFile << "rsp - " << taggedAddress.value; 86 | } else if (taggedAddress.type == MEM_TAG_ALLOC) { 87 | if (isLocalAllocationAligned(taggedAddress.localLabel)) { 88 | ADDRINT alignedAllocationAddr = ALIGN64(taggedAddress.start); 89 | ADDRINT alignedDelta = addr - alignedAllocationAddr; 90 | traceMemFile << "ALIGN64(" << taggedAddress.localLabel << " + " 91 | << alignedDelta << ")"; 92 | } else { 93 | traceMemFile << taggedAddress.localLabel << " + " 94 | << taggedAddress.value; 95 | } 96 | } else if (taggedAddress.type == MEM_TAG_SECTION) { 97 | traceMemFile << taggedAddress.label << " + " << taggedAddress.value; 98 | } else if (taggedAddress.type == MEM_TAG_ABSOLUTE) { 99 | traceMemFile << taggedAddress.value; 100 | } 101 | 102 | traceMemFile << ")" << std::endl; 103 | 104 | updateMemoryAccessDigest(ip, &taggedAddress, false, false); 105 | } 106 | } 107 | 108 | void Tracer::updateMemoryWrite(uint64_t ip, uint64_t addr) { 109 | if (enabled) { 110 | uint32_t ipVisitCount = ipVisitCounter->getCount(ip); 111 | 112 | TaggedMemoryAddress taggedAddress; 113 | convertMemoryAddressToTag(addr, &taggedAddress); 114 | 115 | traceMemFile << "MemWrite: " << ip << "." << ipVisitCount << " ("; 116 | 117 | if (taggedAddress.type == MEM_TAG_POS_RSP_OFFSET) { 118 | traceMemFile << "rsp + " << taggedAddress.value; 119 | } else if (taggedAddress.type == MEM_TAG_NEG_RSP_OFFSET) { 120 | traceMemFile << "rsp - " << taggedAddress.value; 121 | } else if (taggedAddress.type == MEM_TAG_ALLOC) { 122 | if (isLocalAllocationAligned(taggedAddress.localLabel)) { 123 | ADDRINT alignedAllocationAddr = ALIGN64(taggedAddress.start); 124 | ADDRINT alignedDelta = addr - alignedAllocationAddr; 125 | traceMemFile << "ALIGN64(" << taggedAddress.localLabel << " + " 126 | << alignedDelta << ")"; 127 | } else { 128 | traceMemFile << taggedAddress.localLabel << " + " 129 | << taggedAddress.value; 130 | } 131 | } else if (taggedAddress.type == MEM_TAG_SECTION) { 132 | traceMemFile << taggedAddress.label << " + " << taggedAddress.value; 133 | } else if (taggedAddress.type == MEM_TAG_ABSOLUTE) { 134 | traceMemFile << taggedAddress.value; 135 | } 136 | 137 | traceMemFile << ")" << std::endl; 138 | 139 | updateMemoryAccessDigest(ip, &taggedAddress, true, false); 140 | } 141 | } 142 | 143 | void Tracer::enterTargetRoutine(uint32_t targetRoutineIndex, uint64_t argNValue, 144 | uint64_t rsp) { 145 | uint32_t newTargetRoutineMask = 146 | currentTargetRoutineMask | (1 << targetRoutineIndex); 147 | 148 | if ((newTargetRoutineMask == refTargetRoutineMask) && 149 | (currentTargetRoutineMask != refTargetRoutineMask)) { 150 | enabled = true; 151 | savedRSP = rsp; 152 | nextLocalLabelId = 0; 153 | instructionPointerDigest = 0xFFFFFFFF; 154 | memoryAccessDigest = 0xFFFFFFFF; 155 | openTraceMemFile(); 156 | openTraceIPFile(); 157 | openTaintFile(); 158 | traceMemFile << "TraceMemStart" << std::endl; 159 | traceIPFile << "TraceIPStart" << std::endl; 160 | taintFile << "TraceStart" << std::endl; 161 | ipVisitCounter->reset(); 162 | ipVisitCounter->enable(); 163 | } 164 | 165 | if (markLut && (targetRoutineIndex == lutTargetRoutineIndex)) { 166 | lutAddress = argNValue; 167 | } 168 | 169 | currentTargetRoutineMask = newTargetRoutineMask; 170 | } 171 | 172 | void Tracer::exitTargetRoutine(uint32_t targetRoutineIndex) { 173 | uint32_t newTargetRoutineMask = 174 | currentTargetRoutineMask & ~(1 << targetRoutineIndex); 175 | if ((newTargetRoutineMask != refTargetRoutineMask) && 176 | (currentTargetRoutineMask == refTargetRoutineMask)) { 177 | enabled = false; 178 | exitCount++; 179 | instructionPointerDigestHistory.push_back(instructionPointerDigest); 180 | memoryAccessDigestHistory.push_back(memoryAccessDigest); 181 | traceMemFile << "TraceMemEnd" << std::endl; 182 | traceIPFile << "TraceIPEnd" << std::endl; 183 | taintFile << "TraceEnd" << std::endl; 184 | 185 | finalizeTraceMemFile(); 186 | finalizeTraceIPFile(); 187 | finalizeTaintFile(); 188 | 189 | ipVisitCounter->disable(); 190 | ipVisitCounter->reset(); 191 | } 192 | currentTargetRoutineMask = newTargetRoutineMask; 193 | } 194 | 195 | AllocationRecord *Tracer::findAllocation(uint64_t addr) { 196 | std::map::iterator it = 197 | allocationMap.upper_bound(addr); 198 | 199 | if (it == allocationMap.begin()) { 200 | return NULL; 201 | } 202 | 203 | --it; 204 | if ((addr >= it->second->addr) && 205 | (addr < (it->second->addr + it->second->size))) { 206 | return it->second; 207 | } else { 208 | return NULL; 209 | } 210 | return NULL; 211 | } 212 | 213 | AllocationRecord *Tracer::findSection(uint64_t addr) { 214 | std::map::iterator it = 215 | sectionMap.upper_bound(addr); 216 | 217 | if (it == sectionMap.begin()) { 218 | return NULL; 219 | } 220 | 221 | --it; 222 | if ((addr >= it->second->addr) && 223 | (addr < (it->second->addr + it->second->size))) { 224 | return it->second; 225 | } else { 226 | return NULL; 227 | } 228 | } 229 | 230 | void Tracer::convertMemoryAddressToTag(uint64_t addr, 231 | TaggedMemoryAddress *outTag) { 232 | if ((addr >= savedRSP) && (addr < (savedRSP + 8192))) { 233 | outTag->type = MEM_TAG_POS_RSP_OFFSET; 234 | outTag->value = addr - savedRSP; 235 | return; 236 | } 237 | 238 | if ((addr < savedRSP) && (addr >= (savedRSP - 8192))) { 239 | outTag->type = MEM_TAG_NEG_RSP_OFFSET; 240 | outTag->value = savedRSP - addr; 241 | return; 242 | } 243 | 244 | AllocationRecord *alloc = findAllocation(addr); 245 | if (alloc != NULL) { 246 | outTag->type = MEM_TAG_ALLOC; 247 | outTag->label = alloc->label; 248 | outTag->localLabel = localizeAllocationLabel(alloc->label); 249 | outTag->value = addr - alloc->addr; 250 | outTag->start = alloc->addr; 251 | return; 252 | } 253 | 254 | AllocationRecord *sec = findSection(addr); 255 | if (sec != NULL) { 256 | outTag->type = MEM_TAG_SECTION; 257 | outTag->label = sec->label; 258 | outTag->value = addr - sec->addr; 259 | outTag->start = sec->addr; 260 | return; 261 | } 262 | 263 | outTag->type = MEM_TAG_ABSOLUTE; 264 | outTag->value = addr; 265 | } 266 | 267 | std::string Tracer::localizeAllocationLabel(std::string &globalLabel) { 268 | std::map::iterator it = 269 | globalToLocalAllocationLabelMap.find(globalLabel); 270 | if (it == globalToLocalAllocationLabelMap.end()) { 271 | std::stringstream ss; 272 | ss << "lmem" << nextLocalLabelId; 273 | nextLocalLabelId += 1; 274 | std::string localLabel = ss.str(); 275 | globalToLocalAllocationLabelMap.insert( 276 | std::pair(globalLabel, localLabel)); 277 | return localLabel; 278 | } else { 279 | return it->second; 280 | } 281 | } 282 | 283 | void Tracer::addGlobalAllocation(uint64_t addr, uint64_t size) { 284 | std::stringstream ss; 285 | ss << "mem" << nextGlobalLabelId; 286 | nextGlobalLabelId += 1; 287 | std::string label = ss.str(); 288 | 289 | std::map::iterator it = 290 | allocationMap.find(addr); 291 | if (it == allocationMap.end()) { 292 | AllocationRecord *alloc = 293 | (AllocationRecord *)malloc(sizeof(AllocationRecord)); 294 | alloc->addr = addr; 295 | alloc->size = size; 296 | alloc->label = label; 297 | allocationMap[addr] = alloc; 298 | } else { 299 | free(it->second); 300 | AllocationRecord *alloc = 301 | (AllocationRecord *)malloc(sizeof(AllocationRecord)); 302 | alloc->addr = addr; 303 | alloc->size = size; 304 | alloc->label = label; 305 | allocationMap[addr] = alloc; 306 | } 307 | } 308 | 309 | void Tracer::removeGlobalAllocation(uint64_t addr) { 310 | std::map::iterator it = 311 | allocationMap.find(addr); 312 | if (it != allocationMap.end()) { 313 | free(it->second); 314 | allocationMap.erase(addr); 315 | } 316 | } 317 | 318 | void Tracer::addSection(uint64_t addr, uint64_t size, const std::string &name) { 319 | std::map::iterator it = sectionMap.find(addr); 320 | if (it == allocationMap.end()) { 321 | AllocationRecord *alloc = 322 | (AllocationRecord *)malloc(sizeof(AllocationRecord)); 323 | alloc->addr = addr; 324 | alloc->size = size; 325 | alloc->label = name; 326 | sectionMap[addr] = alloc; 327 | } else { 328 | free(it->second); 329 | AllocationRecord *alloc = 330 | (AllocationRecord *)malloc(sizeof(AllocationRecord)); 331 | alloc->addr = addr; 332 | alloc->size = size; 333 | alloc->label = name; 334 | sectionMap[addr] = alloc; 335 | } 336 | } 337 | 338 | void Tracer::updateInstructionPointerDigest(uint64_t ip) { 339 | instructionPointerDigest = CRC::Calculate( 340 | (char *)&ip, sizeof(uint64_t), CRC::CRC_32(), instructionPointerDigest); 341 | } 342 | 343 | void Tracer::updateMemoryAccessDigest(uint64_t ip, 344 | TaggedMemoryAddress *taggedAddress, 345 | bool isWrite, bool isAligned) { 346 | uint8_t readWrite = isWrite ? 1 : 0; 347 | uint8_t aligned = isAligned ? 1 : 0; 348 | memoryAccessDigest = CRC::Calculate((char *)&ip, sizeof(uint64_t), 349 | CRC::CRC_32(), memoryAccessDigest); 350 | memoryAccessDigest = CRC::Calculate((char *)&readWrite, sizeof(uint8_t), 351 | CRC::CRC_32(), memoryAccessDigest); 352 | memoryAccessDigest = 353 | CRC::Calculate((char *)&taggedAddress->type, sizeof(uint32_t), 354 | CRC::CRC_32(), memoryAccessDigest); 355 | memoryAccessDigest = 356 | CRC::Calculate((char *)&taggedAddress->value, sizeof(uint64_t), 357 | CRC::CRC_32(), memoryAccessDigest); 358 | if (taggedAddress->type == MEM_TAG_ALLOC) { 359 | // Here we want to add the localized label of the allocation rather than the 360 | // global label to ensure that digests remain relative to each Function 361 | // Under Test invocation, so they can be compared with each other. 362 | memoryAccessDigest = CRC::Calculate((char *)taggedAddress->localLabel.c_str(), 363 | taggedAddress->localLabel.size(), 364 | CRC::CRC_32(), memoryAccessDigest); 365 | memoryAccessDigest = CRC::Calculate((char *)&aligned, sizeof(uint8_t), 366 | CRC::CRC_32(), memoryAccessDigest); 367 | } 368 | } 369 | 370 | uint32_t Tracer::summarize(const std::string &summaryFileName) { 371 | uint32_t verdict; 372 | std::ofstream summaryFile(summaryFileName.c_str()); 373 | summaryFile << "Iterations detected: " << exitCount << std::endl; 374 | 375 | if (exitCount >= 2) { 376 | bool pass = true; 377 | uint32_t i; 378 | 379 | std::vector::iterator ipIt = 380 | instructionPointerDigestHistory.begin(); 381 | uint32_t ipDigestRef = *ipIt; 382 | ++ipIt; 383 | for (i = 2; ipIt != instructionPointerDigestHistory.end(); ++ipIt, ++i) { 384 | uint32_t ipDigest = *ipIt; 385 | if (ipDigest != ipDigestRef) { 386 | pass = false; 387 | summaryFile << "Instruction pointer digest mismatch: (1: " 388 | << ipDigestRef << ") vs (" << i << ": " << ipDigest << ")" 389 | << std::endl; 390 | } 391 | } 392 | 393 | std::vector::iterator memIt = memoryAccessDigestHistory.begin(); 394 | uint32_t memDigestRef = *memIt; 395 | ++memIt; 396 | for (i = 2; memIt != memoryAccessDigestHistory.end(); ++memIt, ++i) { 397 | uint32_t memDigest = *memIt; 398 | if (memDigest != memDigestRef) { 399 | pass = false; 400 | summaryFile << "Memory access digest mismatch: (1: " << memDigestRef 401 | << ") vs (" << i << ": " << memDigest << ")" << std::endl; 402 | } 403 | } 404 | 405 | if (pass) { 406 | summaryFile << "PASS" << std::endl; 407 | verdict = VERDICT_PASS; 408 | } else { 409 | summaryFile << "FAIL" << std::endl; 410 | verdict = VERDICT_FAIL; 411 | } 412 | } else { 413 | summaryFile << "INCONCLUSIVE" << std::endl; 414 | verdict = VERDICT_INCONCLUSIVE; 415 | } 416 | 417 | summaryFile.close(); 418 | return verdict; 419 | } 420 | 421 | void Tracer::markLocalAllocationAsAligned(std::string &localLabel) { 422 | alignedLocalAllocations.insert(localLabel); 423 | } 424 | 425 | bool Tracer::isLocalAllocationAligned(std::string &localLabel) { 426 | std::set::iterator it = alignedLocalAllocations.find(localLabel); 427 | return (it != alignedLocalAllocations.end()); 428 | } 429 | 430 | bool Tracer::isLutAddr(uint64_t addr) { 431 | return ((addr >= ALIGN64(lutAddress)) && 432 | (addr < (ALIGN64(lutAddress) + lutSize))); 433 | } 434 | 435 | void Tracer::setTaintFlag(void) { taintFlag = true; } 436 | 437 | void Tracer::clearTaintFlag(void) { taintFlag = false; } 438 | 439 | void Tracer::openTraceMemFile(void) { 440 | if (traceMemFile.is_open()) { 441 | traceMemFile.close(); 442 | } 443 | 444 | std::stringstream traceStream; 445 | traceStream << traceMemFilePath << "/trace" << exitCount; 446 | traceMemFileName = traceStream.str(); 447 | std::cout << "Opening trace mem file: " << traceMemFileName << std::endl; 448 | traceMemFile.open(traceMemFileName.c_str()); 449 | traceMemFile << std::hex; 450 | } 451 | 452 | void Tracer::openTraceIPFile(void) { 453 | if (traceIPFile.is_open()) { 454 | traceIPFile.close(); 455 | } 456 | 457 | std::stringstream traceStream; 458 | traceStream << traceIPFilePath << "/trace" << exitCount; 459 | traceIPFileName = traceStream.str(); 460 | std::cout << "Opening trace IP file: " << traceIPFileName << std::endl; 461 | traceIPFile.open(traceIPFileName.c_str()); 462 | traceIPFile << std::hex; 463 | } 464 | 465 | void Tracer::openTaintFile(void) { 466 | if (taintFile.is_open()) { 467 | taintFile.close(); 468 | } 469 | 470 | std::stringstream taintStream; 471 | taintStream << taintFilePath << "/taint" << exitCount; 472 | taintFileName = taintStream.str(); 473 | taintFile.open(taintFileName.c_str()); 474 | taintFile << std::hex; 475 | } 476 | 477 | void Tracer::finalizeTraceMemFile(void) { 478 | traceMemFile.close(); 479 | 480 | if (exitCount < 2) { 481 | return; 482 | } 483 | 484 | uint32_t refMemDigest = memoryAccessDigestHistory.at(0); 485 | uint32_t lastMemDigest = memoryAccessDigestHistory.at(exitCount - 1); 486 | 487 | if (refMemDigest == lastMemDigest) { 488 | std::remove(traceMemFileName.c_str()); 489 | } 490 | } 491 | 492 | void Tracer::finalizeTraceIPFile(void) { 493 | traceIPFile.close(); 494 | 495 | if (exitCount < 2) { 496 | return; 497 | } 498 | 499 | uint32_t refIpDigest = instructionPointerDigestHistory.at(0); 500 | uint32_t lastIpDigest = instructionPointerDigestHistory.at(exitCount - 1); 501 | 502 | if (refIpDigest == lastIpDigest) { 503 | std::remove(traceIPFileName.c_str()); 504 | } 505 | } 506 | 507 | void Tracer::finalizeTaintFile(void) { 508 | taintFile.close(); 509 | 510 | if (exitCount < 2) { 511 | return; 512 | } 513 | 514 | uint32_t refIpDigest = instructionPointerDigestHistory.at(0); 515 | uint32_t lastIpDigest = instructionPointerDigestHistory.at(exitCount - 1); 516 | 517 | uint32_t refMemDigest = memoryAccessDigestHistory.at(0); 518 | uint32_t lastMemDigest = memoryAccessDigestHistory.at(exitCount - 1); 519 | 520 | if ((refIpDigest == lastIpDigest) && (refMemDigest == lastMemDigest)) { 521 | std::remove(taintFileName.c_str()); 522 | } 523 | } 524 | 525 | bool Tracer::isEnabled(void) { return enabled; } 526 | 527 | std::ofstream &Tracer::getTaintFile(void) { return taintFile; } 528 | -------------------------------------------------------------------------------- /src/CECTraceTool.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2019 Intel Corporation 2 | * 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | /*! @file 7 | * Base implementation of the CECTraceTool pintool. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "MyPinTool.h" 15 | #include "pin.H" 16 | #include "taint.h" 17 | #include "taint_semantics.h" 18 | #include "tracer.h" 19 | #include "visit_counter.h" 20 | 21 | #include "tat_instr.h" 22 | 23 | /* ================================================================== */ 24 | // Global variables 25 | /* ================================================================== */ 26 | 27 | Tracer* gTracer; 28 | Taint* gTaint; 29 | IpVisitCounter* gIpVisitCounter; 30 | std::unordered_map* gImageMap; 31 | bool* gFuncFound; 32 | uint32_t gMallocEntryCount; 33 | uint32_t gReallocEntryCount; 34 | 35 | #define ROUND_TO_CACHELINE(x) ((x)&0xFFFFFFFFFFFFFFC0L) 36 | #define ROUND_TO_QWORD(x) ((x)&0xFFFFFFFFFFFFFFF8L) 37 | 38 | /* ===================================================================== */ 39 | // Command line switches 40 | /* ===================================================================== */ 41 | KNOB KnobSummaryFile(KNOB_MODE_WRITEONCE, "pintool", "s", "summary.out", 42 | "specify file name for CECTraceTool summary"); 43 | 44 | KNOB KnobFuncName(KNOB_MODE_APPEND, "pintool", "f", "func", 45 | "specify function name for analysis"); 46 | 47 | KNOB KnobLUTArgIndex(KNOB_MODE_WRITEONCE, "pintool", "l", "0", 48 | "argument index of LUT address (experimental)"); 49 | 50 | KNOB KnobAlignList( 51 | KNOB_MODE_WRITEONCE, "pintool", "A", "", 52 | "csv list of local allocations to align to cacheline size"); 53 | 54 | KNOB KnobMarkLUT( 55 | KNOB_MODE_WRITEONCE, "pintool", "m", "no", 56 | "enable (yes) / disable(no - default) LUT marking in trace log (experimental)"); 57 | 58 | KNOB KnobLUTFuncIndex(KNOB_MODE_WRITEONCE, "pintool", "n", "0", 59 | "lut function argument index (experimental)"); 60 | 61 | /* ===================================================================== */ 62 | // Utilities 63 | /* ===================================================================== */ 64 | 65 | /*! 66 | * Print out help message. 67 | */ 68 | INT32 Usage() { 69 | std::cerr << "This tool generates execution trace and taint analysis info for " 70 | "finding non-constant time implementation" 71 | << std::endl; 72 | std::cerr << KNOB_BASE::StringKnobSummary() << std::endl; 73 | return -1; 74 | } 75 | 76 | /* ===================================================================== */ 77 | // Analysis routines 78 | /* ===================================================================== */ 79 | 80 | // Called when the program enters any of the target routines. 81 | void EnterTargetRoutine(ADDRINT rsp, ADDRINT argN, uint32_t targetIndex) { 82 | gTracer->enterTargetRoutine(targetIndex, argN, rsp); 83 | } 84 | 85 | // Called when the program exits any of the target routines. 86 | void ExitTargetRoutine(uint32_t targetIndex) { 87 | gTracer->exitTargetRoutine(targetIndex); 88 | } 89 | 90 | void RecordMemRead(ADDRINT ip, ADDRINT memAddr) { 91 | gTracer->updateMemoryRead(ip, memAddr); 92 | } 93 | 94 | void RecordMemWrite(ADDRINT ip, ADDRINT memAddr) { 95 | gTracer->updateMemoryWrite(ip, memAddr); 96 | } 97 | 98 | void RecordInstructionPointer(ADDRINT ip) { 99 | gTracer->updateInstructionPointer(ip); 100 | gIpVisitCounter->incrementCount(ip); 101 | } 102 | 103 | ADDRINT PIN_FAST_ANALYSIS_CALL InstructionCanBeTainted() { 104 | return gTaint->hasTaintedState(); 105 | } 106 | 107 | ADDRINT PIN_FAST_ANALYSIS_CALL FlagsAreTainted() { 108 | return gTaint->hasTaintedFlags(); 109 | } 110 | 111 | ADDRINT PIN_FAST_ANALYSIS_CALL MemoryIsTainted() { 112 | return gTaint->hasTaintedMemory(); 113 | } 114 | 115 | void PIN_FAST_ANALYSIS_CALL PremarshallMemoryOperand(tat_instr_t* instr, 116 | uint32_t memop_idx, 117 | ADDRINT memea) { 118 | instr->memops_memea[memop_idx] = memea; 119 | } 120 | 121 | void PIN_FAST_ANALYSIS_CALL AnalyzeInstructionForTaint(tat_instr_t* instr) { 122 | gTaint->analyzeAndPropagate(instr); 123 | } 124 | 125 | void MarkSecretMemory(ADDRINT addr, ADDRINT size) { 126 | std::cout << "MarkSecretMemory(" << std::hex << addr << std::dec << ", " << size 127 | << ")" << std::endl; 128 | for (ADDRINT i = 0; i < size; i++) { 129 | gTaint->markTaint(addr + i, true); 130 | } 131 | } 132 | 133 | void ClearSecretMemory(ADDRINT addr, ADDRINT size) { 134 | std::cout << "ClearSecretMemory(" << std::hex << addr << std::dec << ", " << size 135 | << ")" << std::endl; 136 | for (ADDRINT i = 0; i < size; i++) { 137 | gTaint->clearTaint(addr + i, true); 138 | } 139 | } 140 | 141 | void ClearAllSecretState(void) { 142 | std::cout << "ClearAllSecretState()" << std::endl; 143 | gTaint->reset(); 144 | } 145 | 146 | void InstrumentMemoryAccesses(INS ins, void* v) { 147 | uint32_t memOperands = INS_MemoryOperandCount(ins); 148 | 149 | // Iterate over each memory operand of the instruction. 150 | for (uint32_t memOp = 0; memOp < memOperands; memOp++) { 151 | if (INS_MemoryOperandIsRead(ins, memOp)) { 152 | INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead, 153 | IARG_CALL_ORDER, CALL_ORDER_LAST, IARG_INST_PTR, 154 | IARG_MEMORYOP_EA, memOp, IARG_END); 155 | } 156 | // Note that in some architectures a single memory operand can be 157 | // both read and written (for instance incl (%eax) on IA-32) 158 | // In that case we instrument it once for read and once for write. 159 | if (INS_MemoryOperandIsWritten(ins, memOp)) { 160 | INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite, 161 | IARG_CALL_ORDER, CALL_ORDER_LAST, IARG_INST_PTR, 162 | IARG_MEMORYOP_EA, memOp, IARG_END); 163 | } 164 | } 165 | 166 | INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordInstructionPointer, 167 | IARG_CALL_ORDER, CALL_ORDER_LAST, IARG_INST_PTR, IARG_END); 168 | } 169 | 170 | // Wrapper around malloc() that all calls to malloc() will go through. 171 | void* MallocWrapper(AFUNPTR originalFunc, size_t mallocSize, const CONTEXT* ctx, THREADID threadID) { 172 | VOID* mallocAddress; 173 | 174 | gMallocEntryCount++; 175 | PIN_CallApplicationFunction(ctx, threadID, CALLINGSTD_DEFAULT, originalFunc, NULL, PIN_PARG(void*), &mallocAddress, PIN_PARG(size_t), mallocSize, PIN_PARG_END()); 176 | gMallocEntryCount--; 177 | 178 | // Only track this allocation if this was a "top-level" malloc() call, i.e. it 179 | // wasn't called from within malloc() or realloc(). This is necessary because 180 | // under some conditions, there is at least one level of recursion during 181 | // malloc() through malloc_hook_ini(). realloc() can also call malloc(), and 182 | // we want to avoid double-tracking the internal malloc() in that case. 183 | if ((gMallocEntryCount == 0) && (gReallocEntryCount == 0)) { 184 | gTracer->addGlobalAllocation((uint64_t)mallocAddress, mallocSize); 185 | } 186 | 187 | return mallocAddress; 188 | } 189 | 190 | // Wrapper around realloc() that all calls to realloc will go through. 191 | void* ReallocWrapper(AFUNPTR originalFunc, void* reallocPtr, size_t reallocSize, const CONTEXT* ctx, THREADID threadID) { 192 | VOID* reallocAddress; 193 | 194 | gReallocEntryCount++; 195 | PIN_CallApplicationFunction(ctx, threadID, CALLINGSTD_DEFAULT, originalFunc, NULL, PIN_PARG(void*), &reallocAddress, PIN_PARG(void*), reallocPtr, PIN_PARG(size_t), reallocSize, PIN_PARG_END()); 196 | gReallocEntryCount--; 197 | 198 | // Only track this allocation if this was a "top-level" realloc() call, i.e. 199 | // it wasn't called from within malloc() or realloc(). This is necessary 200 | // because under some conditions, there is at least one level of recursion 201 | // that realloc() can go through. 202 | if ((gMallocEntryCount == 0) && (gReallocEntryCount == 0)) { 203 | gTracer->removeGlobalAllocation((uint64_t)reallocPtr); 204 | gTracer->addGlobalAllocation((uint64_t)reallocPtr, reallocSize); 205 | } 206 | 207 | return reallocAddress; 208 | } 209 | 210 | // Wrapper around free() that all calls to free() will go through. 211 | void FreeWrapper(AFUNPTR originalFunc, void* ptrToFree, const CONTEXT* ctx, THREADID threadID) { 212 | PIN_CallApplicationFunction(ctx, threadID, CALLINGSTD_DEFAULT, originalFunc, NULL, PIN_PARG(void), PIN_PARG(void*), ptrToFree, PIN_PARG_END()); 213 | gTracer->removeGlobalAllocation((uint64_t)ptrToFree); 214 | } 215 | 216 | /* ===================================================================== */ 217 | // Instrumentation callbacks 218 | /* ===================================================================== */ 219 | 220 | void Image(IMG img, void* v) { 221 | RTN rtn; 222 | std::string imgName = IMG_Name(img); 223 | 224 | gImageMap->insert(std::make_pair(imgName, (uint64_t)IMG_LoadOffset(img))); 225 | 226 | // Add all sections in the image to the allocation map. 227 | for (SEC sec = IMG_SecHead(img); SEC_Valid(sec); sec = SEC_Next(sec)) { 228 | ADDRINT addr = SEC_Address(sec); 229 | if (addr != 0) { 230 | std::string name = imgName + SEC_Name(sec); 231 | gTracer->addSection(addr, SEC_Size(sec), name); 232 | } 233 | } 234 | 235 | ADDRINT lutArgIndex = (ADDRINT)atoi(KnobLUTArgIndex.Value().c_str()); 236 | 237 | // Instrument all target routines. 238 | for (unsigned int i = 0; i < KnobFuncName.NumberOfValues(); i++) { 239 | std::string funcName = KnobFuncName.Value(i); 240 | 241 | rtn = RTN_FindByName(img, funcName.c_str()); 242 | if (RTN_Valid(rtn)) { 243 | RTN_Open(rtn); 244 | gFuncFound[i] = true; 245 | 246 | std::cout << "Instrumenting " << funcName << " in " << imgName << "(" 247 | << IMG_Id(img) << ") @ " << std::hex << RTN_Address(rtn) 248 | << std::dec << std::endl; 249 | 250 | RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)EnterTargetRoutine, 251 | IARG_REG_VALUE, REG_RSP, IARG_FUNCARG_ENTRYPOINT_VALUE, 252 | lutArgIndex, 253 | IARG_UINT32, i, IARG_END); 254 | 255 | RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)ExitTargetRoutine, IARG_UINT32, 256 | i, IARG_END); 257 | 258 | RTN_Close(rtn); 259 | } 260 | } 261 | 262 | // Replace the malloc() function with a wrapper that just calls malloc(), 263 | // tracks the allocation, and returns the result. We do this instead of just 264 | // instrumenting malloc() because some malloc() implementations return through 265 | // an eliminated tail call, which causes PIN to miss the function exit and 266 | // therefore the exit callback never gets fired and the allocation never gets 267 | // tracked. If we instead patch and wrap the call to malloc(), we know that we 268 | // aren't missing any function exits. 269 | rtn = RTN_FindByName(img, "malloc"); 270 | if (RTN_Valid(rtn)) { 271 | std::cout << "Replacing malloc() with wrapper in " << imgName << "(" 272 | << IMG_Id(img) << ")" << std::endl; 273 | 274 | PROTO protoMalloc = PROTO_Allocate(PIN_PARG(void*), CALLINGSTD_DEFAULT, 275 | "malloc", PIN_PARG(size_t), PIN_PARG_END()); 276 | RTN_ReplaceSignature(rtn, AFUNPTR(MallocWrapper), 277 | IARG_PROTOTYPE, protoMalloc, 278 | IARG_ORIG_FUNCPTR, 279 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 280 | IARG_CONST_CONTEXT, 281 | IARG_THREAD_ID, 282 | IARG_END); 283 | PROTO_Free(protoMalloc); 284 | } 285 | 286 | // Replace the realloc() function with a wrapped version that calls realloc(), 287 | // tracks the allocation, and returns the result. 288 | rtn = RTN_FindByName(img, "realloc"); 289 | if (RTN_Valid(rtn)) { 290 | std::cout << "Replacing realloc() with wrapper in " << imgName << "(" 291 | << IMG_Id(img) << ")" << std::endl; 292 | 293 | PROTO protoRealloc = PROTO_Allocate(PIN_PARG(void*), CALLINGSTD_DEFAULT, 294 | "realloc", PIN_PARG(void*), PIN_PARG(size_t), PIN_PARG_END()); 295 | RTN_ReplaceSignature(rtn, AFUNPTR(ReallocWrapper), 296 | IARG_PROTOTYPE, protoRealloc, 297 | IARG_ORIG_FUNCPTR, 298 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 299 | IARG_FUNCARG_ENTRYPOINT_VALUE, 1, 300 | IARG_CONST_CONTEXT, 301 | IARG_THREAD_ID, 302 | IARG_END); 303 | PROTO_Free(protoRealloc); 304 | } 305 | 306 | // Replace the free() function with a wrapped version that calls free() and 307 | // stops tracking the allocation. 308 | rtn = RTN_FindByName(img, "free"); 309 | if (RTN_Valid(rtn)) { 310 | std::cout << "Replacing free() with wrapper in " << imgName << "(" 311 | << IMG_Id(img) << ")" << std::endl; 312 | 313 | PROTO protoFree = PROTO_Allocate(PIN_PARG(void), CALLINGSTD_DEFAULT, 314 | "free", PIN_PARG(void*), PIN_PARG_END()); 315 | RTN_ReplaceSignature(rtn, AFUNPTR(FreeWrapper), 316 | IARG_PROTOTYPE, protoFree, 317 | IARG_ORIG_FUNCPTR, 318 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 319 | IARG_CONST_CONTEXT, 320 | IARG_THREAD_ID, 321 | IARG_END); 322 | PROTO_Free(protoFree); 323 | } 324 | 325 | rtn = RTN_FindByName(img, "PinBasedCEC_MarkSecret"); 326 | if (RTN_Valid(rtn)) { 327 | std::cout << "Instrumenting PinBasedCEC_MarkSecret() in " << imgName << "(" 328 | << IMG_Id(img) << ")" << std::endl; 329 | 330 | RTN_Open(rtn); 331 | RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)MarkSecretMemory, 332 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 333 | IARG_FUNCARG_ENTRYPOINT_VALUE, 1, IARG_END); 334 | RTN_Close(rtn); 335 | } 336 | 337 | rtn = RTN_FindByName(img, "PinBasedCEC_ClearSecret"); 338 | if (RTN_Valid(rtn)) { 339 | std::cout << "Instrumenting PinBasedCEC_ClearSecret() in " << imgName << "(" 340 | << IMG_Id(img) << ")" << std::endl; 341 | 342 | RTN_Open(rtn); 343 | RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)ClearSecretMemory, 344 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 345 | IARG_FUNCARG_ENTRYPOINT_VALUE, 1, IARG_END); 346 | RTN_Close(rtn); 347 | } 348 | 349 | rtn = RTN_FindByName(img, "PinBasedCEC_ClearSecrets"); 350 | if (RTN_Valid(rtn)) { 351 | std::cout << "Instrumenting PinBasedCEC_ClearSecrets() in " << imgName << "(" 352 | << IMG_Id(img) << ")" << std::endl; 353 | 354 | RTN_Open(rtn); 355 | RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)ClearAllSecretState, 356 | IARG_FUNCARG_ENTRYPOINT_VALUE, 0, 357 | IARG_FUNCARG_ENTRYPOINT_VALUE, 1, IARG_END); 358 | RTN_Close(rtn); 359 | } 360 | } 361 | 362 | void saveImageMap(const std::string& filename) { 363 | std::ofstream outFile(filename.c_str(), std::ios::out); 364 | 365 | outFile << "{" << std::endl; 366 | 367 | for (std::unordered_map::iterator it = 368 | gImageMap->begin(); 369 | it != gImageMap->end(); ++it) { 370 | if (it != gImageMap->begin()) { 371 | outFile << "," << std::endl; 372 | } 373 | outFile << "\t\"" << it->first << "\": \"" << std::hex << it->second 374 | << std::dec << "\""; 375 | } 376 | 377 | outFile << std::endl; 378 | outFile << "}" << std::endl; 379 | 380 | outFile.close(); 381 | } 382 | 383 | // Cleanup file handles and determine overall pass/fail 384 | void Fini(INT32 code, void* v) { 385 | std::string summaryFileName = KnobSummaryFile.Value(); 386 | gTracer->summarize(summaryFileName); 387 | 388 | saveImageMap("image_map"); 389 | 390 | delete gTaint; 391 | delete gTracer; 392 | delete gIpVisitCounter; 393 | delete gImageMap; 394 | 395 | std::cout << "Done." << std::endl; 396 | } 397 | 398 | // Parse comma-seperated list of labels to treat as 64-bit aligned. 399 | void parseAlignmentKnob() { 400 | std::string s = KnobAlignList.Value(); 401 | size_t i = 0; 402 | while (i != std::string::npos) { 403 | size_t j = s.find(",", i); 404 | size_t count = (j == std::string::npos) ? std::string::npos : j - i; 405 | std::string ss = s.substr(i, count); 406 | if (ss.size() > 0) { 407 | gTracer->markLocalAllocationAsAligned(ss); 408 | } 409 | i = (j == std::string::npos) ? j : j + 1; 410 | } 411 | } 412 | 413 | void makeLogDirectories(void) { 414 | OS_RETURN_CODE retval; 415 | OS_FILE_ATTRIBUTES attr; 416 | 417 | retval = OS_GetFileAttributes("memtrace/", &attr); 418 | if ((retval.generic_err != OS_RETURN_CODE_NO_ERROR) || 419 | ((attr & OS_FILE_ATTRIBUTES_EXIST) == 0)) { 420 | OS_MkDir("memtrace/", OS_FILE_PERMISSION_TYPE_ALL_USER); 421 | } 422 | 423 | retval = OS_GetFileAttributes("iptrace/", &attr); 424 | if ((retval.generic_err != OS_RETURN_CODE_NO_ERROR) || 425 | ((attr & OS_FILE_ATTRIBUTES_EXIST) == 0)) { 426 | OS_MkDir("iptrace/", OS_FILE_PERMISSION_TYPE_ALL_USER); 427 | } 428 | 429 | retval = OS_GetFileAttributes("taint/", &attr); 430 | if ((retval.generic_err != OS_RETURN_CODE_NO_ERROR) || 431 | ((attr & OS_FILE_ATTRIBUTES_EXIST) == 0)) { 432 | OS_MkDir("taint/", OS_FILE_PERMISSION_TYPE_ALL_USER); 433 | } 434 | } 435 | 436 | /*! 437 | * The main procedure of the tool. 438 | * This function is called when the application image is loaded but not yet 439 | * started. 440 | * @param[in] argc total number of elements in the argv array 441 | * @param[in] argv array of command line arguments, 442 | * including pin -t -- ... 443 | */ 444 | int main(int argc, char* argv[]) { 445 | // Initialize PIN library. Print help message if -h(elp) is specified 446 | // in the command line or the command line is invalid 447 | // Initialize pin & symbol manager 448 | PIN_InitSymbols(); 449 | if (PIN_Init(argc, argv)) { 450 | return Usage(); 451 | } 452 | 453 | makeLogDirectories(); 454 | 455 | uint32_t numTargetRoutines = KnobFuncName.NumberOfValues(); 456 | bool markLut = (KnobMarkLUT.Value() == "yes"); 457 | uint32_t lutTargetRoutineIndex = atoi(KnobLUTFuncIndex.Value().c_str()); 458 | uint32_t lutSize = 0; 459 | 460 | gIpVisitCounter = new IpVisitCounter(); 461 | 462 | gTracer = 463 | new Tracer("memtrace", "iptrace", "taint", numTargetRoutines, markLut, 464 | lutTargetRoutineIndex, lutSize, gIpVisitCounter); 465 | 466 | parseAlignmentKnob(); 467 | 468 | gTaint = new Taint(gTracer, gIpVisitCounter, false); 469 | 470 | gFuncFound = new bool[numTargetRoutines]; 471 | for (uint32_t i = 0; i < numTargetRoutines; i++) { 472 | gFuncFound[i] = false; 473 | } 474 | 475 | gImageMap = new std::unordered_map(); 476 | 477 | gMallocEntryCount = 0; 478 | gReallocEntryCount = 0; 479 | 480 | // Register Image to be called to instrument functions. 481 | IMG_AddInstrumentFunction(Image, 0); 482 | INS_AddInstrumentFunction(TaintInstruction, 0); 483 | INS_AddInstrumentFunction(InstrumentMemoryAccesses, 0); 484 | PIN_AddFiniFunction(Fini, 0); 485 | 486 | // Start the program, never returns 487 | PIN_StartProgram(); 488 | 489 | return 0; 490 | } 491 | 492 | /* ===================================================================== */ 493 | /* eof */ 494 | /* ===================================================================== */ 495 | --------------------------------------------------------------------------------