Skip to content

Commit

Permalink
Merge pull request #81 from trailofbits/polytracker-python
Browse files Browse the repository at this point in the history
PolyTracker Python and Installation Refactor
  • Loading branch information
ESultanik authored Jul 23, 2020
2 parents bc3382e + 91b6a6e commit f9fc28e
Show file tree
Hide file tree
Showing 25 changed files with 501 additions and 73 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/dockerimage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ jobs:
--exclude 'polytracker/src/dfsan_rt/interception/*'
- name: Python lint/typecheck
run: |
black --check polybuild polyprocess tests --line-length=127
mypy --ignore-missing-imports polybuild polyprocess tests
- name: Build the base image
black --check polytracker tests --exclude '/(polytracker/src|polytracker/scripts)/' --line-length=127
mypy --ignore-missing-imports polytracker tests
- name: Build the base image
run: docker build . --file Dockerfile --tag trailofbits/polytracker --no-cache
- name: Poly* tests
run: |
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/pythonpublish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

name: Upload Python Package

on:
release:
types: [published]

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
19 changes: 8 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@ project(TAPP)
# If there is no explicit -DCMAKE_INSTALL_PREFIX=DIR setting given,
# then install underneath the build directory
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set (CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/bin CACHE PATH "default install path" FORCE)
set (CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR} CACHE PATH "default install path" FORCE)
endif()

