Remove data
Этот коммит содержится в:
родитель
0c9e8df91c
Коммит
9dd1b44a14
@ -1 +0,0 @@
|
||||
*~
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
This repo contains some materials related to a talk
|
||||
on security r&d projects that in some way use LLVM
|
||||
for doing their work.
|
||||
|
||||
|
||||
- projects.md is a list of such projects
|
||||
- code is a set of very basic, example code to help those interested in getting up to speed with some basics of LLVM. Don't hate.
|
||||
- slides is the slide deck
|
||||
|
||||
#### Again on the code
|
||||
I must repeat myself: the provided code is only meant to be basic helpers for learning about LLVM
|
||||
and is not meant to be some new awesome tool (sadly, no). There is a great deal of research in
|
||||
dynamic and program analysis that is being done and the goal of this code is to make it so you
|
||||
can start to more easily read some of the code from those projects ... and then make sense of it.
|
||||
This is to avoid getting lost in code versus meaning.
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
Very basic example codes.
|
||||
|
||||
- bbskel: BasicBlock pass skeleton
|
||||
- cgspskel: CallGraphSCC pass skeleton
|
||||
- comminute: tool illustrating pass mgr, pass deps, and more
|
||||
- fpskel: FunctionPass skeleton
|
||||
- intflip: integer argument randomizer|bit-flipper
|
||||
- mpskel: ModulePass skeleton
|
||||
- npassert: NULL pointer check insertion
|
||||
- rpskel: RegionPass skeleton
|
||||
- visitorskel: Using a InstVisitor class
|
||||
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
LLVM_VER=3.9
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR)
|
||||
|
||||
|
||||
PASS=BBSkel.so
|
||||
PASS_OBJECTS=BBSkel.o
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built test/*.bc
|
||||
|
||||
|
||||
tests:
|
||||
$(CC) -emit-llvm -o test/foo.bc -c test/foo.c
|
||||
|
||||
runtests:
|
||||
$(OPT) -load built/BBSkel.so -mem2reg -bbskel < test/foo.bc
|
||||
@ -1,20 +0,0 @@
|
||||
|
||||
# BBSkel
|
||||
|
||||
This is a basic block pass skeleton. Note, there are also
|
||||
loop passes, region passes, etc.
|
||||
|
||||
|
||||
# Build & Run
|
||||
|
||||
First check the Makefile to set path to llvm-config and version.
|
||||
3.8, 3.9 should be fine, so should 4.0
|
||||
|
||||
```
|
||||
$ make
|
||||
$ opt-X.Y -load built/BBSkel.so -bbskel < file.bc
|
||||
...
|
||||
$
|
||||
```
|
||||
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "BBSkel.h"
|
||||
|
||||
void
|
||||
BBSkel::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
// No changes to CFG, so tell the pass manager
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
bool
|
||||
BBSkel::doFinalization(Function &F)
|
||||
{
|
||||
errs() << "#BBs: " << bbcount << "\n";
|
||||
errs() << "#Is: " << icount << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BBSkel::runOnBasicBlock(BasicBlock &B)
|
||||
{
|
||||
bbcount++;
|
||||
errs() << " Basic Block found:\n";
|
||||
for (auto &I : B) { // Iterate through instructions in the block
|
||||
++icount;
|
||||
// Note in output if instruction is a call/invoke
|
||||
if (isa<CallInst>(I) || isa<InvokeInst>(I)) {
|
||||
errs() << " C ";
|
||||
} else {
|
||||
errs() << " ";
|
||||
}
|
||||
I.dump();
|
||||
errs() << " used by:\n";
|
||||
// Go through and dump the uses for each instruction.
|
||||
for (auto ui = I.user_begin(); ui != I.user_end(); ++ui) {
|
||||
errs() << " U: ";
|
||||
ui->dump();
|
||||
}
|
||||
errs() << " ~~~~ \n";
|
||||
}
|
||||
errs() << " --- end of basic block ---\n";
|
||||
|
||||
// return true if CFG has changed.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Register this pass to be made usable.
|
||||
* Needs the static ID initialized and the pass declaration given.
|
||||
*/
|
||||
char BBSkel::ID = 0;
|
||||
static RegisterPass<BBSkel> XX("bbskel", "BasicBlock Pass Skeleton");
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
#ifndef __BBSKEL_H
|
||||
#define __BBSKEL_H
|
||||
|
||||
struct BBSkel : public BasicBlockPass {
|
||||
/*
|
||||
* For all of your passes you will need this and to define it.
|
||||
* It's address is used by pass system, so the value does not matter.
|
||||
*/
|
||||
static char ID;
|
||||
|
||||
unsigned bbcount;
|
||||
unsigned icount;
|
||||
|
||||
BBSkel() : BasicBlockPass(ID) {
|
||||
bbcount = 0;
|
||||
icount = 0;
|
||||
}
|
||||
|
||||
// Called on each BasicBlock in given compilation unit
|
||||
virtual bool runOnBasicBlock(BasicBlock &);
|
||||
|
||||
/*
|
||||
* Used to help order passes by pass manager.
|
||||
* Declare any passes you need run prior here.. as well as
|
||||
* any information such as preserving CFG or similar.
|
||||
*/
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
/*
|
||||
* Called after each exec of a runOnBasicBlock.
|
||||
*/
|
||||
virtual bool doFinalization(Function &);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,28 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
void
|
||||
leaks_passwd()
|
||||
{
|
||||
char *p;
|
||||
struct addrinfo hints, *result;
|
||||
|
||||
p = getpass("enter passwd: ");
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
(void)getaddrinfo(p, "http", &hints, &result);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_passwd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
LLVM_VER=3.9
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR)
|
||||
|
||||
PASS=CGSSkel.so
|
||||
PASS_OBJECTS=CGSSkel.o
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built test/*.bc
|
||||
|
||||
|
||||
tests:
|
||||
$(CC) -emit-llvm -o test/foo.bc -c test/foo.c
|
||||
|
||||
runtests:
|
||||
$(OPT) -load built/CGSSkel.so -cgsskel < test/foo.bc
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
# CallGraphSCCPass Skeleton
|
||||
|
||||
Bottom up style to assist to use + augment for CG building
|
||||
|
||||
# Build & Run
|
||||
|
||||
First check the Makefile to set path to llvm-config and version.
|
||||
3.8, 3.9 should be fine, so should 4.0
|
||||
|
||||
```
|
||||
$ make
|
||||
$ opt-X.Y -load built/CGSSkel.so -cgsskel < file.bc
|
||||
...
|
||||
$
|
||||
```
|
||||
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Analysis/CallGraphSCCPass.h"
|
||||
#include "llvm/Analysis/CallGraph.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "CGSSkel.h"
|
||||
|
||||
void
|
||||
CGSSkel::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
// No changes to CFG, so tell the pass manager
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
bool
|
||||
CGSSkel::doFinalization(CallGraph &G)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CGSSkel::doInitialization(CallGraph &G)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CGSSkel::runOnSCC(CallGraphSCC &GSCC)
|
||||
{
|
||||
errs() << " Strongly connected component found:\n";
|
||||
|
||||
/*
|
||||
* Singular SCC's can be used to detect recursion. See:
|
||||
* http://llvm.org/docs/doxygen/html/FunctionAttrs_8.cpp_source.html
|
||||
*/
|
||||
if (GSCC.isSingular()) {
|
||||
errs() << " SCC is singular\n";
|
||||
}
|
||||
for (auto &G : GSCC) {
|
||||
errs() << " Dump:\n";
|
||||
G->dump();
|
||||
}
|
||||
errs() << " --- end of SCC ---\n";
|
||||
|
||||
// return true if Module has been changed.
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register this pass to be made usable.
|
||||
* Needs the static ID initialized and the pass declaration given.
|
||||
*/
|
||||
char CGSSkel::ID = 0;
|
||||
static RegisterPass<CGSSkel> XX("cgsskel", "CallGraphSCC Pass Skeleton");
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
#ifndef __CGSSKEL_H
|
||||
#define __CGSSKEL_H
|
||||
|
||||
struct CGSSkel : public CallGraphSCCPass {
|
||||
/*
|
||||
* For all of your passes you will need this and to define it.
|
||||
* It's address is used by pass system, so the value does not matter.
|
||||
*/
|
||||
static char ID;
|
||||
|
||||
CGSSkel() : CallGraphSCCPass(ID) {
|
||||
}
|
||||
|
||||
// Return true if Module was modified, otherwise false.
|
||||
virtual bool runOnSCC(CallGraphSCC &);
|
||||
|
||||
/*
|
||||
* Used to help order passes by pass manager.
|
||||
* Declare any passes you need run prior here.. as well as
|
||||
* any information such as preserving CFG or similar.
|
||||
*/
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
virtual bool doInitialization(CallGraph &CG);
|
||||
virtual bool doFinalization(CallGraph &);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,28 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
void
|
||||
leaks_passwd()
|
||||
{
|
||||
char *p;
|
||||
struct addrinfo hints, *result;
|
||||
|
||||
p = getpass("enter passwd: ");
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
(void)getaddrinfo(p, "http", &hints, &result);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_passwd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,235 +0,0 @@
|
||||
LLBIN=/usr/lib/llvm-3.9/bin
|
||||
LLVM_CONFIG=$(LLBIN)/llvm-config
|
||||
#QUIET:=@
|
||||
QUIET:=
|
||||
|
||||
BUILDJSONCPP:=#
|
||||
ifdef WITHJSONCPP
|
||||
BUILDJSONCPP:=
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags) -Lthirdparty/jsoncpp-1.8.0/build/src/lib_json -ljsoncpp
|
||||
|
||||
COMMON_FLAGS=-Wall -Wextra -g
|
||||
|
||||
|
||||
CXXFLAGS+=$(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags)
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags) -std=c++11 -I$(SRC_DIR) -Ithirdparty/jsoncpp-1.8.0/include
|
||||
|
||||
LOADABLE_MODULE_OPTIONS=-shared -Wl,-O1
|
||||
|
||||
LDIS=$(LLBIN)/llvm-dis
|
||||
CPP=$(LLBIN)/clang++
|
||||
CC=$(LLBIN)/clang
|
||||
|
||||
BOD=build/obj
|
||||
PASSMGR=Comminute
|
||||
OPM=build/bin/$(PASSMGR)
|
||||
PASS=ComminuteShared.so
|
||||
PASS_OBJECTS=Analysis/TargetCallSitesPass.o \
|
||||
Analysis/StoreCollector.o \
|
||||
Transform/FunctionExternalizer.o \
|
||||
Transform/ChoosePhiValue.o \
|
||||
Analysis/NaiveSensitiveDataLeak.o \
|
||||
Analysis/NaiveFileDescLeak.o \
|
||||
Analysis/PotentiallyDangerousScan.o \
|
||||
Analysis/PotentiallyDangerousScanUserMethod.o \
|
||||
Analysis/PotentiallyDangerousScanFunctionPass.o \
|
||||
Analysis/NaiveConstantArgCheck.o
|
||||
|
||||
# XXX
|
||||
# This is awful... Im just like "PUT IT ALL IN"
|
||||
LIBS=$(shell $(LLVM_CONFIG) --libs) -lclang
|
||||
LIBS+=-lpthread -ldl -lncurses -lz
|
||||
|
||||
TDIR=build/tests
|
||||
|
||||
default: prep $(PASS) passmgr
|
||||
|
||||
prep:
|
||||
@echo "Prep phase"
|
||||
$(QUIET)mkdir -p build/obj
|
||||
$(QUIET)mkdir -p build/obj/Analysis
|
||||
$(QUIET)mkdir -p build/obj/Transform
|
||||
$(QUIET)mkdir -p build/bin
|
||||
$(QUIET)mkdir -p build/lib
|
||||
|
||||
define builditdood
|
||||
$(QUIET)$(CPP) -o $(BOD)/$(1)/$(@F) -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
endef
|
||||
|
||||
Transform/%.o: $(SRC_DIR)/Transform/%.cpp
|
||||
@echo "Compiling $*.cpp"
|
||||
$(call builditdood,Transform)
|
||||
|
||||
Analysis/%.o: $(SRC_DIR)/Analysis/%.cpp
|
||||
@echo "Compiling $*.cpp"
|
||||
$(call builditdood,Analysis)
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo "Compiling $*.cpp"
|
||||
$(call builditdood,.)
|
||||
|
||||
passmgr:
|
||||
@echo "Building passmanager clean up ldflags XXX"
|
||||
$(QUIET)$(CPP) -o $(BOD)/Comminute.o -c $(CPPFLAGS) $(CXXFLAGS) src/Comminute.cpp
|
||||
$(QUIET)$(CPP) -o $(OPM) $(CXXFLAGS) build/obj/Comminute.o ${addprefix $(BOD)/,$(PASS_OBJECTS)} $(LDFLAGS) $(LIBS)
|
||||
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo "Linking $@"
|
||||
$(QUIET)$(CPP) -o build/lib/$@ $(LOADABLE_MODULE_OPTIONS) $(CXXFLAGS) $(LDFLAGS) ${addprefix $(BOD)/,$^}
|
||||
|
||||
test: testprep testnca testnsdl testpd testnfdl
|
||||
|
||||
testprep:
|
||||
$(QUIET)mkdir -p $(TDIR)
|
||||
|
||||
testnca:
|
||||
$(QUIET)$(CC) -o $(TDIR)/NCA001 tests/NCA001.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NCA001.bc -c tests/NCA001.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NCA001.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NCA002 tests/NCA002.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NCA002.bc -c tests/NCA002.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NCA002.bc
|
||||
|
||||
testnsdl:
|
||||
$(QUIET)$(CC) -o $(TDIR)/NSDL001 tests/NSDL001.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NSDL001.bc -c tests/NSDL001.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NSDL001.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NSDL002 tests/NSDL002.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NSDL002.bc -c tests/NSDL002.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NSDL002.bc
|
||||
|
||||
testpd:
|
||||
$(QUIET)$(CC) -o $(TDIR)/PD001 tests/PD001.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/PD001.bc -c tests/PD001.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/PD001.bc
|
||||
|
||||
testnfdl:
|
||||
$(QUIET)$(CC) -o $(TDIR)/NFDL001 tests/NFDL001.c
|
||||
$(QUIET)$(CC) -emit-llvm -o $(TDIR)/NFDL001.bc -c tests/NFDL001.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NFDL001.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NFDL002 tests/NFDL002.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NFDL002.bc -c tests/NFDL002.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NFDL002.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NFDL003 tests/NFDL003.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NFDL003.bc -c tests/NFDL003.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NFDL003.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NFDL004 tests/NFDL004.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NFDL004.bc -c tests/NFDL004.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NFDL004.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NFDL005 tests/NFDL005.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NFDL005.bc -c tests/NFDL005.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NFDL005.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NFDL006 tests/NFDL006.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NFDL006.bc -c tests/NFDL006.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NFDL006.bc
|
||||
$(QUIET)$(CC) -o $(TDIR)/NFDL007 tests/NFDL007.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -o $(TDIR)/NFDL007.bc -c tests/NFDL007.c
|
||||
$(QUIET)$(LDIS) $(TDIR)/NFDL007.bc
|
||||
|
||||
help:
|
||||
@echo "make jsoncpp"
|
||||
@echo "make "
|
||||
@echo "...See build/"
|
||||
@echo "make clean or make cleanall which requires jsoncpp rebuild"
|
||||
@echo "make test"
|
||||
@echo "make runtests"
|
||||
@echo "make runconstantarg"
|
||||
@echo "make runsensitiveleak"
|
||||
@echo "make runfdleak"
|
||||
@echo "make rundangerfn"
|
||||
|
||||
|
||||
runtests: runconstantarg runsensitiveleak runfdleak rundangerfn
|
||||
|
||||
runconstantarg:
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive constant arg on NCA001 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-constant-arg $(TDIR)/NCA001.bc $(TDIR)/NCA001_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive constant arg on NCA002 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-constant-arg $(TDIR)/NCA002.bc $(TDIR)/NCA002_out.bc
|
||||
|
||||
runsensitiveleak:
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive sensitive leak on NSDL001 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-sensitive-data-leak $(TDIR)/NSDL001.bc $(TDIR)/NSDL001_out.bc
|
||||
$(QUIET)$(LDIS) $(TDIR)/NSDL001_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive sensitive leak on NSDL002 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-sensitive-data-leak $(TDIR)/NSDL002.bc $(TDIR)/NSDL002_out.bc
|
||||
$(QUIET)$(LDIS) $(TDIR)/NSDL002_out.bc
|
||||
|
||||
rundangerfn:
|
||||
@echo "***"
|
||||
@echo "*** Running: Potentially danger function on PD001 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -dangerous-function $(TDIR)/PD001.bc $(TDIR)/PD001_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Potentially danger function via user method on PD001 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -dangerous-function-user-method $(TDIR)/PD001.bc $(TDIR)/PD001_out-um.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Potentially danger function via function pass on PD001 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -dangerous-function-fpass $(TDIR)/PD001.bc $(TDIR)/PD001_out-fp.bc
|
||||
|
||||
runfdleak:
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive file descriptor leak on NFDL001 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-fd-leak $(TDIR)/NFDL001.bc $(TDIR)/NFDL001_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive file descriptor leak on NFDL002 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-fd-leak $(TDIR)/NFDL002.bc $(TDIR)/NFDL002_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive file descriptor leak on NFDL003 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-fd-leak $(TDIR)/NFDL003.bc $(TDIR)/NFDL003_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive file descriptor leak on NFDL004 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-fd-leak $(TDIR)/NFDL004.bc $(TDIR)/NFDL004_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive file descriptor leak on NFDL005 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-fd-leak $(TDIR)/NFDL005.bc $(TDIR)/NFDL005_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive file descriptor leak on NFDL006 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-fd-leak $(TDIR)/NFDL006.bc $(TDIR)/NFDL006_out.bc
|
||||
@echo "***"
|
||||
@echo "*** Running: Naive file descriptor leak on NFDL007 ***"
|
||||
@echo "***"
|
||||
$(QUIET)$(OPM) -naive-fd-leak $(TDIR)/NFDL007.bc $(TDIR)/NFDL007_out.bc
|
||||
|
||||
|
||||
# :^D
|
||||
jsoncpp:
|
||||
@echo "Building jsoncpp-1.8.0"
|
||||
cd thirdparty && \
|
||||
tar zxvf jsoncpp-1.8.0.tar.gz && \
|
||||
cd jsoncpp-1.8.0 && \
|
||||
rm -rf build && \
|
||||
mkdir -p build && \
|
||||
cd build && \
|
||||
cmake .. && \
|
||||
make && \
|
||||
cd ../../
|
||||
|
||||
jsonclean:
|
||||
$(QUIET)rm -rf thirdparty/jsoncpp-1.8.0
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf build tests/*.ll
|
||||
|
||||
cleanall: clean jsonclean
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
|
||||
# Comminute
|
||||
|
||||
The code here is intended to be some basics that help with learning
|
||||
the LLVM API. The points dealt with here are:
|
||||
|
||||
- Pass manager use
|
||||
- Pass dependency
|
||||
- Some basic IR instruction and value analysis
|
||||
- Use/User API
|
||||
|
||||
It is not meant to be a some great bug hunting tool. It is meant
|
||||
to help get you to the point where you can start to think about
|
||||
interprocedural and other analyses. Once you get the feel you
|
||||
should start to look at other code, like SVF, to get into things.
|
||||
You should look at building CFGs so you can evaluate globals
|
||||
better. You should look at some extended interprocedural SSA or
|
||||
perhaps using Andersen's Alias Analysis for analyzing pointers.
|
||||
There is a lot to doing good static analysis and that's where the
|
||||
meat of the research is!
|
||||
|
||||
I repeat... Just a learning tool.
|
||||
|
||||
### Multiple potentially dangerous examples
|
||||
|
||||
The potent-danger examples are there to illustrate you can do the
|
||||
thing a few different ways. Essentially, depending on your
|
||||
design needs and end goals, you may want to implement one
|
||||
methodology or another...
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"srandom" : 0,
|
||||
"srand48" : 0
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"close" : 0
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"socket" : -1,
|
||||
"open" : -1
|
||||
}
|
||||
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@ -1,3 +0,0 @@
|
||||
{
|
||||
"functions" : [ "strcpy" ]
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"getaddrinfo" : 0
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"getpass" : -1
|
||||
}
|
||||
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* NaiveConstantArgCheck
|
||||
*
|
||||
* There are some functions that you do not want constant values
|
||||
* as arguments... some such are seed functions to (P)RNGs.
|
||||
* This attempts some basic detection of such cases, but is quite
|
||||
* naive
|
||||
*
|
||||
* Note:
|
||||
* Things would be better if this used the TargetCallSitesPass.
|
||||
* Mostly wanted to show using instruction iteration in an
|
||||
* example. There are many improvements to be made.. C'est la vie.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Constant.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/DebugInfoMetadata.h"
|
||||
#include "llvm/IR/DebugLoc.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Analysis/LazyCallGraph.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <json/config.h>
|
||||
#include <json/value.h>
|
||||
#include <json/reader.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "NaiveConstantArgCheck.h"
|
||||
|
||||
void
|
||||
NaiveConstantArgCheck::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
NaiveConstantArgCheck::runOnModule(Module &M)
|
||||
{
|
||||
errs() << "Running naive constant argument check pass\n";
|
||||
|
||||
Json::Value caDict;
|
||||
std::ifstream cfgFileStream;
|
||||
std::map<std::string, std::pair<Function *, unsigned>> _existingToCheck;
|
||||
|
||||
cfgFileStream.open(this->getConfigFilePath());
|
||||
cfgFileStream >> caDict;
|
||||
cfgFileStream.close();
|
||||
|
||||
Json::Value::Members mems = caDict.getMemberNames();
|
||||
for (auto memberIt = mems.begin(); memberIt != mems.end(); ++memberIt) {
|
||||
std::string fnName = *memberIt;
|
||||
unsigned fnArgIdx = caDict[fnName].asUInt();
|
||||
|
||||
// Lookup Function by name in this module
|
||||
Function *f = M.getFunction(fnName);
|
||||
if (f == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If arg count for target fn and this fn don't match, don't add it.
|
||||
if (f->arg_size() == 0 || f->arg_size() <= fnArgIdx) {
|
||||
continue;
|
||||
}
|
||||
_existingToCheck[fnName] = std::make_pair(f, fnArgIdx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through every function (cept some)
|
||||
* Go through every call instruction
|
||||
* Determine if the called function is a sink function
|
||||
* Naively check if the argument in question is constant (to that function)
|
||||
* If so, add to result set.
|
||||
* See User in naive sensitive data leak for another way to do things.
|
||||
*
|
||||
*/
|
||||
for (auto &f : M) {
|
||||
Function *parentFunction = &f;
|
||||
|
||||
/*
|
||||
* Skip analyzing any functions we check the calling of.
|
||||
*/
|
||||
auto etcIt = _existingToCheck.begin();
|
||||
for ( ; etcIt != _existingToCheck.end(); ++etcIt) {
|
||||
auto fi = etcIt->second;
|
||||
Function *ignoreMe = fi.first;
|
||||
if (ignoreMe == parentFunction) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (etcIt != _existingToCheck.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate through the instructions that compose the function.
|
||||
*
|
||||
* Instead of going through instructions to find CallInst, etc,
|
||||
* we could use an instruction visitor. For such an example, see the
|
||||
* visitor code found in the intflip base above this directory.
|
||||
*
|
||||
* Further, as per two comments above and code in another file, could
|
||||
* just utilize the User list associated with the Function.
|
||||
*/
|
||||
for (inst_iterator iIt = inst_begin(f); iIt != inst_end(f); ++iIt) {
|
||||
/*
|
||||
* Check if the current instruction is a call instruction. If not,
|
||||
* skip to the next instruction.
|
||||
*
|
||||
* Some might use:
|
||||
* if (CallInst *ci = dyn_cast<CallInst>(&fInst) {
|
||||
* } else if (InvokeInst *ii ....)
|
||||
* instead...
|
||||
*
|
||||
*/
|
||||
Instruction *fInst = &*iIt;
|
||||
if (!isa<CallInst>(fInst) && !isa<InvokeInst>(fInst)) {
|
||||
continue;
|
||||
}
|
||||
DILocation *lineInfo = fInst->getDebugLoc().get();
|
||||
CallSite cs(fInst);
|
||||
Function *calledFunction = cs.getCalledFunction();
|
||||
|
||||
auto fIt = _existingToCheck.begin();
|
||||
for (; fIt != _existingToCheck.end(); ++fIt) {
|
||||
auto j = fIt->second;
|
||||
Function *badFunc = j.first;
|
||||
unsigned idx = j.second;
|
||||
|
||||
if (badFunc == calledFunction) {
|
||||
|
||||
// We just assume there needs to be enough arguments
|
||||
// Improvement would check function signature or something.
|
||||
unsigned nOps = cs.getNumArgOperands();
|
||||
if (nOps <= idx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get argument by index.
|
||||
Value *arg = cs.getArgOperand(idx);
|
||||
|
||||
// and determine if this argument is constant
|
||||
if (!isa<Constant>(arg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the arg was constant and was to a fn of interest,
|
||||
* then save off this info as a finding to display.
|
||||
*
|
||||
*/
|
||||
NaiveConstantArgCheckResult n(parentFunction,
|
||||
calledFunction, arg, idx, lineInfo);
|
||||
n.printResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
NaiveConstantArgCheckResult::printResult()
|
||||
{
|
||||
bool hl = hasLocation();
|
||||
bool hn = caller->hasName();
|
||||
|
||||
if (hn && hl) {
|
||||
unsigned line = loc->getLine();
|
||||
StringRef file = loc->getFilename();
|
||||
StringRef fdir = loc->getDirectory();
|
||||
std::cout << " !" << caller->getName().str() << " calls "
|
||||
<< callee->getName().str() << " where arg index: "
|
||||
<< argIndex << " is a constant\n";
|
||||
std::cout << " " << file.str() << ":" << line << "\n";
|
||||
} else if (hn && !hl) {
|
||||
std::cout << " !" << caller->getName().str() << " calls "
|
||||
<< callee->getName().str() << " with argument "
|
||||
<< argIndex << " of constant value: \n ";
|
||||
argument->dump();
|
||||
}
|
||||
}
|
||||
|
||||
char NaiveConstantArgCheck::ID = 0;
|
||||
static RegisterPass<NaiveConstantArgCheck> XX("naive-con-arg-check", "Basic constant arg check");
|
||||
@ -1,46 +0,0 @@
|
||||
#ifndef _NAIVECONSTANTARGCHECK_H
|
||||
#define _NAIVECONSTANTARGCHECK_H
|
||||
|
||||
class NaiveConstantArgCheckResult {
|
||||
Function *caller;
|
||||
Function *callee;
|
||||
Value *argument;
|
||||
unsigned argIndex;
|
||||
DILocation *loc;
|
||||
|
||||
public:
|
||||
NaiveConstantArgCheckResult(Function *aCaller,
|
||||
Function *aCallee, Value *arg, unsigned idx, DILocation *d) :
|
||||
caller(aCaller), callee(aCallee), argument(arg), argIndex(idx), loc(d) {};
|
||||
|
||||
Function *getCaller() { return caller; }
|
||||
Function *getCallee() { return callee; }
|
||||
Value *getArgument() { return argument; }
|
||||
unsigned getArgumentIndex() { return argIndex; }
|
||||
bool hasLocation() {
|
||||
if (loc == NULL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
DILocation *getLocation() { return loc; }
|
||||
void printResult();
|
||||
};
|
||||
|
||||
struct NaiveConstantArgCheck : public ModulePass {
|
||||
private:
|
||||
std::vector<NaiveConstantArgCheckResult> _results;
|
||||
std::string configFilePath;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
NaiveConstantArgCheck() : ModulePass(ID) {}
|
||||
|
||||
virtual bool runOnModule(Module &M);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
void setConfigFilePath(std::string a) { configFilePath = a; }
|
||||
std::string getConfigFilePath() { return configFilePath; }
|
||||
};
|
||||
#endif // !_CONSTANTARGCHECK_H
|
||||
@ -1,180 +0,0 @@
|
||||
/*
|
||||
* NaiveFileDescLeak
|
||||
*
|
||||
* Look for cases where
|
||||
*
|
||||
* %k = call srcfnthatreturnsfd
|
||||
* ...
|
||||
*
|
||||
* and no call close %k in the same function
|
||||
*/
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/DebugLoc.h"
|
||||
#include "llvm/IR/DebugInfoMetadata.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "StoreCollector.h"
|
||||
#include "TargetCallSitesPass.h"
|
||||
#include "NaiveFileDescLeak.h"
|
||||
|
||||
void
|
||||
NaiveFileDescLeak::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
AU.addRequired<TargetCallSitesPass>();
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
void
|
||||
NaiveFileDescLeak::printLeak(TargetCallSite *s)
|
||||
{
|
||||
Instruction *i = s->getInstruction();
|
||||
DILocation *loc = i->getDebugLoc().get();
|
||||
|
||||
errs() << " ! file descriptor leak:\n";
|
||||
if (loc) {
|
||||
errs() << " " << s->getCaller()->getName() << " calls "
|
||||
<< s->getCalled()->getName() << " at " << loc->getFilename()
|
||||
<< ":" << loc->getLine() << " and never closes\n";
|
||||
} else {
|
||||
// No debug info.
|
||||
errs() << " " << s->getCaller()->getName() << " calls "
|
||||
<< s->getCalled()->getName() << " and never closes \n";
|
||||
i->getDebugLoc().dump();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NaiveFileDescLeak::printLikelyFP(TargetCallSite *s, std::string reason)
|
||||
{
|
||||
Instruction *i = s->getInstruction();
|
||||
DILocation *loc = i->getDebugLoc().get();
|
||||
|
||||
errs() << " ! file descriptor FP likely:\n";
|
||||
if (loc) {
|
||||
errs() << " " << s->getCaller()->getName() << " calls "
|
||||
<< s->getCalled()->getName() << " at " << loc->getFilename()
|
||||
<< ":" << loc->getLine() << " reason: " << reason << "\n";
|
||||
} else {
|
||||
// No debug info.
|
||||
errs() << " " << s->getCaller()->getName() << " calls "
|
||||
<< s->getCalled()->getName() << " reason: " << reason << "\n";
|
||||
i->getDebugLoc().dump();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
NaiveFileDescLeak::runOnModule(Module &M)
|
||||
{
|
||||
errs() << "Running naive file descriptor leak pass\n";
|
||||
|
||||
/*
|
||||
* Make use of the result of running the TargetCallSitesPass.
|
||||
* It gives locations where file descriptors were created
|
||||
* and locations of file descriptor destroyer calls (e.g. close(2)).
|
||||
*/
|
||||
TargetCallSitesPass &p = getAnalysis<TargetCallSitesPass>();
|
||||
if (p.src_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there exist no close-like calls, then every source is
|
||||
* leaking or we are missing some function that closes (e.g.
|
||||
* it exists in a different compilation unit (Module) or we
|
||||
* do not know some call from an API we externalized does a
|
||||
* close).
|
||||
*/
|
||||
if (p.snk_empty()) {
|
||||
errs() << " ! No close-like calls found.\n";
|
||||
for (auto tcs = p.src_begin(); tcs != p.src_end(); ++tcs) {
|
||||
TargetCallSite *s = tcs->get();
|
||||
printLeak(s);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* For every open()-like, if there is not even a use of the
|
||||
* Value, then we know there is something up.
|
||||
*/
|
||||
for (auto srcIt = p.src_begin(); srcIt != p.src_end(); ) {
|
||||
TargetCallSite *srcSite = &*srcIt->get();
|
||||
Value *possiblyLeaked = srcSite->getTarget();
|
||||
if (possiblyLeaked->user_empty()) {
|
||||
printLeak(srcSite);
|
||||
srcIt = p.src_erase(srcIt);
|
||||
} else {
|
||||
++srcIt;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto snkIt = p.snk_begin(); snkIt != p.snk_end(); ++snkIt) {
|
||||
TargetCallSite *snkSite = &*snkIt->get();
|
||||
Value *closingVar = snkSite->getTarget();
|
||||
if (isa<Argument>(closingVar)) {
|
||||
printLikelyFP(snkSite, "Value is an Argument to parent function (unsupported)");
|
||||
} else if (isa<GlobalVariable>(closingVar)) {
|
||||
printLikelyFP(snkSite, "Value is a GlobalVariable (unsupported)");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, for every close()-like, go through and see if we can easily
|
||||
* find a source open(). If we can, we remove the value from being
|
||||
* a possible leak.
|
||||
*
|
||||
* Note that this is naive. Note that if you use the PHINode axe that
|
||||
* you may have FN. Etc etc etc :P
|
||||
*/
|
||||
for (auto snkIt = p.snk_begin(); snkIt != p.snk_end(); ) {
|
||||
TargetCallSite *snkSite = &*snkIt->get();
|
||||
Value *closedValue = snkSite->getTarget();
|
||||
bool remd = false;
|
||||
for (auto srcIt = p.src_begin(); srcIt != p.src_end(); ) {
|
||||
TargetCallSite *srcSite = &*srcIt->get();
|
||||
Value *possiblyLeaked = srcSite->getTarget();
|
||||
if (closedValue == possiblyLeaked) {
|
||||
snkIt = p.snk_erase(snkIt);
|
||||
srcIt = p.src_erase(srcIt);
|
||||
remd = true;
|
||||
break;
|
||||
} else {
|
||||
++srcIt;
|
||||
}
|
||||
}
|
||||
if (remd == false) {
|
||||
++snkIt;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we did not remove some sources then either:
|
||||
* (a) we failed to track things properly (very likely! :D)
|
||||
* (b) we have some fd leak
|
||||
* these are basic, weak assumptions.
|
||||
*/
|
||||
for (auto srcIt = p.src_begin(); srcIt != p.src_end(); ++srcIt) {
|
||||
TargetCallSite *srcSite = &*srcIt->get();
|
||||
Value *v = srcSite->getTarget();
|
||||
if (isa<Argument>(v)) {
|
||||
printLikelyFP(srcSite, "Value is an Argument to parent function (unsupported)");
|
||||
} else if (isa<GlobalVariable>(v)) {
|
||||
printLikelyFP(srcSite, "Value is a GlobalVariable (unsupported)");
|
||||
} else {
|
||||
printLeak(srcSite);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char NaiveFileDescLeak::ID = 0;
|
||||
static RegisterPass<NaiveFileDescLeak> XX("naive-fd-leak", "Naive fd leak");
|
||||
@ -1,22 +0,0 @@
|
||||
#ifndef __NAIVEFILEDESCLEAK_H
|
||||
#define __NAIVEFILEDESCLEAK_H
|
||||
|
||||
struct NaiveFileDescLeak : public ModulePass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
typedef std::pair<Function *, unsigned> FuncArg;
|
||||
typedef std::map<std::string, FuncArg> FuncArgMap;
|
||||
|
||||
NaiveFileDescLeak() : ModulePass(ID) { }
|
||||
virtual bool runOnModule(Module &);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
void printLeak(TargetCallSite *s);
|
||||
void printLikelyFP(TargetCallSite *s, std::string r);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* NaiveSensitiveDataLeak
|
||||
*
|
||||
* It makes use of the TargetCallSitesPass as a way to get uses and
|
||||
* target values of interest (sinks, sources, and the related data);
|
||||
* it runs prior to this pass.
|
||||
*
|
||||
* There is no handling of special entry points or callbacks that are
|
||||
* tainted a priori.
|
||||
*/
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/DebugInfoMetadata.h"
|
||||
#include "llvm/IR/DebugLoc.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "TargetCallSitesPass.h"
|
||||
#include "NaiveSensitiveDataLeak.h"
|
||||
|
||||
void
|
||||
NaiveSensitiveDataLeak::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
AU.addRequired<TargetCallSitesPass>();
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
bool
|
||||
NaiveSensitiveDataLeak::runOnModule(Module &M)
|
||||
{
|
||||
errs() << "Running naive sensitive data leak pass\n";
|
||||
/*
|
||||
* We can use the upstream analysis from the TargetCallSitesPass.
|
||||
* Always nice use already available tools.
|
||||
*/
|
||||
TargetCallSitesPass &p = getAnalysis<TargetCallSitesPass>();
|
||||
|
||||
|
||||
if (p.src_empty()) {
|
||||
return false;
|
||||
}
|
||||
if (p.snk_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each sink value available, we must attempt to trace it to a source
|
||||
for (auto snkIt = p.snk_begin(); snkIt != p.snk_end(); ++snkIt) {
|
||||
TargetCallSite *snkSite = &*snkIt->get();
|
||||
Value *leakData = snkSite->getTarget();
|
||||
auto srcIt = p.src_end();
|
||||
--srcIt;
|
||||
bool brk_back = false;
|
||||
for (; brk_back == false; --srcIt) {
|
||||
if (srcIt == p.src_begin()) {
|
||||
brk_back = true;
|
||||
}
|
||||
TargetCallSite *srcSite = &*srcIt->get();
|
||||
Value *originalSourceData = srcSite->getTarget();
|
||||
Value *sourceData = originalSourceData;
|
||||
if (isa<CallInst>(leakData) || isa<InvokeInst>(leakData)) {
|
||||
if (leakData == sourceData) {
|
||||
printResult(srcSite, snkSite);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
NaiveSensitiveDataLeak::printResult(TargetCallSite *srcSite,
|
||||
TargetCallSite *snkSite)
|
||||
{
|
||||
Instruction *snkIn = snkSite->getInstruction();
|
||||
Instruction *srcIn = srcSite->getInstruction();
|
||||
|
||||
DILocation *snkLoc = snkIn->getDebugLoc().get();
|
||||
DILocation *srcLoc = srcIn->getDebugLoc().get();
|
||||
|
||||
errs() << " ! sensitive data leak \n";
|
||||
errs() << " " << snkSite->getCaller()->getName()
|
||||
<< " calls " << snkSite->getCalled()->getName()
|
||||
<< " where arg idx #" << snkSite->getArgIndex()
|
||||
<< " is tainted sensitive. file: " << snkLoc->getFilename()
|
||||
<< " line: " << snkLoc->getLine() << "\n";
|
||||
errs() << " source: " << srcSite->getCaller()->getName()
|
||||
<< " calls " << srcSite->getCalled()->getName()
|
||||
<< " at line: "<< srcLoc->getLine() << " of file: "
|
||||
<< srcLoc->getFilename() << "\n";
|
||||
}
|
||||
|
||||
char NaiveSensitiveDataLeak::ID = 0;
|
||||
static RegisterPass<NaiveSensitiveDataLeak> XX("naive-sensitive-leak",
|
||||
"Naive sensitive data leak");
|
||||
@ -1,19 +0,0 @@
|
||||
#ifndef __NAIVESENSITIVEDATALEAK_H
|
||||
#define __NAIVESENSITIVEDATALEAK_H
|
||||
|
||||
struct NaiveSensitiveDataLeak : public ModulePass {
|
||||
static char ID;
|
||||
typedef std::pair<Function *, unsigned> FuncArg;
|
||||
typedef std::map<std::string, FuncArg> FuncArgMap;
|
||||
|
||||
NaiveSensitiveDataLeak() : ModulePass(ID) { }
|
||||
virtual bool runOnModule(Module &);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
private:
|
||||
void parseAndCheckConfig(FuncArgMap *, bool);
|
||||
void printResult(TargetCallSite *srcSite, TargetCallSite *snkSite);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* XXX I accidentally committed this file.. so maybe ignore.. but
|
||||
* leaving it because it might be useful to read for someone.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NaiveSensitiveDataLeak
|
||||
*
|
||||
* It makes use of the TargetCallSitesPass as a way to get uses and
|
||||
* target values of interest (sinks, sources, and the related data);
|
||||
* it runs prior to this pass.
|
||||
*
|
||||
* It is using the StoreCollector a poor means of tracking
|
||||
* memory load/store use. That could be a pass but it isn't. There
|
||||
* are much better ways for handling this... Andersen's AA or
|
||||
* MemoryDependenceAnalysis. The former is a bit more memory intensive
|
||||
* and agressive, while the latter is ``lazy''.
|
||||
*
|
||||
* There is no handling of special entry points or callbacks that are
|
||||
* tainted a priori.
|
||||
*/
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/DebugInfoMetadata.h"
|
||||
#include "llvm/IR/DebugLoc.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "TargetCallSitesPass.h"
|
||||
#include "NaiveSensitiveDataLeak.h"
|
||||
#include "StoreCollector.h"
|
||||
|
||||
void
|
||||
NaiveSensitiveDataLeak::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
AU.addRequired<TargetCallSitesPass>();
|
||||
AU.preservesCFG();
|
||||
}
|
||||
|
||||
bool
|
||||
NaiveSensitiveDataLeak::runOnModule(Module &M)
|
||||
{
|
||||
errs() << "Running naive sensitive data leak pass\n";
|
||||
TargetCallSitesPass &p = getAnalysis<TargetCallSitesPass>();
|
||||
|
||||
|
||||
if (p.src_empty()) {
|
||||
return false;
|
||||
}
|
||||
if (p.snk_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StoreCollector *store = new StoreCollector();
|
||||
|
||||
// For each sink value available, we must attempt to trace it to a source
|
||||
for (auto snkIt = p.snk_begin(); snkIt != p.snk_end(); ++snkIt) {
|
||||
TargetCallSite *snkSite = &*snkIt->get();
|
||||
Value *leakData = snkSite->getTarget();
|
||||
|
||||
#if 0
|
||||
/* Lazily refresh the StoreInst holder */
|
||||
if (snkSite->getCaller() != store->getFunction()) {
|
||||
store->collect(snkSite->getCaller());
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto srcIt = p.src_begin(); srcIt != p.src_end(); ++srcIt) {
|
||||
TargetCallSite *srcSite = &*srcIt->get();
|
||||
Value *originalSourceData = srcSite->getTarget();
|
||||
Value *sourceData = originalSourceData;
|
||||
|
||||
while (true) {
|
||||
if (isa<CallInst>(leakData) || isa<InvokeInst>(leakData)) {
|
||||
if (leakData == sourceData) {
|
||||
printResult(srcSite, snkSite);
|
||||
}
|
||||
/*
|
||||
* Does not handle propagators at this point :(
|
||||
* so any call is either a source or dead end.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
if (isa<Constant>(leakData)) {
|
||||
break;
|
||||
}
|
||||
if (isa<AllocaInst>(leakData)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (CastInst *ci = dyn_cast<CastInst>(leakData)) {
|
||||
User *cu = cast<User>(ci);
|
||||
leakData = cu->getOperand(0);
|
||||
continue;
|
||||
}
|
||||
if (LoadInst *li = dyn_cast<LoadInst>(leakData)) {
|
||||
Value *memLoc = li->getPointerOperand();
|
||||
leakData = store->find(memLoc);
|
||||
assert(leakData != NULL && "memLoc not in storeCollect");
|
||||
continue;
|
||||
}
|
||||
if (GetElementPtrInst *gp =
|
||||
dyn_cast<GetElementPtrInst>(leakData)) {
|
||||
leakData = gp->getPointerOperand();
|
||||
continue;
|
||||
}
|
||||
if (isa<Argument>(leakData)) {
|
||||
errs() << "Data leaked escaped function analysis, unknown result.\n";
|
||||
break;
|
||||
}
|
||||
errs() << "Unhandled:\n ";
|
||||
leakData->dump();
|
||||
assert(0 == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete store;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
NaiveSensitiveDataLeak::printResult(TargetCallSite *srcSite,
|
||||
TargetCallSite *snkSite)
|
||||
{
|
||||
Instruction *snkIn = snkSite->getInstruction();
|
||||
Instruction *srcIn = srcSite->getInstruction();
|
||||
|
||||
DILocation *snkLoc = snkIn->getDebugLoc().get();
|
||||
DILocation *srcLoc = srcIn->getDebugLoc().get();
|
||||
|
||||
errs() << " ! sensitive data leak \n";
|
||||
errs() << " " << snkSite->getCaller()->getName()
|
||||
<< " calls " << snkSite->getCalled()->getName()
|
||||
<< " where arg idx #" << snkSite->getArgIndex()
|
||||
<< " is tainted sensitive. file: " << snkLoc->getFilename()
|
||||
<< " line: " << snkLoc->getLine() << "\n";
|
||||
errs() << " source: " << srcSite->getCaller()->getName()
|
||||
<< " calls " << srcSite->getCalled()->getName()
|
||||
<< " at line: "<< srcLoc->getLine() << " of file: "
|
||||
<< srcLoc->getFilename() << "\n";
|
||||
}
|
||||
|
||||
char NaiveSensitiveDataLeak::ID = 0;
|
||||
static RegisterPass<NaiveSensitiveDataLeak> XX("naive-sensitive-leak",
|
||||
"Naive sensitive data leak");
|
||||
@ -1,155 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* PotentiallyDangerousScan
|
||||
*
|
||||
* This is yet another naive scan :-). This is just
|
||||
* checking for CWE 676 which is just the use of potentially
|
||||
* dangerous functions.
|
||||
*
|
||||
* There are, again, multiple ways that one might do this
|
||||
* check in LLVM (ie, ignoring objdump | grep :)). You
|
||||
* could use a visitor, instruction iteration, or even
|
||||
* the User checking of the functions in question.
|
||||
* Each of those are valid, but here we perform this
|
||||
* by relying on the CallGraph pass being run prior
|
||||
* to this pass. We then just go through those results
|
||||
* and perform the analysis. So this code illustrates
|
||||
* pass dependency (via getAnalysisUsage()) and using
|
||||
* the CallgraphPass as a source of data.
|
||||
*
|
||||
*/
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/ValueHandle.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Analysis/CallGraph.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <json/config.h>
|
||||
#include <json/value.h>
|
||||
#include <json/reader.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "PotentiallyDangerousScan.h"
|
||||
|
||||
/*
|
||||
* This informs the pass manager that prior to running this pass, the
|
||||
* CallGraphWrapperPass should be run. This helps in ordering passes so
|
||||
* you can have a reasonable expectation of state of the IR (or other)
|
||||
* upon entry to your runOn*() function.
|
||||
*
|
||||
*/
|
||||
void
|
||||
PotentiallyDangerousScan::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
AU.addRequired<CallGraphWrapperPass>();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PotentiallyDangerousScan::runOnModule(Module &M)
|
||||
{
|
||||
Json::Value fnDict;
|
||||
std::ifstream cfgFileStream;
|
||||
std::vector<Function *> pdFunctions;
|
||||
|
||||
/*
|
||||
* Make use of the libjsoncpp to ingest a json config file.
|
||||
* This file houses the set of functions that one might consider
|
||||
* to fall under CWE 676. Then see if any of them exist in this
|
||||
* module.
|
||||
*
|
||||
* I am being trusting of the configs..
|
||||
*/
|
||||
cfgFileStream.open(this->getConfigFilePath());
|
||||
cfgFileStream >> fnDict;
|
||||
cfgFileStream.close();
|
||||
Json::Value fnList = fnDict["functions"];
|
||||
assert(fnList.isArray() && "fnList was not an array");
|
||||
Json::ArrayIndex aLen = fnList.size();
|
||||
for (Json::ArrayIndex ai = 0; ai < aLen; ai++) {
|
||||
Function *f = M.getFunction(fnList[ai].asString());
|
||||
if (f != NULL) {
|
||||
pdFunctions.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A great thing about the pass design is that you can share information
|
||||
* between them. Here we are getting the call graph as previously built
|
||||
* by the call graph pass. If you see the getAnalysisUsage() function at
|
||||
* the bottom of this file, you will note the dependency on that pass
|
||||
* being run prior to this one.
|
||||
*
|
||||
*/
|
||||
CallGraphWrapperPass &cgPass = getAnalysis<CallGraphWrapperPass>();
|
||||
|
||||
/* store results in pairs of caller and callee */
|
||||
typedef std::pair<Function *, Function *> PDRes;
|
||||
std::vector<PDRes> results;
|
||||
|
||||
/*
|
||||
* The container we go through is a map with key Function * and value of
|
||||
* CallGraphNode list. The key is the caller and value the callees.
|
||||
*
|
||||
* The first entry this map is NULL caller and contains the set of all functions.
|
||||
*
|
||||
*/
|
||||
CallGraph &cg = cgPass.getCallGraph();
|
||||
for (auto cgIt = cg.begin(); cgIt != cg.end(); ++cgIt) {
|
||||
/*
|
||||
* Calling function
|
||||
*/
|
||||
Function *caller = const_cast<Function *>(cgIt->first);
|
||||
if (caller == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CallGraphNode &callees = *cgIt->second;
|
||||
if (callees.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We iterate through a vector of CallRecords, which is:
|
||||
* typedef std::pair<WeakVH, CallGraphNode *> CallRecord;
|
||||
* in which each CGN is a called function.
|
||||
*
|
||||
*/
|
||||
for (const auto &crIt : callees) {
|
||||
if (Function *callee = crIt.second->getFunction()) {
|
||||
|
||||
/* determine if the callee is something we consider dangerous */
|
||||
for (auto pdIt = pdFunctions.begin(); pdIt != pdFunctions.end(); ++pdIt) {
|
||||
Function *pdFn = *pdIt;
|
||||
if (pdFn == callee) {
|
||||
/* Save a result since matched a pd function */
|
||||
auto r = std::make_pair(caller, callee);
|
||||
results.push_back(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errs() << "Results for potentially dangerous function call usage:\n";
|
||||
for (auto rIt = results.begin(); rIt != results.end(); ++rIt) {
|
||||
Function *caller = rIt->first;
|
||||
Function *callee = rIt->second;
|
||||
errs() << " " << caller->getName().str() << " calls " << \
|
||||
callee->getName().str() << "\n";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char PotentiallyDangerousScan::ID = 1;
|
||||
static RegisterPass<PotentiallyDangerousScan> XX("pot-danger", "Potentially Dangerous Call (CWE 676)");
|
||||
@ -1,19 +0,0 @@
|
||||
#ifndef __POTENTIALLYDANGEROUSSCAN_H
|
||||
#define __POTENTIALLYDANGEROUSSCAN_H
|
||||
|
||||
struct PotentiallyDangerousScan : public ModulePass {
|
||||
private:
|
||||
std::string _cfgFilePath;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PotentiallyDangerousScan() : ModulePass(ID) { }
|
||||
virtual bool runOnModule(Module &);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
void setConfigFilePath(std::string s) { _cfgFilePath = s; }
|
||||
std::string getConfigFilePath() { return _cfgFilePath; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,97 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* PotentiallyDangerousScanFunctionPass
|
||||
*
|
||||
* This example implements the scan as a function pass and
|
||||
* then iterate through the instructions for calls. This could
|
||||
* also be done as a visitor.
|
||||
*
|
||||
*/
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/ValueHandle.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Analysis/CallGraph.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <json/config.h>
|
||||
#include <json/value.h>
|
||||
#include <json/reader.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "PotentiallyDangerousScanFunctionPass.h"
|
||||
|
||||
void
|
||||
PotentiallyDangerousScanFunctionPass::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PotentiallyDangerousScanFunctionPass::setConfigFilePath(std::string s)
|
||||
{
|
||||
|
||||
_cfgFilePath = s;
|
||||
pdFunctions.clear();
|
||||
lookupPDFunctions = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PotentiallyDangerousScanFunctionPass::runOnFunction(Function &f)
|
||||
{
|
||||
std::string caller = "unnamed_func";
|
||||
|
||||
if (lookupPDFunctions) {
|
||||
Json::Value fnDict;
|
||||
std::ifstream cfgFileStream;
|
||||
|
||||
cfgFileStream.open(this->getConfigFilePath());
|
||||
cfgFileStream >> fnDict;
|
||||
cfgFileStream.close();
|
||||
Json::Value fnList = fnDict["functions"];
|
||||
assert(fnList.isArray() && "fnList was not an array");
|
||||
Json::ArrayIndex aLen = fnList.size();
|
||||
Module *m = f.getParent();
|
||||
for (Json::ArrayIndex ai = 0; ai < aLen; ai++) {
|
||||
Function *pdFn = m->getFunction(fnList[ai].asString());
|
||||
if (pdFn != NULL) {
|
||||
pdFunctions.push_back(pdFn);
|
||||
}
|
||||
}
|
||||
lookupPDFunctions = false;
|
||||
}
|
||||
// Nothing to find.
|
||||
if (pdFunctions.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f.hasName()) {
|
||||
caller = f.getName().str();
|
||||
}
|
||||
for (auto ii = inst_begin(f); ii != inst_end(f); ++ii) {
|
||||
Instruction *in = &*ii;
|
||||
if (isa<CallInst>(in) || isa<InvokeInst>(in)) {
|
||||
CallSite cs(in);
|
||||
Function *called = cs.getCalledFunction();
|
||||
for (auto pdi = pdFunctions.begin(); pdi != pdFunctions.end(); ++pdi) {
|
||||
Function *pdf = *pdi;
|
||||
if (called == pdf) {
|
||||
std::cout << " " << caller << " called " << pdf->getName().str() << "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char PotentiallyDangerousScanFunctionPass::ID = 1;
|
||||
static RegisterPass<PotentiallyDangerousScanFunctionPass> XX("pot-danger-function-pas", "Potentially Dangerous Call (CWE 676) func pass");
|
||||
@ -1,25 +0,0 @@
|
||||
#ifndef __POTENTIALLYDANGEROUSSCANFUNCTIONPASS_H
|
||||
#define __POTENTIALLYDANGEROUSSCANFUNCTIONPASS_H
|
||||
|
||||
struct PotentiallyDangerousScanFunctionPass : public FunctionPass {
|
||||
private:
|
||||
std::string _cfgFilePath;
|
||||
std::vector<Function *> pdFunctions;
|
||||
bool lookupPDFunctions;
|
||||
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PotentiallyDangerousScanFunctionPass() : FunctionPass(ID) {
|
||||
pdFunctions.clear();
|
||||
lookupPDFunctions = true;
|
||||
}
|
||||
virtual bool runOnFunction(Function &);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
std::string getConfigFilePath() { return _cfgFilePath; }
|
||||
void setConfigFilePath(std::string);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* PotentiallyDangerousScanUserMethodUserMethod
|
||||
*
|
||||
* This example is implemented looking at User list associated
|
||||
* with p.d. function.
|
||||
*
|
||||
* Similar to PotentiallyDangerousScanUserMethod and
|
||||
* PotentiallyDangerousScanUserMethodFunctionPass
|
||||
*
|
||||
*/
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/ValueHandle.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/Analysis/CallGraph.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <json/config.h>
|
||||
#include <json/value.h>
|
||||
#include <json/reader.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "PotentiallyDangerousScanUserMethod.h"
|
||||
|
||||
void
|
||||
PotentiallyDangerousScanUserMethod::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PotentiallyDangerousScanUserMethod::runOnModule(Module &M)
|
||||
{
|
||||
Json::Value fnDict;
|
||||
std::ifstream cfgFileStream;
|
||||
|
||||
cfgFileStream.open(this->getConfigFilePath());
|
||||
cfgFileStream >> fnDict;
|
||||
cfgFileStream.close();
|
||||
Json::Value fnList = fnDict["functions"];
|
||||
assert(fnList.isArray() && "fnList was not an array");
|
||||
Json::ArrayIndex aLen = fnList.size();
|
||||
for (Json::ArrayIndex ai = 0; ai < aLen; ai++) {
|
||||
Function *f = M.getFunction(fnList[ai].asString());
|
||||
if (f == NULL) {
|
||||
continue;
|
||||
}
|
||||
for (auto fi = f->user_begin(); fi != f->user_end(); ++fi) {
|
||||
User *u = *fi;
|
||||
if (isa<CallInst>(u) || isa<InvokeInst>(u)) {
|
||||
|
||||
/* CallSite is a nice container for call and invoke */
|
||||
CallSite cs(u);
|
||||
Function *caller = cs.getCaller();
|
||||
std::string callerName = "unnamed_func";
|
||||
if (caller->hasName()) {
|
||||
callerName = caller->getName().str();
|
||||
}
|
||||
std::cout << " " << callerName << " calls "
|
||||
<< fnList[ai].asString() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char PotentiallyDangerousScanUserMethod::ID = 1;
|
||||
static RegisterPass<PotentiallyDangerousScanUserMethod> XX("pot-danger-user-method", "Potentially Dangerous Call (CWE 676) done with User list");
|
||||
@ -1,19 +0,0 @@
|
||||
#ifndef __POTENTIALLYDANGEROUSSCANUSERMETHOD_H
|
||||
#define __POTENTIALLYDANGEROUSSCANUSERMETHOD_H
|
||||
|
||||
struct PotentiallyDangerousScanUserMethod : public ModulePass {
|
||||
private:
|
||||
std::string _cfgFilePath;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PotentiallyDangerousScanUserMethod() : ModulePass(ID) { }
|
||||
virtual bool runOnModule(Module &);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
void setConfigFilePath(std::string s) { _cfgFilePath = s; }
|
||||
std::string getConfigFilePath() { return _cfgFilePath; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* There are methods that are much better than what I am doing. There are
|
||||
* aggressive methods such as Andersen's Alias Analysis and there are
|
||||
* lazy methods such as making use of the MemoryDependenceAnalysis API.
|
||||
*
|
||||
* What is going on here is quite basic and will miss many things.
|
||||
*/
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "StoreCollector.h"
|
||||
|
||||
void
|
||||
StoreCollector::collect(Function *f)
|
||||
{
|
||||
collectedFunction = f;
|
||||
if (storeMap.empty() == false) {
|
||||
storeMap.clear();
|
||||
}
|
||||
for (auto ii = inst_begin(*f); ii != inst_end(*f); ++ii) {
|
||||
Instruction *in = &*ii;
|
||||
if (!isa<StoreInst>(in)) {
|
||||
continue;
|
||||
}
|
||||
StoreInst *s = cast<StoreInst>(in);
|
||||
Value *storedVal = s->getValueOperand();
|
||||
Value *storedLoc = s->getPointerOperand();
|
||||
storeMap[storedLoc] = storedVal;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
#ifndef __STORECOLLECTOR_H
|
||||
#define __STORECOLLECTOR_H
|
||||
|
||||
class StoreCollector {
|
||||
// key is pointer and value is the stored value
|
||||
std::map<Value *, Value *> storeMap;
|
||||
Function *collectedFunction;
|
||||
|
||||
public:
|
||||
StoreCollector() {
|
||||
collectedFunction = NULL;
|
||||
}
|
||||
void collect(Function *);
|
||||
Function *getFunction() {
|
||||
return collectedFunction;
|
||||
}
|
||||
|
||||
Value *find(Value *storeLoc) {
|
||||
auto s = storeMap.find(storeLoc);
|
||||
if (s == storeMap.end()) {
|
||||
return NULL;
|
||||
}
|
||||
return s->second;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* This is a dependency for a few of the passes. Collects
|
||||
* call sites and organizes them by TargetCallType.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <json/config.h>
|
||||
#include <json/value.h>
|
||||
#include <json/reader.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "TargetCallSitesPass.h"
|
||||
#include "../Transform/FunctionExternalizer.h"
|
||||
|
||||
void
|
||||
TargetCallSitesPass::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TargetCallSitesPass::parseConfig(std::string configFilePath, TargetCallType tct,
|
||||
Module *M)
|
||||
{
|
||||
Json::Value dict;
|
||||
std::ifstream strm;
|
||||
|
||||
// XXX quite trusting
|
||||
strm.open(configFilePath);
|
||||
strm >> dict;
|
||||
strm.close();
|
||||
Json::Value::Members mems = dict.getMemberNames();
|
||||
for (auto memIt = mems.begin(); memIt != mems.end(); ++memIt) {
|
||||
std::string fnName = *memIt;
|
||||
|
||||
int argIdx = dict[fnName].asInt();
|
||||
assert(argIdx >= -1 && "Argument index should be >= -1");
|
||||
|
||||
/*
|
||||
* Determine if the name given for the function in the config is
|
||||
* a name of a function in this module.
|
||||
*/
|
||||
Function *fp = M->getFunction(fnName);
|
||||
if (fp == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if argument counts match up
|
||||
* If we were cool, we would check arg types if we detected
|
||||
* no name mangling.
|
||||
*
|
||||
* The difference between sink and source cases is that at some point
|
||||
* want to not just handle return value of source but allow for in/out
|
||||
* or out arguments to be tainted.
|
||||
*/
|
||||
if (argIdx != -1 && \
|
||||
(fp->arg_size() == 0 || fp->arg_size() <= (unsigned)argIdx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check this Function's User list. If there is no instruction using
|
||||
* this function, then we have nothing to check for it.
|
||||
*/
|
||||
if (fp->user_empty() == true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ok, so we have name, argument, function, and a non-empty user list */
|
||||
for (auto userIt = fp->user_begin(); userIt != fp->user_end();
|
||||
++userIt) {
|
||||
User *targUser = *userIt;
|
||||
|
||||
/*
|
||||
* Not handling functions passed call backs or entry pts based on
|
||||
* RT environment, so just get CallSites. Then make sure the called
|
||||
* function is the targeted function (from config file)
|
||||
*/
|
||||
if (!isa<CallInst>(targUser) && !isa<InvokeInst>(targUser)) {
|
||||
continue;
|
||||
}
|
||||
Instruction *targInst = cast<Instruction>(targUser);
|
||||
std::unique_ptr<TargetCallSite> tcs(new TargetCallSite(targInst,
|
||||
argIdx));
|
||||
targetCallMap[tct].push_back(std::move(tcs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TargetCallSitesPass::runOnModule(Module &M)
|
||||
{
|
||||
errs() << "Running target call sites pass.\n";
|
||||
|
||||
for (auto k = targetConfigMap.begin(); k != targetConfigMap.end(); ++k) {
|
||||
TargetCallType t = k->first;
|
||||
std::string p = k->second;
|
||||
parseConfig(p, t, &M);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
char TargetCallSitesPass::ID = 0;
|
||||
static RegisterPass<TargetCallSitesPass> XX("target-call-sites", "");
|
||||
@ -1,102 +0,0 @@
|
||||
#ifndef __TARGETCALLSITESPASS_H
|
||||
#define __TARGETCALLSITESPASS_H
|
||||
|
||||
// Probably should just derive from CallSite, but so it goes.
|
||||
class TargetCallSite {
|
||||
CallSite callSite;
|
||||
int argOperandIndex;
|
||||
|
||||
public:
|
||||
TargetCallSite(Instruction *c, int i) : callSite(c), argOperandIndex(i) {}
|
||||
~TargetCallSite() { }
|
||||
|
||||
int getArgIndex() {
|
||||
return argOperandIndex;
|
||||
}
|
||||
|
||||
Instruction *getInstruction() {
|
||||
return callSite.getInstruction();
|
||||
}
|
||||
|
||||
Function *getCaller() {
|
||||
return callSite.getCaller();
|
||||
}
|
||||
|
||||
Function *getCalled() {
|
||||
return callSite.getCalledFunction();
|
||||
}
|
||||
|
||||
Value *getTarget() {
|
||||
if (argOperandIndex == -1) {
|
||||
return callSite.getInstruction();
|
||||
}
|
||||
return callSite.getArgOperand(argOperandIndex);
|
||||
}
|
||||
};
|
||||
|
||||
struct TargetCallSitesPass : public ModulePass {
|
||||
static char ID;
|
||||
|
||||
typedef enum _TargetCallType {
|
||||
SinkCall,
|
||||
SourceCall
|
||||
} TargetCallType;
|
||||
|
||||
TargetCallSitesPass() : ModulePass(ID) {
|
||||
// There is probably a better, c++-ier way to do this with templates
|
||||
// or something.
|
||||
targetConfigMap[SinkCall] = "";
|
||||
targetCallMap[SinkCall].reserve(0);
|
||||
targetConfigMap[SourceCall] = "";
|
||||
targetCallMap[SourceCall].reserve(0);
|
||||
}
|
||||
~TargetCallSitesPass() {
|
||||
for (auto k = targetCallMap.begin(); k != targetCallMap.end(); ++k) {
|
||||
targetCallMap[k->first].clear();
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool runOnModule(Module &);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
void setConfig(TargetCallType ty, std::string path) {
|
||||
targetConfigMap[ty] = path;
|
||||
}
|
||||
|
||||
typedef std::vector<std::unique_ptr<TargetCallSite>> TargetVector;
|
||||
typedef TargetVector::const_iterator iterator;
|
||||
|
||||
iterator src_begin() {
|
||||
return targetCallMap[SourceCall].begin();
|
||||
}
|
||||
iterator src_end() {
|
||||
return targetCallMap[SourceCall].end();
|
||||
}
|
||||
bool src_empty() {
|
||||
return targetCallMap[SourceCall].empty();
|
||||
}
|
||||
iterator src_erase(iterator pos) {
|
||||
return targetCallMap[SourceCall].erase(pos);
|
||||
}
|
||||
|
||||
iterator snk_begin() {
|
||||
return targetCallMap[SinkCall].begin();
|
||||
}
|
||||
iterator snk_end() {
|
||||
return targetCallMap[SinkCall].end();
|
||||
}
|
||||
bool snk_empty() {
|
||||
return targetCallMap[SinkCall].empty();
|
||||
}
|
||||
iterator snk_erase(iterator pos) {
|
||||
return targetCallMap[SinkCall].erase(pos);
|
||||
}
|
||||
|
||||
private:
|
||||
void parseConfig(std::string, TargetCallType, Module *);
|
||||
std::map<TargetCallType, std::string> targetConfigMap;
|
||||
std::map<TargetCallType, TargetVector> targetCallMap;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Comminute
|
||||
*
|
||||
* This is a very basic example of using a (legacy) pass
|
||||
* manager. We configure passes to run based on command
|
||||
* line program options given. Some passes have dependencies,
|
||||
* some do not. It is meant to help show how you can
|
||||
* not have to do passes via opt, but via a programmatic
|
||||
* means.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "llvm/LinkAllPasses.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Analysis/MemoryDependenceAnalysis.h"
|
||||
#include "llvm/IRReader/IRReader.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Bitcode/ReaderWriter.h"
|
||||
#include "llvm/Bitcode/BitcodeWriterPass.h"
|
||||
#include "llvm-c/Core.h"
|
||||
#include "llvm/Analysis/CallGraph.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "Analysis/TargetCallSitesPass.h"
|
||||
#include "Transform/FunctionExternalizer.h"
|
||||
#include "Transform/ChoosePhiValue.h"
|
||||
#include "Analysis/NaiveConstantArgCheck.h"
|
||||
#include "Analysis/NaiveSensitiveDataLeak.h"
|
||||
#include "Analysis/NaiveFileDescLeak.h"
|
||||
#include "Analysis/PotentiallyDangerousScan.h"
|
||||
#include "Analysis/PotentiallyDangerousScanUserMethod.h"
|
||||
#include "Analysis/PotentiallyDangerousScanFunctionPass.h"
|
||||
|
||||
/*
|
||||
* Command line arguments...
|
||||
*
|
||||
* comminute <command> <input.bc> <output.bc>
|
||||
*
|
||||
* commands:
|
||||
* -choose-phi-value <int> :: choose incoming edge to phi node
|
||||
* -naive-sensitive-data-leak :: very basic sensitive data leak check
|
||||
* -naive-fd-leak :: very basic file descriptor leak check
|
||||
* -naive-constant-arg :: check some functions for constant arg usage
|
||||
* -dangerous-function :: looks for calls a ``weak'' API
|
||||
* -dangerous-function-user-method :: " but using User list
|
||||
* -dangerous-function-function-pass :: " but as a Function pass
|
||||
*
|
||||
*/
|
||||
cl::opt<std::string> InputBitcodeFile(cl::Positional, cl::desc("<input.bc>"),
|
||||
cl::Required);
|
||||
cl::opt<std::string> OutputBitcodeFile(cl::Positional, cl::desc("<output.bc>"),
|
||||
cl::Required);
|
||||
cl::opt<int> ChoosePhiValuePass("choose-phi-value",
|
||||
cl::desc("Choose value to use from PhiNode (defaults to first)"),
|
||||
cl::init(-1));
|
||||
cl::opt<bool> NaiveSDL("naive-sensitive-data-leak",
|
||||
cl::desc("Perform Naive Sensitive Data Leak Analysis"), cl::init(false));
|
||||
cl::opt<bool> NaiveCAC("naive-constant-arg",
|
||||
cl::desc("Perform Naive Constant Argument Check"), cl::init(false));
|
||||
cl::opt<bool> NaiveFDL("naive-fd-leak",
|
||||
cl::desc("Perform Naive File Desc Leak check"), cl::init(false));
|
||||
cl::opt<bool> PotentiallyDangerous("dangerous-function",
|
||||
cl::desc("Silly CWE 676"), cl::init(false));
|
||||
cl::opt<bool> PotentiallyDangerousUserMethod("dangerous-function-user-method",
|
||||
cl::desc("Silly CWE 676 using User list"), cl::init(false));
|
||||
cl::opt<bool> PotentiallyDangerousFunctionPass("dangerous-function-fpass",
|
||||
cl::desc("Silly CWE 676 using function pass"), cl::init(false));
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
legacy::PassManager passManager;
|
||||
std::unique_ptr<Module> irModule;
|
||||
ModulePass *modPass;
|
||||
SMDiagnostic err;
|
||||
raw_fd_ostream *outputStream;
|
||||
|
||||
cl::ParseCommandLineOptions(argc, argv);
|
||||
|
||||
std::cout << "<C> Reading input bitcode file: " << InputBitcodeFile << "\n";
|
||||
irModule = parseIRFile(InputBitcodeFile, err,
|
||||
*unwrap(LLVMGetGlobalContext()));
|
||||
if (irModule == nullptr) {
|
||||
std::cout << "<C> IR issue: " << err.getMessage().str() << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the pass that removes the body of some functions we do not
|
||||
* wish to have as local to this module.
|
||||
*/
|
||||
std::cout << "<C> Adding function externalizer pass.\n";
|
||||
FunctionExternalizer *fe = new FunctionExternalizer();
|
||||
fe->setFunctionListFilePath("conf/fexternalizer.txt");
|
||||
passManager.add(fe);
|
||||
|
||||
/*
|
||||
* The mem2reg pass promotes alloc+load+store to register based.
|
||||
* This helps with analysis by reducing load/store chasing.
|
||||
*/
|
||||
std::cout << "<C> Adding mem2reg pass.\n";
|
||||
passManager.add(createPromoteMemoryToRegisterPass());
|
||||
|
||||
std::cout << "<C> Adding constant propagation passes.\n";
|
||||
passManager.add(createConstantPropagationPass());
|
||||
passManager.add(createIPConstantPropagationPass());
|
||||
|
||||
/*
|
||||
* The issue here is that one selection is made. To be better,
|
||||
* you would want to reason, as best you can, about the paths
|
||||
* selected. The same goes for branch analysis, etc. This is like
|
||||
* taking an axe to things :-P.
|
||||
*/
|
||||
if (ChoosePhiValuePass >= 0) {
|
||||
std::cout << "<C> Adding phi value selector pass\n";
|
||||
std::cout << "<C> Using edge index: " << ChoosePhiValuePass << "\n";
|
||||
ChoosePhiValue *c = new ChoosePhiValue();
|
||||
c->setEdgeIndex(ChoosePhiValuePass);
|
||||
passManager.add(c);
|
||||
}
|
||||
|
||||
if (NaiveSDL) {
|
||||
/*
|
||||
* Add the naive sensitive data leak checking pass.
|
||||
*
|
||||
*/
|
||||
std::cout << "<C> Adding naive sensitive data leak pass.\n";
|
||||
TargetCallSitesPass *pt = new TargetCallSitesPass();
|
||||
pt->setConfig(TargetCallSitesPass::SourceCall,
|
||||
"conf/sensitivesource.cfg");
|
||||
pt->setConfig(TargetCallSitesPass::SinkCall,
|
||||
"conf/sensitivesink.cfg");
|
||||
passManager.add(pt);
|
||||
NaiveSensitiveDataLeak *n = new NaiveSensitiveDataLeak();
|
||||
passManager.add(n);
|
||||
} else if (NaiveFDL) {
|
||||
/*
|
||||
* Add the fd leak check.
|
||||
*/
|
||||
std::cout << "<C> Adding naive file descriptor leak pass.\n";
|
||||
TargetCallSitesPass *pt = new TargetCallSitesPass();
|
||||
pt->setConfig(TargetCallSitesPass::SourceCall,
|
||||
"conf/fdsource.cfg");
|
||||
pt->setConfig(TargetCallSitesPass::SinkCall,
|
||||
"conf/fdsink.cfg");
|
||||
passManager.add(pt);
|
||||
NaiveFileDescLeak *n = new NaiveFileDescLeak();
|
||||
passManager.add(n);
|
||||
} else if (NaiveCAC) {
|
||||
/*
|
||||
* Add the naive constant argument checker pass. This also adds
|
||||
* the ConstantPropagation pass which attempts to lower cases like
|
||||
* int i = 0; foo(i); to foo(0);. This makes it easier for us to
|
||||
* do the constant checking without having to follow-back.
|
||||
*
|
||||
*/
|
||||
std::cout << "<C> Adding naive constant argument pass.\n";
|
||||
NaiveConstantArgCheck *nca = new NaiveConstantArgCheck();
|
||||
nca->setConfigFilePath("conf/constantarg.cfg");
|
||||
passManager.add(nca);
|
||||
} else if (PotentiallyDangerous) {
|
||||
std::cout << "<C> Adding call graph pass\n";
|
||||
passManager.add(new CallGraphWrapperPass());
|
||||
std::cout << "<C> Adding dangerous fn scan pass.\n";
|
||||
PotentiallyDangerousScan *p = new PotentiallyDangerousScan();
|
||||
p->setConfigFilePath("conf/pdfunctions.cfg");
|
||||
passManager.add(p);
|
||||
} else if (PotentiallyDangerousUserMethod) {
|
||||
std::cout << "<C> Adding dangerous fn scan user method pass.\n";
|
||||
PotentiallyDangerousScanUserMethod *p = new PotentiallyDangerousScanUserMethod();
|
||||
p->setConfigFilePath("conf/pdfunctions.cfg");
|
||||
passManager.add(p);
|
||||
} else if (PotentiallyDangerousFunctionPass) {
|
||||
std::cout << "<C> Adding dangerous fn scan function pass.\n";
|
||||
PotentiallyDangerousScanFunctionPass *p = new PotentiallyDangerousScanFunctionPass();
|
||||
p->setConfigFilePath("conf/pdfunctions.cfg");
|
||||
passManager.add(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open output stream and use that as conduit for writing output
|
||||
* bitcode file.
|
||||
*
|
||||
*/
|
||||
std::cout << "<C> Adding bitcode writer pass\n";
|
||||
outputStream = new raw_fd_ostream(OutputBitcodeFile, ec, sys::fs::F_None);
|
||||
modPass = createBitcodeWriterPass(*outputStream, false, true);
|
||||
passManager.add(modPass);
|
||||
|
||||
/*
|
||||
* Actually run the passes added on this module. With this very
|
||||
* basic tool, there is just results to std[out|err].
|
||||
*
|
||||
*/
|
||||
std::cout << "<C> Running passes\n";
|
||||
passManager.run(*irModule.get());
|
||||
outputStream->close();
|
||||
std::cout << "<C> Finished...\n";
|
||||
return 0;
|
||||
}
|
||||
@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Choose a value from a Phi node and replace all
|
||||
* Phi node uses with it. This ignores some paths, but
|
||||
* simplifies analysis. Will attempt to drop no longer
|
||||
* used basic blocks.
|
||||
*/
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/SymbolTableListTraits.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "ChoosePhiValue.h"
|
||||
|
||||
bool
|
||||
ChoosePhiValue::runOnModule(Module &M)
|
||||
{
|
||||
errs() << "Running choose phi value pass.\n";
|
||||
bool rv = false;
|
||||
|
||||
/*
|
||||
* For every function in this compilation unit, iterate through
|
||||
* the instructions looking for PHINodes. When a PHINode is found,
|
||||
* the chosen incoming edge will be used, if it exists, otherwise
|
||||
* the first (0) is chosen. The edge is removed.
|
||||
*
|
||||
* Take all the removed edges and replace the corresponding PHINode
|
||||
* uses with the edge's value. So, where before things were using
|
||||
* PHINode as the Value, they will use the incoming edge Value.
|
||||
* Remove all other incoming edges and then erase the PHINode.
|
||||
* Then, for each unused incoming edge, attempt to erase it's
|
||||
* BasicBlock.
|
||||
*
|
||||
*/
|
||||
for (auto &f : M) {
|
||||
std::vector<std::pair<PHINode *, Value *>> replaceList;
|
||||
|
||||
for (auto ii = inst_begin(f); ii != inst_end(f); ++ii) {
|
||||
Instruction *in = &*ii;
|
||||
if (PHINode *pn = dyn_cast<PHINode>(in)) {
|
||||
unsigned usedEdge = edgeIndex;
|
||||
if (pn->getNumIncomingValues() <= edgeIndex) {
|
||||
errs() << "Not enough incoming values...using 0\n";
|
||||
usedEdge = 0;
|
||||
}
|
||||
// Remove chosen incoming value, add to replacement list.
|
||||
Value *x = pn->removeIncomingValue(usedEdge, false);
|
||||
replaceList.push_back(std::make_pair(pn, x));
|
||||
|
||||
}
|
||||
}
|
||||
// We know we are going to change the CFG.
|
||||
if (replaceList.empty() == false) {
|
||||
rv = true;
|
||||
}
|
||||
for (auto pc : replaceList) {
|
||||
/* Replace all uses of the PHINode with the selected Value */
|
||||
pc.first->replaceAllUsesWith(pc.second);
|
||||
while (pc.first->getNumIncomingValues() > 0) {
|
||||
Value *d = pc.first->removeIncomingValue((unsigned)0, false);
|
||||
// Each instruction resides in a BasicBlock
|
||||
assert(isa<Instruction>(d) == true);
|
||||
Instruction *vi = cast<Instruction>(d);
|
||||
BasicBlock *bb = vi->getParent();
|
||||
if (bb->user_empty()) {
|
||||
bb->eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
// Attempt to remove users of BasicBlock so we can axe it
|
||||
attemptUserReduction(bb);
|
||||
if (bb->user_empty()) {
|
||||
bb->eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
assert(pc.first->users_empty());
|
||||
pc.first->eraseFromParent();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
for (auto ii = inst_begin(f); ii != inst_end(f); ++ii) {
|
||||
Instruction *in = &*ii;
|
||||
assert(!isa<PHINode>(in) && "PHINode chooser missed.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
ChoosePhiValue::attemptUserReduction(BasicBlock *bb)
|
||||
{
|
||||
/*
|
||||
* Go through each user of this and we should just be finding
|
||||
* BranchInst. If there is just one operand, then this is
|
||||
* an unconditional branch and cannot be mucked with (unless
|
||||
* we did deeper analysis), so we just return because
|
||||
* we know there will always be a user of this BasicBlock.
|
||||
*
|
||||
* Otherwise, we make the true/false branch be the same and
|
||||
* not our basic block. ... Repeat all this until gone
|
||||
* through all users...
|
||||
*/
|
||||
for (auto ui = bb->user_begin(); ui != bb->user_end(); ++ui) {
|
||||
User *u = *ui;
|
||||
if (Instruction *i = dyn_cast<Instruction>(u)) {
|
||||
if (BranchInst *bi = dyn_cast<BranchInst>(i)) {
|
||||
if (bi->getNumOperands() == 1) {
|
||||
return;
|
||||
}
|
||||
Value *tb = bi->getOperand(1);
|
||||
Value *fb = bi->getOperand(2);
|
||||
if (tb == cast<Value>(bb)) {
|
||||
bi->setOperand(1, fb);
|
||||
} else if (fb == cast<Value>(bb)) {
|
||||
bi->setOperand(2, tb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char ChoosePhiValue::ID = 0;
|
||||
static RegisterPass<ChoosePhiValue> XX("choose-phi-value",
|
||||
"Choose Phi value to use");
|
||||
@ -1,23 +0,0 @@
|
||||
#ifndef __CHOOSEPHIVALUE_H
|
||||
#define __CHOOSEPHIVALUE_H
|
||||
|
||||
struct ChoosePhiValue : public ModulePass {
|
||||
private:
|
||||
unsigned edgeIndex;
|
||||
void attemptUserReduction(BasicBlock *bb);
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
ChoosePhiValue() : ModulePass(ID) { }
|
||||
virtual bool runOnModule(Module &);
|
||||
|
||||
// phi [ a, b], [ c, d] ....
|
||||
// this gets us [a, b] if edgeIndex = 0.
|
||||
void setEdgeIndex(unsigned e) {
|
||||
edgeIndex = e;
|
||||
}
|
||||
unsigned getEdgeIndex() {
|
||||
return edgeIndex;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Often in Android a thing to do is to
|
||||
* grab source to a project and just build it
|
||||
* in with your native code. This is painful from
|
||||
* the analysis perspective because you (we) do
|
||||
* not want to scan non-customer code. So, what
|
||||
* this attempts to do is to look for any functions
|
||||
* that are libc, libssl, DESLib, etc and convert
|
||||
* them from their lifted-wrapper versions to
|
||||
* just be the external declaration.
|
||||
*
|
||||
* To do this, you just deleteBody() on the function.
|
||||
*
|
||||
*/
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/SymbolTableListTraits.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "FunctionExternalizer.h"
|
||||
|
||||
bool
|
||||
FunctionExternalizer::runOnModule(Module &M)
|
||||
{
|
||||
errs() << "Running function externalizer pass.\n";
|
||||
|
||||
|
||||
std::ifstream fileHandle(this->_functionListFile);
|
||||
std::string fnName;
|
||||
bool rv = false;
|
||||
|
||||
/*
|
||||
* Each line is a function name to extern. It does not do any
|
||||
* checking of the target function's signature etc, so be aware.
|
||||
*/
|
||||
while (std::getline(fileHandle, fnName)) {
|
||||
// skip comment line.
|
||||
if (fnName.find("#", 0) == 0) {
|
||||
continue;
|
||||
}
|
||||
// Does the function exist within this module?
|
||||
Function *f = M.getFunction(fnName);
|
||||
if (f == NULL) {
|
||||
continue;
|
||||
}
|
||||
// Definition is already outside of this module.
|
||||
if (f->isDeclaration()) {
|
||||
continue;
|
||||
}
|
||||
// Remove the body (definition) of the function. Leave declaration.
|
||||
errs() << "Deleting body of function: " << f->getName().str() << "\n";
|
||||
f->deleteBody();
|
||||
rv = true;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
char FunctionExternalizer::ID = 0;
|
||||
static RegisterPass<FunctionExternalizer> XX("fn-extern",
|
||||
"Function externalizer");
|
||||
@ -1,16 +0,0 @@
|
||||
#ifndef __FUNCTIONEXTERNALIZER_H
|
||||
#define __FUNCTIONEXTERNALIZER_H
|
||||
|
||||
struct FunctionExternalizer : public ModulePass {
|
||||
private:
|
||||
std::string _functionListFile;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
FunctionExternalizer() : ModulePass(ID) { }
|
||||
virtual bool runOnModule(Module &);
|
||||
void setFunctionListFilePath(std::string a) { this->_functionListFile = a; }
|
||||
std::string getFunctionListFilePath() { return this->_functionListFile; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,13 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
FE001_foo()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* NCA001
|
||||
*
|
||||
* NaiveConstantArg 001
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void
|
||||
seed_random()
|
||||
{
|
||||
|
||||
srandom(0xdeadbeef);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
long int rv;
|
||||
|
||||
seed_random();
|
||||
rv = random();
|
||||
(void)printf("random value = %ld\n", rv);
|
||||
return 0;
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* NCA002
|
||||
*
|
||||
* NaiveConstantArg 002
|
||||
*
|
||||
* The diff between this and 001 is using a pointer
|
||||
* which causes load/store to occur. They should be
|
||||
* removed by mem2reg. Let's see.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void
|
||||
seed_random()
|
||||
{
|
||||
unsigned int *seedp;
|
||||
|
||||
seedp = (unsigned int *)malloc(sizeof(unsigned int));
|
||||
if (seedp == NULL) return;
|
||||
*seedp = 0xdeadbeef;
|
||||
srandom(*seedp);
|
||||
free(seedp);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
long int rv;
|
||||
|
||||
seed_random();
|
||||
rv = random();
|
||||
(void)printf("random value = %ld\n", rv);
|
||||
return 0;
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* NFDL001
|
||||
*
|
||||
* Naive File Descriptor Leak 001
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
void
|
||||
leaks_fd()
|
||||
{
|
||||
int fd;
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_fd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* NFDL002
|
||||
*
|
||||
* Naive File Descriptor Leak 002
|
||||
* .. well this does not leak.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
foo(char *k)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
leaks_fd()
|
||||
{
|
||||
int fd;
|
||||
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_fd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* NFDL003
|
||||
*
|
||||
* Naive File Descriptor Leak 003
|
||||
*
|
||||
* copies fd to another local variable and closes that... so no leak
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
foo(char *k)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
leaks_fd()
|
||||
{
|
||||
int fd;
|
||||
int fd2;
|
||||
|
||||
fd2 = 0;
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
fd2 = fd;
|
||||
close(fd2);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_fd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* NFDL004
|
||||
*
|
||||
* Naive File Descriptor Leak 004
|
||||
*
|
||||
* One leaks one does not
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
foo(char *k)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
leaks_fd()
|
||||
{
|
||||
int fd, fd2;
|
||||
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
fd2 = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd2 == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_fd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* NFDL005
|
||||
*
|
||||
* Naive File Descriptor Leak 005
|
||||
*
|
||||
* global variable that ``seems'' to leak.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
foo(char *k)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int fd;
|
||||
|
||||
void
|
||||
leaks_fd()
|
||||
{
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_fd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* NFDL006
|
||||
*
|
||||
* Naive File Descriptor Leak 005
|
||||
*
|
||||
* global variable that does not leak, but naive analysis won't catch.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
foo(char *k)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int fd;
|
||||
|
||||
void
|
||||
leaks_fd()
|
||||
{
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
closes_fd()
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_fd();
|
||||
closes_fd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* NFDL005
|
||||
*
|
||||
* Naive File Descriptor Leak 005
|
||||
*
|
||||
* opens and closes a global var fd
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
foo(char *k)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int fd;
|
||||
|
||||
void
|
||||
leaks_fd()
|
||||
{
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
}
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_fd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* NSDL001
|
||||
*
|
||||
* Naive Sensitive Data Leak 001
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
void
|
||||
leaks_passwd()
|
||||
{
|
||||
char *p;
|
||||
struct addrinfo hints, *result;
|
||||
|
||||
p = getpass("enter passwd: ");
|
||||
/* l.v. p is now tainted with sensitive data */
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
/* leak password via getaddrinfo() DNS lookup. contrived af. */
|
||||
(void)getaddrinfo(p, "http", &hints, &result);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_passwd();
|
||||
return 0;
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* NSDL002
|
||||
*
|
||||
* Naive Sensitive Data Leak 002
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
void
|
||||
leaks_passwd(unsigned lookup)
|
||||
{
|
||||
char *p;
|
||||
struct addrinfo hints, *result;
|
||||
|
||||
p = getpass("enter passwd: ");
|
||||
/* l.v. p is now tainted with sensitive data */
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
/* leak password via getaddrinfo() DNS lookup. contrived af. */
|
||||
memset(p, 0, strlen(p)); // XXX :PpPp
|
||||
(void)getaddrinfo(p, "http", &hints, &result);
|
||||
}
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_passwd(random());
|
||||
return 0;
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* NSDL003
|
||||
*
|
||||
* Naive Sensitive Data Leak 003
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
void
|
||||
leaks_passwd(unsigned lookup)
|
||||
{
|
||||
char *p, *a2l = "www.cw-complex.com";
|
||||
struct addrinfo hints, *result;
|
||||
if (lookup) {
|
||||
p = getpass("enter passwd: ");
|
||||
} else {
|
||||
p = a2l;
|
||||
}
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
(void)getaddrinfo(p, "http", &hints, &result); /* which path did p take? */
|
||||
}
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
leaks_passwd(random());
|
||||
return 0;
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* PD001
|
||||
*
|
||||
* Potentially Dangerous 001
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
void
|
||||
blind_copy()
|
||||
{
|
||||
char buf[512];
|
||||
char buf2[32];
|
||||
struct addrinfo hints, *result;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
(void)getaddrinfo("www.evilgoogle.com", "http", &hints, &result);
|
||||
int sfd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||
if (sfd == -1) {
|
||||
return;
|
||||
}
|
||||
memset(&buf, 0, 512);
|
||||
connect(sfd, result->ai_addr, result->ai_addrlen);
|
||||
read(sfd, &buf, 511);
|
||||
strcpy(buf2, buf);
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
blind_copy();
|
||||
return 0;
|
||||
}
|
||||
Двоичные данные
Security Research and Development with LLVM - Andrew Reiter/code/comminute/thirdparty/jsoncpp-1.8.0.tar.gz
поставляемый
Двоичные данные
Security Research and Development with LLVM - Andrew Reiter/code/comminute/thirdparty/jsoncpp-1.8.0.tar.gz
поставляемый
Двоичный файл не отображается.
@ -1,46 +0,0 @@
|
||||
LLVM_VER=3.9
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR)
|
||||
|
||||
|
||||
PASS=FPSkel.so
|
||||
PASS_OBJECTS=FPSkel.o
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
|
||||
# FPSkel
|
||||
|
||||
This is a function pass skeleton.
|
||||
|
||||
|
||||
# Build & Run
|
||||
|
||||
First check the Makefile to set path to llvm-config and version.
|
||||
3.8, 3.9 should be fine, so should 4.0
|
||||
|
||||
```
|
||||
$ make
|
||||
$ opt-X.Y -load built/FPSkel.so -fpskel < file.bc
|
||||
...
|
||||
$
|
||||
```
|
||||
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "FPSkel.h"
|
||||
|
||||
void
|
||||
FPSkel::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
FPSkel::runOnFunction(Function &F)
|
||||
{
|
||||
unsigned nbb = 0;
|
||||
unsigned ins = 0;
|
||||
|
||||
if (F.isDeclaration()) {
|
||||
errs() << "Ignoring function declaration.\n";
|
||||
return false;
|
||||
}
|
||||
if (F.hasName()) {
|
||||
errs() << "\nFunction: " << F.getName() << "\n";
|
||||
} else {
|
||||
errs() << "\nFunction: not named\n";
|
||||
}
|
||||
|
||||
for (auto &B : F) { // Iterate through Basic Blocks in a function
|
||||
++nbb;
|
||||
errs() << " Basic Block found:\n";
|
||||
B.dump();
|
||||
for (auto &I : B) { // Iterate through instructions in the block
|
||||
++ins;
|
||||
}
|
||||
errs() << " --- end of basic block ---\n";
|
||||
}
|
||||
errs() << " Total of " << nbb << " blocks in this function\n";
|
||||
errs() << " Total of " << ins << " instructions in this function\n";
|
||||
errs() << "--- end of function ---\n";
|
||||
|
||||
// return true if CFG has changed.
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register this pass to be made usable with -fpskel option.
|
||||
* Needs the static ID initialized and the pass declaration given.
|
||||
*/
|
||||
char FPSkel::ID = 0;
|
||||
static RegisterPass<FPSkel> XX("fpskel", "Function Pass Skeleton");
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
#ifndef __FPSKEL_H
|
||||
#define __FPSKEL_H
|
||||
|
||||
struct FPSkel : public FunctionPass {
|
||||
/*
|
||||
* For all of your passes you will need this and to define it.
|
||||
* It's address is used by pass system, so the value does not matter.
|
||||
*/
|
||||
static char ID;
|
||||
|
||||
FPSkel() : FunctionPass(ID) { }
|
||||
|
||||
// Called on each function in given compilation unit
|
||||
virtual bool runOnFunction(Function &);
|
||||
|
||||
/*
|
||||
* Used to help order passes by pass manager.
|
||||
* Declare any passes you need run prior here.. as well as
|
||||
* any information such as preserving CFG or similar.
|
||||
*/
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,134 +0,0 @@
|
||||
#
|
||||
# This is all a bit hack-ish, but should give the idea
|
||||
# about llvm-config usage, which is the key tool to make
|
||||
# life easy^Hier.
|
||||
#
|
||||
# make jsoncpp (once)
|
||||
# make
|
||||
#
|
||||
# I assume you have things in /usr/bin. Often this is the
|
||||
# case, but providing a path for you to set.
|
||||
#
|
||||
LLVM_VER=3.9
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-Lthirdparty/jsoncpp-1.8.0/build/src/lib_json -ljsoncpp
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR) -Ithirdparty/jsoncpp-1.8.0/include
|
||||
|
||||
|
||||
PASS=libIntFlip.so
|
||||
PASS_OBJECTS=FlipConfig.o \
|
||||
LiftConstantIntPass.o \
|
||||
ReplaceRandomizer.o \
|
||||
BitFlipRandomizer.o \
|
||||
InjectRandomizers.o \
|
||||
IntReplacerVisitor.o \
|
||||
IntReplacerIterate.o
|
||||
|
||||
|
||||
# IntReplacerVisitor.o
|
||||
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
# Quite the hack :-P
|
||||
jsoncpp:
|
||||
@echo Building jsoncpp-1.8.0
|
||||
cd thirdparty && \
|
||||
tar zxvf jsoncpp-1.8.0.tar.gz && \
|
||||
cd jsoncpp-1.8.0 && \
|
||||
rm -rf build && \
|
||||
mkdir -p build && \
|
||||
cd build && \
|
||||
cmake .. && \
|
||||
make && \
|
||||
cd ../../
|
||||
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo "CPPFLAGS: ${CPPFLAGS}"
|
||||
@echo "CXXFLAGS: ${CXXFLAGS}"
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built
|
||||
|
||||
jsonclean:
|
||||
$(QUIET)rm -rf thirdparty/jsoncpp-1.8.0
|
||||
|
||||
tests:
|
||||
$(QUIET)echo "Generating bitcode from C"
|
||||
$(QUIET)$(CC) -emit-llvm -c -o test/foo.bc test/foo.c
|
||||
$(QUIET)echo "Lifting constants to local variables"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -lift-constant-int-args -o test/foo2.bc < test/foo.bc
|
||||
$(QUIET)echo "Injecting randomizer functions"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -inject-randomizers -o test/foo3.bc < test/foo2.bc
|
||||
$(QUIET)echo "Replacing ints with visitor method based on replace.cfg"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -replace-ints-visitor -o test/foo4.bc -repcfg=replace.cfg < test/foo3.bc
|
||||
$(QUIET)echo "Replacing ints with iteration method based on replace.cfg"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -replace-ints-iterate -o test/foo5.bc -repcfg=replace.cfg < test/foo3.bc
|
||||
$(QUIET)echo "llvm-dissing..."
|
||||
$(QUIET)$(DIS) --o=test/foo.ll test/foo.bc
|
||||
$(QUIET)$(DIS) --o=test/foo2.ll test/foo2.bc
|
||||
$(QUIET)$(DIS) --o=test/foo3.ll test/foo3.bc
|
||||
$(QUIET)$(DIS) --o=test/foo4.ll test/foo4.bc
|
||||
$(QUIET)$(DIS) --o=test/foo5.ll test/foo5.bc
|
||||
$(QUIET)echo "Building foo, foo4, foo5 executables"
|
||||
$(QUIET)$(CC) -o test/foo5 test/foo5.bc -pthread -lbsd
|
||||
$(QUIET)$(CC) -o test/foo4 test/foo4.bc -pthread -lbsd
|
||||
$(QUIET)$(CC) -o test/foo test/foo.bc -pthread -lbsd
|
||||
$(QUIET)echo ""
|
||||
$(QUIET)echo "Generating bitcode from C++ source"
|
||||
$(QUIET)$(CXX) -emit-llvm -c -o test/foopp.bc test/foo.cpp
|
||||
$(QUIET)echo "Lifting constants to local variables"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -lift-constant-int-args -o test/foopp2.bc < test/foopp.bc
|
||||
$(QUIET)echo "Injecting randomizer functions"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -inject-randomizers -o test/foopp3.bc < test/foopp2.bc
|
||||
$(QUIET)echo "Replacing ints with visitor method based on replacepp.cfg"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -replace-ints-visitor -o test/foopp4.bc -repcfg=replacepp.cfg < test/foopp3.bc
|
||||
$(QUIET)echo "Replacing ints with iteration method based on replacepp.cfg"
|
||||
$(QUIET)$(OPT) -load built/libIntFlip.so -replace-ints-iterate -o test/foopp5.bc -repcfg=replacepp.cfg < test/foopp3.bc
|
||||
$(QUIET)echo "llvm-dissing..."
|
||||
$(QUIET)$(DIS) --o=test/foopp.ll test/foopp.bc
|
||||
$(QUIET)$(DIS) --o=test/foopp2.ll test/foopp2.bc
|
||||
$(QUIET)$(DIS) --o=test/foopp3.ll test/foopp3.bc
|
||||
$(QUIET)$(DIS) --o=test/foopp4.ll test/foopp4.bc
|
||||
$(QUIET)$(DIS) --o=test/foopp5.ll test/foopp5.bc
|
||||
$(QUIET)echo "Building foopp, foopp4, foopp5 executables"
|
||||
$(QUIET)$(CXX) -o test/foopp5 test/foopp5.bc -pthread -lbsd
|
||||
$(QUIET)$(CXX) -o test/foopp4 test/foopp4.bc -pthread -lbsd
|
||||
$(QUIET)$(CXX) -o test/foopp test/foopp.bc -pthread -lbsd
|
||||
|
||||
cleantests:
|
||||
rm -f test/*.bc test/*.ll foo foo4 foo5 foopp foopp4 foopp5
|
||||
|
||||
cleanall: clean jsonclean cleantests
|
||||
@ -1,138 +0,0 @@
|
||||
|
||||
# intflip
|
||||
|
||||
Currently, what this does is replaces all 8, 16, 32, and 64 bit integers arguments to functions
|
||||
with possibly randomly changed values. The purpose is to re-run test suites
|
||||
with the modified application in order to simulate the low-probability bit or
|
||||
more flips that could occur in certain extreme situations. The aim is to perform
|
||||
some basic analysis as to the stability of the code given random changes.
|
||||
|
||||
## Aside
|
||||
|
||||
- I do not do much of anything with being sane about memory usage. You're warned. :-P
|
||||
|
||||
- The way I do the RNG bit is possibly overkill. *shrug*. ISO C standard
|
||||
one should be fine.
|
||||
|
||||
- The randomizer insertion could just be C code that gets linked in, but
|
||||
going through the generation and insertion of a function is a good
|
||||
exercise.
|
||||
|
||||
# Requirements
|
||||
|
||||
- LLVM 3.9.0 (should work with 3.8 & 4.0)
|
||||
|
||||
# Building
|
||||
|
||||
$ make jsoncpp
|
||||
$ make
|
||||
|
||||
# Process
|
||||
|
||||
All executables listed below are supposed to be in your path. You will
|
||||
``compile'' to IR and then work on that. I would follow the basic steps
|
||||
and then determine how you want to handle performing the analysis. The
|
||||
analysis would be a combination of running unit, or other, tests with
|
||||
the injected randomizers and analyzing how those runs performed given
|
||||
the probability distribution you are looking at.
|
||||
|
||||
## Passes
|
||||
|
||||
This is a list of passes available. They are intended to be used per the
|
||||
``basic steps'' section below.
|
||||
|
||||
- -lift-constant-int-args
|
||||
- -inject-randomizers
|
||||
- -replace-ints-visitor
|
||||
- -replace-ints-iterate
|
||||
- -replace-ints-cgpass
|
||||
|
||||
## Basic Work Flow
|
||||
|
||||
*Setup replace.cfg*
|
||||
|
||||
The file replace.cfg informs the passes how to setup the random integer replacement action. As the
|
||||
key, you specify the function that will have any call instructions /in/ it that have at least one
|
||||
integer argument, replaced to use an integer value that is randomly selected.
|
||||
|
||||
Each value is a dict that should have the keys/values:
|
||||
- "analyze" : true|false
|
||||
- "type" : 0|1 ... 0 for randomly replace with random value. 1 for randomly replace with random bit flipp
|
||||
- "mean" : we are dealing with 32-bit integers, and compare <= mean on random number.
|
||||
The for
|
||||
{
|
||||
"foo" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 500
|
||||
}
|
||||
}
|
||||
|
||||
*Compile your code to IR*
|
||||
|
||||
> clang++-3.9 -emit-llvm -o foo.bc -c foo.c
|
||||
|
||||
Of course with a larger code base there will be more work involved.
|
||||
There is a tool that is out there to help with, at least, the merging
|
||||
of multiple bitcode files, it may be found [link](https://github.com/travitch/whole-program-llvm "whole-program-llvm").
|
||||
|
||||
*Lift ConstantInt to local variable*
|
||||
|
||||
> opt-3.9 -load built/libIntFlip.so -lift-constant-ints -o foo2.bc < foo.bc
|
||||
|
||||
This will convert constant integers into local variables. This helps
|
||||
the next step of replacing integers used with randomizer function as
|
||||
there is no special casing for constant integers.. we just go after
|
||||
the local vars.
|
||||
|
||||
*Inject the randomizer functions*
|
||||
|
||||
> opt-3.9 -load built/libIntFlip.so -inject-randomizers -o foo3.bc < foo2.bc
|
||||
|
||||
This generates the functions that will possibly change integer values
|
||||
and injects them into the bitcode file. We could just link in code, but
|
||||
we do this to demonstrate some writing of our own functions via the API.
|
||||
|
||||
*Modify integer arguments to use randomizer functions*
|
||||
|
||||
> opt-3.9 -load built/libIntFlip.so -replace-ints -o foo4.bc < foo3.bc
|
||||
|
||||
*Build the executable*
|
||||
|
||||
> llc-3.9 -o=foo4.s foo4.bc
|
||||
> clang++-3.9 -o foo4 foo4.s
|
||||
|
||||
or however you want...
|
||||
|
||||
*Dump IR from resultant bitcode file*
|
||||
|
||||
> llvm-dis-3.9 -o=foo.ll test/foo.bc
|
||||
|
||||
You can do the above for each bitcode file and compare them.
|
||||
|
||||
|
||||
# Some things one might wish to do
|
||||
|
||||
- Wrap all and use a pass manager
|
||||
-- add pass dependencies for ordering
|
||||
- Improved control over configurations
|
||||
- Build out a whole test run harness
|
||||
-- run test cases that have known outcomes (typically, just unit tests and some other)
|
||||
-- adjusting probability distribution mean and seeing how that impacts results
|
||||
- Improved probability distributions..
|
||||
-- evolutionary (evolve with execution steps)
|
||||
- Overall model of app that is intelligently injected based on a model of a specific event
|
||||
-- that would be like a model based on a real gamma ray birst or something weird
|
||||
- Randomly change instructions
|
||||
-- Could be done in a few ways, but would want to be arch specific
|
||||
-- to find all 1-bit mutatable instructions for instruction i, find all instructions j st dist(i,j) = 1
|
||||
-- similar for 2 bit.. just make dist(i,j) = 2...
|
||||
-- Then lift those sets to IR
|
||||
-- so.. you attempt to fix it in IR.. this will not always work because of ``nice graph'' desires.
|
||||
- whatever.
|
||||
|
||||
|
||||
# Inspirational credits
|
||||
- NASA
|
||||
- John Regehr (Utah)
|
||||
- Gamma rays, alpha particles
|
||||
@ -1,49 +0,0 @@
|
||||
{
|
||||
"global" : {
|
||||
},
|
||||
"skip" : [
|
||||
"printf"
|
||||
],
|
||||
"functions" : {
|
||||
"thd_bf8" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"thd_bf16" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"thd_bf32" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"thd_bf64" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 1000000000
|
||||
},
|
||||
"thd_rr8" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"thd_rr16" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"thd_rr32" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"thd_rr64" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
{
|
||||
"global" : {
|
||||
},
|
||||
"skip" : [
|
||||
"printf"
|
||||
],
|
||||
"functions" : {
|
||||
"_ZL7thd_bf8Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"_ZL8thd_bf16Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"_ZL8thd_bf32Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"_ZL8thd_bf64Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 1,
|
||||
"mean" : 1000000000
|
||||
},
|
||||
"_ZL7thd_rr8Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"_ZL8thd_rr16Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"_ZL8thd_rr32Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
},
|
||||
"_ZL8thd_rr64Pv" : {
|
||||
"analyze" : true,
|
||||
"type" : 0,
|
||||
"mean" : 100000000
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
#ifndef __BASERANDOMIZER_H
|
||||
#define __BASERANDOMIZER_H
|
||||
|
||||
class BaseRandomizer {
|
||||
public:
|
||||
static void inject(llvm::Module& M);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Replace = 1,
|
||||
BitFlip = 2
|
||||
} RandomizerKind;
|
||||
|
||||
#endif // !__BASERANDOMIZER_H
|
||||
@ -1,126 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
|
||||
#include "BitFlipRandomizer.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
/*
|
||||
* This macro helps with the varying bit cases. It generates the code
|
||||
* that generates the code where K = nBits is a value:
|
||||
*
|
||||
* intK_t
|
||||
* __bitflip_randomize_iK(intK inArg0)
|
||||
* {
|
||||
* unsigned rv = arc4random();
|
||||
* if (rv <= (unsigned)2^31) {
|
||||
* rv = 1 << (rv % K);
|
||||
* return inArg0 ^ rv;
|
||||
* }
|
||||
* return inArg0;
|
||||
* }
|
||||
*
|
||||
* So this says get a random number and if it is less than some mean
|
||||
* then we flip a random bit of the input integer and return it.
|
||||
* Otherwise, we just return it unscathed.
|
||||
*
|
||||
*/
|
||||
#define injectBFIntN(nBits) \
|
||||
static void \
|
||||
injectInt##nBits(llvm::Module& M, Function *fnRand) \
|
||||
{ \
|
||||
/* \
|
||||
* Add the function intN_t __bitflip_randomizer_iN__(intN_t, int32_t) \
|
||||
*/ \
|
||||
LLVMContext& ctx = M.getContext(); \
|
||||
/* \
|
||||
* define i8 @__bitflip_randomizer_i8__(i8 %intToFlip, i32 %meanValue) { \
|
||||
* } \
|
||||
*/ \
|
||||
std::string int##nBits##_rand = "__bitflip_randomizer_i" #nBits "__"; \
|
||||
Constant *cTmp = M.getOrInsertFunction(int##nBits##_rand, \
|
||||
Type::getInt##nBits##Ty(ctx), \
|
||||
Type::getInt##nBits##Ty(ctx), \
|
||||
Type::getInt32Ty(ctx), \
|
||||
NULL); \
|
||||
Function *bf_i##nBits = cast<Function>(cTmp); \
|
||||
bf_i##nBits->setCallingConv(CallingConv::C); \
|
||||
\
|
||||
Argument& inArg0 = bf_i##nBits->getArgumentList().front(); \
|
||||
inArg0.setName("intToFlip"); \
|
||||
Argument& inArg1 = bf_i##nBits->getArgumentList().back(); \
|
||||
inArg1.setName("meanValue"); \
|
||||
\
|
||||
/* \
|
||||
* entry: \
|
||||
* bf_it: \
|
||||
* return: \
|
||||
*/ \
|
||||
BasicBlock *blkEntry = BasicBlock::Create(ctx, "entry", bf_i##nBits); \
|
||||
BasicBlock *blkBitFlipIt = BasicBlock::Create(ctx, "bf_it", bf_i##nBits); \
|
||||
BasicBlock *blkReturn = BasicBlock::Create(ctx, "return", bf_i##nBits); \
|
||||
/* \
|
||||
* entry: \
|
||||
* %__bf_rand_ = call i32 @arc4random() \
|
||||
* %__bf_lessthan_ = icmp ule i32 %__bf_rand_, %meanValue \
|
||||
* br i1 %__bf_lessthan_, label %bf_it, label %return \
|
||||
*/ \
|
||||
IRBuilder<> builder(blkEntry); \
|
||||
Value *callArc4Random = builder.CreateCall(fnRand, None, "__bf_rand_", nullptr); \
|
||||
Value *lessThan = builder.CreateICmpULE(callArc4Random, &inArg1, "__bf_lessthan_"); \
|
||||
Value *branchBitFlip = builder.CreateCondBr(lessThan, blkBitFlipIt, blkReturn); \
|
||||
\
|
||||
/* \
|
||||
* bf_it: ; preds = %entry \
|
||||
* %__bf_bitflip_ = urem i32 %__bf_rand_, 8 \
|
||||
* %__bf_cast_randrem_ = trunc i32 %__bf_bitflip_ to i8 \
|
||||
* %__bf_shifted_bit_ = shl i8 1, %__bf_cast_randrem_ \
|
||||
* %__bf_xord_retval_ = xor i8 %intToFlip, %__bf_shifted_bit_ \
|
||||
* ret i8 %__bf_xord_retval_ \
|
||||
*/ \
|
||||
builder.SetInsertPoint(blkBitFlipIt); \
|
||||
Value *randModulus = ConstantInt::get(IntegerType::get(ctx, 32), nBits, false); \
|
||||
Value *randRemainder = builder.CreateURem(callArc4Random, randModulus, \
|
||||
"__bf_bitflip_"); \
|
||||
Value *defaultBit = ConstantInt::get(IntegerType::get(ctx, nBits), 1, false); \
|
||||
Value *castRandRem = builder.CreateZExtOrTrunc(randRemainder, \
|
||||
Type::getInt##nBits##Ty(ctx), "__bf_cast_randrem_"); \
|
||||
Value *shiftedBit = builder.CreateShl(defaultBit, castRandRem, \
|
||||
"__bf_shifted_bit_"); \
|
||||
Value *xordReturnVal = builder.CreateXor(&inArg0, shiftedBit, "__bf_xord_retval_"); \
|
||||
builder.CreateRet(xordReturnVal); \
|
||||
\
|
||||
/* \
|
||||
* return: ; preds = %entry \
|
||||
* ret i8 %intToFlip \
|
||||
*/ \
|
||||
builder.SetInsertPoint(blkReturn); \
|
||||
builder.CreateRet(&inArg0); \
|
||||
} \
|
||||
|
||||
|
||||
|
||||
injectBFIntN(64)
|
||||
injectBFIntN(32)
|
||||
injectBFIntN(16)
|
||||
injectBFIntN(8)
|
||||
|
||||
void
|
||||
BitFlipRandomizer::inject(Module& M)
|
||||
{
|
||||
LLVMContext& ctx = M.getContext();
|
||||
|
||||
/*
|
||||
* If arc4random is already in the module, then return the Function for it; otherwise,
|
||||
* declare it so it will be pulled in. It should be within scope, so we assert.
|
||||
*/
|
||||
Constant *lookupRand = M.getOrInsertFunction("arc4random", Type::getInt32Ty(ctx), NULL);
|
||||
assert(lookupRand != NULL && "BitFlipRandomizer::inject: Unable to getFunction(arc4random)\n");
|
||||
|
||||
Function *fnRand = cast<Function>(lookupRand);
|
||||
|
||||
injectInt64(M, fnRand);
|
||||
injectInt32(M, fnRand);
|
||||
injectInt16(M, fnRand);
|
||||
injectInt8(M, fnRand);
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
#ifndef __BITFLIPRANDOMIZER_H
|
||||
#define __BITFLIPRANDOMIZER_H
|
||||
|
||||
#include "BaseRandomizer.h"
|
||||
|
||||
class BitFlipRandomizer : BaseRandomizer {
|
||||
public:
|
||||
static void inject(llvm::Module& M);
|
||||
};
|
||||
|
||||
#endif // !__BITFLIPRANDOMIZER_H
|
||||
@ -1,57 +0,0 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <json/config.h>
|
||||
#include <json/value.h>
|
||||
#include <json/reader.h>
|
||||
|
||||
#include "FlipConfig.h"
|
||||
|
||||
std::vector<std::string> skip_replace_functions = {
|
||||
"__rep_randomizer_i64__",
|
||||
"__rep_randomizer_i32__",
|
||||
"__rep_randomizer_i16__",
|
||||
"__rep_randomizer_i8__",
|
||||
"__bitflip_randomizer_i64__",
|
||||
"__bitflip_randomizer_i32__",
|
||||
"__bitflip_randomizer_i16__",
|
||||
"__bitflip_randomizer_i8__",
|
||||
"__cxa_allocate_exception",
|
||||
"__cxa_throw" // ... There are many RT and other functions to skip... but so it goes.
|
||||
};
|
||||
|
||||
|
||||
FlipConfig::FlipConfig(std::string c)
|
||||
{
|
||||
_configFile = c;
|
||||
std::ifstream cStream;
|
||||
cStream.open(_configFile);
|
||||
cStream >> _configDict;
|
||||
cStream.close();
|
||||
|
||||
Json::Value globalConfig =_configDict["global"]; // XXX Not used yet
|
||||
Json::Value fnConfig = _configDict["functions"];
|
||||
Json::Value skipFn = _configDict["skip"];
|
||||
Json::Value::Members m = fnConfig.getMemberNames();
|
||||
for (auto iii = m.begin(); iii != m.end(); ++iii) {
|
||||
std::string name = *iii;
|
||||
|
||||
Json::Value nv = fnConfig[name];
|
||||
Json::Value meanVal = nv["mean"];
|
||||
Json::Value typeVal = nv["type"];
|
||||
Json::Value analyzeVal = nv["analyze"];
|
||||
|
||||
FunctionConfig fc{analyzeVal.asBool(), typeVal.asUInt(), meanVal.asUInt()};
|
||||
_replace[name] = fc;
|
||||
}
|
||||
if (skipFn.isArray()) {
|
||||
Json::ArrayIndex aLen = skipFn.size();
|
||||
for (Json::ArrayIndex ai = 0; ai < aLen; ai++) {
|
||||
skip_replace_functions.push_back(skipFn[ai].asString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
#ifndef __FLIPCONFIG_H
|
||||
#define __FLIPCONFIG_H
|
||||
|
||||
struct FunctionConfig {
|
||||
bool shouldAnalyze;
|
||||
unsigned randomType; // weak, i know. 0 = full replace, 1 = bit replace
|
||||
unsigned mean; // replace int, if random value is <= mean
|
||||
|
||||
FunctionConfig() { shouldAnalyze = false; randomType = 0; mean = 2828282; }
|
||||
FunctionConfig(bool a, unsigned t, unsigned m) : shouldAnalyze(a), randomType(t), mean(m) {}
|
||||
};
|
||||
|
||||
typedef std::map<std::string, FunctionConfig> ReplaceMap;
|
||||
|
||||
class FlipConfig {
|
||||
private:
|
||||
std::string _configFile;
|
||||
Json::Value _configDict;
|
||||
ReplaceMap _replace;
|
||||
|
||||
public:
|
||||
FlipConfig(std::string c);
|
||||
Json::Value getDict() { return _configDict; }
|
||||
ReplaceMap getReplaceMap() { return _replace; }
|
||||
};
|
||||
|
||||
extern std::vector<std::string> skip_replace_functions;
|
||||
|
||||
#endif // !__FLIPCONFIG_H
|
||||
@ -1,23 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
|
||||
#include "ReplaceRandomizer.h"
|
||||
#include "BitFlipRandomizer.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
struct InjectRandomizers : public ModulePass {
|
||||
static char ID;
|
||||
|
||||
InjectRandomizers() : ModulePass(ID) {}
|
||||
|
||||
virtual bool
|
||||
runOnModule(Module &M)
|
||||
{
|
||||
ReplaceRandomizer::inject(M);
|
||||
BitFlipRandomizer::inject(M);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
char InjectRandomizers::ID = 0;
|
||||
static RegisterPass<InjectRandomizers> XX("inject-randomizers", "Inject randomizer functions");
|
||||
@ -1,134 +0,0 @@
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
#include "TypeValueSupport.h"
|
||||
#include "BaseRandomizer.h"
|
||||
|
||||
#include <json/value.h>
|
||||
#include "FlipConfig.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
extern cl::opt<std::string> ReplaceConfigFileName;
|
||||
|
||||
struct IntReplacerIterate: public ModulePass {
|
||||
static char ID;
|
||||
FlipConfig *_zConfig;
|
||||
|
||||
|
||||
IntReplacerIterate() : ModulePass(ID)
|
||||
{
|
||||
_zConfig = new FlipConfig(ReplaceConfigFileName);
|
||||
|
||||
}
|
||||
virtual bool
|
||||
runOnModule(Module &M)
|
||||
{
|
||||
for (auto mIt = _zConfig->getReplaceMap().begin();
|
||||
mIt != _zConfig->getReplaceMap().end(); ++mIt) {
|
||||
|
||||
std::string fnName = mIt->first;
|
||||
FunctionConfig mFc = mIt->second;
|
||||
|
||||
if (mFc.shouldAnalyze == false) {
|
||||
errs() << "Skipping analysis of " << fnName << " \n";
|
||||
continue;
|
||||
}
|
||||
|
||||
Function *f = M.getFunction(fnName);
|
||||
if (f == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (inst_iterator I = inst_begin(f), E = inst_end(f); I != E; ++I) {
|
||||
if (isa<CallInst>(&*I) || isa<InvokeInst>(&*I)) {
|
||||
CallSite cs(&*I);
|
||||
Function *called = cs.getCalledFunction();
|
||||
if (!called->hasName()) {
|
||||
continue; // XXX Currently require functions to have names
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the call is of a function we don't want to replace args of..
|
||||
*/
|
||||
if (std::find(skip_replace_functions.begin(),
|
||||
skip_replace_functions.end(),
|
||||
called->getName()) != skip_replace_functions.end()) {
|
||||
errs() << "Skipping: " << called->getName().str() << "\n";
|
||||
continue; // Skip it.
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through all the called function's arguments. See if
|
||||
* any are supported by replacement.
|
||||
*/
|
||||
unsigned numArgOps = cs.getNumArgOperands();
|
||||
for (unsigned ii = 0; ii < numArgOps; ii++) {
|
||||
Value *va = cs.getArgOperand(ii);
|
||||
Type *ta = va->getType();
|
||||
|
||||
/*
|
||||
* If not a 8, 16, 32, or 64 bit integer, we skip it.
|
||||
*/
|
||||
if (TypeValueSupport::isReplaceable(ta, va) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Based on configuration, choose the randomization method.
|
||||
*/
|
||||
std::string rndtype = "";
|
||||
if (mFc.randomType == 0) {
|
||||
rndtype = "rep";
|
||||
} else {
|
||||
rndtype = "bitflip";
|
||||
}
|
||||
unsigned nBits = ta->getIntegerBitWidth();
|
||||
std::string rndFnName = "__" + rndtype + "_randomizer_i" + std::to_string(nBits) + "__";
|
||||
|
||||
/*
|
||||
* The randomizer functions /should/ already be in the module, so get the handle.
|
||||
*/
|
||||
Function *insertedRndFn = M.getFunction(rndFnName);
|
||||
assert(insertedRndFn != NULL);
|
||||
|
||||
/*
|
||||
* We allow different means for different functions.
|
||||
*/
|
||||
ConstantInt *mn = ConstantInt::get(M.getContext(), APInt(32, mFc.mean, false));
|
||||
|
||||
/*
|
||||
* Insert call to randomizer with input integer and a mean value.
|
||||
* It will be inserted before the CallInst.
|
||||
*/
|
||||
CallInst *callNewFunc = CallInst::Create(insertedRndFn,
|
||||
{ va, mn }, // Arguments are the integer to maybe flip and the mean value
|
||||
"__rnd_replicant_",
|
||||
cs.getInstruction()); // insert our call to the rnd fn before the targeted call instruction
|
||||
|
||||
/*
|
||||
* Replace the old integer argument with the randomized one
|
||||
*/
|
||||
cs.setArgument(ii, callNewFunc);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
char IntReplacerIterate::ID = 0;
|
||||
static RegisterPass<IntReplacerIterate> XX("replace-ints-iterate", "Replace int function args with randomizer-integers using instruction iteration");
|
||||
@ -1,184 +0,0 @@
|
||||
/*
|
||||
* This is one of three examples of integer replacer pass.
|
||||
* In this example, we make use of the visitor patterned
|
||||
* API.. which allows us to implement an InstVisitor which
|
||||
* let's avoid the if statment checking of instructions
|
||||
* that you can see in the second integer replacer pass
|
||||
*
|
||||
* We implement visitCallSite() which handles both CallInst and
|
||||
* InvokeInst cases as one.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
#include "TypeValueSupport.h"
|
||||
#include "BaseRandomizer.h"
|
||||
|
||||
#include <json/value.h>
|
||||
#include "FlipConfig.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
cl::opt<std::string> ReplaceConfigFileName("repcfg", cl::desc("<replacer cfg>"));
|
||||
|
||||
class CodeModificationLocation {
|
||||
private:
|
||||
Instruction *callLocation;
|
||||
Type *argumentType;
|
||||
Value *argumentValue;
|
||||
unsigned argumentIdx;
|
||||
|
||||
public:
|
||||
CodeModificationLocation(Instruction *c, Type *t, Value *v, unsigned i) : callLocation(c), argumentType(t), argumentValue(v), argumentIdx(i) { };
|
||||
CallSite getCallSite() { return CallSite(callLocation); }
|
||||
Type *getArgumentType() { return argumentType; }
|
||||
Value *getArgumentValue() { return argumentValue; }
|
||||
unsigned getArgumentIdx() { return argumentIdx; }
|
||||
};
|
||||
|
||||
struct IntReplacerVisitor: public ModulePass {
|
||||
static char ID;
|
||||
|
||||
std::vector<CodeModificationLocation> modificationList;
|
||||
FlipConfig *_zConfig;
|
||||
|
||||
IntReplacerVisitor() : ModulePass(ID)
|
||||
{
|
||||
_zConfig = new FlipConfig(ReplaceConfigFileName);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
runOnModule(Module &M)
|
||||
{
|
||||
for (auto mIt = _zConfig->getReplaceMap().begin();
|
||||
mIt != _zConfig->getReplaceMap().end(); ++mIt) {
|
||||
|
||||
std::string fnName = mIt->first;
|
||||
FunctionConfig mFc = mIt->second;
|
||||
if (mFc.shouldAnalyze == false) {
|
||||
errs() << "Per config: skipping analysis of " << fnName << " \n";
|
||||
continue;
|
||||
}
|
||||
|
||||
Function *f = M.getFunction(fnName);
|
||||
if (f == NULL) {
|
||||
errs() << "Can't find function: " << fnName << " \n";
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* The way this currently works -- you can do it many ways -- but the
|
||||
* way this works is that the visit finds any/all call instructions in
|
||||
* the target function. There, it looks to see if there are any integer
|
||||
* arguments that we want to replace with a randomizer.. if there are,
|
||||
* we save them to a list of locations to change. Once gathering that
|
||||
* list, we iterate and modify.
|
||||
*/
|
||||
CheckCallInsts cCheck;
|
||||
cCheck.setIntReplacerVisitor(this);
|
||||
cCheck.visit(*f);
|
||||
|
||||
for (auto iml = modificationList.begin();
|
||||
iml != modificationList.end(); ++iml) {
|
||||
CodeModificationLocation cLoc = *iml;
|
||||
CallSite cs = cLoc.getCallSite();
|
||||
IntegerType *t = cast<IntegerType>(cLoc.getArgumentType());
|
||||
unsigned nBits = t->getBitWidth();
|
||||
|
||||
/* Lookup randomizer function to use */
|
||||
std::string rndtype = "";
|
||||
if (mFc.randomType == 0) {
|
||||
rndtype = "rep";
|
||||
} else {
|
||||
rndtype = "bitflip";
|
||||
}
|
||||
std::string rndFnName = "__" + rndtype + "_randomizer_i" \
|
||||
+ std::to_string(nBits) + "__";
|
||||
|
||||
// the randomizers should already be in, so we assert.
|
||||
Function *insertedRndFn = cs.getParent()->getModule()->getFunction(rndFnName);
|
||||
assert(insertedRndFn != NULL);
|
||||
|
||||
// Base mean value on what the configuration says
|
||||
ConstantInt *mn = ConstantInt::get(M.getContext(), APInt(32, mFc.mean, false));
|
||||
|
||||
CallInst *callNewFunc = CallInst::Create(insertedRndFn,
|
||||
{ cLoc.getArgumentValue(), mn }, // Arguments are the integer to maybe flip and the mean value
|
||||
"__rnd_replicant_",
|
||||
cs.getInstruction()); // insert our call to the rnd fn before the targeted call instruction
|
||||
|
||||
cs.setArgument(cLoc.getArgumentIdx(), callNewFunc);
|
||||
}
|
||||
modificationList.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct CheckCallInsts : public InstVisitor<CheckCallInsts> {
|
||||
IntReplacerVisitor *__ziInst;
|
||||
|
||||
void
|
||||
setIntReplacerVisitor(IntReplacerVisitor *p)
|
||||
{
|
||||
__ziInst = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Could do this with two separate functions visitCallInst() and
|
||||
* visitInvokeInst().. the difference is one is in the exception handling
|
||||
* case and tthe other (the former) is for normal calling contexts.
|
||||
* CallSite provides a reasonable common ground for the two.
|
||||
*
|
||||
*/
|
||||
void
|
||||
visitCallSite(CallSite callSite)
|
||||
{
|
||||
/*
|
||||
* Skip called functions that are on the skip_replace list.
|
||||
*/
|
||||
Function *called = callSite.getCalledFunction();
|
||||
if (!called->hasName()) {
|
||||
return;
|
||||
}
|
||||
if (std::find(skip_replace_functions.begin(),
|
||||
skip_replace_functions.end(),
|
||||
called->getName()) != skip_replace_functions.end()) {
|
||||
errs() << "Skipping replace function: " << called->getName() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each argument, determine if it of type and value that is replaceable.
|
||||
* At this point, we just note the location by storing CallSite, Type,
|
||||
* Value, and an arg index as a place to change.
|
||||
*
|
||||
*/
|
||||
unsigned numArgOps = callSite.getNumArgOperands();
|
||||
for (unsigned ii = 0; ii < numArgOps; ii++) {
|
||||
Value *va = callSite.getArgOperand(ii);
|
||||
Type *ta = va->getType();
|
||||
if (TypeValueSupport::isReplaceable(ta, va)) {
|
||||
CodeModificationLocation ml = CodeModificationLocation(
|
||||
callSite.getInstruction(),
|
||||
ta,
|
||||
va,
|
||||
ii);
|
||||
__ziInst->modificationList.push_back(ml);
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
};
|
||||
};
|
||||
|
||||
char IntReplacerVisitor::ID = 0;
|
||||
static RegisterPass<IntReplacerVisitor> XX("replace-ints-visitor", "Replace int function args with randomizer-integers using instruction visitor");
|
||||
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* This is a module pass that likely will not have
|
||||
* impact given possibly run passes, but for our
|
||||
* purposes we should have it just in case.
|
||||
*
|
||||
* What it does is identifies call instructions that
|
||||
* are passed a integral constant to the target function.
|
||||
* We lift that constant into a local variable and
|
||||
* adjust the function call to use the variable.
|
||||
*
|
||||
* The reason for doing this is that the passes run after
|
||||
* this want to act on variables of integer type and so
|
||||
* this makes that possible to do without checking types..
|
||||
*
|
||||
*/
|
||||
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
|
||||
#include "TypeValueSupport.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
struct LiftConstantIntPass : public ModulePass {
|
||||
static char ID;
|
||||
|
||||
LiftConstantIntPass() : ModulePass(ID) {}
|
||||
|
||||
virtual bool
|
||||
runOnModule(Module &M)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
/*
|
||||
* Visit the call instructions of each function in this
|
||||
* module.
|
||||
*/
|
||||
for (auto &f : M) {
|
||||
|
||||
CheckCallInsts cCheck;
|
||||
cCheck.visit(f);
|
||||
if (cCheck.modified) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
struct CheckCallInsts : public InstVisitor<CheckCallInsts> {
|
||||
bool modified = false;
|
||||
|
||||
void
|
||||
visitCallSite(CallSite callSite)
|
||||
{
|
||||
unsigned numArgOps = callSite.getNumArgOperands();
|
||||
unsigned argIdx;
|
||||
|
||||
/*
|
||||
* Check all arguments to the called function to see
|
||||
* they are a constant integer of a size we can lift.
|
||||
*
|
||||
* If an argument is liftable, we allocated space for it
|
||||
* and store the constant to it. Then, load value and use
|
||||
* it to replace the constant integer argument to the
|
||||
* function called.
|
||||
*/
|
||||
for (argIdx = 0; argIdx < numArgOps; argIdx++) {
|
||||
Value *va = callSite.getArgOperand(argIdx);
|
||||
|
||||
if (TypeValueSupport::isLiftable(va) == true) {
|
||||
ConstantInt *con = cast<ConstantInt>(va);
|
||||
|
||||
unsigned nBits = con->getBitWidth();
|
||||
|
||||
AllocaInst *localized__alloc = new AllocaInst(
|
||||
IntegerType::get(callSite.getParent()->getContext(), nBits), // type to allocate
|
||||
"__intflip_localized", // give the slot a label
|
||||
callSite.getInstruction()); // Insert before call instruction
|
||||
|
||||
StoreInst *localized__store = new StoreInst(
|
||||
con, // value to store
|
||||
localized__alloc, // where to store it
|
||||
callSite.getInstruction()); // Insert before call instruction
|
||||
|
||||
LoadInst *localized__load = new LoadInst(
|
||||
localized__alloc, // pointer to load from
|
||||
(const char *)"__intflip_loaded", // label the slot
|
||||
callSite.getInstruction()); // Insert before call instruction
|
||||
|
||||
/*
|
||||
* after that series we now have:
|
||||
* alloca
|
||||
* store
|
||||
* load
|
||||
* call
|
||||
* sequence of instructions
|
||||
*/
|
||||
|
||||
/* replace the constant in the function call */
|
||||
callSite.setArgument(argIdx, localized__load);
|
||||
new_vars++;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
unsigned new_vars = 0;
|
||||
};
|
||||
};
|
||||
|
||||
char LiftConstantIntPass::ID = 0;
|
||||
static RegisterPass<LiftConstantIntPass> XX("lift-constant-int-args", "Lifts constant int fn args to a local variable");
|
||||
@ -1,113 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
|
||||
#include "ReplaceRandomizer.h"
|
||||
|
||||
using namespace llvm;
|
||||
/*
|
||||
|
||||
* This, for each K bits integer type, will inject the function
|
||||
*
|
||||
* intK_t
|
||||
* __rep_randomizer_iK(intK_t inArg0)
|
||||
* {
|
||||
* unsigned rv = arc4random();
|
||||
* if (rv <= (unsigned)meanValue) {
|
||||
* return rv;
|
||||
* }
|
||||
* return inArg0
|
||||
* }
|
||||
*
|
||||
* The comments in the macro below show the generated IR along each step..but for
|
||||
* K=64.
|
||||
*/
|
||||
#define injectRIntN(nBits) \
|
||||
static void \
|
||||
injectInt##nBits(llvm::Module& M, Function *fnRand) \
|
||||
{ \
|
||||
LLVMContext& ctx = M.getContext(); \
|
||||
/* \
|
||||
* define i64 @__rep_randomizer_i64__(i64 %intToFlip, i32 %meanValue) { \
|
||||
* } \
|
||||
*/ \
|
||||
std::string int##nBits##_rand = "__rep_randomizer_i" #nBits "__"; \
|
||||
Constant *cTmp = M.getOrInsertFunction(int##nBits##_rand, \
|
||||
Type::getInt##nBits##Ty(ctx), \
|
||||
Type::getInt##nBits##Ty(ctx), \
|
||||
Type::getInt32Ty(ctx), \
|
||||
NULL); \
|
||||
Function *rep_i##nBits = cast<Function>(cTmp); \
|
||||
rep_i##nBits->setCallingConv(CallingConv::C); \
|
||||
Argument& inArg0 = rep_i##nBits->getArgumentList().front(); \
|
||||
inArg0.setName("intToFlip"); \
|
||||
Argument& inArg1 = rep_i##nBits->getArgumentList().back(); \
|
||||
inArg1.setName("meanValue"); \
|
||||
/* \
|
||||
* entry: \
|
||||
* replace_it: \
|
||||
* return: \
|
||||
*/ \
|
||||
BasicBlock *blkEntry = BasicBlock::Create(ctx, "entry", rep_i##nBits); \
|
||||
BasicBlock *blkRepIt = BasicBlock::Create(ctx, "replace_it", rep_i##nBits); \
|
||||
BasicBlock *blkReturn = BasicBlock::Create(ctx, "return", rep_i##nBits); \
|
||||
/* \
|
||||
* entry: \
|
||||
* %__rep_rand_ = call i32 @arc4random() \
|
||||
* %__rep_lessthan_ = icmp ule i32 %__rep_rand_, %meanValue \
|
||||
* br i1 %__rep_lessthan_, label %replace_it, label %return \
|
||||
* replace_it: \
|
||||
* return: \
|
||||
*/ \
|
||||
IRBuilder<> builder(blkEntry); \
|
||||
Value *callArc4Random = builder.CreateCall(fnRand, None, "__rep_rand_", nullptr); \
|
||||
Value *lessThan = builder.CreateICmpULE(callArc4Random, &inArg1, "__rep_lessthan_");\
|
||||
Value *branchRep = builder.CreateCondBr(lessThan, blkRepIt, blkReturn); \
|
||||
\
|
||||
/* \
|
||||
* ... \
|
||||
* replace_it: ; preds = %entry \
|
||||
* %__rep_cast_ = zext i32 %__rep_rand_ to i64 \
|
||||
* ret i64 %__rep_cast_ \
|
||||
*/ \
|
||||
builder.SetInsertPoint(blkRepIt); \
|
||||
Value *castRand = builder.CreateZExtOrTrunc(callArc4Random, \
|
||||
Type::getInt##nBits##Ty(ctx), "__rep_cast_"); \
|
||||
Value *cr = builder.CreateRet(castRand); \
|
||||
\
|
||||
/* \
|
||||
* ... \
|
||||
* return: ; preds = %entry \
|
||||
* ret i64 %intToFlip \
|
||||
*/ \
|
||||
builder.SetInsertPoint(blkReturn); \
|
||||
Value *cr2 = builder.CreateRet(&inArg0); \
|
||||
} \
|
||||
|
||||
|
||||
injectRIntN(64)
|
||||
injectRIntN(32)
|
||||
injectRIntN(16)
|
||||
injectRIntN(8)
|
||||
|
||||
void
|
||||
ReplaceRandomizer::inject(Module& M)
|
||||
{
|
||||
LLVMContext& ctx = M.getContext();
|
||||
|
||||
/*
|
||||
* If arc4random is already in the module, then return the Function for it; otherwise,
|
||||
* declare it so it will be pulled in. It should be within scope, so we assert.
|
||||
*/
|
||||
Constant *lookupRand = M.getOrInsertFunction("arc4random", Type::getInt32Ty(ctx), NULL);
|
||||
assert(lookupRand != NULL && "ReplaceRandomizer::inject(): failed getFunction(arc4random)\n");
|
||||
|
||||
Function *fnRand = cast<Function>(lookupRand);
|
||||
|
||||
injectInt64(M, fnRand);
|
||||
injectInt32(M, fnRand);
|
||||
injectInt16(M, fnRand);
|
||||
injectInt8(M, fnRand);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
#ifndef __REPLACERANDOMIZER_H
|
||||
#define __REPLACERANDOMIZER_H
|
||||
|
||||
#include "BaseRandomizer.h"
|
||||
|
||||
class ReplaceRandomizer : BaseRandomizer {
|
||||
public:
|
||||
static void inject(llvm::Module& M);
|
||||
};
|
||||
#endif // !__REPLACERANDOMIZER_H
|
||||
@ -1,53 +0,0 @@
|
||||
#ifndef __TYPEVALUESUPPORT_H
|
||||
#define __TYPEVALUESUPPORT_H
|
||||
|
||||
class TypeValueSupport {
|
||||
|
||||
public:
|
||||
TypeValueSupport() {}
|
||||
|
||||
static bool
|
||||
isLiftable(llvm::Value *v)
|
||||
{
|
||||
/* dyn_cast<>() will return NULL if cast fails */
|
||||
if (llvm::ConstantInt *c = llvm::dyn_cast<llvm::ConstantInt>(v)) {
|
||||
unsigned nBits = c->getBitWidth();
|
||||
switch (nBits) {
|
||||
case 64:
|
||||
case 32:
|
||||
case 16:
|
||||
case 8:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
isReplaceable(llvm::Type *t, llvm::Value *v)
|
||||
{
|
||||
|
||||
/* isa<>() returns boolean as to whether can cast */
|
||||
if (llvm::isa<llvm::ConstantInt>(v) == true) {
|
||||
/*
|
||||
* Maybe assert() and say you should run the lifting
|
||||
* pass before this one? Or just report that...
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/* dyn_cast<>() will return NULL if cast fails */
|
||||
if (llvm::IntegerType *intType = llvm::dyn_cast<llvm::IntegerType>(t)) {
|
||||
unsigned nBits = intType->getBitWidth();
|
||||
switch (nBits) {
|
||||
case 64:
|
||||
case 32:
|
||||
case 16:
|
||||
case 8:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
#endif // !__TYPEVALUESUPPORT_H
|
||||
@ -1,142 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define MAXITER (4 * 4096)
|
||||
|
||||
char
|
||||
bf8(char in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf8(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf8: %d\n", bf8(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
short
|
||||
bf16(short in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf16(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf16: %d\n", bf16(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
bf32(int in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf32(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf32: %d\n", bf32(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
int64_t
|
||||
bf64(int64_t in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf64(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf64: %ld\n", bf64(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
char
|
||||
rr8(char in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr8(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr8: %d\n", rr8(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
short
|
||||
rr16(short in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr16(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr16: %d\n", rr16(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
rr32(int in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr32(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr32: %d\n", rr32(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
int64_t
|
||||
rr64(int64_t in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr64(void *a)
|
||||
{
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr64: %ld\n", rr64(0));
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
pthread_t a,b,c,d,e,f,g,h;
|
||||
printf("Creating.\n");
|
||||
pthread_create(&a, NULL, &thd_bf8, NULL);
|
||||
pthread_create(&b, NULL, &thd_bf16, NULL);
|
||||
pthread_create(&c, NULL, &thd_bf32, NULL);
|
||||
pthread_create(&d, NULL, &thd_bf64, NULL);
|
||||
pthread_create(&e, NULL, &thd_rr8, NULL);
|
||||
pthread_create(&f, NULL, &thd_rr16, NULL);
|
||||
pthread_create(&g, NULL, &thd_rr32, NULL);
|
||||
pthread_create(&h, NULL, &thd_rr64, NULL);
|
||||
printf("Created.\n");
|
||||
pthread_join(a, NULL);
|
||||
pthread_join(b, NULL);
|
||||
pthread_join(c, NULL);
|
||||
pthread_join(d, NULL);
|
||||
pthread_join(e, NULL);
|
||||
pthread_join(f, NULL);
|
||||
pthread_join(g, NULL);
|
||||
pthread_join(h, NULL);
|
||||
return 0;
|
||||
}
|
||||
@ -1,183 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define MAXITER (8 * 4096)
|
||||
|
||||
char
|
||||
bf8(char in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf8(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf8: %d\n", bf8(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
short
|
||||
bf16(short in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf16(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf16: %d\n", bf16(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
bf32(int in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf32(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf32: %d\n", bf32(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
int64_t
|
||||
bf64(int64_t in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_bf64(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_bf64: %ld\n", bf64(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
char
|
||||
rr8(char in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr8(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr8: %d\n", rr8(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
short
|
||||
rr16(short in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr16(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr16: %d\n", rr16(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
rr32(int in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr32(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr32: %d\n", rr32(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
int64_t
|
||||
rr64(int64_t in)
|
||||
{
|
||||
if (in != 0 && " 0" == NULL) {
|
||||
throw 2;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static void *
|
||||
thd_rr64(void *a)
|
||||
{
|
||||
try {
|
||||
for (unsigned i = 0; i < MAXITER; i++)
|
||||
printf("thd_rr64: %ld\n", rr64(0));
|
||||
} catch (int e) {}
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
pthread_t a,b,c,d,e,f,g,h;
|
||||
printf("Max iter = %u\n", MAXITER);
|
||||
printf("Creating.\n");
|
||||
pthread_create(&a, NULL, &thd_bf8, NULL);
|
||||
pthread_create(&b, NULL, &thd_bf16, NULL);
|
||||
pthread_create(&c, NULL, &thd_bf32, NULL);
|
||||
pthread_create(&d, NULL, &thd_bf64, NULL);
|
||||
pthread_create(&e, NULL, &thd_rr8, NULL);
|
||||
pthread_create(&f, NULL, &thd_rr16, NULL);
|
||||
pthread_create(&g, NULL, &thd_rr32, NULL);
|
||||
pthread_create(&h, NULL, &thd_rr64, NULL);
|
||||
printf("Created.\n");
|
||||
pthread_join(a, NULL);
|
||||
pthread_join(b, NULL);
|
||||
pthread_join(c, NULL);
|
||||
pthread_join(d, NULL);
|
||||
pthread_join(e, NULL);
|
||||
pthread_join(f, NULL);
|
||||
pthread_join(g, NULL);
|
||||
pthread_join(h, NULL);
|
||||
return 0;
|
||||
}
|
||||
Двоичные данные
Security Research and Development with LLVM - Andrew Reiter/code/intflip/thirdparty/jsoncpp-1.8.0.tar.gz
поставляемый
Двоичные данные
Security Research and Development with LLVM - Andrew Reiter/code/intflip/thirdparty/jsoncpp-1.8.0.tar.gz
поставляемый
Двоичный файл не отображается.
@ -1,51 +0,0 @@
|
||||
LLVM_VER=3.9
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR)
|
||||
|
||||
PASS=LPSkel.so
|
||||
PASS_OBJECTS=LPSkel.o
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built test/*.bc
|
||||
|
||||
|
||||
tests:
|
||||
$(CC) -emit-llvm -o test/foo.bc -c test/foo.c
|
||||
|
||||
runtests:
|
||||
$(OPT) -load built/LPSkel.so -lpskel < test/foo.bc
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
# LoopPass Skeleton
|
||||
|
||||
Visit loops in code; outer loop met last.
|
||||
|
||||
# Build & Run
|
||||
|
||||
First check the Makefile to set path to llvm-config and version.
|
||||
3.8, 3.9 should be fine, so should 4.0
|
||||
|
||||
```
|
||||
$ make
|
||||
$ opt-X.Y -load built/LPSkel.so -lpskel < file.bc
|
||||
...
|
||||
$
|
||||
```
|
||||
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Analysis/LoopPass.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "LPSkel.h"
|
||||
|
||||
void
|
||||
LPSkel::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
// No changes to CFG, so tell the pass manager
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
bool
|
||||
LPSkel::doFinalization()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LPSkel::doInitialization(Loop *L, LPPassManager &LP)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LPSkel::runOnLoop(Loop *L, LPPassManager &LP)
|
||||
{
|
||||
errs() << " Loop found:\n";
|
||||
L->print(errs(), 1);
|
||||
errs() << " --- end of Loop ---\n";
|
||||
|
||||
// return true if Function has been changed.
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register this pass to be made usable.
|
||||
* Needs the static ID initialized and the pass declaration given.
|
||||
*/
|
||||
char LPSkel::ID = 0;
|
||||
static RegisterPass<LPSkel> XX("lpskel", "Loop Pass Skeleton");
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
#ifndef __LPSKEL_H
|
||||
#define __LPSKEL_H
|
||||
|
||||
struct LPSkel : public LoopPass {
|
||||
/*
|
||||
* For all of your passes you will need this and to define it.
|
||||
* It's address is used by pass system, so the value does not matter.
|
||||
*/
|
||||
static char ID;
|
||||
|
||||
LPSkel() : LoopPass(ID) {
|
||||
}
|
||||
|
||||
// Return true if Function was modified, otherwise false.
|
||||
virtual bool runOnLoop(Loop *L, LPPassManager &LP);
|
||||
|
||||
/*
|
||||
* Used to help order passes by pass manager.
|
||||
* Declare any passes you need run prior here.. as well as
|
||||
* any information such as preserving CFG or similar.
|
||||
*/
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
virtual bool doInitialization(Loop *L, LPPassManager &LPM);
|
||||
virtual bool doFinalization();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,13 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
unsigned z = 0;
|
||||
if (argc <= 0) argc = 30;
|
||||
for (unsigned k = 0; k < argc; ++k) {
|
||||
z += k * 2;
|
||||
}
|
||||
printf("zeta: %u\n", z);
|
||||
return 0;
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
LLVM_VER=3.9
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR)
|
||||
|
||||
|
||||
PASS=MPSkel.so
|
||||
PASS_OBJECTS=MPSkel.o
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
|
||||
# MPSkel
|
||||
|
||||
This is a module pass skeleton.
|
||||
|
||||
|
||||
# Build & Run
|
||||
|
||||
First check the Makefile to set path to llvm-config and version.
|
||||
3.8, 3.9 should be fine, so should 4.0
|
||||
|
||||
```
|
||||
$ make
|
||||
$ opt-X.Y -load built/MPSkel.so -mpskel < file.bc
|
||||
...
|
||||
$
|
||||
```
|
||||
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "MPSkel.h"
|
||||
|
||||
void
|
||||
MPSkel::getAnalysisUsage(AnalysisUsage &AU) const
|
||||
{
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
bool
|
||||
MPSkel::runOnModule(Module &M)
|
||||
{
|
||||
|
||||
/* Iterate through all functions in this module */
|
||||
for (auto &F : M) {
|
||||
std::string fname = "not named";
|
||||
if (F.hasName()) {
|
||||
fname = F.getName().str();
|
||||
}
|
||||
|
||||
// If no uses, don't look further.
|
||||
if (F.user_empty()) {
|
||||
errs() << "Function (" << fname << ") not used.\n";
|
||||
continue;
|
||||
}
|
||||
errs() << "Listing uses for function (" << fname << ")\n";
|
||||
for (auto uit = F.user_begin(); uit != F.user_end(); ++uit) {
|
||||
User *u = *uit;
|
||||
errs() << " ";
|
||||
std::string pn = "";
|
||||
|
||||
// Is this use a Call or Invoke instruction?
|
||||
if (isa<CallInst>(u) || isa<InvokeInst>(u)) {
|
||||
// It is, so let's use the common class CallSite
|
||||
CallSite cs(dyn_cast<Instruction>(u));
|
||||
|
||||
// Instruction in a BasicBlock in a Function.
|
||||
Function *caller = cs.getParent()->getParent();
|
||||
if (caller->hasName()) {
|
||||
pn = caller->getName().str();
|
||||
} else {
|
||||
pn = "not named";
|
||||
}
|
||||
errs() << pn << ": ";
|
||||
}
|
||||
|
||||
// Just print out what Value is
|
||||
u->dump();
|
||||
// If has debug info, we should just dump that as well
|
||||
}
|
||||
errs() << "\n";
|
||||
}
|
||||
return false; // CFG did not change
|
||||
}
|
||||
|
||||
/*
|
||||
* Register this pass to be made usable.
|
||||
* Needs the static ID initialized and the pass declaration given.
|
||||
*/
|
||||
char MPSkel::ID = 0;
|
||||
static RegisterPass<MPSkel> XX("mpskel", "Module Pass Skeleton");
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
#ifndef __MPSKEL_H
|
||||
#define __MPSKEL_H
|
||||
|
||||
struct MPSkel : public ModulePass {
|
||||
/*
|
||||
* For all of your passes you will need this and to define it.
|
||||
* It's address is used by pass system, so the value does not matter.
|
||||
*/
|
||||
static char ID;
|
||||
|
||||
MPSkel() : ModulePass(ID) { }
|
||||
|
||||
// Called on each compilation unit
|
||||
virtual bool runOnModule(Module &);
|
||||
|
||||
/*
|
||||
* Used to help order passes by pass manager.
|
||||
* Declare any passes you need run prior here.. as well as
|
||||
* any information such as preserving CFG or similar.
|
||||
*/
|
||||
virtual void getAnalysisUsage(AnalysisUsage &) const;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,76 +0,0 @@
|
||||
LLVM_VER=3.8
|
||||
#LLVM_VER=3.9
|
||||
#LLVM_VER=4.0
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR)
|
||||
|
||||
PASS=libnpassert.so
|
||||
PASS_OBJECTS=NullPtrAssertPass.o
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built
|
||||
|
||||
tests:
|
||||
$(QUIET)echo "Generating bitcode from C"
|
||||
$(QUIET)$(CC) -g -emit-llvm -c -o test/ex01.bc test/ex01.c
|
||||
$(QUIET)$(CC) -g -emit-llvm -c -o test/ex02.bc test/ex02.c
|
||||
$(QUIET)$(OPT) -load built/libnpassert.so -null-ptr-assert -npa-use-function -o test/ex02c.bc < test/ex02.bc
|
||||
$(QUIET)echo "Attempting to inject assertions"
|
||||
$(QUIET)$(OPT) -load built/libnpassert.so -null-ptr-assert -o test/ex01a.bc < test/ex01.bc
|
||||
$(QUIET)$(OPT) -load built/libnpassert.so -null-ptr-assert -o test/ex02a.bc < test/ex02.bc
|
||||
$(QUIET)echo "Running inject with config file"
|
||||
$(QUIET)$(OPT) -load built/libnpassert.so -null-ptr-assert -npa-target-config conf/targ.cfg -o test/ex01b.bc < test/ex01.bc
|
||||
$(QUIET)$(OPT) -load built/libnpassert.so -null-ptr-assert -npa-target-config conf/targ.cfg -o test/ex02b.bc < test/ex02.bc
|
||||
$(QUIET)echo "Running llvm-dis on the bitcode files"
|
||||
$(QUIET)$(DIS) --o=test/ex01a.ll test/ex01a.bc
|
||||
$(QUIET)$(DIS) --o=test/ex01b.ll test/ex01b.bc
|
||||
$(QUIET)$(DIS) --o=test/ex01.ll test/ex01.bc
|
||||
$(QUIET)$(DIS) --o=test/ex02a.ll test/ex02a.bc
|
||||
$(QUIET)$(DIS) --o=test/ex02b.ll test/ex02b.bc
|
||||
$(QUIET)$(DIS) --o=test/ex02.ll test/ex02.bc
|
||||
$(QUIET)echo "Compiling to machine code (elf)"
|
||||
$(QUIET)$(CC) -g -o test/ex01a test/ex01a.bc
|
||||
$(QUIET)$(CC) -g -o test/ex01 test/ex01.bc
|
||||
$(QUIET)$(CC) -g -o test/ex02a test/ex02a.bc
|
||||
$(QUIET)$(CC) -g -o test/ex02c test/ex02c.bc
|
||||
$(QUIET)$(CC) -g -o test/ex02 test/ex02.bc
|
||||
|
||||
cleantests:
|
||||
rm -f test/*.bc test/*.ll test/ex01 test/ex01a test/ex02 test/ex02a test/ex02c
|
||||
|
||||
cleanall: clean cleantests
|
||||
@ -1,118 +0,0 @@
|
||||
|
||||
The code will look for any/all pointer function arguments and will
|
||||
insert an assert(ptr != NULL); statement. It shows looking at
|
||||
functions, their arguments, a function pass, declaring a function,
|
||||
and inserting code. Why? Eh. A few reasons that are BS, but mostly
|
||||
to help learn API.
|
||||
|
||||
|
||||
## Build and Run
|
||||
|
||||
This requires LLVM and Clang 3.8, 3.9, or 4.0 releases. You should
|
||||
review the Makefile to setup the LLVM path and version.
|
||||
|
||||
```
|
||||
$ make
|
||||
$ make tests
|
||||
$ cd tests
|
||||
```
|
||||
|
||||
If you do not specify a configuration file, the code will look for all
|
||||
functions and their arguments. If you specify a configuration file,
|
||||
it will specify the function and the argument to check. The tests/ex0*b....
|
||||
files are those in which a configuration file was specified and the
|
||||
ex0*a are those in which none was specified.. so you should see a difference
|
||||
|
||||
### Example running by hand
|
||||
```
|
||||
$ clang -g -emit-llvm -c -o FOO.bc FOO.c
|
||||
$ opt -load built/libnpassert.so -null-ptr-assert -o FOO_assertall.bc < FOO.bc
|
||||
$ opt -load built/libnpassert.so -null-ptr-assert -npa-target-config FOO.cfg -o FOO_assertbyconfigfile.bc < FOO.bc
|
||||
$ clang -g -o FOO_assertall FOO_assertall.bc
|
||||
$ clang -g -o FOO_assertbyconfigfile FOO_assertbyconfigfile.bc
|
||||
```
|
||||
|
||||
The last clang steps generate an runnable executable.
|
||||
Use llvm-dis on bitcode (.bc) files to get the human readable IR
|
||||
to view the differences. One may also use the -npa-use-function option
|
||||
which will tell the pass to create a separate function to perform
|
||||
the assertion in.
|
||||
|
||||
So with the above instead of getting a crash like:
|
||||
```
|
||||
(gdb) r
|
||||
Starting program: /home/hoser/code/npassert/test/ex02
|
||||
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0000000000400549 in foo (s=0x0) at test/ex02.c:16
|
||||
16 return s->one;
|
||||
(gdb) disass foo
|
||||
Dump of assembler code for function foo:
|
||||
0x0000000000400530 <+0>: push %rbp
|
||||
0x0000000000400531 <+1>: mov %rsp,%rbp
|
||||
0x0000000000400534 <+4>: sub $0x10,%rsp
|
||||
0x0000000000400538 <+8>: mov %rdi,-0x8(%rbp)
|
||||
0x000000000040053c <+12>: callq 0x400420 <random@plt>
|
||||
0x0000000000400541 <+17>: mov %rax,-0x10(%rbp)
|
||||
0x0000000000400545 <+21>: mov -0x8(%rbp),%rax
|
||||
=> 0x0000000000400549 <+25>: mov (%rax),%eax
|
||||
0x000000000040054b <+27>: add $0x10,%rsp
|
||||
0x000000000040054f <+31>: pop %rbp
|
||||
0x0000000000400550 <+32>: retq
|
||||
End of assembler dump.
|
||||
(gdb)
|
||||
```
|
||||
|
||||
You would get a crash early in the function..
|
||||
```
|
||||
(gdb) r
|
||||
Starting program: /home/hoser/code/npassert/test/ex02a
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0000000000400554 in foo (s=0x0) at test/ex02.c:12
|
||||
12 {
|
||||
(gdb) disass foo
|
||||
Dump of assembler code for function foo:
|
||||
0x0000000000400530 <+0>: push %rbp
|
||||
0x0000000000400531 <+1>: mov %rsp,%rbp
|
||||
0x0000000000400534 <+4>: sub $0x20,%rsp
|
||||
0x0000000000400538 <+8>: cmp $0x0,%rdi
|
||||
0x000000000040053c <+12>: mov %rdi,-0x8(%rbp)
|
||||
0x0000000000400540 <+16>: jne 0x400558 <foo+40>
|
||||
0x0000000000400546 <+22>: xor %eax,%eax
|
||||
0x0000000000400548 <+24>: mov %eax,%ecx
|
||||
0x000000000040054a <+26>: mov %rsp,%rdx
|
||||
0x000000000040054d <+29>: add $0xfffffffffffffff0,%rdx
|
||||
0x0000000000400551 <+33>: mov %rdx,%rsp
|
||||
=> 0x0000000000400554 <+36>: mov (%rcx),%eax
|
||||
0x0000000000400556 <+38>: mov %eax,(%rdx)
|
||||
0x0000000000400558 <+40>: mov %rsp,%rax
|
||||
0x000000000040055b <+43>: add $0xfffffffffffffff0,%rax
|
||||
0x000000000040055f <+47>: mov %rax,%rsp
|
||||
0x0000000000400562 <+50>: mov %rsp,%rcx
|
||||
0x0000000000400565 <+53>: add $0xfffffffffffffff0,%rcx
|
||||
0x0000000000400569 <+57>: mov %rcx,%rsp
|
||||
0x000000000040056c <+60>: mov -0x8(%rbp),%rdx
|
||||
0x0000000000400570 <+64>: mov %rdx,(%rax)
|
||||
0x0000000000400573 <+67>: mov %rax,-0x10(%rbp)
|
||||
0x0000000000400577 <+71>: mov %rcx,-0x18(%rbp)
|
||||
0x000000000040057b <+75>: callq 0x400420 <random@plt>
|
||||
0x0000000000400580 <+80>: mov -0x18(%rbp),%rcx
|
||||
0x0000000000400584 <+84>: mov %rax,(%rcx)
|
||||
0x0000000000400587 <+87>: mov -0x10(%rbp),%rax
|
||||
0x000000000040058b <+91>: mov (%rax),%rdx
|
||||
0x000000000040058e <+94>: mov (%rdx),%eax
|
||||
0x0000000000400590 <+96>: mov %rbp,%rsp
|
||||
0x0000000000400593 <+99>: pop %rbp
|
||||
0x0000000000400594 <+100>: retq
|
||||
```
|
||||
|
||||
Or if you add the -npa-use-function option
|
||||
```
|
||||
(gdb) r
|
||||
Starting program: /home/hoser/code/npassert/test/ex02c
|
||||
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x00000000004005b3 in __NPA_assert_8__ ()
|
||||
(gdb)
|
||||
```
|
||||
it will fault in the assertion function.
|
||||
@ -1,5 +0,0 @@
|
||||
# Ignore lines with #
|
||||
# index by 0
|
||||
foo,0
|
||||
foo3,1
|
||||
foo3,0
|
||||
@ -1,251 +0,0 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/Constant.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#include "NullPtrAssertPass.h"
|
||||
|
||||
/*
|
||||
* cl::opt represent options taken from the command line, they can include defaults
|
||||
* help descriptions, etc. This is how a pass can be made more granular or specifify
|
||||
* a type of analysis for instance.
|
||||
*/
|
||||
cl::opt<std::string> ReplaceConfigFileName("npa-target-config",
|
||||
cl::desc("configuration file for np asserts"), cl::init(""));
|
||||
|
||||
cl::opt<bool> AssertFunction("npa-use-function",
|
||||
cl::desc("if set, then create new assertion function"), cl::init(false));
|
||||
|
||||
void
|
||||
NullPtrAssertPass::insertAssertionFunction(Module *M)
|
||||
{
|
||||
LLVMContext &ctx = M->getContext();
|
||||
|
||||
/*
|
||||
* Insert our own assertion function. void __NPA_assert_8__(i8 *, i32)
|
||||
*/
|
||||
Constant *assertFnCon = M->getOrInsertFunction("__NPA_assert_8__",
|
||||
Type::getVoidTy(ctx),
|
||||
Type::getInt8PtrTy(ctx), Type::getInt32Ty(ctx), NULL);
|
||||
assertFn = cast<Function>(assertFnCon);
|
||||
assertFn->setCallingConv(CallingConv::C);
|
||||
Argument *arg = &assertFn->getArgumentList().front();
|
||||
arg->setName("ptrToCheck");
|
||||
|
||||
/*
|
||||
* Insert blocks for entry and branches
|
||||
*/
|
||||
BasicBlock *blkEntry = BasicBlock::Create(ctx, "npa_entry_blk", assertFn);
|
||||
BasicBlock *blkAssert = BasicBlock::Create(ctx, "npa_assert_blk", assertFn);
|
||||
BasicBlock *blkReturn = BasicBlock::Create(ctx, "npa_return_blk", assertFn);
|
||||
|
||||
/*
|
||||
* Creates a null pointer and compares it with the argument.
|
||||
* If equal, branch to blkAssert, if not, return block
|
||||
*/
|
||||
IRBuilder<> builder8(blkEntry);
|
||||
PointerType *ptNull = cast<PointerType>(Type::getInt8PtrTy(ctx));
|
||||
Value *cpNull0 = ConstantPointerNull::get(ptNull);
|
||||
Value *eqNull = builder8.CreateICmpEQ(arg, cpNull0);
|
||||
(void)builder8.CreateCondBr(eqNull, blkAssert, blkReturn);
|
||||
|
||||
/*
|
||||
* attempt to deref null
|
||||
*/
|
||||
builder8.SetInsertPoint(blkAssert);
|
||||
Value *crLoad = builder8.CreateAlloca(IntegerType::get(ctx, 32));
|
||||
auto ptNullB = PointerType::get(IntegerType::get(ctx, 32), 0);
|
||||
Value *cpNull = ConstantPointerNull::get(ptNullB);
|
||||
Value *doLoad = builder8.CreateLoad(cpNull);
|
||||
Value *doStore = builder8.CreateStore(doLoad, crLoad, true);
|
||||
(void)builder8.CreateRetVoid();
|
||||
|
||||
/* return block */
|
||||
builder8.SetInsertPoint(blkReturn);
|
||||
(void)builder8.CreateRetVoid();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
NullPtrAssertPass::insertAssertionFunctionCall(Module *M, Function *F,
|
||||
Argument *A)
|
||||
{
|
||||
LLVMContext &ctx = M->getContext();
|
||||
BasicBlock *oe = &F->front();
|
||||
Instruction *oi = &oe->front();
|
||||
APInt a(32, A->getArgNo(), false);
|
||||
ConstantInt *cv = ConstantInt::get(ctx, a);
|
||||
Value *v = new BitCastInst(A, Type::getInt8PtrTy(ctx), "bitcastme_", oi);
|
||||
CallInst::Create(assertFn, {v, cv}, "", oi);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
NullPtrAssertPass::insertAssertion(Module *M, Function *F, Argument *A)
|
||||
{
|
||||
LLVMContext &ctx = M->getContext();
|
||||
|
||||
/*
|
||||
* Get original first BasicBlock in the function.
|
||||
*/
|
||||
BasicBlock *origEntry = &F->front();
|
||||
|
||||
/*
|
||||
* Add a new BasicBlock before this original first one.
|
||||
* This block will hold code that contains the compare and
|
||||
* conditional branch statements.
|
||||
*/
|
||||
BasicBlock *entryBlock = BasicBlock::Create(ctx, "npa_entry_blk", F,
|
||||
origEntry);
|
||||
/*
|
||||
* Add block between the new entry block and the original entry that
|
||||
* will hold code for causing crash.
|
||||
*/
|
||||
BasicBlock *assertBlock = BasicBlock::Create(ctx, "npa_assert_blk", F,
|
||||
origEntry);
|
||||
|
||||
/*
|
||||
* Using the IRBuilder API to add code
|
||||
* Get a NULL pointer and see if it and the argument A
|
||||
* are equal.
|
||||
* Branch to the assert block if equal, otherwise, go to
|
||||
* the original entry block and continue.
|
||||
*/
|
||||
IRBuilder<> builder(entryBlock);
|
||||
Type *typeNull = A->getType();
|
||||
assert(isa<PointerType>(typeNull) && "typeNull not PointerType");
|
||||
PointerType *ptNull = cast<PointerType>(typeNull);
|
||||
Value *cpNull0 = ConstantPointerNull::get(ptNull);
|
||||
Value *eqNull = builder.CreateICmpEQ(A, cpNull0);
|
||||
(void)builder.CreateCondBr(eqNull, assertBlock, origEntry);
|
||||
|
||||
/*
|
||||
* Add the crash code to the assertBlock.
|
||||
* The basic idea is trying to deref null ptr. We could do this
|
||||
* to re-use some NULLs we might have, but to show more of the API
|
||||
* we do it in the following manner.
|
||||
*/
|
||||
builder.SetInsertPoint(assertBlock);
|
||||
Value *crLoad = builder.CreateAlloca(IntegerType::get(ctx, 32));
|
||||
auto ptNullB = PointerType::get(IntegerType::get(ctx, 32), 0);
|
||||
Value *cpNull = ConstantPointerNull::get(ptNullB);
|
||||
Value *doLoad = builder.CreateLoad(cpNull);
|
||||
Value *doStore = builder.CreateStore(doLoad, crLoad, true);
|
||||
// Add a terminator for this block so the IR is sane
|
||||
builder.CreateBr(origEntry);
|
||||
}
|
||||
|
||||
bool
|
||||
NullPtrAssertPass::attemptInsertAssert(Module *M, Function *f, int targetIdx)
|
||||
{
|
||||
bool chg = false;
|
||||
int argIdx = -1;
|
||||
|
||||
/* Decl not defn */
|
||||
if (f->isDeclaration() == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* No body, nothing to inject */
|
||||
if (f->empty() == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Deal with just named functions. */
|
||||
if (f->hasName() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* No arguments, nothing to possibly assert. */
|
||||
if (f->arg_empty() == true) {
|
||||
return false;
|
||||
}
|
||||
/* We have a specified argument index */
|
||||
if (targetIdx >= 0 && f->arg_size() <= (unsigned)targetIdx) {
|
||||
return false;
|
||||
}
|
||||
for (auto ait = f->arg_begin(); ait != f->arg_end(); ++ait) {
|
||||
++argIdx;
|
||||
if (targetIdx != -1 && argIdx != targetIdx) {
|
||||
continue;
|
||||
}
|
||||
Argument *a = &*ait;
|
||||
Type *ty = a->getType();
|
||||
|
||||
// The type of this argument is not of pointer; nothing to do.
|
||||
if (ty->isPointerTy() == false) {
|
||||
continue;
|
||||
}
|
||||
if (AssertFunction) {
|
||||
insertAssertionFunctionCall(M, f, a);
|
||||
} else {
|
||||
insertAssertion(M, f, a);
|
||||
}
|
||||
chg = true;
|
||||
}
|
||||
return chg;
|
||||
}
|
||||
|
||||
bool
|
||||
NullPtrAssertPass::runOnModule(Module &M)
|
||||
{
|
||||
bool chg = false;
|
||||
|
||||
if (AssertFunction) {
|
||||
insertAssertionFunction(&M);
|
||||
}
|
||||
/* Do'em all */
|
||||
if (ReplaceConfigFileName == "") {
|
||||
for (auto fit = M.functions().begin();
|
||||
fit != M.functions().end(); ++fit) {
|
||||
Function *f = &*fit;
|
||||
if (f->hasName()) {
|
||||
if (f->getName().str().find("__NPA_",0) == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
chg = attemptInsertAssert(&M, f, -1);
|
||||
}
|
||||
} else {
|
||||
std::ifstream hand(ReplaceConfigFileName);
|
||||
std::string line;
|
||||
std::string fnName;
|
||||
std::string argIdx;
|
||||
int idx;
|
||||
// We trust that the user is giving us a sane file.
|
||||
while (std::getline(hand, line)) {
|
||||
if (line.find("#", 0) == 0) {
|
||||
continue;
|
||||
}
|
||||
size_t i = line.find(",", 0);
|
||||
fnName = line.substr(0, i);
|
||||
argIdx = line.substr(i+1); // XXX ;^p
|
||||
idx = stoi(argIdx, nullptr, 10);
|
||||
if (idx < 0) {
|
||||
idx = -1;
|
||||
}
|
||||
Function *f = M.getFunction(fnName);
|
||||
if (f == NULL) {
|
||||
errs() << "Unable to find function: " << fnName << "\n";
|
||||
continue;
|
||||
}
|
||||
chg = attemptInsertAssert(&M, f, idx);
|
||||
}
|
||||
}
|
||||
return chg;
|
||||
}
|
||||
|
||||
char NullPtrAssertPass::ID = 0;
|
||||
static RegisterPass<NullPtrAssertPass> XX("null-ptr-assert", "Inject null ptr checks");
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
#ifndef __NULLPTRASSERTPASS_H
|
||||
#define __NULLPTRASSERTPASS_H
|
||||
|
||||
struct NullPtrAssertPass : public ModulePass {
|
||||
static char ID;
|
||||
|
||||
NullPtrAssertPass() : ModulePass(ID) { }
|
||||
virtual bool runOnModule(Module &);
|
||||
|
||||
private:
|
||||
void insertAssertion(Module *M, Function *F, Argument *A);
|
||||
bool attemptInsertAssert(Module *M, Function *F, int);
|
||||
void insertAssertionFunction(Module *M);
|
||||
void insertAssertionFunctionCall(Module *M, Function *F, Argument *A);
|
||||
|
||||
Function *assertFn;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,38 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void
|
||||
foo(int *ptr)
|
||||
{
|
||||
long int k = 0;
|
||||
k = random();
|
||||
printf("Yessir %ld %d\n", k, *ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
foo2(char *dingle)
|
||||
{
|
||||
long int k = 0;
|
||||
|
||||
k = random();
|
||||
printf("dingle %ld %s\n", k, dingle);
|
||||
}
|
||||
void
|
||||
foo3(int *ptr, char *foo)
|
||||
{
|
||||
long int k = 0;
|
||||
k = random();
|
||||
printf("Yessir %ld %d %s\n", k, *ptr, foo);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
foo(&argc);
|
||||
foo2("hi there");
|
||||
foo3(&argc, "hi there");
|
||||
foo2(NULL);
|
||||
foo((int *)NULL);
|
||||
return 0;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct shame {
|
||||
int one;
|
||||
int two;
|
||||
int three;
|
||||
};
|
||||
|
||||
int
|
||||
foo(struct shame *s)
|
||||
{
|
||||
long int z;
|
||||
|
||||
z = random();
|
||||
return s->one;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct shame *s = (struct shame *)NULL;
|
||||
foo(s);
|
||||
return 0;
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
LLVM_VER=3.9
|
||||
LLVM_HOME=/usr/bin
|
||||
LLVM_CONFIG?=$(LLVM_HOME)/llvm-config-$(LLVM_VER)
|
||||
|
||||
ifndef VERBOSE
|
||||
QUIET:=@
|
||||
endif
|
||||
|
||||
SRC_DIR?=$(PWD)/src
|
||||
|
||||
CXX=$(LLVM_HOME)/clang++-$(LLVM_VER)
|
||||
CC=$(LLVM_HOME)/clang-$(LLVM_VER)
|
||||
OPT=$(LLVM_HOME)/opt-$(LLVM_VER)
|
||||
DIS=$(LLVM_HOME)/llvm-dis-$(LLVM_VER)
|
||||
LNK=$(LLVM_HOME)/llvm-link-$(LLVM_VER)
|
||||
|
||||
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
|
||||
LDFLAGS+=-shared -Wl,-O1
|
||||
|
||||
CXXFLAGS+=-I$(shell $(LLVM_CONFIG) --includedir)
|
||||
CXXFLAGS+=-std=c++11 -fPIC -fvisibility-inlines-hidden
|
||||
CXXFLAGS+=-Wall -Wextra -g -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags)
|
||||
CPPFLAGS+=-I$(SRC_DIR)
|
||||
|
||||
PASS=RPSkel.so
|
||||
PASS_OBJECTS=RPSkel.o
|
||||
|
||||
default: prep $(PASS)
|
||||
|
||||
prep:
|
||||
$(QUIET)mkdir -p built
|
||||
|
||||
%.o : $(SRC_DIR)/%.cpp
|
||||
@echo Compiling $*.cpp
|
||||
$(QUIET)$(CXX) -o built/$*.o -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
$(PASS) : $(PASS_OBJECTS)
|
||||
@echo Linking $@
|
||||
$(QUIET)$(CXX) -o built/$@ $(LDFLAGS) $(CXXFLAGS) built/*.o
|
||||
|
||||
clean:
|
||||
$(QUIET)rm -rf built test/*.bc
|
||||
|
||||
|
||||
tests:
|
||||
$(CC) -emit-llvm -o test/foo.bc -c test/foo.c
|
||||
|
||||
runtests:
|
||||
$(OPT) -load built/RPSkel.so -rpskel < test/foo.bc
|
||||
@ -1,17 +0,0 @@
|
||||
|
||||
# RegionPass Skeleton
|
||||
|
||||
|
||||
# Build & Run
|
||||
|
||||
First check the Makefile to set path to llvm-config and version.
|
||||
3.8, 3.9 should be fine, so should 4.0
|
||||
|
||||
```
|
||||
$ make
|
||||
$ opt-X.Y -load built/RPSkel.so -rpskel < file.bc
|
||||
...
|
||||
$
|
||||
```
|
||||
|
||||
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
x
Ссылка в новой задаче
Block a user