set(POLYTRACK_BIN_DIR "${CMAKE_INSTALL_PREFIX}/polytracker")
set(POLYTRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/lib")
set(POLYTRACK_TRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/track")
set(POLYTRACK_RULE_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/abi_lists")
set(POLYTRACK_TESTS_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/tests")
set(POLYTRACK_PASS_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/pass")
set(POLYTRACK_CXX_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/")
set(POLYTRACK_BIN_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/bin")
set(POLYTRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/lib")
set(POLYTRACK_TRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/track")
set(POLYTRACK_RULE_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/abi_lists")
set(POLYTRACK_TESTS_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/tests")
set(POLYTRACK_PASS_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/pass")
set(POLYTRACK_CXX_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker")

add_subdirectory(polytracker)
install(DIRECTORY "./polybuild" DESTINATION ${POLYTRACK_CXX_DIR})


18 changes: 7 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get -y update \
libgraphviz-dev \
graphviz

RUN python3.7 -m pip install pip
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 10
RUN python3 -m pip install pip

RUN go get github.com/SRI-CSL/gllvm/cmd/...

Expand All @@ -36,12 +37,7 @@ COPY . /polytracker

WORKDIR /polytracker

RUN python3.7 -m pip install pytest

RUN python3.7 -m pip install .

RUN rm /usr/bin/python3
RUN cp /usr/bin/python3.7 /usr/bin/python3
RUN pip3 install pytest .

RUN rm -rf build && mkdir -p build

Expand All @@ -50,13 +46,13 @@ WORKDIR /polytracker/build
ENV PATH="/usr/lib/llvm-7/bin:${PATH}"

RUN cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_VERBOSE_MAKEFILE=TRUE .. && ninja install
ENV CC=/polytracker/build/bin/polytracker/polybuild/polybuild.py
ENV CXX=/polytracker/build/bin/polytracker/polybuild/polybuild++.py
ENV PATH="/polytracker/build/bin/:${PATH}"
ENV CC=polybuild
ENV CXX=polybuild++
ENV LLVM_COMPILER=clang
RUN chmod +x ${CC}
RUN mkdir -p "/build_artifacts"

# Set the BC store path to the <install_path>/cxx_libs/bitcode/bitcode_store}
ENV WLLVM_BC_STORE="/polytracker/build/bin/polytracker/cxx_libs/bitcode/bitcode_store"
ENV WLLVM_BC_STORE="/polytracker/build/share/polytracker/cxx_libs/bitcode/bitcode_store"
ENV WLLVM_ARTIFACT_STORE="/build_artifacts"
WORKDIR /polytracker
4 changes: 2 additions & 2 deletions format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ clang-format -i polytracker/include/polyclang/*.h
clang-format -i polytracker/include/dfsan/*.h

# Black to auto format code, mypy for type checking
black polybuild polyprocess tests --line-length=127
mypy --ignore-missing-imports polybuild polyprocess tests
black polytracker tests --exclude '/(polytracker/src|polytracker/scripts)/' --line-length=127
mypy --ignore-missing-imports polytracker tests
1 change: 0 additions & 1 deletion polybuild/polybuild++.py

This file was deleted.

1 change: 0 additions & 1 deletion polyprocess/__init__.py

This file was deleted.

26 changes: 19 additions & 7 deletions polytracker/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ project(polytracker LANGUAGES C CXX ASM)

include_directories(include)

set(POLYTRACK_BIN_DIR "${CMAKE_INSTALL_PREFIX}/polytracker")
set(POLYTRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/lib")
set(POLYTRACK_TRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/track")
set(POLYTRACK_RULE_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/abi_lists")
set(POLYTRACK_TESTS_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/tests")
set(POLYTRACK_PASS_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/pass")
set(POLYTRACK_CXX_DIR "${CMAKE_INSTALL_PREFIX}/polytracker/")
set(POLYTRACK_BIN_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/bin")
set(POLYTRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/lib")
set(POLYTRACK_TRACK_LIB_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/track")
set(POLYTRACK_RULE_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/abi_lists")
set(POLYTRACK_TESTS_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/tests")
set(POLYTRACK_PASS_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/pass")
set(POLYTRACK_CXX_DIR "${CMAKE_INSTALL_PREFIX}/share/polytracker/")

add_subdirectory(src)
#add_subdirectory(custom_abi)
install(DIRECTORY "./cxx_libs" DESTINATION ${POLYTRACK_CXX_DIR})
install(DIRECTORY "./abi_lists" DESTINATION ${POLYTRACK_CXX_DIR})

install(PROGRAMS "scripts/polybuild.py" DESTINATION ${POLYTRACK_BIN_DIR} RENAME "polybuild")
install(PROGRAMS "scripts/polybuild.py" DESTINATION ${POLYTRACK_BIN_DIR} RENAME "polybuild++")

macro(install_symlink filepath sympath)
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} ${sympath})")
install(CODE "message(\"-- Created symlink: ${sympath} -> ${filepath}\")")
endmacro(install_symlink)

install(DIRECTORY DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
install_symlink(../share/polytracker/bin/polybuild ${CMAKE_INSTALL_PREFIX}/bin/polybuild)
install_symlink(../share/polytracker/bin/polybuild ${CMAKE_INSTALL_PREFIX}/bin/polybuild++)
1 change: 1 addition & 0 deletions polytracker/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .polytracker import *
177 changes: 177 additions & 0 deletions polytracker/cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import math

from typing import (
Callable,
Collection,
Dict,
FrozenSet,
Generic,
ItemsView,
Iterable,
KeysView,
List,
Optional,
Set,
TypeVar,
Union,
)

import graphviz
import networkx as nx

N = TypeVar("N")


class DiGraph(nx.DiGraph, Generic[N]):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._dominator_forest: Optional[DiGraph[N]] = None
self._roots: Optional[Collection[N]] = None
self._path_lengths: Optional[Dict[N, Dict[N, int]]] = None

def path_length(self, from_node: N, to_node: N) -> Union[int, float]:
if self._path_lengths is None:
self._path_lengths = dict(nx.all_pairs_shortest_path_length(self, cutoff=None))
if from_node not in self._path_lengths or to_node not in self._path_lengths[from_node]:
return math.inf
else:
return self._path_lengths[from_node][to_node]

def set_roots(self, roots: Collection[N]):
self._roots = roots

def _find_roots(self) -> Iterable[N]:
return (n for n, d in self.in_degree() if d == 0)

@property
def roots(self) -> Collection[N]:
if self._roots is None:
self._roots = tuple(self._find_roots())
return self._roots

def depth(self, node: N) -> Union[int, float]:
return min(self.path_length(root, node) for root in self.roots)

def ancestors(self, node: N) -> Set[N]:
return nx.ancestors(self, node)

def descendants(self, node: N) -> FrozenSet[N]:
return frozenset(nx.dfs_successors(self, node).keys())

@property
def dominator_forest(self) -> "DAG[N]":
if self._dominator_forest is not None:
return self._dominator_forest
self._dominator_forest = DAG()
for root in self.roots:
for node, dominated_by in nx.immediate_dominators(self, root).items():
if node != dominated_by:
self._dominator_forest.add_edge(dominated_by, node)
return self._dominator_forest

def to_dot(
self, comment: Optional[str] = None, labeler: Optional[Callable[[N], str]] = None, node_filter=None
) -> graphviz.Digraph:
if comment is not None:
dot = graphviz.Digraph(comment=comment)
else:
dot = graphviz.Digraph()
if labeler is None:
labeler = str
node_ids = {node: i for i, node in enumerate(self.nodes)}
for node in self.nodes:
if node_filter is None or node_filter(node):
dot.node(f"func{node_ids[node]}", label=labeler(node))
for caller, callee in self.edges:
if node_filter is None or (node_filter(caller) and node_filter(callee)):
dot.edge(f"func{node_ids[caller]}", f"func{node_ids[callee]}")
return dot


class DAG(DiGraph[N], Generic[N]):
def vertex_induced_subgraph(self, vertices: Iterable[N]) -> "DAG[N]":
vertices = frozenset(vertices)
subgraph = self.copy()
to_remove = set(self.nodes) - vertices
for v in vertices:
node = v
parent = None
while True:
parents = tuple(subgraph.predecessors(node))
if not parents:
if parent is not None:
subgraph.remove_edge(parent, v)
subgraph.add_edge(node, v)
break
assert len(parents) == 1
ancestor = parents[0]
if parent is None:
parent = ancestor
if ancestor in vertices:
to_remove.add(v)
break
node = ancestor
subgraph.remove_nodes_from(to_remove)
return subgraph


class FunctionInfo:
def __init__(
self,
name: str,
cmp_bytes: Dict[str, List[int]],
input_bytes: Dict[str, List[int]] = None,
called_from: Iterable[str] = (),
):
self.name: str = name
self.called_from: FrozenSet[str] = frozenset(called_from)
self.cmp_bytes: Dict[str, List[int]] = cmp_bytes
if input_bytes is None:
self.input_bytes: Dict[str, List[int]] = cmp_bytes
else:
self.input_bytes = input_bytes

@property
def taint_sources(self) -> KeysView[str]:
return self.input_bytes.keys()

def __getitem__(self, input_source_name: str) -> List[int]:
return self.input_bytes[input_source_name]

def __iter__(self) -> Iterable[str]:
return self.taint_sources

def items(self) -> ItemsView[str, List[int]]:
return self.input_bytes.items()

def __hash__(self):
return hash(self.name)

def __str__(self):
return self.name

def __repr__(self):
return f"{self.__class__.__name__}(name={self.name!r}, cmp_bytes={self.cmp_bytes!r}, input_bytes={self.input_bytes!r}, called_from={self.called_from!r})"


class CFG(DiGraph[FunctionInfo]):
def __init__(self):
super().__init__()

def to_dot(
self,
comment: Optional[str] = "PolyTracker Program Trace",
labeler: Optional[Callable[[FunctionInfo], str]] = None,
node_filter=None,
) -> graphviz.Digraph:
function_labels: Dict[str, str] = {}

def func_labeler(f):
if labeler is not None:
return labeler(f)
elif f.name in function_labels:
return f"{f.name} ({function_labels[f.name]})"
else:
return f.name

return super().to_dot(comment, labeler=func_labeler, node_filter=node_filter)
2 changes: 1 addition & 1 deletion polytracker/include/dfsan/dfsan_log_mgmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "dfsan/dfsan.h"
#include "json.hpp"
#include "polyclang/polytracker.h"
#include "polytracker/polytracker.h"
#include <iostream>
#include <list>
#include <map>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#ifndef __POLYTRACKER_H__
#define __POLYTRACKER_H__

// NOTE: Whenever the version is updated, make sure to add support to the JSON parsing code in polytracker.py!

#define POLYTRACKER_VERSION_MAJOR 2
#define POLYTRACKER_VERSION_MINOR 0
#define POLYTRACKER_VERSION_MINOR 1
#define POLYTRACKER_VERSION_REVISION 0

// Set the version note to an empty string if there is no note
#define POLYTRACKER_SUFFIX "alpha2.2"
// If there is a suffix, it should always start with a hypen, like "-alpha2.2".
// If there is no suffix, set POLYTRACKER_VERSION_SUFFIX to an empty string.
#define POLYTRACKER_VERSION_SUFFIX ""

/**********************************************************************************/

Expand All @@ -16,6 +19,6 @@
#define POLYTRACKER_VERSION \
PF_MAKE_STR(POLYTRACKER_VERSION_MAJOR) \
"." PF_MAKE_STR(POLYTRACKER_VERSION_MINOR) "." PF_MAKE_STR( \
POLYTRACKER_VERSION_REVISION)
POLYTRACKER_VERSION_REVISION) POLYTRACKER_VERSION_SUFFIX

#endif
1 change: 1 addition & 0 deletions polytracker/polyprocess/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .polyprocess import *
File renamed without changes.
1 change: 1 addition & 0 deletions polytracker/polyprocess/mimid
File renamed without changes.
Loading

0 comments on commit f9fc28e

Please sign in to comment.