├── pass ├── .gitignore ├── KSym │ ├── Trace.cpp │ ├── Alias.cpp │ ├── Lower.h │ ├── Trace.h │ ├── Record.h │ ├── CMakeLists.txt │ ├── Tool.h │ ├── Fetch.h │ ├── Fetch.cpp │ ├── Util.h │ ├── Alias.h │ ├── Logger.h │ ├── Oracle.cpp │ ├── Func.h │ ├── Unroll.h │ ├── Record.cpp │ ├── PA.h │ ├── Util.cpp │ ├── Asmcall.cpp │ ├── Logger.cpp │ ├── Node.h │ ├── Project.h │ ├── Tool.cpp │ ├── Node.cpp │ ├── Oracle.h │ ├── Unroll.cpp │ ├── Asmcall.def │ ├── DAG.cpp │ ├── Symbolic.cpp │ ├── DAG.h │ ├── Libcall.def │ ├── Func.cpp │ ├── Libcall.cpp │ ├── PA.cpp │ └── Global.cpp └── CMakeLists.txt ├── work ├── .gitignore ├── kill.sh ├── conf.py ├── main.py ├── llvm.py ├── xnu_stable.py ├── util.py ├── linux_stable.py ├── draw.py └── cmd.py ├── llvm ├── .gitignore └── init.sh ├── unit ├── Linux │ ├── fetch │ │ ├── Makefile │ │ ├── get_user.c │ │ ├── memdup_user.c │ │ └── copy_from_user.c │ ├── .gitignore │ ├── common.h │ └── Makefile └── .gitignore ├── .gitignore ├── deps └── z3 │ ├── .gitignore │ └── init.sh ├── .gitmodules ├── note └── prelim │ ├── 0001-mpt3sas-downgrade-full-copy_from_user-to-access_ok-c.patch │ ├── 0001-mpt3sas-remove-redundant-copy_from_user-in-_ctl_geti.patch │ ├── 0001-sched-core-fix-a-potential-double-fetch-bug-on-attr.patch │ ├── 0001-hid-uhid-fix-a-double-fetch-bug-when-copying-event-f.patch │ ├── 0001-nvdimm-move-the-check-on-nd_reserved2-to-the-endpoin.patch │ ├── 0001-scsi-skip-message-header-in-next-fetch.patch │ ├── 0001-scsi-ensure-the-header-peeked-does-not-change-in-the.patch │ ├── 0001-fs-coda-ensure-the-header-peeked-at-is-the-same-in-t.patch │ ├── 0001-net-compat-assert-the-size-of-cmsg-copied-in-is-as-e.patch │ ├── 0001-aacraid-fix-potential-double-fetch-issue.patch │ ├── 0001-perf-core-fix-potential-double-fetch-bug.patch │ ├── 0001-isdn-i4l-check-the-message-proto-does-not-change-acr.patch │ ├── 0001-ALSA-asihpi-fix-a-potential-double-fetch-bug-when-co.patch │ ├── 0001-acpi-fix-potential-double-fetch-bug.patch │ ├── 0001-nvdimm-fix-potential-double-fetch-bug.patch │ ├── 0001-isdn-i4l-fetch-the-ppp_write-buffer-in-one-shot.patch │ └── NOTE └── README.md /pass/.gitignore: -------------------------------------------------------------------------------- 1 | /Build/ 2 | -------------------------------------------------------------------------------- /work/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /llvm/.gitignore: -------------------------------------------------------------------------------- 1 | /bins/ 2 | /syms/ 3 | -------------------------------------------------------------------------------- /unit/Linux/fetch/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | /code/ 4 | -------------------------------------------------------------------------------- /deps/z3/.gitignore: -------------------------------------------------------------------------------- 1 | /srcs/ 2 | /bins/ 3 | -------------------------------------------------------------------------------- /unit/.gitignore: -------------------------------------------------------------------------------- 1 | *.bc 2 | *.ll 3 | *.sym 4 | -------------------------------------------------------------------------------- /pass/KSym/Trace.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | -------------------------------------------------------------------------------- /pass/KSym/Alias.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | -------------------------------------------------------------------------------- /unit/Linux/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.ko 3 | *.cmd 4 | *.mod.c 5 | *.mod.o 6 | *.o.d 7 | Module.symvers 8 | modules.order 9 | .tmp_versions/ 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "apps/linux-stable"] 2 | path = apps/linux-stable 3 | url = git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git 4 | -------------------------------------------------------------------------------- /work/kill.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ps aux | grep "main.py check" | awk '{print $2}' | xargs kill 4 | ps aux | grep "KSym.so -KSym" | awk '{print $2}' | xargs kill 5 | -------------------------------------------------------------------------------- /unit/Linux/fetch/get_user.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | int handle(long dst, long __user *src) { 4 | get_user(dst, src); 5 | return 0; 6 | } 7 | 8 | REST_OF_MODULE 9 | -------------------------------------------------------------------------------- /unit/Linux/fetch/memdup_user.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | int handle(void *dst, void __user *src, int len) { 4 | dst = memdup_user(src, len); 5 | return 0; 6 | } 7 | 8 | REST_OF_MODULE 9 | -------------------------------------------------------------------------------- /unit/Linux/fetch/copy_from_user.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | int handle(void *dst, void __user *src, int len) { 4 | copy_from_user(dst, src, len); 5 | return 0; 6 | } 7 | 8 | REST_OF_MODULE 9 | -------------------------------------------------------------------------------- /pass/KSym/Lower.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef LOWER_H_ 3 | #define LOWER_H_ 4 | 5 | void lowerSwitch(Function &f); 6 | void breakConstantExpr(Module &m); 7 | 8 | #endif /* LOWER_H_ */ 9 | -------------------------------------------------------------------------------- /pass/KSym/Trace.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef TRACE_H_ 3 | #define TRACE_H_ 4 | 5 | #include "Project.h" 6 | 7 | typedef pair SeqStmt; 8 | 9 | struct TraceWork { 10 | }; 11 | 12 | #endif /* TRACE_H_ */ 13 | -------------------------------------------------------------------------------- /pass/KSym/Record.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef RECORD_H_ 3 | #define RECORD_H_ 4 | 5 | #include "Project.h" 6 | 7 | class Record { 8 | public: 9 | static void CFG(Function &f, Logger &l); 10 | static void CFG(Slice &s, Logger &l); 11 | }; 12 | 13 | #endif /* RECORD_H_ */ 14 | -------------------------------------------------------------------------------- /deps/z3/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # paths 4 | LLVM=$ROOT/../../llvm/bins/bin 5 | 6 | ROOT=$(pwd) 7 | SRCS=$ROOT/srcs 8 | BINS=$ROOT/bins 9 | 10 | # init paths 11 | rm -rf $BINS 12 | mkdir $BINS 13 | 14 | # clone z3 15 | git clone https://github.com/Z3Prover/z3.git $SRCS 16 | 17 | # configure 18 | cd $SRCS 19 | CXX=$LLVM/clang++ CC=$LLVM/clang \ 20 | python scripts/mk_make.py --noomp --prefix=$BINS 21 | 22 | # build 23 | cd build 24 | make -j$(nproc) 25 | 26 | # install 27 | make install 28 | 29 | -------------------------------------------------------------------------------- /pass/KSym/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_llvm_loadable_module(KSym 2 | Lower.cpp 3 | Alias.cpp 4 | 5 | Util.cpp 6 | Logger.cpp 7 | 8 | Slice.cpp 9 | DAG.cpp 10 | Unroll.cpp 11 | Oracle.cpp 12 | 13 | Symbolic.cpp 14 | Trace.cpp 15 | Libcall.cpp 16 | Asmcall.cpp 17 | SEG.cpp 18 | 19 | Fetch.cpp 20 | Func.cpp 21 | Tool.cpp 22 | 23 | Record.cpp 24 | 25 | Node.cpp 26 | Global.cpp 27 | PA.cpp 28 | ) 29 | 30 | set_target_properties(KSym PROPERTIES 31 | COMPILE_FLAGS "-fno-rtti" 32 | ) 33 | -------------------------------------------------------------------------------- /pass/KSym/Tool.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef TOOL_H_ 3 | #define TOOL_H_ 4 | 5 | #include "Project.h" 6 | 7 | class KSym: public ModulePass { 8 | public: 9 | static char ID; 10 | 11 | KSym(); 12 | ~KSym(); 13 | 14 | void getAnalysisUsage(AnalysisUsage &au) const override; 15 | bool runOnModule(Module &m) override; 16 | }; 17 | 18 | struct KSymError : public runtime_error { 19 | Function *func; 20 | 21 | KSymError(Function *f, std::string const& m) 22 | : std::runtime_error(f->getName().str() + "::" + m), func(f) {} 23 | }; 24 | 25 | #endif /* TOOL_H_ */ 26 | -------------------------------------------------------------------------------- /pass/KSym/Fetch.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef FETCH_H_ 3 | #define FETCH_H_ 4 | 5 | #include "Project.h" 6 | 7 | struct FetchDef { 8 | int src; 9 | int len; 10 | int dst; 11 | }; 12 | 13 | struct Fetch { 14 | // fields 15 | Instruction *inst; 16 | 17 | Value *src; 18 | Value *len; 19 | Value *dst; 20 | 21 | // methods 22 | Fetch() {} 23 | 24 | Fetch(Instruction *i, Value *s, Value *l, Value *d) 25 | : inst(i), src(s), len(l), dst(d) {} 26 | 27 | ~Fetch() {} 28 | 29 | // statics 30 | static const FetchDef *findDefMatch(const string &name); 31 | }; 32 | 33 | #endif /* FETCH_H_ */ 34 | -------------------------------------------------------------------------------- /llvm/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # binary choices 4 | BASE="http://releases.llvm.org" 5 | VERN="4.0.0" 6 | 7 | # platform specifics 8 | case "$OSTYPE" in 9 | linux*) 10 | DIST="x86_64-linux-gnu-ubuntu-16.04" 11 | ;; 12 | darwin*) 13 | DIST="x86_64-apple-darwin" 14 | ;; 15 | bsd*) 16 | DIST="amd64-unknown-freebsd10" 17 | ;; 18 | *) 19 | echo "Unknown OS ($OSTYPE)"; exit 20 | ;; 21 | esac 22 | 23 | # download pre-built package 24 | PKGN=clang+llvm-$VERN-$DIST 25 | TARF=$PKGN.tar.xz 26 | 27 | LINK=$BASE/$VERN/$TARF 28 | wget $LINK 29 | 30 | # extract to location 31 | tar xvf $TARF 32 | mv $PKGN bins 33 | 34 | # remove file 35 | rm $TARF 36 | -------------------------------------------------------------------------------- /pass/KSym/Fetch.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // fetch signatures 5 | static const map FILTERS({ 6 | // most common ones 7 | {string("_copy_from_user"), {1, 2, 0}}, 8 | {string("call __get_user_${4:P}"), {0, 1, -1}}, 9 | {string("memdup_user"), {0, 1, -1}}, 10 | // less common ones 11 | {string("__copy_user_flushcache"), {1, 2, 0}}, 12 | {string("__copy_user_nocache"), {1, 2, 0}}, 13 | }); 14 | 15 | // helpers 16 | const FetchDef *Fetch::findDefMatch(const string &name) { 17 | auto i = FILTERS.find(name); 18 | if(i == FILTERS.end()){ 19 | return nullptr; 20 | } else { 21 | return &i->second; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /pass/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | 3 | find_package(LLVM REQUIRED CONFIG) 4 | 5 | # llvm deps 6 | list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") 7 | include(AddLLVM) 8 | 9 | add_definitions(${LLVM_DEFINITIONS}) 10 | include_directories(${LLVM_INCLUDE_DIRS}) 11 | 12 | # util dependencies 13 | include_directories("${DEPS_UTIL_DIR}") 14 | 15 | # z3 16 | include_directories("${DEPS_Z3_DIR}/bins/include") 17 | link_directories("${DEPS_Z3_DIR}/bins/lib") 18 | link_libraries(z3) 19 | 20 | # flags 21 | set(CMAKE_CXX_STANDARD 11) 22 | 23 | set(CMAKE_CXX_FLAGS_RELEASE 24 | "${CMAKE_CXX_FLAGS}") 25 | 26 | set(CMAKE_CXX_FLAGS_PROFILE 27 | "${CMAKE_CXX_FLAGS} -DKSYM_DEBUG -DKSYM_DEBUG_${KSYM_DEBUG_ITEM}") 28 | 29 | # ksym 30 | add_subdirectory(KSym) 31 | -------------------------------------------------------------------------------- /pass/KSym/Util.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef UTIL_H_ 3 | #define UTIL_H_ 4 | 5 | #include "Project.h" 6 | 7 | class Helper { 8 | public: 9 | // LLVM value 10 | static string getValueName(Value *v); 11 | static string getValueType(Value *v); 12 | static string getValueRepr(Value *v); 13 | static string getCtypeRepr(Type *t); 14 | 15 | // Z3 expr 16 | static string getExprType(Z3_context ctxt, Z3_ast ast); 17 | static string getExprRepr(Z3_context ctxt, Z3_ast ast); 18 | 19 | // string conversion 20 | static void convertDotInName(string &name); 21 | }; 22 | 23 | class Dumper { 24 | public: 25 | Dumper() {} 26 | ~Dumper() {} 27 | 28 | // LLVM value 29 | void valueName(Value *val); 30 | void typedValue(Value *val); 31 | void ctypeValue(Value *val); 32 | 33 | // Z3 expr 34 | void typedExpr(Z3_context ctxt, Z3_ast ast); 35 | }; 36 | 37 | #endif /* UTIL_H_ */ 38 | -------------------------------------------------------------------------------- /pass/KSym/Alias.h: -------------------------------------------------------------------------------- 1 | #ifndef ALIAS_H_ 2 | #define ALIAS_H_ 3 | 4 | #include "Project.h" 5 | 6 | class CombinedAA { 7 | public: 8 | CombinedAA( 9 | const DataLayout &dl, 10 | TargetLibraryInfo &tli, 11 | AssumptionCache &ac, 12 | DominatorTree &dt, 13 | LoopInfo &li) : 14 | basics(dl, tli, ac, &dt, &li), 15 | anders(tli), 16 | steens(tli), 17 | combo(tli) 18 | { 19 | combo.addAAResult(basics); 20 | combo.addAAResult(anders); 21 | combo.addAAResult(steens); 22 | 23 | memdep = new MemoryDependenceResults(combo, ac, tli, dt); 24 | } 25 | 26 | ~CombinedAA() { 27 | delete memdep; 28 | } 29 | 30 | protected: 31 | // available analysis 32 | BasicAAResult basics; 33 | CFLAndersAAResult anders; 34 | CFLSteensAAResult steens; 35 | 36 | // worker 37 | AAResults combo; 38 | MemoryDependenceResults *memdep; 39 | }; 40 | 41 | #endif /* ALIAS_H_ */ 42 | -------------------------------------------------------------------------------- /pass/KSym/Logger.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef LOGGER_H_ 3 | #define LOGGER_H_ 4 | 5 | #include "Project.h" 6 | 7 | class Logger { 8 | public: 9 | Logger() : rec(json::object()), cur(&rec) { 10 | stk.push(cur); 11 | } 12 | 13 | ~Logger() {} 14 | 15 | // level + 1 16 | void vec(); 17 | void vec(const string &key); 18 | 19 | void map(); 20 | void map(const string &key); 21 | 22 | // stay on same level 23 | template 24 | void log(const T &msg); 25 | void log(const char *msg); 26 | 27 | template 28 | void log(const string &key, const T &msg); 29 | void log(const string &key, const char *msg); 30 | 31 | // level - 1 32 | void pop(); 33 | 34 | // dump to file 35 | void dump(raw_ostream &stm, int indent = 2); 36 | void dump(const string &fn, int indent = 2); 37 | 38 | protected: 39 | json rec; 40 | json *cur; 41 | stack stk; 42 | }; 43 | 44 | #endif /* LOGGER_H_ */ 45 | -------------------------------------------------------------------------------- /pass/KSym/Oracle.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // reachability 5 | void FuncOracle::getReachBlocks(BasicBlock *cur, set &blks) { 6 | if(blks.find(cur) != blks.end()){ 7 | return; 8 | } 9 | 10 | blks.insert(cur); 11 | 12 | pred_iterator pi = pred_begin(cur), pe = pred_end(cur); 13 | for(; pi != pe; ++pi){ 14 | getReachBlocks(*pi, blks); 15 | } 16 | } 17 | 18 | // dominance 19 | LLVMSliceBlock *SliceOracle::getIDom(LLVMSliceBlock *bb) { 20 | SliceDomTreeNode *node = dt.getNode(bb); 21 | assert(node != nullptr); 22 | 23 | SliceDomTreeNode *idom = node->getIDom(); 24 | if(idom == nullptr){ 25 | return nullptr; 26 | } 27 | 28 | return idom->getBlock(); 29 | } 30 | 31 | // loop info 32 | LLVMSliceLoop *SliceOracle::getOuterLoopInScope(LLVMSliceLoop *scope, 33 | LLVMSliceBlock *bb) { 34 | 35 | LLVMSliceLoop *l = li.getLoopFor(bb); 36 | LLVMSliceLoop *c = nullptr; 37 | 38 | while(l != scope){ 39 | c = l; 40 | l = l->getParentLoop(); 41 | } 42 | 43 | return c; 44 | } 45 | -------------------------------------------------------------------------------- /unit/Linux/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H_ 2 | #define COMMON_H_ 3 | 4 | /* common headers */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | /* metainfo */ 13 | MODULE_LICENSE("GPL"); 14 | MODULE_AUTHOR("Meng Xu"); 15 | 16 | /* complete the moduile */ 17 | #define REST_OF_MODULE \ 18 | static int __init prog_init(void) { \ 19 | printk(KERN_INFO "module init"); \ 20 | return 0; \ 21 | } \ 22 | \ 23 | static void __exit prog_exit(void) { \ 24 | printk(KERN_INFO "module fini"); \ 25 | } \ 26 | \ 27 | module_init(prog_init); \ 28 | module_exit(prog_exit); 29 | 30 | #endif /* COMMON_H_ */ 31 | -------------------------------------------------------------------------------- /note/prelim/0001-mpt3sas-downgrade-full-copy_from_user-to-access_ok-c.patch: -------------------------------------------------------------------------------- 1 | From c92ac69bec6a186c6e0b3681236592e129a5d1a6 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 23:01:34 -0400 4 | Subject: [PATCH] mpt3sas: downgrade full copy_from_user to access_ok check 5 | 6 | Since right after the user copy, we are going to 7 | memset(&karg, 0, sizeof(karg)), I guess an access_ok check is enough? 8 | 9 | Signed-off-by: Meng Xu 10 | --- 11 | drivers/scsi/mpt3sas/mpt3sas_ctl.c | 2 +- 12 | 1 file changed, 1 insertion(+), 1 deletion(-) 13 | 14 | diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c 15 | index bdffb69..b363d2d 100644 16 | --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c 17 | +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c 18 | @@ -1065,7 +1065,7 @@ _ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg) 19 | { 20 | struct mpt3_ioctl_iocinfo karg; 21 | 22 | - if (copy_from_user(&karg, arg, sizeof(karg))) { 23 | + if (!access_ok(VERIFY_READ, arg, sizeof(karg))) { 24 | pr_err("failure at %s:%d/%s()!\n", 25 | __FILE__, __LINE__, __func__); 26 | return -EFAULT; 27 | -- 28 | 2.7.4 29 | 30 | -------------------------------------------------------------------------------- /pass/KSym/Func.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNC_H_ 2 | #define FUNC_H_ 3 | 4 | #include "Project.h" 5 | 6 | class FuncHandle { 7 | public: 8 | FuncHandle(Function &f, ModuleOracle &m) 9 | : func(f), mo(m), fo(f, m.getDataLayout(), m.getTargetLibraryInfo()) 10 | {} 11 | 12 | ~FuncHandle() { 13 | for(auto const &i : fts){ 14 | delete i.second; 15 | } 16 | } 17 | 18 | void run(); 19 | 20 | protected: 21 | // fetch collection 22 | void collectFetch(); 23 | Fetch *getFetchFromInst(Instruction *inst); 24 | 25 | // fetch analysis 26 | void analyzeFetch(Fetch &fetch); 27 | void analyzeFetchPerTrace(Fetch &fetch, SliceOracle &so, blist &blks); 28 | 29 | // fetch results 30 | void addResult(Fetch *f1, Fetch *f2, CheckResult res); 31 | unsigned countResult(CheckResult res); 32 | 33 | protected: 34 | // context 35 | Function &func; 36 | 37 | // oracle 38 | ModuleOracle &mo; 39 | FuncOracle fo; 40 | 41 | // fetches 42 | map fts; 43 | 44 | // results 45 | map, CheckResult> result; 46 | set> failed; 47 | }; 48 | 49 | #endif /* FUNC_H_ */ 50 | -------------------------------------------------------------------------------- /note/prelim/0001-mpt3sas-remove-redundant-copy_from_user-in-_ctl_geti.patch: -------------------------------------------------------------------------------- 1 | From 8da9ea488b4df10999c82b8a4db267703bb33300 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Wed, 20 Sep 2017 11:08:44 -0400 4 | Subject: [PATCH] mpt3sas: remove redundant copy_from_user in _ctl_getiocinfo 5 | 6 | Since right after the user copy, we are going to 7 | memset(&karg, 0, sizeof(karg)), the copy_from_user is redundant 8 | 9 | Signed-off-by: Meng Xu 10 | --- 11 | drivers/scsi/mpt3sas/mpt3sas_ctl.c | 6 ------ 12 | 1 file changed, 6 deletions(-) 13 | 14 | diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c 15 | index bdffb69..d448fed 100644 16 | --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c 17 | +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c 18 | @@ -1065,12 +1065,6 @@ _ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg) 19 | { 20 | struct mpt3_ioctl_iocinfo karg; 21 | 22 | - if (copy_from_user(&karg, arg, sizeof(karg))) { 23 | - pr_err("failure at %s:%d/%s()!\n", 24 | - __FILE__, __LINE__, __func__); 25 | - return -EFAULT; 26 | - } 27 | - 28 | dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, 29 | __func__)); 30 | 31 | -- 32 | 2.7.4 33 | 34 | -------------------------------------------------------------------------------- /note/prelim/0001-sched-core-fix-a-potential-double-fetch-bug-on-attr.patch: -------------------------------------------------------------------------------- 1 | From a21ae7627f7e36575ff49f09d95aba2ee7957a27 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 29 Aug 2017 14:52:09 -0400 4 | Subject: [PATCH] sched/core: Fix a potential double fetch bug on attr->size 5 | 6 | `attr->size` after the second fetch `copy_from_user(attr, uattr, size)`, 7 | can be different from what is initially fetched in and checked 8 | `get_user(size, &uattr->size)` by racing condition in the userspace. 9 | 10 | The issue and the patch are both similar to commit f12f42a 11 | (in kernel/events/core.c). 12 | 13 | Signed-off-by: Meng Xu 14 | --- 15 | kernel/sched/core.c | 2 ++ 16 | 1 file changed, 2 insertions(+) 17 | 18 | diff --git a/kernel/sched/core.c b/kernel/sched/core.c 19 | index 0869b20..c22d2b4 100644 20 | --- a/kernel/sched/core.c 21 | +++ b/kernel/sched/core.c 22 | @@ -4349,6 +4349,8 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a 23 | if (ret) 24 | return -EFAULT; 25 | 26 | + attr->size = size; 27 | + 28 | /* 29 | * XXX: Do we want to be lenient like existing syscalls; or do we want 30 | * to be strict and return an error on out-of-bounds values? 31 | -- 32 | 2.7.4 33 | 34 | -------------------------------------------------------------------------------- /pass/KSym/Unroll.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef UNROLL_H_ 3 | #define UNROLL_H_ 4 | 5 | #include "Project.h" 6 | 7 | typedef list blist; 8 | 9 | class UnrollPath { 10 | public: 11 | UnrollPath() {} 12 | 13 | ~UnrollPath() { 14 | for(blist *i : traces){ 15 | delete i; 16 | } 17 | } 18 | 19 | void add(blist *trace) { 20 | traces.push_back(trace); 21 | } 22 | 23 | unsigned size() { 24 | return traces.size(); 25 | } 26 | 27 | typedef typename list::iterator iterator; 28 | 29 | iterator begin() { 30 | return traces.begin(); 31 | } 32 | 33 | iterator end() { 34 | return traces.end(); 35 | } 36 | 37 | protected: 38 | list traces; 39 | }; 40 | 41 | class UnrollCache { 42 | public: 43 | UnrollCache() {} 44 | 45 | ~UnrollCache() { 46 | for(auto const &i : cache){ 47 | delete i.second; 48 | } 49 | } 50 | 51 | UnrollPath *getUnrolled(DAPath *path, LLVMSliceBlock *term, DAGraph *graph); 52 | 53 | protected: 54 | void unrollRecursive(DATrace::iterator cur, DATrace::iterator end, 55 | LLVMSliceBlock *term, DAGraph *graph, blist *blks, UnrollPath *unrolled); 56 | 57 | protected: 58 | map, UnrollPath *> cache; 59 | }; 60 | 61 | #endif /* UNROLL_H_ */ 62 | -------------------------------------------------------------------------------- /pass/KSym/Record.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | void Record::CFG(Function &f, Logger &l) { 5 | l.log("root", Helper::getValueName(&f.getEntryBlock())); 6 | 7 | l.vec("item"); 8 | for(BasicBlock &bb : f){ 9 | BasicBlock *ptr = &bb; 10 | l.map(); 11 | 12 | l.log("name", Helper::getValueName(ptr)); 13 | l.log("size", ptr->size()); 14 | 15 | pred_iterator pi = pred_begin(ptr), pe = pred_end(ptr); 16 | l.vec("pred"); 17 | for(; pi != pe; ++pi){ 18 | l.log(Helper::getValueName(*pi)); 19 | } 20 | l.pop(); 21 | 22 | succ_iterator si = succ_begin(ptr), se = succ_end(ptr); 23 | l.vec("succ"); 24 | for(; si != se; ++si){ 25 | l.log(Helper::getValueName(*si)); 26 | } 27 | l.pop(); 28 | 29 | l.pop(); 30 | } 31 | l.pop(); 32 | } 33 | 34 | void Record::CFG(Slice &s, Logger &l) { 35 | l.log("root", Helper::getValueName(s.getEntry()->getBlock())); 36 | 37 | l.vec("item"); 38 | Slice::iterator i = s.begin(), ie = s.end(); 39 | for(; i != ie; ++i){ 40 | SliceBlock *ptr = *i; 41 | l.map(); 42 | 43 | l.log("name", Helper::getValueName(ptr->getBlock())); 44 | l.log("size", ptr->numInsts()); 45 | 46 | SliceBlock::linkIter pi = ptr->predBegin(), pe = ptr->predEnd(); 47 | l.vec("pred"); 48 | for(; pi != pe; ++pi){ 49 | l.log(Helper::getValueName((*pi)->getBlock())); 50 | } 51 | l.pop(); 52 | 53 | SliceBlock::linkIter si = ptr->succBegin(), se = ptr->succEnd(); 54 | l.vec("succ"); 55 | for(; si != se; ++si){ 56 | l.log(Helper::getValueName((*si)->getBlock())); 57 | } 58 | l.pop(); 59 | 60 | l.pop(); 61 | } 62 | l.pop(); 63 | } 64 | -------------------------------------------------------------------------------- /pass/KSym/PA.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef __PA__H 3 | #define __PA__H 4 | 5 | #include "Node.h" 6 | #include "Project.h" 7 | 8 | typedef DenseMap Value2NodeMap; 9 | 10 | class PA 11 | { 12 | public: 13 | Value2NodeMap AllNodes; 14 | const Module *module; 15 | const DataLayout *dataLayout; 16 | 17 | Node* lookupNode(const Value *value) { 18 | if (const ConstantData *cData = dyn_cast(value)) { 19 | return Node::GLOBAL_CONSTANT_NODE; 20 | } 21 | Value2NodeMap::iterator it = AllNodes.find(value); 22 | if (it == AllNodes.end()) 23 | return nullptr; 24 | return it->second; 25 | } 26 | 27 | ~PA() { 28 | for (auto it : AllNodes) { 29 | Node *node = it.second; 30 | delete node; 31 | } 32 | AllNodes.clear(); 33 | } 34 | 35 | void summarizeFunctions(Module *module); 36 | 37 | void addGlobalNode(const Value *gVal, uint64_t offset, Node *node) { 38 | DenseMap::iterator it = AllNodes.find(gVal); 39 | if (it == AllNodes.end()) { 40 | llvm_unreachable("cannot find global node"); 41 | } 42 | it->second->insertNode(offset, node); 43 | } 44 | 45 | Node* getGlobalNode(const Value *gVal) { 46 | DenseMap::iterator it = AllNodes.find(gVal); 47 | if (it == AllNodes.end()) { 48 | llvm_unreachable("cannot find global node"); 49 | } 50 | return it->second; 51 | } 52 | 53 | Node* getNodeForConstantExpr(Value *v); 54 | void initGlobal(const Value *V, const Constant *C, SmallVector& indexOps); 55 | int64_t initGlobal(const Value *V, const Constant *C, int64_t offset); 56 | void initializeGlobals(const Module *module); 57 | void analyzePointTo(vector iList, SliceOracle &so); 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /work/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import exit 4 | from os import listdir 5 | from os.path import abspath, join 6 | from platform import system 7 | from multiprocessing import cpu_count 8 | 9 | # platforms 10 | HOST = system() 11 | assert HOST in ("Linux", "Darwin") 12 | 13 | # paths 14 | PATH_ROOT = abspath(join(__file__, "..", "..")) 15 | 16 | PATH_APPS = join(PATH_ROOT, "apps") 17 | PATH_DEPS = join(PATH_ROOT, "deps") 18 | 19 | PATH_LLVM = join(PATH_ROOT, "llvm") 20 | PATH_PASS = join(PATH_ROOT, "pass") 21 | PATH_TEST = join(PATH_ROOT, "unit") 22 | 23 | PATH_CODE = join(PATH_ROOT, "code") 24 | PATH_LOGS = join(PATH_CODE, "logs") 25 | PATH_OUTS = join(PATH_CODE, "outs") 26 | PATH_SRCS = join(PATH_CODE, "srcs") 27 | PATH_OBJS = join(PATH_CODE, "objs") 28 | PATH_BINS = join(PATH_CODE, "bins") 29 | PATH_EXTS = join(PATH_CODE, "exts") 30 | PATH_BCFS = join(PATH_CODE, "bcfs") 31 | PATH_MODS = join(PATH_CODE, "mods") 32 | PATH_TRAS = join(PATH_CODE, "tras") 33 | PATH_SYMS = join(PATH_CODE, "syms") 34 | 35 | PATH_WORK = join(PATH_ROOT, "work") 36 | 37 | # apps 38 | LIST_APPS = listdir(PATH_APPS) 39 | 40 | # deps 41 | DEPS_Z3 = join(PATH_DEPS, "z3") 42 | DEPS_UTIL = join(PATH_DEPS, "util") 43 | 44 | # pass 45 | PASS_BDIR = join(PATH_PASS, "Build") 46 | PASS_KSYM = join(PASS_BDIR, "KSym", "KSym.so") 47 | 48 | # llvm 49 | LLVM_PREP = join(PATH_LLVM, "bins") 50 | 51 | LLVM_BINS = join(LLVM_PREP, "bin") 52 | LLVM_BIN_CPP = join(LLVM_BINS, "clang-cpp") 53 | LLVM_BIN_CLA = join(LLVM_BINS, "clang") 54 | LLVM_BIN_CXX = join(LLVM_BINS, "clang++") 55 | LLVM_BIN_LLD = join(LLVM_BINS, "ld.lld") 56 | LLVM_BIN_BLD = join(LLVM_BINS, "llvm-link") 57 | LLVM_BIN_OPT = join(LLVM_BINS, "opt") 58 | 59 | LLVM_SYMS = join(PATH_LLVM, "syms") 60 | 61 | # opts 62 | OPTS_NCPU = cpu_count() 63 | OPTS_TIME = 43200 64 | -------------------------------------------------------------------------------- /note/prelim/0001-hid-uhid-fix-a-double-fetch-bug-when-copying-event-f.patch: -------------------------------------------------------------------------------- 1 | From ba8dca30662daa22d27fb2527d9d169a902e3bb6 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 00:13:09 -0400 4 | Subject: [PATCH] hid/uhid: fix a double-fetch bug when copying event from user 5 | 6 | When in_compat_syscall(), a user could make type != UHID_CREATE when 7 | get_user(type, buffer) [first fetch] and later make event->type == 8 | UHID_CREATE in copy_from_user(event, buffer, ...) [second fetch]. 9 | 10 | By doing so, an attacker might circumvent the specific logic to handle 11 | the type == UHID_CREATE case and later cause undefined behaviors. 12 | 13 | This patch enforces that event->type is overriden to the type value 14 | copied in the first fetch and thus, mitigate this race condition attack. 15 | 16 | Signed-off-by: Meng Xu 17 | --- 18 | drivers/hid/uhid.c | 15 ++++++++++++--- 19 | 1 file changed, 12 insertions(+), 3 deletions(-) 20 | 21 | diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c 22 | index 7f8ff39..4bbfd8a 100644 23 | --- a/drivers/hid/uhid.c 24 | +++ b/drivers/hid/uhid.c 25 | @@ -448,11 +448,20 @@ static int uhid_event_from_user(const char __user *buffer, size_t len, 26 | kfree(compat); 27 | return 0; 28 | } 29 | + 30 | /* All others can be copied directly */ 31 | - } 32 | + if (copy_from_user(event, buffer, min(len, sizeof(*event)))) 33 | + return -EFAULT; 34 | 35 | - if (copy_from_user(event, buffer, min(len, sizeof(*event)))) 36 | - return -EFAULT; 37 | + /* 38 | + * Override type in case the user process rushes to change it 39 | + * between two fetches 40 | + * */ 41 | + event->type = type; 42 | + } else { 43 | + if (copy_from_user(event, buffer, min(len, sizeof(*event)))) 44 | + return -EFAULT; 45 | + } 46 | 47 | return 0; 48 | } 49 | -- 50 | 2.7.4 51 | 52 | -------------------------------------------------------------------------------- /note/prelim/0001-nvdimm-move-the-check-on-nd_reserved2-to-the-endpoin.patch: -------------------------------------------------------------------------------- 1 | From c86512aad865209412688d824443d745676972cb Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Mon, 4 Sep 2017 11:19:07 -0400 4 | Subject: [PATCH] nvdimm: move the check on nd_reserved2 to the endpoint 5 | 6 | This patch delays the check of nd_reserved2 to the actual endpoint 7 | (acpi_nfit_ctl) that uses it, as a prevention of a potential 8 | double-fetch bug. 9 | 10 | Detailed discussion can be found at 11 | https://marc.info/?l=linux-kernel&m=150421938113092&w=2 12 | 13 | Signed-off-by: Meng Xu 14 | --- 15 | drivers/acpi/nfit/core.c | 4 ++++ 16 | drivers/nvdimm/bus.c | 4 ---- 17 | 2 files changed, 4 insertions(+), 4 deletions(-) 18 | 19 | diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c 20 | index 19182d0..694b1b1 100644 21 | --- a/drivers/acpi/nfit/core.c 22 | +++ b/drivers/acpi/nfit/core.c 23 | @@ -228,6 +228,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, 24 | if (cmd == ND_CMD_CALL) { 25 | call_pkg = buf; 26 | func = call_pkg->nd_command; 27 | + 28 | + for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++) 29 | + if (call_pkg->nd_reserved2[i]) 30 | + return -EINVAL; 31 | } 32 | 33 | if (nvdimm) { 34 | diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c 35 | index 937fafa..0fb9adb 100644 36 | --- a/drivers/nvdimm/bus.c 37 | +++ b/drivers/nvdimm/bus.c 38 | @@ -980,10 +980,6 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 39 | dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n", 40 | __func__, dimm_name, pkg.nd_command, 41 | in_len, out_len, buf_len); 42 | - 43 | - for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++) 44 | - if (pkg.nd_reserved2[i]) 45 | - return -EINVAL; 46 | } 47 | 48 | /* process an output envelope */ 49 | -- 50 | 2.7.4 51 | 52 | -------------------------------------------------------------------------------- /pass/KSym/Util.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | string Helper::getValueName(Value *v) { 5 | if(!v->hasName()){ 6 | return to_string(reinterpret_cast(v)); 7 | } else { 8 | return v->getName().str(); 9 | } 10 | } 11 | 12 | string Helper::getValueType(Value *v) { 13 | if(Instruction *inst = dyn_cast(v)){ 14 | return string(inst->getOpcodeName()); 15 | } else { 16 | return string("value " + to_string(v->getValueID())); 17 | } 18 | } 19 | 20 | string Helper::getValueRepr(Value *v) { 21 | string str; 22 | raw_string_ostream stm(str); 23 | 24 | v->print(stm); 25 | stm.flush(); 26 | 27 | return str; 28 | } 29 | 30 | string Helper::getCtypeRepr(Type *t) { 31 | string str; 32 | raw_string_ostream stm(str); 33 | 34 | t->print(stm); 35 | stm.flush(); 36 | 37 | return str; 38 | } 39 | 40 | string Helper::getExprType(Z3_context ctxt, Z3_ast ast) { 41 | return string(Z3_sort_to_string(ctxt, Z3_get_sort(ctxt, ast))); 42 | } 43 | 44 | string Helper::getExprRepr(Z3_context ctxt, Z3_ast ast) { 45 | return string(Z3_ast_to_string(ctxt, ast)); 46 | } 47 | 48 | void Helper::convertDotInName(string &name) { 49 | std::replace(name.begin(), name.end(), '.', '_'); 50 | } 51 | 52 | void Dumper::valueName(Value *val) { 53 | errs() << Helper::getValueName(val) << "\n"; 54 | } 55 | 56 | void Dumper::typedValue(Value *val) { 57 | errs() 58 | << "[" << Helper::getValueType(val) << "]" 59 | << Helper::getValueRepr(val) 60 | << "\n"; 61 | } 62 | 63 | void Dumper::ctypeValue(Value *val) { 64 | errs() 65 | << "[" << Helper::getCtypeRepr(val->getType()) << "]" 66 | << Helper::getValueRepr(val) 67 | << "\n"; 68 | } 69 | 70 | void Dumper::typedExpr(Z3_context ctxt, Z3_ast ast) { 71 | errs() 72 | << "[" << Helper::getExprType(ctxt, ast) << "]" 73 | << Helper::getExprRepr(ctxt, ast) 74 | << "\n"; 75 | } 76 | -------------------------------------------------------------------------------- /note/prelim/0001-scsi-skip-message-header-in-next-fetch.patch: -------------------------------------------------------------------------------- 1 | From a1f44f0f15bf6fb233c50faadcff05d47e6030ec Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 17:20:51 -0400 4 | Subject: [PATCH] scsi: skip message header in next fetch 5 | 6 | In sg_scsi_ioctl(), the header of the userspace buffer, sic->data, is 7 | fetched twice from the userspace. The first fetch is used to peek at the 8 | opcode and derive cmdlen while the second fetch copies the whole message. 9 | However, the userspace memory backing opcode can change across fetches 10 | which means that the corresponding opcode field in req->cmd could be 11 | different from what is fetched in for the first time (and so is the 12 | derived cmdlen). 13 | 14 | Whether this double-fetch situation is a security critical bug depends 15 | on how req->cmd will be used later. However, given that it is hard to 16 | enumerate all the possible use cases, a safer way is to construct the message 17 | by concating the opcode fetched before and the rest of the message from 18 | userspace. 19 | 20 | Signed-off-by: Meng Xu 21 | --- 22 | block/scsi_ioctl.c | 8 +++++++- 23 | 1 file changed, 7 insertions(+), 1 deletion(-) 24 | 25 | diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c 26 | index 7440de4..8fe1e05 100644 27 | --- a/block/scsi_ioctl.c 28 | +++ b/block/scsi_ioctl.c 29 | @@ -463,7 +463,13 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, 30 | */ 31 | err = -EFAULT; 32 | req->cmd_len = cmdlen; 33 | - if (copy_from_user(req->cmd, sic->data, cmdlen)) 34 | + 35 | + /* 36 | + * avoid copying the opcode twice 37 | + */ 38 | + memcpy(req->cmd, &opcode, sizeof(opcode)); 39 | + if (copy_from_user(req->cmd + sizeof(opcode), 40 | + sic->data + sizeof(opcode), cmdlen - sizeof(opcode))) 41 | goto error; 42 | 43 | if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) 44 | -- 45 | 2.7.4 46 | 47 | -------------------------------------------------------------------------------- /note/prelim/0001-scsi-ensure-the-header-peeked-does-not-change-in-the.patch: -------------------------------------------------------------------------------- 1 | From b3f20282dc8ce3b742a6bb691c7ff8a280f1c129 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 11:08:26 -0400 4 | Subject: [PATCH] scsi: ensure the header peeked does not change in the actual 5 | message 6 | 7 | In sg_scsi_ioctl(), the header of the userspace buffer, sic->data, is 8 | fetched twice from the userspace. The first fetch is used to peek at the 9 | opcode and derive cmdlen while the second fetch copies the whole message. 10 | However, the userspace memory backing opcode can change across fetches 11 | which means that the corresponding opcode field in req->cmd could be 12 | different from what is fetched in for the first time (and so is the 13 | derived cmdlen). 14 | 15 | Whether this double-fetch situation is a security critical bug depends 16 | on how req->cmd will be used later. However, given that it is hard to 17 | enumerate all the possible use cases, a safer way is to ensure that 18 | the peeked header does not change after the second fetch. 19 | 20 | This patch enforces that the opcode field in req->cmd is the same as what 21 | is fetched in originally. 22 | 23 | Signed-off-by: Meng Xu 24 | --- 25 | block/scsi_ioctl.c | 6 ++++++ 26 | 1 file changed, 6 insertions(+) 27 | 28 | diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c 29 | index 7440de4..971044d 100644 30 | --- a/block/scsi_ioctl.c 31 | +++ b/block/scsi_ioctl.c 32 | @@ -466,6 +466,12 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, 33 | if (copy_from_user(req->cmd, sic->data, cmdlen)) 34 | goto error; 35 | 36 | + /* 37 | + * override the request header (opcode) to make sure that it matches 38 | + * the first fetch from sic->data 39 | + */ 40 | + *((unsigned int *)req->cmd) = opcode; 41 | + 42 | if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) 43 | goto error; 44 | 45 | -- 46 | 2.7.4 47 | 48 | -------------------------------------------------------------------------------- /pass/KSym/Asmcall.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // expansion 5 | #define ASMCALL_DERIVE_0(name) \ 6 | SymVal *SEOpvAsm_##name::derive(SymExec &sym) 7 | 8 | #define ASMCALL_DERIVE_1(name, arg0) \ 9 | SymVal *SEOpvAsm_##name::derive(SymExec &sym, \ 10 | SymVal *arg0) 11 | 12 | #define ASMCALL_DERIVE_2(name, arg0, arg1) \ 13 | SymVal *SEOpvAsm_##name::derive(SymExec &sym, \ 14 | SymVal *arg0, SymVal *arg1) 15 | 16 | #define ASMCALL_DERIVE_3(name, arg0, arg1, arg2) \ 17 | SymVal *SEOpvAsm_##name::derive(SymExec &sym, \ 18 | SymVal *arg0, SymVal *arg1, SymVal *arg2) 19 | 20 | // helpers 21 | #define CHECK_INST \ 22 | assert(isa(inst)); \ 23 | CallInst *op = cast(inst); 24 | 25 | #define GET_ORACLE \ 26 | ModuleOracle &oracle = sym.getOracle(); 27 | 28 | #define INIT_EXPR \ 29 | SymExpr expr; \ 30 | SymCond cond; 31 | 32 | #define FINI_EXPR \ 33 | return new SymVal(expr, cond); 34 | 35 | #define IGNORED \ 36 | INIT_EXPR; \ 37 | FINI_EXPR; 38 | 39 | // asm call symbolization routines 40 | 41 | // fetch 42 | ASMCALL_DERIVE_2(__get_user_n, src, size) { 43 | INIT_EXPR; 44 | 45 | CHECK_INST; 46 | 47 | expr.expr = sym.createVarPointer(); 48 | 49 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 50 | size->getExpr().expr, sym.createConstPointer(0))); 51 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 52 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 53 | 54 | FINI_EXPR; 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deadline 2 | Deadline provide a formal and precise definition of double-fetch bugs and then implement a static analysis system to automatically detect double-fetch bugs in OS kernels. Deadline uses static program analysis techniques to systematically find multi-reads throughout the kernel and employs specialized symbolic checking to vet each multi-read for double-fetch bugs. We apply Deadline to Linux and FreeBSD kernels and find 23 new bugs in Linux and one new bug in FreeBSD. 3 | 4 | This repository is provided under the terms of the MIT license. 5 | 6 | ## Init 7 | - LLVM : cd llvm && ./init.sh 8 | - Z3 : cd deps/z3 && ./init.sh 9 | 10 | ## Build 11 | - For first time buildi : ./llvm.py build -c 12 | - For later build : ./llvm.py build 13 | - For debug build : ./llvm.py build -c -d 14 | 15 | ## Test 16 | - Set test env : ./llvm.py work 17 | - Run on bitcode file : opt -symf 18 | - Exit test env : Ctrl + D or exit 19 | 20 | ## Kernel 21 | (In the case of Linux kernel) 22 | 23 | - Setup submodule : git submodule update --init -- app/linux-stable 24 | - Checkout a version : ./main.py checkout 25 | - Config : ./main.py config 26 | - Build w/gcc (3 hours) : ./main.py build 27 | - Parse build procedure : ./main.py parse 28 | - Build w/llvm : ./main.py irgen 29 | - Group into modules : ./main.py group 30 | - Optimize and LTO : ./main.py trans 31 | - Run the checker : ./main.py check 32 | - Check failure/timeouts : ./main.py stat 33 | 34 | ## Reference 35 | https://ieeexplore.ieee.org/abstract/document/8418630 36 | ``` 37 | @inproceedings{xu2018precise, 38 | title={Precise and scalable detection of double-fetch bugs in OS kernels}, 39 | author={Xu, Meng and Qian, Chenxiong and Lu, Kangjie and Backes, Michael and Kim, Taesoo}, 40 | booktitle={2018 IEEE Symposium on Security and Privacy (SP)}, 41 | pages={661--678}, 42 | year={2018}, 43 | organization={IEEE} 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /note/prelim/0001-fs-coda-ensure-the-header-peeked-at-is-the-same-in-t.patch: -------------------------------------------------------------------------------- 1 | From 041d4830710d9f335458fdf311e5bcb07468acd0 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 10:46:16 -0400 4 | Subject: [PATCH] fs/coda: ensure the header peeked at is the same in the 5 | actual message 6 | 7 | In coda_psdev_write(), the header of the buffer is fetched twice from the 8 | userspace. The first fetch is used to peek at the opcode and unique id while 9 | the second fetch copies the whole message. However, there could be 10 | inconsistency in these two fields between two fetches as buf resides in 11 | userspace memory and a user process can rush to change it across fetches. 12 | Which means that the corresponding opcode and unique id fields in 13 | req->uc_data could be different from what is fetched in for the first time. 14 | 15 | Whether this double-fetch situation is a security critical bug depends on 16 | how req->uc_data will be used later. However, given that it is hard to 17 | enumerate all the possible use cases, a safer way is to ensure that the 18 | peeked header is actually the same message header after the second fetch. 19 | 20 | This patch enforces that the header of the message fetched into req->uc_data 21 | is the same as what is fetched in originally. In other words, hdr.opcode and 22 | hdr.unique do not change. 23 | 24 | Signed-off-by: Meng Xu 25 | --- 26 | fs/coda/psdev.c | 6 ++++++ 27 | 1 file changed, 6 insertions(+) 28 | 29 | diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c 30 | index f40e395..b9dbdd8 100644 31 | --- a/fs/coda/psdev.c 32 | +++ b/fs/coda/psdev.c 33 | @@ -178,6 +178,12 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf, 34 | goto out; 35 | } 36 | 37 | + /* 38 | + * Override the request header to make sure that it matches the 39 | + * first fetch from buf 40 | + */ 41 | + memcpy(req->uc_data, &hdr, 2 * sizeof(u_long)); 42 | + 43 | /* adjust outsize. is this useful ?? */ 44 | req->uc_outSize = nbytes; 45 | req->uc_flags |= CODA_REQ_WRITE; 46 | -- 47 | 2.7.4 48 | 49 | -------------------------------------------------------------------------------- /note/prelim/0001-net-compat-assert-the-size-of-cmsg-copied-in-is-as-e.patch: -------------------------------------------------------------------------------- 1 | From dd557c0274054803e1437499c8a77ba7bead26e8 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 12:54:42 -0400 4 | Subject: [PATCH] net: compat: assert the size of cmsg copied in is as expected 5 | 6 | The actual length of cmsg fetched in during the second loop 7 | (i.e., kcmsg - kcmsg_base) could be different from what we 8 | get from the first loop (i.e., kcmlen). 9 | 10 | The main reason is that the two get_user() calls in the two 11 | loops (i.e., get_user(ucmlen, &ucmsg->cmsg_len) and 12 | __get_user(ucmlen, &ucmsg->cmsg_len)) could cause ucmlen 13 | to have different values even they fetch from the same userspace 14 | address, as user can race to change the memory content in 15 | &ucmsg->cmsg_len across fetches. 16 | 17 | Although in the second loop, the sanity check 18 | if ((char *)kcmsg_base + kcmlen - (char *)kcmsg < CMSG_ALIGN(tmp)) 19 | is inplace, it only ensures that the cmsg fetched in during the 20 | second loop does not exceed the length of kcmlen, but not 21 | necessarily equal to kcmlen. But indicated by the assignment 22 | kmsg->msg_controllen = kcmlen, we should enforce that. 23 | 24 | This patch adds this additional sanity check and ensures that 25 | what is recorded in kmsg->msg_controllen is the actual cmsg length. 26 | 27 | Signed-off-by: Meng Xu 28 | --- 29 | net/compat.c | 7 +++++++ 30 | 1 file changed, 7 insertions(+) 31 | 32 | diff --git a/net/compat.c b/net/compat.c 33 | index 6ded6c8..2238171 100644 34 | --- a/net/compat.c 35 | +++ b/net/compat.c 36 | @@ -185,6 +185,13 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk, 37 | ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); 38 | } 39 | 40 | + /* 41 | + * check the length of messages copied in is the same as the 42 | + * what we get from the first loop 43 | + */ 44 | + if ((char *)kcmsg - (char *)kcmsg_base != kcmlen) 45 | + goto Einval; 46 | + 47 | /* Ok, looks like we made it. Hook it up and return success. */ 48 | kmsg->msg_control = kcmsg_base; 49 | kmsg->msg_controllen = kcmlen; 50 | -- 51 | 2.7.4 52 | 53 | -------------------------------------------------------------------------------- /pass/KSym/Logger.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | void Logger::vec() { 5 | assert(cur->is_array()); 6 | 7 | cur->emplace_back(json::array()); 8 | 9 | cur = &cur->back(); 10 | stk.push(cur); 11 | } 12 | 13 | void Logger::vec(const string &key) { 14 | assert(cur->is_object()); 15 | 16 | auto res = cur->emplace(key, json::array()); 17 | assert(res.second); 18 | 19 | cur = &res.first.value(); 20 | stk.push(cur); 21 | } 22 | 23 | void Logger::map() { 24 | assert(cur->is_array()); 25 | 26 | cur->emplace_back(json::object()); 27 | 28 | cur = &cur->back(); 29 | stk.push(cur); 30 | } 31 | 32 | void Logger::map(const string &key) { 33 | assert(cur->is_object()); 34 | 35 | auto res = cur->emplace(key, json::object()); 36 | assert(res.second); 37 | 38 | cur = &res.first.value(); 39 | stk.push(cur); 40 | } 41 | 42 | template 43 | void Logger::log(const T &msg) { 44 | assert(cur->is_array()); 45 | cur->emplace_back(msg); 46 | } 47 | 48 | void Logger::log(const char *msg) { 49 | assert(cur->is_array()); 50 | cur->emplace_back(msg); 51 | } 52 | 53 | template 54 | void Logger::log(const string &key, const T &msg) { 55 | assert(cur->is_object()); 56 | cur->emplace(key, msg); 57 | } 58 | 59 | void Logger::log(const string &key, const char *msg) { 60 | assert(cur->is_object()); 61 | cur->emplace(key, msg); 62 | } 63 | 64 | void Logger::pop() { 65 | stk.pop(); 66 | cur = stk.top(); 67 | } 68 | 69 | void Logger::dump(raw_ostream &stm, int indent) { 70 | stm << rec.dump(indent); 71 | } 72 | 73 | void Logger::dump(const string &fn, int indent) { 74 | error_code ec; 75 | raw_fd_ostream stm(StringRef(fn), ec, sys::fs::F_RW); 76 | assert(ec.value() == 0); 77 | 78 | dump(stm, indent); 79 | } 80 | 81 | #define EXT_TEMPLATE(type) \ 82 | template void Logger::log(const type &msg); \ 83 | template void Logger::log(const string &key, const type &msg); 84 | 85 | EXT_TEMPLATE(bool); 86 | EXT_TEMPLATE(int); 87 | EXT_TEMPLATE(unsigned int); 88 | EXT_TEMPLATE(long); 89 | EXT_TEMPLATE(unsigned long); 90 | EXT_TEMPLATE(string); 91 | -------------------------------------------------------------------------------- /unit/Linux/Makefile: -------------------------------------------------------------------------------- 1 | # toolchain 2 | CC := clang 3 | CK := opt 4 | 5 | # files 6 | SRCS := $(wildcard *.c) 7 | BCFS := $(patsubst %.c, %.bc, $(SRCS)) 8 | SYMS := $(patsubst %.c, %.sym, $(SRCS)) 9 | 10 | # paths 11 | VERSION = 4.12.5 12 | 13 | LINUX_WKS := ../../../code 14 | LINUX_SRC := $(LINUX_WKS)/srcs/linux-stable-$(VERSION) 15 | LINUX_OBJ := $(LINUX_WKS)/objs/linux-stable-$(VERSION) 16 | LINUX_BIN := $(LINUX_WKS)/bins/linux-stable-$(VERSION) 17 | 18 | # basic flags 19 | FLAGS := -std=gnu89 -I$(PWD)/../ 20 | 21 | # includes 22 | FLAGS += -nostdinc \ 23 | -isystem /usr/lib/gcc/x86_64-linux-gnu/5.4.0/include \ 24 | -I$(LINUX_SRC)/arch/x86/include \ 25 | -I$(LINUX_OBJ)/arch/x86/include \ 26 | -I$(LINUX_OBJ)/arch/x86/include/generated \ 27 | -I$(LINUX_OBJ)/arch/x86/include/generated/uapi \ 28 | -I$(LINUX_SRC)/include \ 29 | -I$(LINUX_OBJ)/include \ 30 | -I$(LINUX_OBJ)/include/generated \ 31 | -I$(LINUX_OBJ)/include/generated/uapi \ 32 | -I$(LINUX_BIN)/include \ 33 | -I$(LINUX_BIN)/include/uapi \ 34 | -include $(LINUX_SRC)/include/linux/kconfig.h 35 | 36 | # defines 37 | FLAGS += -D__KERNEL__ \ 38 | -DCONFIG_X86_X32_ABI \ 39 | -DCONFIG_AS_CFI=1 \ 40 | -DCONFIG_AS_CFI_SIGNAL_FRAME=1 \ 41 | -DCONFIG_AS_CFI_SECTIONS=1 \ 42 | -DCONFIG_AS_FXSAVEQ=1 \ 43 | -DCONFIG_AS_SSSE3=1 \ 44 | -DCONFIG_AS_CRC32=1 \ 45 | -DCONFIG_AS_AVX=1 \ 46 | -DCONFIG_AS_AVX2=1 \ 47 | -DCONFIG_AS_SHA1_NI=1 \ 48 | -DCONFIG_AS_SHA256_NI=1 \ 49 | -DCC_USING_FENTRY \ 50 | -DMODULE \ 51 | -D"KBUILD_STR(s)=\#s" \ 52 | -D"KBUILD_BASENAME=KBUILD_STR(prog)" \ 53 | -D"KBUILD_MODNAME=KBUILD_STR(prog)" 54 | 55 | # quite warnings from llvm 56 | FLAGS += -Wno-address-of-packed-member \ 57 | -Wno-pointer-sign \ 58 | -Wno-gnu-variable-sized-type-not-at-end \ 59 | -Wno-unused-result 60 | 61 | # rules 62 | all: build check 63 | 64 | %.bc: %.c 65 | $(CC) $(FLAGS) -S -emit-llvm -o $@ $< 66 | 67 | %.sym: %.bc 68 | @echo $< 69 | @$(CK) -symf $@ $< >/dev/null 70 | @echo "" 71 | 72 | $(SYMS): .FORCE 73 | 74 | .FORCE: 75 | 76 | # targets 77 | build: $(BCFS) 78 | 79 | check: $(SYMS) 80 | 81 | clean: 82 | rm -rf *.o *.bc *.sym 83 | 84 | .PHONY: build check clean all .FORCE 85 | -------------------------------------------------------------------------------- /note/prelim/0001-aacraid-fix-potential-double-fetch-issue.patch: -------------------------------------------------------------------------------- 1 | From 7277490ee956946a63c286e5ccce1ede299aac93 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 23:55:09 -0400 4 | Subject: [PATCH] aacraid: fix potential double-fetch issue 5 | 6 | While examining the kernel source code, I found a dangerous operation 7 | that could turn into a double-fetch situation (a race condition bug) 8 | where the same userspace memory region are fetched twice into kernel 9 | with sanity checks after the first fetch while missing checks after the 10 | second fetch. 11 | 12 | 1. The first userspace fetch happens in 13 | copy_from_user(&fibsize, &user_srb->count,sizeof(u32)) 14 | 15 | 2. Subsequently fibsize undergoes a few sanity checks and is then 16 | used to allocate user_srbcmd = kmalloc(fibsize, GFP_KERNEL). 17 | 18 | 3. The second userspace fetch happens in 19 | copy_from_user(user_srbcmd, user_srb,fibsize) 20 | 21 | 4. Given that the memory region pointed by user_srb can be fully 22 | controlled in userspace, an attacker can race to override the 23 | user_srb->count to arbitrary value (say 0XFFFFFFFF) after the first 24 | fetch but before the second. The changed value will be copied to 25 | user_srbcmd. 26 | 27 | The patch explicitly overrides user_srbcmd->count after the second 28 | userspace fetch with the value fibsize from the first userspace fetch. 29 | In this way, it is assured that the relation, user_srbcmd->count stores 30 | the size of the user_srbcmd buffer, still holds after the second fetch. 31 | 32 | Signed-off-by: Meng Xu 33 | --- 34 | drivers/scsi/aacraid/commctrl.c | 6 ++++++ 35 | 1 file changed, 6 insertions(+) 36 | 37 | diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c 38 | index 9ab0fa9..734bf11 100644 39 | --- a/drivers/scsi/aacraid/commctrl.c 40 | +++ b/drivers/scsi/aacraid/commctrl.c 41 | @@ -540,6 +540,12 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) 42 | goto cleanup; 43 | } 44 | 45 | + /* 46 | + * re-establish the relation that user_srbcmd->count holds the 47 | + * size of user_srbcmd 48 | + */ 49 | + user_srbcmd->count = fibsize; 50 | + 51 | flags = user_srbcmd->flags; /* from user in cpu order */ 52 | switch (flags & (SRB_DataIn | SRB_DataOut)) { 53 | case SRB_DataOut: 54 | -- 55 | 2.7.4 56 | 57 | -------------------------------------------------------------------------------- /pass/KSym/Node.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef __NODE__H 3 | #define __NODE__H 4 | 5 | #include "llvm/IR/Value.h" 6 | #include "llvm/IR/Function.h" 7 | #include "llvm/IR/DataLayout.h" 8 | #include "llvm/IR/Constants.h" 9 | #include "llvm/IR/Instructions.h" 10 | #include "llvm/IR/Module.h" 11 | #include "llvm/ADT/DenseMap.h" 12 | #include "llvm/Support/raw_ostream.h" 13 | #include "llvm/Support/FileSystem.h" 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace llvm; 19 | using namespace std; 20 | 21 | class Node; 22 | 23 | // four node types: 24 | // - GLOBAL: global variables, functions 25 | // - ALLOC: local variables allocated in functions 26 | // - HEAP: objects allocated by calling malloc/calloc 27 | // - UNDEFINED: the node doesn't point to any value 28 | enum NodeType 29 | { 30 | GLOBAL, 31 | ALLOC, 32 | HEAP, 33 | UNDEFINED, 34 | }; 35 | 36 | // instructions may affect the memory: 37 | // - WRITE: the instruction will write the memory 38 | // - READ: the instruction will read the memory 39 | enum InstType 40 | { 41 | INIT, 42 | WRITE, 43 | READ, 44 | NONE, 45 | }; 46 | 47 | class Node 48 | { 49 | public: 50 | NodeType nType; 51 | InstType iType; 52 | // the value represent the memory, a = allocate ..., b = malloc (...) 53 | const Value *value = nullptr; 54 | unordered_map *mem = nullptr; 55 | // save the 'mem' allocated by this node, deconstructor can delete the 'mem' 56 | unordered_map *savedMem = nullptr; 57 | int64_t startOffset = 0; 58 | 59 | // the instruction/argument may touch the memory 60 | const Value *inst = nullptr; 61 | 62 | static Node *GLOBAL_CONSTANT_NODE; 63 | 64 | public: 65 | Node(NodeType nType_, InstType iType_, const Value *value_ = nullptr, const Value *inst_ = nullptr): 66 | nType (nType_), iType (iType_), value (value_), inst (inst_) { 67 | mem = new unordered_map (); 68 | savedMem = mem; 69 | } 70 | 71 | ~Node() { 72 | delete savedMem; 73 | } 74 | 75 | Node* getNode(); 76 | Node* getNode(int64_t offset); 77 | 78 | void insertNode(int64_t offset, Node *node); 79 | void insertNode(Node *node); 80 | 81 | // copy this's mem to dstNode's mem 82 | void copy(Node *dstNode); 83 | void deepCopy(Node * dstNode); 84 | 85 | void dump(); 86 | void dump(std::unordered_set &, unsigned); 87 | }; 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /note/prelim/0001-perf-core-fix-potential-double-fetch-bug.patch: -------------------------------------------------------------------------------- 1 | From 3f419dcaa836a4bdbdf3a3304a0712660f61ddcb Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Wed, 23 Aug 2017 16:10:53 -0400 4 | Subject: [PATCH] perf/core: fix potential double-fetch bug 5 | 6 | While examining the kernel source code, I found a dangerous operation that 7 | could turn into a double-fetch situation (a race condition bug) where the same 8 | userspace memory region are fetched twice into kernel with sanity checks after 9 | the first fetch while missing checks after the second fetch. 10 | 11 | 1. The first fetch happens in line 9573 get_user(size, &uattr->size). 12 | 13 | 2. Subsequently the `size` variable undergoes a few sanity checks and 14 | transformations (line 9577 to 9584). 15 | 16 | 3. The second fetch happens in line 9610 copy_from_user(attr, uattr, size) 17 | 18 | 4. Given that `uattr` can be fully controlled in userspace, an attacker can 19 | race condition to override `uattr->size` to arbitrary value (say, 0xFFFFFFFF) 20 | after the first fetch but before the second fetch. The changed value will be 21 | copied to `attr->size`. 22 | 23 | 5. There is no further checks on `attr->size` until the end of this function, 24 | and once the function returns, we lose the context to verify that `attr->size` 25 | conforms to the sanity checks performed in step 2 (line 9577 to 9584). 26 | 27 | 6. My manual analysis shows that `attr->size` is not used elsewhere later, 28 | so, there is no working exploit against it right now. However, this could 29 | easily turns to an exploitable one if careless developers start to use 30 | `attr->size` later. 31 | 32 | Proposed patch: 33 | 34 | The patch is a one-liner which overrides `attr->size` from the second fetch to 35 | the one from the first fetch, regardless of what is actually copied in. 36 | In this way, it is assured that `attr->size` is in consistent with the checks 37 | performed after the first fetch. 38 | 39 | Signed-off-by: Meng Xu 40 | --- 41 | kernel/events/core.c | 2 ++ 42 | 1 file changed, 2 insertions(+) 43 | 44 | diff --git a/kernel/events/core.c b/kernel/events/core.c 45 | index ee20d4c..c0d7946 100644 46 | --- a/kernel/events/core.c 47 | +++ b/kernel/events/core.c 48 | @@ -9611,6 +9611,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, 49 | if (ret) 50 | return -EFAULT; 51 | 52 | + attr->size = size; 53 | + 54 | if (attr->__reserved_1) 55 | return -EINVAL; 56 | 57 | -- 58 | 2.7.4 59 | 60 | -------------------------------------------------------------------------------- /note/prelim/0001-isdn-i4l-check-the-message-proto-does-not-change-acr.patch: -------------------------------------------------------------------------------- 1 | From 1a47b1b49975c9c68d027d978b7ad62eed5458f1 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 13:57:07 -0400 4 | Subject: [PATCH] isdn/i4l: check the message proto does not change across 5 | fetches 6 | 7 | In isdn_ppp_write(), the header (i.e., protobuf) of the buffer is fetched 8 | twice from userspace. The first fetch is used to peek at the protocol 9 | of the message and reset the huptimer if necessary; while the second 10 | fetch copies in the whole buffer. However, given that buf resides in 11 | userspace memory, a user process can race to change its memory content 12 | across fetches. By doing so, we can either avoid resetting the huptimer 13 | for any type of packets (by first setting proto to PPP_LCP and later 14 | change to the actual type) or force resetting the huptimer for LCP packets. 15 | 16 | This patch does a memcmp between the two fetches and abort if changes to 17 | the protobuf is detected across fetches. 18 | 19 | Signed-off-by: Meng Xu 20 | --- 21 | drivers/isdn/i4l/isdn_ppp.c | 13 ++++++++++++- 22 | 1 file changed, 12 insertions(+), 1 deletion(-) 23 | 24 | diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c 25 | index 6c44609..21a9ae8 100644 26 | --- a/drivers/isdn/i4l/isdn_ppp.c 27 | +++ b/drivers/isdn/i4l/isdn_ppp.c 28 | @@ -857,6 +857,7 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) 29 | (lp->flags & ISDN_NET_CONNECTED)) { 30 | unsigned short hl; 31 | struct sk_buff *skb; 32 | + void *skb_tail; 33 | /* 34 | * we need to reserve enough space in front of 35 | * sk_buff. old call to dev_alloc_skb only reserved 36 | @@ -869,11 +870,21 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) 37 | return count; 38 | } 39 | skb_reserve(skb, hl); 40 | - if (copy_from_user(skb_put(skb, count), buf, count)) 41 | + skb_tail = skb_put(skb, count); 42 | + if (copy_from_user(skb_tail, buf, count)) 43 | { 44 | kfree_skb(skb); 45 | return -EFAULT; 46 | } 47 | + 48 | + /* 49 | + * abort if the message proto is changed between the fetches 50 | + */ 51 | + if (memcmp(skb_tail, protobuf, 4)) { 52 | + kfree_skb(skb); 53 | + return -EFAULT; 54 | + } 55 | + 56 | if (is->debug & 0x40) { 57 | printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); 58 | isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); 59 | -- 60 | 2.7.4 61 | 62 | -------------------------------------------------------------------------------- /note/prelim/0001-ALSA-asihpi-fix-a-potential-double-fetch-bug-when-co.patch: -------------------------------------------------------------------------------- 1 | From 89c1bb6a1464396134093d9a4e7287c887ab75a6 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 00:55:14 -0400 4 | Subject: [PATCH] ALSA: asihpi: fix a potential double-fetch bug when copying 5 | puhm 6 | 7 | The hm->h.size is intended to hold the actual size of the hm struct 8 | that is copied from userspace and should always be <= sizeof(*hm). 9 | 10 | However, after copy_from_user(hm, puhm, hm->h.size), since userspace 11 | process has full control over the memory region pointed by puhm, it is 12 | possible that the value of hm->h.size is different from what is fetched-in 13 | previously (get_user(hm->h.size, (u16 __user *)puhm)). In other words, 14 | hm->h.size is overriden and the relation between hm->h.size and the hm 15 | struct is broken. 16 | 17 | This patch proposes to use a seperate variable, msg_size, to hold 18 | the value of the first fetch and override hm->h.size to msg_size 19 | after the second fetch to maintain the relation. 20 | 21 | Signed-off-by: Meng Xu 22 | --- 23 | sound/pci/asihpi/hpioctl.c | 12 ++++++++---- 24 | 1 file changed, 8 insertions(+), 4 deletions(-) 25 | 26 | diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c 27 | index 7e3aa50..5badd08 100644 28 | --- a/sound/pci/asihpi/hpioctl.c 29 | +++ b/sound/pci/asihpi/hpioctl.c 30 | @@ -103,6 +103,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 31 | void __user *puhr; 32 | union hpi_message_buffer_v1 *hm; 33 | union hpi_response_buffer_v1 *hr; 34 | + u16 msg_size; 35 | u16 res_max_size; 36 | u32 uncopied_bytes; 37 | int err = 0; 38 | @@ -127,22 +128,25 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 39 | } 40 | 41 | /* Now read the message size and data from user space. */ 42 | - if (get_user(hm->h.size, (u16 __user *)puhm)) { 43 | + if (get_user(msg_size, (u16 __user *)puhm)) { 44 | err = -EFAULT; 45 | goto out; 46 | } 47 | - if (hm->h.size > sizeof(*hm)) 48 | - hm->h.size = sizeof(*hm); 49 | + if (msg_size > sizeof(*hm)) 50 | + msg_size = sizeof(*hm); 51 | 52 | /* printk(KERN_INFO "message size %d\n", hm->h.wSize); */ 53 | 54 | - uncopied_bytes = copy_from_user(hm, puhm, hm->h.size); 55 | + uncopied_bytes = copy_from_user(hm, puhm, msg_size); 56 | if (uncopied_bytes) { 57 | HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes); 58 | err = -EFAULT; 59 | goto out; 60 | } 61 | 62 | + /* Override h.size in case it is changed between two userspace fetches */ 63 | + hm->h.size = msg_size; 64 | + 65 | if (get_user(res_max_size, (u16 __user *)puhr)) { 66 | err = -EFAULT; 67 | goto out; 68 | -- 69 | 2.7.4 70 | 71 | -------------------------------------------------------------------------------- /pass/KSym/Project.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef PROJECT_H_ 3 | #define PROJECT_H_ 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | 60 | #include 61 | 62 | #include "json.hpp" 63 | 64 | using namespace std; 65 | using namespace llvm; 66 | using json = nlohmann::json; 67 | 68 | // existing passes 69 | #include "Lower.h" 70 | #include "Alias.h" 71 | 72 | // helper includes 73 | #include "Util.h" 74 | #include "Logger.h" 75 | 76 | // project globals 77 | extern Dumper DUMP; 78 | 79 | #ifdef KSYM_DEBUG 80 | extern Logger SLOG; 81 | #endif 82 | 83 | extern set EXCEPT; 84 | 85 | // project includes 86 | #include "Slice.h" 87 | #include "DAG.h" 88 | #include "Unroll.h" 89 | #include "Oracle.h" 90 | 91 | #include "Symbolic.h" 92 | #include "Trace.h" 93 | #include "SEG.h" 94 | 95 | #include "Fetch.h" 96 | #include "Func.h" 97 | #include "Tool.h" 98 | 99 | // static recorder that dumps various info for debugging 100 | #include "Record.h" 101 | 102 | #endif /* PROJECT_H_ */ 103 | -------------------------------------------------------------------------------- /note/prelim/0001-acpi-fix-potential-double-fetch-bug.patch: -------------------------------------------------------------------------------- 1 | From 22ca63ce809ed96b3e52257915ff1e3856e81030 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Wed, 23 Aug 2017 16:34:29 -0400 4 | Subject: [PATCH] acpi: fix potential double-fetch bug 5 | 6 | While examining the kernel source code, I found a dangerous operation that 7 | could turn into a double-fetch situation (a race condition bug) where 8 | the same userspace memory region are fetched twice into kernel with sanity 9 | checks after the first fetch while missing checks after the second fetch. 10 | 11 | In the case of *ppos == 0: 12 | 13 | 1. The first fetch happens in line 36 14 | copy_from_user(&table, user_buf, sizeof(struct acpi_table_header))) 15 | 16 | 2. Subsequently `table.length` variable is used to allocate the `buf` 17 | (line 40). 18 | 19 | 3. The second fetch happens in line 54 20 | copy_from_user(buf + (*ppos), user_buf, count) 21 | 22 | 4. Given that `user_buf` can be fully controlled in userspace, an attacker can 23 | race condition to override the header part of `user_buf`, say, 24 | `((struct acpi_table_header *)user_buf)->length` to arbitrary value 25 | (say 0xFFFFFFFF) after the first fetch but before the second fetch. The changed 26 | value will be copied to `buf`. 27 | 28 | 5. There is no checks on `((struct acpi_table_header *)buf)->length` until the 29 | use of it in line 64 status = acpi_install_method(buf), which means that the 30 | assumed relation, `buf->length` == length of the buffer, might not hold after 31 | the second fetch. And once the control goes to function `acpi_install_method`, 32 | we lose the context to assert that. 33 | 34 | 6. It is lucky that `buf->length` is not used in function `acpi_install_method` 35 | so, there is no working exploit against it right now. However, this could 36 | easily turns to an exploitable one if careless developers start to use 37 | `buf->length` later. 38 | 39 | Proposed patch: 40 | 41 | The patch explicitly overrides `buf->length` after the second fetch with the 42 | value `max_size` from the first fetch. In this way, it is assured that the 43 | relation, `buf->length` == length of the buffer, holds after the second fetch. 44 | 45 | Signed-off-by: Meng Xu 46 | --- 47 | drivers/acpi/custom_method.c | 7 +++++++ 48 | 1 file changed, 7 insertions(+) 49 | 50 | diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c 51 | index c68e724..eea7986 100644 52 | --- a/drivers/acpi/custom_method.c 53 | +++ b/drivers/acpi/custom_method.c 54 | @@ -57,6 +57,13 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, 55 | return -EFAULT; 56 | } 57 | 58 | + if (!(*ppos)) { 59 | + struct acpi_table_header *hdr = 60 | + ACPI_CAST_PTR(struct acpi_table_header, buf); 61 | + 62 | + hdr->length = max_size; 63 | + } 64 | + 65 | uncopied_bytes -= count; 66 | *ppos += count; 67 | 68 | -- 69 | 2.7.4 70 | 71 | -------------------------------------------------------------------------------- /note/prelim/0001-nvdimm-fix-potential-double-fetch-bug.patch: -------------------------------------------------------------------------------- 1 | From 5c44b020d128a6fd40a389b7f9f6661a793b1f26 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Wed, 23 Aug 2017 16:44:08 -0400 4 | Subject: [PATCH] nvdimm: fix potential double-fetch bug 5 | 6 | While examining the kernel source code, I found a dangerous operation that 7 | could turn into a double-fetch situation (a race condition bug) where 8 | the same userspace memory region are fetched twice into kernel with sanity 9 | checks after the first fetch while missing checks after the second fetch. 10 | 11 | In the case of _IOC_NR(ioctl_cmd) == ND_CMD_CALL: 12 | 13 | 1. The first fetch happens in line 935 copy_from_user(&pkg, p, sizeof(pkg) 14 | 15 | 2. subsequently `pkg.nd_reserved2` is asserted to be all zeroes 16 | (line 984 to 986). 17 | 18 | 3. The second fetch happens in line 1022 copy_from_user(buf, p, buf_len) 19 | 20 | 4. Given that `p` can be fully controlled in userspace, an attacker can 21 | race condition to override the header part of `p`, say, 22 | `((struct nd_cmd_pkg *)p)->nd_reserved2` to arbitrary value 23 | (say nine 0xFFFFFFFF for `nd_reserved2`) after the first fetch but before the 24 | second fetch. The changed value will be copied to `buf`. 25 | 26 | 5. There is no checks on the second fetches until the use of it in 27 | line 1034: nd_cmd_clear_to_send(nvdimm_bus, nvdimm, cmd, buf) and 28 | line 1038: nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, &cmd_rc) 29 | which means that the assumed relation, `p->nd_reserved2` are all zeroes might 30 | not hold after the second fetch. And once the control goes to these functions 31 | we lose the context to assert the assumed relation. 32 | 33 | 6. Based on my manual analysis, `p->nd_reserved2` is not used in function 34 | `nd_cmd_clear_to_send` and potential implementations of `nd_desc->ndctl` 35 | so there is no working exploit against it right now. However, this could 36 | easily turns to an exploitable one if careless developers start to use 37 | `p->nd_reserved2` later and assume that they are all zeroes. 38 | 39 | Proposed patch: 40 | 41 | The patch explicitly overrides `buf->nd_reserved2` after the second fetch with 42 | the value `pkg.nd_reserved2` from the first fetch. In this way, it is assured 43 | that the relation, `buf->nd_reserved2` are all zeroes, holds after the second 44 | fetch. 45 | 46 | Signed-off-by: Meng Xu 47 | --- 48 | drivers/nvdimm/bus.c | 6 ++++++ 49 | 1 file changed, 6 insertions(+) 50 | 51 | diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c 52 | index 937fafa..20c4d0f 100644 53 | --- a/drivers/nvdimm/bus.c 54 | +++ b/drivers/nvdimm/bus.c 55 | @@ -1024,6 +1024,12 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 56 | goto out; 57 | } 58 | 59 | + if (cmd == ND_CMD_CALL) { 60 | + struct nd_cmd_pkg *hdr = (struct nd_cmd_pkg *)buf; 61 | + memcpy(hdr->nd_reserved2, pkg.nd_reserved2, 62 | + sizeof(pkg.nd_reserved2)); 63 | + } 64 | + 65 | nvdimm_bus_lock(&nvdimm_bus->dev); 66 | rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf); 67 | if (rc) 68 | -- 69 | 2.7.4 70 | 71 | -------------------------------------------------------------------------------- /pass/KSym/Tool.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // option setup 5 | cl::opt OUTF("symf", cl::Required, cl::desc("")); 6 | 7 | // pass info 8 | char KSym::ID = 0; 9 | static RegisterPass X("KSym", "Kernel Static Symbolizer", false, true); 10 | 11 | // globals 12 | Dumper DUMP; 13 | 14 | #ifdef KSYM_DEBUG 15 | Logger SLOG; 16 | #endif 17 | 18 | set EXCEPT; 19 | 20 | // black lists 21 | static const set BLACKLIST({ 22 | // llvm loop construction bug 23 | string("vmw_execbuf_process"), 24 | string("do_io_rw"), 25 | string("i915_gem_do_execbuffer"), 26 | string("atomisp_cp_general_isp_parameters"), 27 | // user copying 28 | string("copy_strings"), 29 | string("mcopy_atomic"), 30 | string("strndup_user"), 31 | // sync 32 | string("do_futex"), 33 | string("futex_requeue"), 34 | // core dump 35 | string("do_usercopy_stack"), 36 | string("do_usercopy_heap_size"), 37 | string("do_usercopy_heap_flag"), 38 | string("elf_core_dump"), 39 | string("elf_core_dump.2051"), 40 | // path explosion 41 | string("pktgen_if_write"), 42 | string("pktgen_thread_write"), 43 | // loop explosion 44 | string("mptctl_do_fw_download"), 45 | string("qib_user_sdma_writev"), 46 | string("snd_emu10k1_icode_poke"), 47 | string("snd_soundfont_load"), 48 | string("st_write"), 49 | string("vringh_getdesc_user"), 50 | // manually checked 51 | string("__nd_ioctl"), 52 | }); 53 | 54 | // class kAA 55 | KSym::KSym() : ModulePass(ID) { 56 | // do nothing 57 | } 58 | 59 | KSym::~KSym() { 60 | #ifdef KSYM_DEBUG 61 | SLOG.dump(OUTF.getValue()); 62 | #endif 63 | } 64 | 65 | void KSym::getAnalysisUsage(AnalysisUsage &au) const { 66 | // since we are one-shot pass, simply mark everything is preserved 67 | // regardless of whether it is actually preserved or not 68 | ModulePass::getAnalysisUsage(au); 69 | au.setPreservesAll(); 70 | } 71 | 72 | // entry point 73 | bool KSym::runOnModule(Module &m) { 74 | // run module pass 75 | breakConstantExpr(m); 76 | 77 | // create module-level vars 78 | ModuleOracle mo(m); 79 | 80 | // per-function handling 81 | for(Function &f : m){ 82 | // ignored non-defined functions 83 | if(f.isIntrinsic() || f.isDeclaration()){ 84 | continue; 85 | } 86 | 87 | // ignore blacklisted functions 88 | if(BLACKLIST.find(f.getName().str()) != BLACKLIST.end()){ 89 | continue; 90 | } 91 | 92 | // lower swtich here 93 | lowerSwitch(f); 94 | 95 | // create and run function handler 96 | #ifdef KSYM_DEBUG 97 | SLOG.map(f.getName().str()); 98 | #endif 99 | 100 | FuncHandle handle(f, mo); 101 | handle.run(); 102 | 103 | #ifdef KSYM_DEBUG 104 | SLOG.pop(); 105 | #endif 106 | } 107 | 108 | // dump exceptions 109 | for(Function *ex : EXCEPT){ 110 | errs() << "[!] " << ex->getName() << "\n"; 111 | } 112 | 113 | // mark nothing have changed 114 | return false; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /work/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import exit 4 | from argparse import ArgumentParser 5 | 6 | from conf import * 7 | from util import * 8 | 9 | from linux_stable import APP_LINUX_STABLE 10 | from xnu_stable import APP_XNU_STABLE 11 | 12 | # main 13 | def main(): 14 | if HOST == "Linux": 15 | APPS = { 16 | "linux-stable" : APP_LINUX_STABLE, 17 | } 18 | 19 | DEFS = "linux-stable" 20 | 21 | elif HOST == "Darwin": 22 | APPS = { 23 | "xnu-stable" : APP_XNU_STABLE, 24 | } 25 | 26 | DEFS = "xnu-stable" 27 | 28 | else: 29 | exit("Unknown host: %s" % HOST) 30 | 31 | # init 32 | parser = ArgumentParser() 33 | parser.add_argument("-a", "--app", 34 | default=DEFS, choices=APPS.keys()) 35 | parser.add_argument("-t", "--tag", default=None) 36 | 37 | subs = parser.add_subparsers(dest = "cmd") 38 | 39 | sub_checkout = subs.add_parser("checkout") 40 | sub_config = subs.add_parser("config") 41 | sub_build = subs.add_parser("build") 42 | 43 | sub_parse = subs.add_parser("parse") 44 | 45 | sub_irgen = subs.add_parser("irgen") 46 | sub_irgen.add_argument("-i", "--input", action="append", default=None) 47 | sub_irgen.add_argument("-f", "--force", action="store_true") 48 | 49 | sub_group = subs.add_parser("group") 50 | 51 | sub_trans = subs.add_parser("trans") 52 | sub_trans.add_argument("-i", "--input", action="append", default=None) 53 | sub_trans.add_argument("-f", "--force", action="store_true") 54 | 55 | sub_check = subs.add_parser("check") 56 | sub_check.add_argument("-i", "--input", action="append", default=None) 57 | sub_check.add_argument("-s", "--show", action="store_false") 58 | 59 | sub_stat = subs.add_parser("stat") 60 | sub_dump = subs.add_parser("dump") 61 | sub_result = subs.add_parser("result") 62 | sun_analyze = subs.add_parser("analyze") 63 | 64 | # parse 65 | args = parser.parse_args() 66 | 67 | if args.tag is None: 68 | APP = APPS[args.app]() 69 | else: 70 | APP = APPS[args.app](args.tag) 71 | 72 | # exec 73 | if args.cmd == "checkout": 74 | APP.checkout() 75 | elif args.cmd == "config": 76 | APP.config() 77 | elif args.cmd == "build": 78 | APP.build() 79 | elif args.cmd == "parse": 80 | APP.parse() 81 | elif args.cmd == "irgen": 82 | APP.irgen(args.input, args.force) 83 | elif args.cmd == "group": 84 | APP.group() 85 | elif args.cmd == "trans": 86 | APP.trans(args.input, args.force) 87 | elif args.cmd == "check": 88 | APP.check(args.input, args.show) 89 | elif args.cmd == "dump": 90 | APP.dump() 91 | elif args.cmd == "stat": 92 | APP.stat() 93 | elif args.cmd == "result": 94 | APP.result() 95 | elif args.cmd == "analyze": 96 | APP.analyze() 97 | else: 98 | parser.print_usage() 99 | 100 | # main 101 | if __name__ == "__main__": 102 | with envpath("LD_LIBRARY_PATH", resolve(DEPS_Z3, "bins", "lib")): 103 | main() 104 | -------------------------------------------------------------------------------- /work/llvm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os import symlink 4 | from os.path import exists 5 | from shutil import rmtree 6 | from argparse import ArgumentParser 7 | 8 | from conf import * 9 | from util import * 10 | 11 | # functions 12 | def build(clean = False, debug = None): 13 | psrc = PATH_PASS 14 | if not exists(psrc): 15 | LOG_ERR("Src path %s does not exist" % psrc) 16 | return 17 | 18 | pobj = PASS_BDIR 19 | if clean: 20 | mkdirs(pobj, force = True) 21 | else: 22 | if not exists(pobj): 23 | LOG_ERR("Obj path %s does not exist" % pobj) 24 | return 25 | 26 | with cd(pobj): 27 | if clean: 28 | if debug is not None: 29 | btype = "Profile" 30 | ditem = debug.upper() 31 | else: 32 | btype = "Release" 33 | ditem = "NONE" 34 | 35 | cmd = ("cmake " + \ 36 | "-G 'Unix Makefiles' " + \ 37 | "-DCMAKE_BUILD_TYPE=%s " % btype + \ 38 | "-DCMAKE_PREFIX_PATH=%s " % LLVM_PREP + \ 39 | "-DDEPS_Z3_DIR=%s " % DEPS_Z3 + \ 40 | "-DDEPS_UTIL_DIR=%s " % DEPS_UTIL + \ 41 | "-DKSYM_DEBUG_ITEM=%s " % ditem + \ 42 | psrc) 43 | 44 | if shell(cmd) == 0: 45 | LOG_INF("Config done") 46 | else: 47 | LOG_ERR("Config failed") 48 | return 49 | 50 | cmd = "make -j%d" % OPTS_NCPU 51 | 52 | if shell(cmd) == 0: 53 | LOG_INF("Build done") 54 | else: 55 | LOG_ERR("Build failed") 56 | return 57 | 58 | def work(plain = False): 59 | pbin = LLVM_BINS 60 | if not exists(pbin): 61 | LOG_ERR("Bin path %sd does not exist" % pbin) 62 | return 63 | 64 | passthrough(resolve(LLVM_SYMS, "cpp"), LLVM_BIN_CPP, []) 65 | passthrough(resolve(LLVM_SYMS, "cc"), LLVM_BIN_CLA, []) 66 | passthrough(resolve(LLVM_SYMS, "c++"), LLVM_BIN_CXX, []) 67 | passthrough(resolve(LLVM_SYMS, "clang-cpp"), LLVM_BIN_CPP, []) 68 | passthrough(resolve(LLVM_SYMS, "clang"), LLVM_BIN_CLA, []) 69 | passthrough(resolve(LLVM_SYMS, "clang++"), LLVM_BIN_CXX, []) 70 | 71 | if plain: 72 | args = ["-S", "-emit-llvm"] 73 | else: 74 | args = ["-load", PASS_KSYM, "-KSym"] 75 | 76 | passthrough(resolve(LLVM_SYMS, "opt"), LLVM_BIN_OPT, args) 77 | 78 | with envpath("PATH", LLVM_SYMS): 79 | with envpath("LD_LIBRARY_PATH", resolve(DEPS_Z3, "bins", "lib")): 80 | with cd(resolve(PATH_TEST, HOST)): 81 | shell("bash") 82 | 83 | # main 84 | if __name__ == "__main__": 85 | # init 86 | parser = ArgumentParser() 87 | subs = parser.add_subparsers(dest = "cmd") 88 | 89 | sub_build = subs.add_parser("build") 90 | sub_build.add_argument("-c", "--clean", action="store_true") 91 | sub_build.add_argument("-d", "--debug", type=str, default=None) 92 | 93 | sub_work = subs.add_parser("work") 94 | sub_work.add_argument("-p", "--plain", action="store_true") 95 | 96 | # parse 97 | args = parser.parse_args() 98 | 99 | # exec 100 | if args.cmd == "build": 101 | build(args.clean, args.debug) 102 | elif args.cmd == "work": 103 | work(args.plain) 104 | else: 105 | parser.print_usage() 106 | -------------------------------------------------------------------------------- /pass/KSym/Node.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Node.h" 3 | 4 | Node* Node::GLOBAL_CONSTANT_NODE = new Node(NodeType::UNDEFINED, InstType::NONE, nullptr, nullptr); 5 | 6 | // copy this's mem to dstNode's mem 7 | void Node::copy(Node *dstNode) { 8 | dstNode->nType = this->nType; 9 | dstNode->value = this->value; 10 | dstNode->mem = this->mem; 11 | dstNode->startOffset = this->startOffset; 12 | } 13 | 14 | void Node::deepCopy(Node * dstNode) { 15 | dstNode->nType = this->nType; 16 | dstNode->value = this->value; 17 | dstNode->startOffset = this->startOffset; 18 | 19 | for (auto iter = this->mem->begin(); iter != this->mem->end(); iter++) 20 | (*dstNode->mem)[iter->first] = iter->second; 21 | } 22 | 23 | Node* Node::getNode(int64_t offset) { 24 | int64_t realOffset = startOffset + offset; 25 | 26 | if (realOffset < 0) { 27 | errs() << startOffset << ' ' << offset << '\n'; 28 | llvm_unreachable("wrong offset"); 29 | } 30 | 31 | unordered_map::iterator ret = mem->find(realOffset); 32 | Node *retNode = nullptr; 33 | 34 | if (ret == mem->end()) { 35 | Node *newNode = new Node(NodeType::UNDEFINED, InstType::NONE, nullptr, nullptr); 36 | 37 | pair p (realOffset, newNode); 38 | mem->insert(p); 39 | 40 | retNode = newNode; 41 | } else { 42 | retNode = ret->second; 43 | } 44 | 45 | if (!retNode) 46 | llvm_unreachable("return null node"); 47 | 48 | return retNode; 49 | } 50 | 51 | Node* Node::getNode() { 52 | return getNode(0); 53 | } 54 | 55 | void Node::insertNode(Node *node) { 56 | insertNode(0, node); 57 | } 58 | 59 | void Node::insertNode(int64_t offset, Node *node) { 60 | if (!node) 61 | llvm_unreachable("insert null node\n"); 62 | 63 | int64_t realOffset = startOffset + offset; 64 | 65 | if (realOffset < 0) 66 | llvm_unreachable("wrong offset"); 67 | 68 | if (!node) 69 | llvm_unreachable("insert null node"); 70 | 71 | auto it = mem->find(realOffset); 72 | if (it != mem->end()) { 73 | it->second = node; 74 | } else { 75 | pair p (realOffset, node); 76 | mem->insert(p); 77 | } 78 | } 79 | 80 | void Node::dump() { 81 | std::unordered_set visited; 82 | dump(visited, 0); 83 | visited.clear(); 84 | } 85 | 86 | void Node::dump(std::unordered_set &visited, unsigned indent) 87 | { 88 | std::error_code EC; 89 | raw_fd_ostream * ofs = new raw_fd_ostream("log.txt", EC, sys::fs::F_RW | sys::fs::F_Text); 90 | if (visited.find(this) != visited.end()) 91 | return; 92 | else 93 | visited.insert(this); 94 | 95 | ofs->indent(indent) << "*********this@" << this << "\n"; 96 | if (const Function * F = dyn_cast_or_null(value)) 97 | ofs->indent(indent) << "v: @" << F->getName() << "\n"; 98 | else if (value) 99 | ofs->indent(indent) << "v: " << *value << '\n'; 100 | else 101 | ofs->indent(indent) << "v: null\n"; 102 | 103 | ofs->indent(indent) << "startOffset: " << startOffset << '\n'; 104 | 105 | if (!mem) { 106 | ofs->indent(indent) << "mem is nullptr\n"; 107 | //llvm_unreachable("here"); 108 | } else { 109 | ofs->indent(indent) << "mem size: " << mem->size() << " @ " << mem << '\n'; 110 | unordered_map::iterator it = mem->begin(), ie = mem->end(); 111 | for (; it != ie; ++it) { 112 | ofs->indent(indent) << "offset: " << it->first << '\n'; 113 | Node *subNode = it->second; 114 | if (subNode) { 115 | ofs->indent(indent) <<"subnode@" << subNode << '\n'; 116 | 117 | subNode->dump(visited, indent + 1); 118 | } else 119 | llvm_unreachable("null node\n"); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /work/xnu_stable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os.path import splitext 4 | 5 | from conf import * 6 | from util import * 7 | from cmd import * 8 | from app import * 9 | 10 | # objects 11 | BUILDER = CMDBuilder( 12 | # defs and udef follow a black list approach 13 | defs = CMDChangeList(False, {}), 14 | udef = CMDChangeList(False, {}), 15 | # incs follow a black list approach 16 | incs = CMDChangeList(False, {}), 17 | ihdr = CMDChangeList(False, {}), 18 | isys = CMDChangeList(False, {}), 19 | # libs follow a white list approach 20 | libs = CMDChangeList(True, {}), 21 | elfs = CMDChangeList(True, {}), 22 | # flags follows a white list approach 23 | wset = CMDChangeList(True, {}), 24 | mset = CMDChangeList(True, {}), 25 | flag = CMDChangeList(True, { 26 | "nostdinc": CMDChange(None, None), 27 | "nostdlib": CMDChange(None, None), 28 | }), 29 | fset = CMDChangeList(True, {}), 30 | pars = CMDChangeList(True, {}), 31 | ) 32 | 33 | # functions 34 | class APP_XNU_STABLE(App): 35 | def __init__(self, tag = "10.12"): 36 | super(APP_XNU_STABLE, self).__init__("xnu-stable", tag, BUILDER) 37 | 38 | def convert(self): 39 | return "%s" % self.tag 40 | 41 | def config_impl(self, psrc, pobj, pbin, pext): 42 | return True 43 | 44 | def build_impl(self, psrc, pobj, pbin, pext, plog): 45 | dirs = { 46 | "SRCROOT" : psrc, 47 | "OBJROOT" : pobj, 48 | "DSTROOT" : pbin, 49 | "SYMROOT" : pext, 50 | } 51 | 52 | cfgs = { 53 | "SDKROOT" : "macosx", 54 | "ARCH_CONFIGS" : "X86_64", 55 | "KERNEL_CONFIGS" : "DEVELOPMENT", 56 | } 57 | 58 | dstr = " ".join("%s=%s" % (k, v) for (k, v) in dirs.items()) 59 | cstr = " ".join("%s=%s" % (k, v) for (k, v) in cfgs.items()) 60 | 61 | with cd(psrc): 62 | cmd = "make %s %s VERBOSE=YES MAKEJOBS=-j1" % (dstr, cstr) 63 | with open(plog, "w") as f: 64 | if shell(cmd, out = f) == 0: 65 | LOG_INF("Build done") 66 | else: 67 | LOG_ERR("Build failed") 68 | return False 69 | 70 | cmd = "make %s %s installhdrs" % (dstr, cstr) 71 | if shell(cmd) == 0: 72 | LOG_INF("Install done") 73 | else: 74 | LOG_ERR("Install failed") 75 | return False 76 | 77 | return True 78 | 79 | def parse_line(self, line): 80 | line = line.strip() 81 | toks = line.split(" ", 1) 82 | if not toks[0].endswith("clang"): 83 | return (False, None) 84 | 85 | argv = shellsplit(toks[1]) 86 | try: 87 | i = argv.index("&&") 88 | argv = argv[:i] 89 | except: 90 | pass 91 | 92 | return (True, argv) 93 | 94 | def parse_opts(self, opts): 95 | if opts.mode != "c": 96 | return (False, None) 97 | 98 | if not "KERNEL" in opts.defs: 99 | return (False, None) 100 | 101 | srcs = [] 102 | for src in opts.srcs: 103 | base, ext = splitext(src) 104 | if ext not in [".c", ".o", ".s", ".S"]: 105 | return None 106 | 107 | if ext in [".c"]: 108 | srcs.append(src) 109 | 110 | return (True, srcs) 111 | 112 | def check_filter(self): 113 | return set([]) 114 | -------------------------------------------------------------------------------- /work/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import re 4 | import os 5 | import sys 6 | 7 | from os import chdir, chmod, getcwd, makedirs, walk 8 | from os.path import abspath, dirname, exists, join 9 | from shutil import rmtree 10 | from contextlib import contextmanager 11 | from subprocess import call, check_output, CalledProcessError 12 | from termcolor import colored 13 | 14 | # logs utils 15 | def LOG_ERR(msg, detail = None): 16 | print >> sys.stdout, colored(msg, "red") 17 | if detail is not None: 18 | print >> sys.stderr, detail 19 | 20 | def LOG_WRN(msg, detail = None): 21 | print >> sys.stdout, colored(msg, "yellow") 22 | if detail is not None: 23 | print >> sys.stderr, detail 24 | 25 | def LOG_INF(msg, detail = None): 26 | print >> sys.stdout, colored(msg, "green") 27 | if detail is not None: 28 | print >> sys.stderr, detail 29 | 30 | def LOG_DBG(msg, detail = None): 31 | print >> sys.stdout, msg 32 | if detail is not None: 33 | print >> sys.stderr, detail 34 | 35 | # with stmts 36 | @contextmanager 37 | def cd(pn): 38 | cur = getcwd() 39 | chdir(pn) 40 | yield 41 | chdir(cur) 42 | 43 | @contextmanager 44 | def envpath(key, *pn): 45 | pns = ":".join(pn) 46 | 47 | if key in os.environ: 48 | cur = os.environ[key] 49 | os.environ[key] = pns + ":" + cur 50 | yield 51 | os.environ[key] = cur 52 | else: 53 | os.environ[key] = pns 54 | yield 55 | del os.environ[key] 56 | 57 | # path utils 58 | def resolve(*pn): 59 | return abspath(join(*pn)) 60 | 61 | def prepdn(pn): 62 | dn = dirname(pn) 63 | if exists(dn): 64 | return True 65 | else: 66 | try: 67 | makedirs(dn) 68 | return True 69 | except: 70 | return False 71 | 72 | def mkdirs(pn, force = False): 73 | if exists(pn): 74 | if not force: 75 | ans = raw_input("%s exists, delete ? " % pn) 76 | if ans.lower() != "y": 77 | return False 78 | 79 | rmtree(pn) 80 | 81 | try: 82 | makedirs(pn) 83 | return True 84 | except: 85 | return False 86 | 87 | def passthrough(sym, prog, args): 88 | if not prepdn(sym): 89 | return False 90 | 91 | content = '#!/bin/bash\nexec %s %s $@' % (prog, " ".join(args)) 92 | 93 | with open(sym, "w") as f: 94 | f.write(content) 95 | 96 | try: 97 | chmod(sym, 0777) 98 | return True 99 | except: 100 | return False 101 | 102 | def fcollect(root, ext): 103 | items = [] 104 | 105 | for dirpath, dnames, fnames in walk(root): 106 | for f in fnames: 107 | if f.endswith(ext): 108 | items.append(join(dirpath, f)) 109 | 110 | return items 111 | 112 | # exec utils 113 | def execute(*args): 114 | try: 115 | output = check_output(args).strip() 116 | result = 0 117 | except CalledProcessError as err: 118 | output = err.output.strip() 119 | result = err.returncode 120 | 121 | return (result, output) 122 | 123 | def shell(cmd, out = None, err = None): 124 | return call(cmd, shell = True, stdout = out, stderr = err) 125 | 126 | # common ops 127 | def gitclone(ver, src, dst): 128 | if not mkdirs(dst): 129 | LOG_WRN("Checkout canceled") 130 | return False 131 | 132 | cmd = "git clone --recursive --branch %s %s %s" % \ 133 | (ver, src, dst) 134 | 135 | if shell(cmd) == 0: 136 | LOG_INF("Checkout done") 137 | return True 138 | else: 139 | LOG_ERR("Checkout failed") 140 | return False 141 | 142 | # string utils 143 | SPLIT_PATTERN = re.compile(r'''((?:[^ "']|"[^"]*"|'[^']*')+)''') 144 | 145 | def shellsplit(cmd): 146 | return SPLIT_PATTERN.split(cmd)[1::2] 147 | -------------------------------------------------------------------------------- /note/prelim/0001-isdn-i4l-fetch-the-ppp_write-buffer-in-one-shot.patch: -------------------------------------------------------------------------------- 1 | From 72d2ecd3e58d81374f8b9f7181da5fefe497d661 Mon Sep 17 00:00:00 2001 2 | From: Meng Xu 3 | Date: Tue, 19 Sep 2017 21:24:28 -0400 4 | Subject: [PATCH] isdn/i4l: fetch the ppp_write buffer in one shot 5 | 6 | In isdn_ppp_write(), the header (i.e., protobuf) of the buffer is 7 | fetched twice from userspace. The first fetch is used to peek at the 8 | protocol of the message and reset the huptimer if necessary; while the 9 | second fetch copies in the whole buffer. However, given that buf resides 10 | in userspace memory, a user process can race to change its memory content 11 | across fetches. By doing so, we can either avoid resetting the huptimer 12 | for any type of packets (by first setting proto to PPP_LCP and later 13 | change to the actual type) or force resetting the huptimer for LCP 14 | packets. 15 | 16 | This patch changes this double-fetch behavior into two single fetches 17 | decided by condition (lp->isdn_device < 0 || lp->isdn_channel <0). 18 | A more detailed discussion can be found at 19 | https://marc.info/?l=linux-kernel&m=150586376926123&w=2 20 | 21 | Signed-off-by: Meng Xu 22 | --- 23 | drivers/isdn/i4l/isdn_ppp.c | 37 +++++++++++++++++++++++++------------ 24 | 1 file changed, 25 insertions(+), 12 deletions(-) 25 | 26 | diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c 27 | index 6c44609..cd2b3c6 100644 28 | --- a/drivers/isdn/i4l/isdn_ppp.c 29 | +++ b/drivers/isdn/i4l/isdn_ppp.c 30 | @@ -825,7 +825,6 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) 31 | isdn_net_local *lp; 32 | struct ippp_struct *is; 33 | int proto; 34 | - unsigned char protobuf[4]; 35 | 36 | is = file->private_data; 37 | 38 | @@ -839,24 +838,28 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) 39 | if (!lp) 40 | printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n"); 41 | else { 42 | - /* 43 | - * Don't reset huptimer for 44 | - * LCP packets. (Echo requests). 45 | - */ 46 | - if (copy_from_user(protobuf, buf, 4)) 47 | - return -EFAULT; 48 | - proto = PPP_PROTOCOL(protobuf); 49 | - if (proto != PPP_LCP) 50 | - lp->huptimer = 0; 51 | + if (lp->isdn_device < 0 || lp->isdn_channel < 0) { 52 | + unsigned char protobuf[4]; 53 | + /* 54 | + * Don't reset huptimer for 55 | + * LCP packets. (Echo requests). 56 | + */ 57 | + if (copy_from_user(protobuf, buf, 4)) 58 | + return -EFAULT; 59 | + 60 | + proto = PPP_PROTOCOL(protobuf); 61 | + if (proto != PPP_LCP) 62 | + lp->huptimer = 0; 63 | 64 | - if (lp->isdn_device < 0 || lp->isdn_channel < 0) 65 | return 0; 66 | + } 67 | 68 | if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && 69 | lp->dialstate == 0 && 70 | (lp->flags & ISDN_NET_CONNECTED)) { 71 | unsigned short hl; 72 | struct sk_buff *skb; 73 | + unsigned char *cpy_buf; 74 | /* 75 | * we need to reserve enough space in front of 76 | * sk_buff. old call to dev_alloc_skb only reserved 77 | @@ -869,11 +872,21 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) 78 | return count; 79 | } 80 | skb_reserve(skb, hl); 81 | - if (copy_from_user(skb_put(skb, count), buf, count)) 82 | + cpy_buf = skb_put(skb, count); 83 | + if (copy_from_user(cpy_buf, buf, count)) 84 | { 85 | kfree_skb(skb); 86 | return -EFAULT; 87 | } 88 | + 89 | + /* 90 | + * Don't reset huptimer for 91 | + * LCP packets. (Echo requests). 92 | + */ 93 | + proto = PPP_PROTOCOL(cpy_buf); 94 | + if (proto != PPP_LCP) 95 | + lp->huptimer = 0; 96 | + 97 | if (is->debug & 0x40) { 98 | printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); 99 | isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); 100 | -- 101 | 2.7.4 102 | 103 | -------------------------------------------------------------------------------- /pass/KSym/Oracle.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef ORACLE_H_ 3 | #define ORACLE_H_ 4 | 5 | #include "Project.h" 6 | 7 | class SliceOracle { 8 | public: 9 | SliceOracle(LLVMSlice &s) : dt(s), li(dt), dag() { 10 | dag.recalculate(s, this); 11 | 12 | SliceBlock *sb; 13 | for(LLVMSliceBlock &lb : s){ 14 | sb = lb.getSliceBlock(); 15 | 16 | LLVMSliceBlock::inst_iterator 17 | ii = lb.inst_begin(), ie = lb.inst_end(); 18 | 19 | for(; ii != ie; ++ii){ 20 | insts.insert(make_pair(*ii, sb)); 21 | } 22 | } 23 | } 24 | 25 | ~SliceOracle() {} 26 | 27 | // mapping 28 | SliceBlock *getSliceHost(Instruction *inst) { 29 | auto i = insts.find(inst); 30 | if(i == insts.end()){ 31 | return nullptr; 32 | } else { 33 | return i->second; 34 | } 35 | } 36 | 37 | // dominance 38 | bool dominates(LLVMSliceBlock *dom, LLVMSliceBlock *bb) { 39 | return dt.dominates(dom, bb); 40 | } 41 | 42 | LLVMSliceBlock *getIDom(LLVMSliceBlock *bb); 43 | 44 | // loops 45 | LLVMSliceLoop *getOuterLoopInScope(LLVMSliceLoop *scope, 46 | LLVMSliceBlock *bb); 47 | 48 | LLVMSliceLoop *getInnerLoop(LLVMSliceBlock *bb) { 49 | return li.getLoopFor(bb); 50 | } 51 | 52 | LLVMSliceLoop *getOuterLoop(LLVMSliceBlock *bb) { 53 | return getOuterLoopInScope(nullptr, bb); 54 | } 55 | 56 | // DAG 57 | DAPath *getPath(LLVMSliceBlock *bb) { 58 | return dag.getPath(bb); 59 | } 60 | 61 | UnrollPath *getUnrolled(LLVMSliceBlock *bb) { 62 | DAPath *path = getPath(bb); 63 | assert(path->size() != 0); 64 | return uc.getUnrolled(path, bb, &dag); 65 | } 66 | 67 | protected: 68 | // mapping 69 | map insts; 70 | 71 | // basics 72 | SliceDomTree dt; 73 | SliceLoopInfo li; 74 | 75 | // DAG 76 | DAGraph dag; 77 | UnrollCache uc; 78 | }; 79 | 80 | class FuncOracle { 81 | public: 82 | FuncOracle(Function &f, 83 | const DataLayout &dl, TargetLibraryInfo &tli) : 84 | ac(f), 85 | dt(f), 86 | li(dt), 87 | caa(dl, tli, ac, dt, li) 88 | {} 89 | 90 | ~FuncOracle() {} 91 | 92 | // reachability 93 | void getReachBlocks(BasicBlock *cur, set &blks); 94 | 95 | protected: 96 | AssumptionCache ac; 97 | DominatorTree dt; 98 | LoopInfo li; 99 | CombinedAA caa; 100 | }; 101 | 102 | class ModuleOracle { 103 | public: 104 | ModuleOracle(Module &m) : 105 | dl(m.getDataLayout()), 106 | tli(TargetLibraryInfoImpl(Triple(Twine(m.getTargetTriple())))) 107 | {} 108 | 109 | ~ModuleOracle() {} 110 | 111 | // getter 112 | const DataLayout &getDataLayout() { 113 | return dl; 114 | } 115 | 116 | TargetLibraryInfo &getTargetLibraryInfo() { 117 | return tli; 118 | } 119 | 120 | // data layout 121 | uint64_t getBits() { 122 | return BITS; 123 | } 124 | 125 | uint64_t getPointerWidth() { 126 | return dl.getPointerSizeInBits(); 127 | } 128 | 129 | uint64_t getPointerSize() { 130 | return dl.getPointerSize(); 131 | } 132 | 133 | uint64_t getTypeSize(Type *ty) { 134 | return dl.getTypeAllocSize(ty); 135 | } 136 | 137 | uint64_t getTypeWidth(Type *ty) { 138 | return dl.getTypeSizeInBits(ty); 139 | } 140 | 141 | uint64_t getTypeOffset(Type *type, unsigned idx) { 142 | assert(isa(type)); 143 | return dl.getStructLayout(cast(type))->getElementOffset(idx); 144 | } 145 | 146 | bool isReintPointerType(Type *ty) { 147 | return ty->isPointerTy() || 148 | (ty->isIntegerTy() && ty->getIntegerBitWidth() == getPointerWidth()); 149 | } 150 | 151 | protected: 152 | // info provider 153 | const DataLayout &dl; 154 | TargetLibraryInfo tli; 155 | 156 | // consts 157 | const uint64_t BITS = 8; 158 | }; 159 | 160 | #endif /* ORACLE_H_ */ 161 | -------------------------------------------------------------------------------- /pass/KSym/Unroll.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // parameter to control loop unrolling 5 | // [disabled] #define KSYM_CONFIG_UNROLL_ONCE 6 | #define KSYM_CONFIG_UNROLL_LINK_LIMIT 64 7 | #define KSYM_CONFIG_UNROLL_LATCH_LIMIT 64 8 | #define KSYM_CONFIG_UNROLL_TOTAL_LIMIT 4096 9 | 10 | UnrollPath *UnrollCache::getUnrolled(DAPath *path, LLVMSliceBlock *term, 11 | DAGraph *graph) { 12 | 13 | auto k = make_pair(path, term); 14 | 15 | auto i = cache.find(k); 16 | if(i != cache.end()){ 17 | return i->second; 18 | } 19 | 20 | DAItem *mark = graph->query(term); 21 | assert(mark != nullptr); 22 | 23 | // create new unrolled path 24 | UnrollPath *unrolled = new UnrollPath; 25 | 26 | DAPath::iterator ti = path->begin(), te = path->end(); 27 | for(; ti != te; ++ti){ 28 | DATrace *trace = *ti; 29 | assert(mark == *(trace->rbegin())); 30 | 31 | blist *blks = new blist; 32 | unrollRecursive(trace->begin(), trace->end(), 33 | term, graph, blks, unrolled); 34 | } 35 | 36 | cache.insert(make_pair(k, unrolled)); 37 | return unrolled; 38 | } 39 | 40 | static bool isLinked(LLVMSliceBlock *from, LLVMSliceBlock *to) { 41 | LLVMSliceBlock::succ_iterator si = from->succ_begin(), se = from->succ_end(); 42 | for(; si != se; ++si){ 43 | if(*si == to){ 44 | return true; 45 | } 46 | } 47 | 48 | return false; 49 | } 50 | 51 | void UnrollCache::unrollRecursive(DATrace::iterator cur, DATrace::iterator end, 52 | LLVMSliceBlock *term, DAGraph *graph, blist *blks, UnrollPath *unrolled) { 53 | 54 | // test if reached the end of the DATrace 55 | if(cur == end){ 56 | unrolled->add(blks); 57 | return; 58 | } 59 | 60 | // more unrolling needed 61 | DAItem *item = *cur; 62 | cur++; 63 | 64 | if(DABlock *dab = dyn_cast(item)){ 65 | blks->push_back(dab->getBlock()); 66 | unrollRecursive(cur, end, term, graph, blks, unrolled); 67 | } 68 | 69 | else if(DALoop *dal = dyn_cast(item)){ 70 | LLVMSliceLoop *loop = dal->getLoop(); 71 | DAGraph *sub = graph->subgraph(dal); 72 | 73 | // collect and unroll paths to links 74 | set links; 75 | if(cur != end){ 76 | // the loop is not the final step 77 | SmallVector exits; 78 | loop->getExitingBlocks(exits); 79 | 80 | LLVMSliceBlock *next = (*cur)->entrance(); 81 | for(LLVMSliceBlock *e : exits){ 82 | if(isLinked(e, next)){ 83 | links.insert(e); 84 | } 85 | } 86 | } else { 87 | // the loop is the final step 88 | links.insert(term); 89 | } 90 | 91 | map linkPaths; 92 | for(LLVMSliceBlock *l : links){ 93 | linkPaths.insert(make_pair(l, getUnrolled(sub->getPath(l), l, sub))); 94 | } 95 | 96 | // pick one link path to unroll the loop 97 | for(auto const &i : linkPaths){ 98 | UnrollPath *ps = i.second; 99 | 100 | unsigned c = 0; 101 | UnrollPath::iterator pi = ps->begin(), pe = ps->end(); 102 | for(; pi != pe; ++pi){ 103 | if(c++ >= KSYM_CONFIG_UNROLL_TOTAL_LIMIT){ 104 | continue; 105 | } 106 | 107 | blist *nbl = new blist(*blks); 108 | nbl->insert(nbl->end(), (*pi)->begin(), (*pi)->end()); 109 | 110 | unrollRecursive(cur, end, term, graph, nbl, unrolled); 111 | } 112 | } 113 | 114 | #ifdef KSYM_CONFIG_UNROLL_ONCE 115 | // unroll paths to latches 116 | SmallVector latches; 117 | loop->getLoopLatches(latches); 118 | 119 | map latchPaths; 120 | for(LLVMSliceBlock *l : latches){ 121 | latchPaths.insert(make_pair(l, getUnrolled(sub->getPath(l), l, sub))); 122 | } 123 | 124 | // pick one latch path and one link path to unroll the loop 125 | for(auto const &i : linkPaths){ 126 | UnrollPath *ips = i.second; 127 | for(auto const &j : latchPaths){ 128 | UnrollPath *jps = j.second; 129 | 130 | unsigned ic = 0; 131 | UnrollPath::iterator ipi = ips->begin(), ipe = ips->end(); 132 | for(; ipi != ipe; ++ipi){ 133 | if(ic++ >= KSYM_CONFIG_UNROLL_LINK_LIMIT){ 134 | continue; 135 | } 136 | 137 | unsigned jc = 0; 138 | UnrollPath::iterator jpi = jps->begin(), jpe = jps->end(); 139 | for(; jpi != jpe; ++jpi){ 140 | if(jc++ >= KSYM_CONFIG_UNROLL_LATCH_LIMIT){ 141 | continue; 142 | } 143 | 144 | blist *nbl = new blist(*blks); 145 | nbl->insert(nbl->end(), (*jpi)->begin(), (*jpi)->end()); 146 | nbl->insert(nbl->end(), (*ipi)->begin(), (*ipi)->end()); 147 | 148 | unrollRecursive(cur, end, term, graph, nbl, unrolled); 149 | } 150 | } 151 | } 152 | } 153 | #endif 154 | 155 | delete blks; 156 | } 157 | 158 | else { 159 | llvm_unreachable("Unknown DAItem type"); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /pass/KSym/Asmcall.def: -------------------------------------------------------------------------------- 1 | //=== for enumeration -----------------------------------------------------===// 2 | #ifdef ASMCALL_ENUM 3 | 4 | #define ASMCALL_0(name, bin) \ 5 | SE_Asm_##name, 6 | 7 | #define ASMCALL_1(name, bin, pos0, arg0) \ 8 | SE_Asm_##name, 9 | 10 | #define ASMCALL_2(name, bin, pos0, arg0, pos1, arg1) \ 11 | SE_Asm_##name, 12 | 13 | #define ASMCALL_3(name, bin, pos0, arg0, pos1, arg1, pos2, arg2) \ 14 | SE_Asm_##name, 15 | 16 | #endif 17 | 18 | //=== for SEOpv definition ------------------------------------------------===// 19 | #ifdef ASMCALL_DOPV 20 | 21 | #define ASMCALL_0(name, bin) \ 22 | OPV_0(Asm_##name); 23 | 24 | #define ASMCALL_1(name, bin, pos0, arg0) \ 25 | OPV_1(Asm_##name, arg0); 26 | 27 | #define ASMCALL_2(name, bin, pos0, arg0, pos1, arg1) \ 28 | OPV_2(Asm_##name, arg0, arg1); 29 | 30 | #define ASMCALL_3(name, bin, pos0, arg0, pos1, arg1, pos2, arg2) \ 31 | OPV_3(Asm_##name, arg0, arg1, arg2); 32 | 33 | #endif 34 | 35 | //=== for Slice backtrace -------------------------------------------------===// 36 | #ifdef ASMCALL_BACK 37 | 38 | #define ASMCALL_0(name, bin) \ 39 | else if(fn == bin){ \ 40 | } 41 | 42 | #define ASMCALL_1(name, bin, pos0, arg0) \ 43 | else if(fn == bin){ \ 44 | if(btrace(i_call->getArgOperand(pos0))){ \ 45 | res = true; \ 46 | } \ 47 | } 48 | 49 | #define ASMCALL_2(name, bin, pos0, arg0, pos1, arg1) \ 50 | else if(fn == bin){ \ 51 | if(btrace(i_call->getArgOperand(pos0))){ \ 52 | res = true; \ 53 | } \ 54 | if(btrace(i_call->getArgOperand(pos1))){ \ 55 | res = true; \ 56 | } \ 57 | } 58 | 59 | #define ASMCALL_3(name, bin, pos0, arg0, pos1, arg1, pos2, arg2) \ 60 | else if(fn == bin){ \ 61 | if(btrace(i_call->getArgOperand(pos0))){ \ 62 | res = true; \ 63 | } \ 64 | if(btrace(i_call->getArgOperand(pos1))){ \ 65 | res = true; \ 66 | } \ 67 | if(btrace(i_call->getArgOperand(pos2))){ \ 68 | res = true; \ 69 | } \ 70 | } 71 | 72 | #endif 73 | 74 | //=== for SENode creation -------------------------------------------------===// 75 | #ifdef ASMCALL_NODE 76 | 77 | #define ASMCALL_0(name, bin) \ 78 | if(fn == bin){ \ 79 | INIT_NODE(Asm) { \ 80 | node->addOpv(new SEOpvAsm_##name(seq, op)); \ 81 | } FINI_NODE; \ 82 | } 83 | 84 | #define ASMCALL_1(name, bin, pos0, arg0) \ 85 | if(fn == bin){ \ 86 | INIT_NODE(Asm) { \ 87 | SENode *a0 = getNodeOrBuild(seq, op->getArgOperand(pos0)); \ 88 | node->addOpv(new SEOpvAsm_##name(seq, op, a0)); \ 89 | } FINI_NODE; \ 90 | } 91 | 92 | #define ASMCALL_2(name, bin, pos0, arg0, pos1, arg1) \ 93 | if(fn == bin){ \ 94 | INIT_NODE(Asm) { \ 95 | SENode *a0 = getNodeOrBuild(seq, op->getArgOperand(pos0)); \ 96 | SENode *a1 = getNodeOrBuild(seq, op->getArgOperand(pos1)); \ 97 | node->addOpv(new SEOpvAsm_##name(seq, op, a0, a1)); \ 98 | } FINI_NODE; \ 99 | } 100 | 101 | #define ASMCALL_3(name, bin, pos0, arg0, pos1, arg1, pos2, arg2) \ 102 | if(fn == bin){ \ 103 | INIT_NODE(Asm) { \ 104 | SENode *a0 = getNodeOrBuild(seq, op->getArgOperand(pos0)); \ 105 | SENode *a1 = getNodeOrBuild(seq, op->getArgOperand(pos1)); \ 106 | SENode *a2 = getNodeOrBuild(seq, op->getArgOperand(pos2)); \ 107 | node->addOpv(new SEOpvAsm_##name(seq, op, a0, a1, a2)); \ 108 | } FINI_NODE; \ 109 | } 110 | 111 | #endif 112 | 113 | //=== list of asmcalls ----------------------------------------------------===// 114 | 115 | // fetch 116 | ASMCALL_2(__get_user_n, "call __get_user_${4:P}", 0, src, 1, size) 117 | 118 | //=== undef all macros ----------------------------------------------------===// 119 | #undef ASMCALL_0 120 | #undef ASMCALL_1 121 | #undef ASMCALL_2 122 | #undef ASMCALL_3 123 | 124 | #undef INIT_CALL 125 | #undef FINI_CALL 126 | -------------------------------------------------------------------------------- /pass/KSym/DAG.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // DAG integrity check 5 | void DAItem::verify() { 6 | for(DAItem *i : preds){ 7 | assert(i->succs.find(this) != i->succs.end()); 8 | } 9 | 10 | for(DAItem *i : succs){ 11 | assert(i->preds.find(this) != i->preds.end()); 12 | } 13 | 14 | check(); 15 | } 16 | 17 | void DABlock::check() { 18 | // do nothing 19 | } 20 | 21 | void DALoop::check() { 22 | // ensure single header 23 | LLVMSliceBlock *hdr = loop->getHeader(); 24 | 25 | for(DAItem *p : preds){ 26 | if(DABlock *pb = dyn_cast(p)){ 27 | LLVMSliceBlock::succ_iterator 28 | si = pb->getBlock()->succ_begin(), se = pb->getBlock()->succ_end(); 29 | 30 | for(; si != se; ++si){ 31 | if(loop->contains(*si)){ 32 | assert(*si == hdr); 33 | } 34 | } 35 | } 36 | 37 | else if(DALoop *pl = dyn_cast(p)){ 38 | SmallVector exits; 39 | pl->getLoop()->getExitingBlocks(exits); 40 | 41 | for(LLVMSliceBlock *fb : exits){ 42 | LLVMSliceBlock::succ_iterator 43 | si = fb->succ_begin(), se = fb->succ_end(); 44 | 45 | for(; si != se; ++si){ 46 | if(loop->contains(*si)){ 47 | assert(*si == hdr); 48 | } 49 | } 50 | } 51 | } 52 | 53 | else { 54 | llvm_unreachable("Unknown DAItem type"); 55 | } 56 | } 57 | } 58 | 59 | void DAGraph::verify() { 60 | for(DAItem *i : items){ 61 | i->verify(); 62 | } 63 | } 64 | 65 | // DAG construction 66 | void DAGraph::build(SliceOracle *so, 67 | LLVMSliceLoop *scope, LLVMSliceBlock *header, 68 | const vector &blocks) { 69 | 70 | // populate loop info 71 | LLVMSliceBlock *b; 72 | LLVMSliceLoop *l; 73 | 74 | for(LLVMSliceBlock *bb : blocks) { 75 | l = so->getOuterLoopInScope(scope, bb); 76 | table.insert(make_pair(bb, l)); 77 | } 78 | 79 | // construct the graph 80 | set hist; 81 | for(auto &i : table){ 82 | b = i.first; 83 | l = i.second; 84 | 85 | if(l == nullptr){ 86 | add(b); 87 | } else { 88 | if(hist.find(l) == hist.end()){ 89 | add(l); 90 | hist.insert(l); 91 | } 92 | } 93 | } 94 | 95 | // populate preds and succs 96 | DABlock *bnode; 97 | DALoop *lnode; 98 | 99 | for(auto &i : table){ 100 | b = i.first; 101 | l = i.second; 102 | 103 | if(l == nullptr){ 104 | bnode = get(b); 105 | 106 | LLVMSliceBlock::succ_iterator si = b->succ_begin(), se = b->succ_end(); 107 | for(; si != se; ++si){ 108 | if(scope != nullptr && !scope->contains(*si)){ 109 | continue; 110 | } 111 | 112 | auto pos = table.find(*si); 113 | if(pos->second == nullptr){ 114 | bnode->addSucc(get(pos->first)); 115 | } else { 116 | bnode->addSucc(get(pos->second)); 117 | } 118 | } 119 | 120 | LLVMSliceBlock::pred_iterator pi = b->pred_begin(), pe = b->pred_end(); 121 | for(; pi != pe; ++pi){ 122 | if(scope != nullptr && !scope->contains(*pi)){ 123 | continue; 124 | } 125 | 126 | auto pos = table.find(*pi); 127 | if(pos->second == nullptr){ 128 | bnode->addPred(get(pos->first)); 129 | } else { 130 | bnode->addPred(get(pos->second)); 131 | } 132 | } 133 | } else { 134 | lnode = get(l); 135 | 136 | LLVMSliceBlock::succ_iterator si = b->succ_begin(), se = b->succ_end(); 137 | for(; si != se; ++si){ 138 | if(l->contains(*si)){ 139 | continue; 140 | } 141 | 142 | if(scope != nullptr && !scope->contains(*si)){ 143 | continue; 144 | } 145 | 146 | auto pos = table.find(*si); 147 | if(pos->second == nullptr){ 148 | lnode->addSucc(get(pos->first)); 149 | } else { 150 | lnode->addSucc(get(pos->second)); 151 | } 152 | } 153 | 154 | LLVMSliceBlock::pred_iterator pi = b->pred_begin(), pe = b->pred_end(); 155 | for(; pi != pe; ++pi){ 156 | if(l->contains(*pi)){ 157 | continue; 158 | } 159 | 160 | if(scope != nullptr && !scope->contains(*pi)){ 161 | continue; 162 | } 163 | 164 | auto pos = table.find(*pi); 165 | if(pos->second == nullptr){ 166 | lnode->addPred(get(pos->first)); 167 | } else { 168 | lnode->addPred(get(pos->second)); 169 | } 170 | } 171 | } 172 | } 173 | 174 | // finalize 175 | LLVMSliceBlock *rb = header; 176 | LLVMSliceLoop *rl = table[rb]; 177 | 178 | if(rl == nullptr){ 179 | root = get(rb); 180 | } else { 181 | root = get(rl); 182 | } 183 | 184 | // verify 185 | verify(); 186 | 187 | // recursively build all sub-graphs in loop nodes 188 | for(auto const &iter : loops){ 189 | DAGraph *sub = new DAGraph(); 190 | sub->recalculate(iter.first, so); 191 | subs.insert(make_pair(iter.second, sub)); 192 | } 193 | } 194 | 195 | void DAGraph::recalculate(LLVMSlice &s, SliceOracle *so) { 196 | vector blocks; 197 | 198 | for(LLVMSliceBlock &bb : s){ 199 | blocks.push_back(&bb); 200 | } 201 | 202 | build(so, nullptr, &s.getEntryBlock(), blocks); 203 | } 204 | 205 | void DAGraph::recalculate(LLVMSliceLoop *l, SliceOracle *so) { 206 | build(so, l, l->getHeader(), l->getBlocks()); 207 | } 208 | 209 | // Path construction 210 | static void DFS(DAItem *cur, DAItem *dom, list &hist, DAPath *path) { 211 | hist.push_back(cur); 212 | 213 | if(cur == dom){ 214 | path->add(new DATrace(hist)); 215 | } else { 216 | DAItem::iterator pi = cur->predBegin(), pe = cur->predEnd(); 217 | for(; pi != pe; ++pi){ 218 | DFS(*pi, dom, hist, path); 219 | } 220 | } 221 | 222 | hist.pop_back(); 223 | } 224 | 225 | DAPath *DAGraph::flatten(DAItem *elem) { 226 | DAPath *path = new DAPath; 227 | 228 | list hist; 229 | DFS(elem, root, hist, path); 230 | 231 | return path; 232 | } 233 | -------------------------------------------------------------------------------- /pass/KSym/Symbolic.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // main 5 | SymExec::SymExec(ModuleOracle &m) 6 | : mo(m) { 7 | 8 | // init Z3 9 | Z3_config cfg = Z3_mk_config(); 10 | ctxt = Z3_mk_context(cfg); 11 | Z3_del_config(cfg); 12 | 13 | solver = Z3_mk_solver(ctxt); 14 | Z3_solver_inc_ref(ctxt, solver); 15 | 16 | // create sorts 17 | unsigned ptrsz = mo.getPointerWidth(); 18 | SORT_Pointer = Z3_mk_bv_sort(ctxt, ptrsz); 19 | sints[ptrsz] = SORT_Pointer; 20 | 21 | SORT_MemSlot = Z3_mk_bv_sort(ctxt, mo.getBits()); 22 | SORT_MemBlob = Z3_mk_array_sort(ctxt, SORT_Pointer, SORT_MemSlot); 23 | 24 | // symbol counter 25 | varcount = 0; 26 | 27 | // memory model 28 | memory = Z3_mk_const(ctxt, 29 | Z3_mk_string_symbol(ctxt, "memory"), 30 | SORT_MemBlob); 31 | 32 | sptr = Z3_mk_bvmul(ctxt, createVarPointer(), createConstPointer(PAGE_SIZE)); 33 | addAssert(Z3_mk_bvuge(ctxt, sptr, createConstPointer(STACK_BASE))); 34 | 35 | eptr = Z3_mk_bvmul(ctxt, createVarPointer(), createConstPointer(PAGE_SIZE)); 36 | addAssert(Z3_mk_bvuge(ctxt, eptr, createConstPointer(EXTERN_BASE))); 37 | 38 | hptr = Z3_mk_bvmul(ctxt, createVarPointer(), createConstPointer(PAGE_SIZE)); 39 | addAssert(Z3_mk_bvuge(ctxt, hptr, createConstPointer(HEAP_BASE))); 40 | } 41 | 42 | SymExec::~SymExec() { 43 | // clean up cache 44 | for(auto const &i : cache){ 45 | delete i.second; 46 | } 47 | 48 | // destroy z3 49 | Z3_solver_dec_ref(ctxt, solver); 50 | Z3_del_context(ctxt); 51 | } 52 | 53 | void SymExec::complete() { 54 | // memory model 55 | addAssert(Z3_mk_bvule(ctxt, sptr, createConstPointer(STACK_TERM))); 56 | addAssert(Z3_mk_bvule(ctxt, eptr, createConstPointer(EXTERN_TERM))); 57 | addAssert(Z3_mk_bvule(ctxt, hptr, createConstPointer(HEAP_TERM))); 58 | } 59 | 60 | // symbolize the SEG 61 | void SEGraph::symbolize(SymExec &sym) { 62 | // symbolize all leaf and var nodes 63 | for(auto &i : nodes){ 64 | SENode *node = i.second; 65 | if(isa(node) || isa(node)){ 66 | node->getSymbol(sym); 67 | } 68 | } 69 | 70 | // iterate through the trace 71 | int len = trace.size(); 72 | for(int c = 0; c < len; c++){ 73 | Instruction *i = trace.at(c); 74 | 75 | // check if the stmt is in SEG 76 | SENode *node = getNodeOrNull(c, i); 77 | if(node == nullptr){ 78 | continue; 79 | } 80 | 81 | // symbolize the node 82 | node->getSymbol(sym); 83 | } 84 | 85 | // finish up 86 | sym.complete(); 87 | } 88 | 89 | static inline void replayNode(SENode *node, 90 | SymExec &sym, Z3_context ctxt, Z3_model model) { 91 | 92 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_FUNC) 93 | SLOG.map(); 94 | 95 | SLOG.log("inst", Helper::getValueRepr(node->getVal())); 96 | 97 | SymVar *svar = node->getSymbol(sym); 98 | if(svar == nullptr){ 99 | SLOG.log("expr", ""); 100 | SLOG.log("eval", ""); 101 | } else { 102 | Z3_ast expr = svar->getSingleVal()->getExpr().expr; 103 | SLOG.log("expr", Helper::getExprRepr(ctxt, expr)); 104 | 105 | Z3_ast eval; 106 | assert(Z3_model_eval(ctxt, model, expr, true, &eval) == Z3_TRUE); 107 | SLOG.log("eval", Helper::getExprRepr(ctxt, eval)); 108 | } 109 | 110 | SLOG.pop(); 111 | #endif 112 | } 113 | 114 | void SEGraph::replay(SymExec &sym) { 115 | Z3_context ctxt = sym.getContext(); 116 | Z3_model model = sym.getModel(); 117 | 118 | // dump all var nodes 119 | for(auto &i : nodes){ 120 | SENode *node = i.second; 121 | if(isa(node)){ 122 | replayNode(node, sym, ctxt, model); 123 | } 124 | } 125 | 126 | // iterate through the trace 127 | int len = trace.size(); 128 | for(int c = 0; c < len; c++){ 129 | Instruction *i = trace.at(c); 130 | 131 | // check if the stmt is in SEG 132 | SENode *node = getNodeOrNull(c, i); 133 | if(node == nullptr){ 134 | continue; 135 | } 136 | 137 | // replay the node 138 | replayNode(node, sym, ctxt, model); 139 | } 140 | } 141 | 142 | // checks 143 | CheckResult SymExec::checkOverlap( 144 | SymVar *src1, SymVar *len1, 145 | SymVar *src2, SymVar *len2) { 146 | 147 | // make sure that their symbolic exprs exist 148 | if(src1 == nullptr || len1 == nullptr || src2 == nullptr || len2 == nullptr){ 149 | return CK_SYMERR; 150 | } 151 | 152 | Z3_ast s1 = src1->getSingleVal()->getExpr().expr; 153 | Z3_ast l1 = len1->getSingleVal()->getExpr().expr; 154 | Z3_ast s2 = src2->getSingleVal()->getExpr().expr; 155 | Z3_ast l2 = len2->getSingleVal()->getExpr().expr; 156 | 157 | // NOTE: special treatment on 32bit size 158 | if(!isPointerSort(l1)){ 159 | l1 = castMachIntZExt(l1, mo.getPointerWidth()); 160 | } 161 | 162 | if(!isPointerSort(l2)){ 163 | l2 = castMachIntZExt(l2, mo.getPointerWidth()); 164 | } 165 | 166 | // the actual solver 167 | Z3_lbool result = checkOverlap(s1, l1, s2, l2); 168 | 169 | switch(result){ 170 | case Z3_L_TRUE: 171 | return CK_SAT; 172 | 173 | case Z3_L_FALSE: 174 | return CK_UNSAT; 175 | 176 | case Z3_L_UNDEF: 177 | return CK_UNDEF; 178 | } 179 | 180 | llvm_unreachable("Should not reach here"); 181 | return CK_SYMERR; 182 | } 183 | 184 | Z3_lbool SymExec::checkOverlap( 185 | Z3_ast s1, Z3_ast l1, 186 | Z3_ast s2, Z3_ast l2) { 187 | 188 | // sanity check 189 | assert(isPointerSort(s1) && isPointerSort(l1) && 190 | isPointerSort(s2) && isPointerSort(l2)); 191 | 192 | // save context 193 | Z3_solver_push(ctxt, solver); 194 | 195 | // condition: (s2 <= s1 < s2 + l2) || (s1 <= s2 < s1 + l1) 196 | Z3_ast d1 = Z3_mk_bvadd(ctxt, s1, l1); 197 | addAssert(Z3_mk_bvadd_no_overflow(ctxt, s1, l1, false)); 198 | 199 | Z3_ast d2 = Z3_mk_bvadd(ctxt, s2, l2); 200 | addAssert(Z3_mk_bvadd_no_overflow(ctxt, s2, l2, false)); 201 | 202 | Z3_ast cls[2]; 203 | 204 | Z3_ast c1[2]; 205 | c1[0] = Z3_mk_bvuge(ctxt, s1, s2); 206 | c1[1] = Z3_mk_bvult(ctxt, s1, d2); 207 | cls[0] = Z3_mk_and(ctxt, 2, c1); 208 | 209 | Z3_ast c2[2]; 210 | c2[0] = Z3_mk_bvuge(ctxt, s2, s1); 211 | c2[1] = Z3_mk_bvult(ctxt, s2, d1); 212 | cls[1] = Z3_mk_and(ctxt, 2, c2); 213 | 214 | addAssert(Z3_mk_or(ctxt, 2, cls)); 215 | 216 | // solve 217 | Z3_lbool result = Z3_solver_check(ctxt, solver); 218 | 219 | // restore context 220 | Z3_solver_pop(ctxt, solver, 1); 221 | 222 | return result; 223 | } 224 | -------------------------------------------------------------------------------- /note/prelim/NOTE: -------------------------------------------------------------------------------- 1 | # 0001-acpi-fix-potential-double-fetch-bug.patch 2 | git send-email \ 3 | --to "rjw@rjwysocki.net" \ 4 | --to "lenb@kernel.org" \ 5 | --to "linux-acpi@vger.kernel.org" \ 6 | --to "linux-kernel@vger.kernel.org" \ 7 | --cc "meng.xu@gatech.edu" \ 8 | --cc "sanidhya@gatech.edu" \ 9 | --cc "taesoo@gatech.edu" \ 10 | 0001-acpi-fix-potential-double-fetch-bug.patch 11 | 12 | # 0001-nvdimm-fix-potential-double-fetch-bug.patch 13 | git send-email \ 14 | --to "dan.j.williams@intel.com" \ 15 | --to "linux-nvdimm@lists.01.org" \ 16 | --to "linux-kernel@vger.kernel.org" \ 17 | --cc "meng.xu@gatech.edu" \ 18 | --cc "sanidhya@gatech.edu" \ 19 | --cc "taesoo@gatech.edu" \ 20 | 0001-nvdimm-fix-potential-double-fetch-bug.patch 21 | 22 | # 0001-nvdimm-move-the-check-on-nd_reserved2-to-the-endpoin.patch 23 | git send-email \ 24 | --to "dan.j.williams@intel.com" \ 25 | --to "jerry.hoemann@hpe.com" \ 26 | --to "linux-nvdimm@lists.01.org" \ 27 | --to "linux-kernel@vger.kernel.org" \ 28 | --cc "meng.xu@gatech.edu" \ 29 | --cc "sanidhya@gatech.edu" \ 30 | --cc "taesoo@gatech.edu" \ 31 | 0001-nvdimm-move-the-check-on-nd_reserved2-to-the-endpoin.patch 32 | 33 | # 0001-perf-core-fix-potential-double-fetch-bug.patch 34 | git send-email \ 35 | --to "peterz@infradead.org" \ 36 | --to "mingo@redhat.com" \ 37 | --to "acme@kernel.org" \ 38 | --to "alexander.shishkin@linux.intel.com" \ 39 | --to "linux-kernel@vger.kernel.org" \ 40 | --cc "meng.xu@gatech.edu" \ 41 | --cc "sanidhya@gatech.edu" \ 42 | --cc "taesoo@gatech.edu" \ 43 | 0001-perf-core-fix-potential-double-fetch-bug.patch 44 | 45 | # 0001-sched-core-fix-a-potential-double-fetch-bug-on-attr.patch 46 | git send-email \ 47 | --to "mingo@redhat.com" \ 48 | --to "peterz@infradead.org" \ 49 | --to "linux-kernel@vger.kernel.org" \ 50 | --cc "meng.xu@gatech.edu" \ 51 | --cc "sanidhya@gatech.edu" \ 52 | --cc "taesoo@gatech.edu" \ 53 | 0001-sched-core-fix-a-potential-double-fetch-bug-on-attr.patch 54 | 55 | # 0001-hid-uhid-fix-a-double-fetch-bug-when-copying-event-f.patch 56 | git send-email \ 57 | --to "dh.herrmann@googlemail.com" \ 58 | --to "jikos@kernel.org" \ 59 | --to "benjamin.tissoires@redhat.com" \ 60 | --to "linux-input@vger.kernel.org" \ 61 | --to "linux-kernel@vger.kernel.org" \ 62 | --cc "meng.xu@gatech.edu" \ 63 | --cc "sanidhya@gatech.edu" \ 64 | --cc "taesoo@gatech.edu" \ 65 | 0001-hid-uhid-fix-a-double-fetch-bug-when-copying-event-f.patch 66 | 67 | # 0001-ALSA-asihpi-fix-a-potential-double-fetch-bug-when-co.patch 68 | git send-email \ 69 | --to "perex@perex.cz" \ 70 | --to "tiwai@suse.com" \ 71 | --to "vlad@tsyrklevich.net" \ 72 | --to "alsa-devel@alsa-project.org" \ 73 | --to "linux-kernel@vger.kernel.org" \ 74 | --cc "meng.xu@gatech.edu" \ 75 | --cc "sanidhya@gatech.edu" \ 76 | --cc "taesoo@gatech.edu" \ 77 | 0001-ALSA-asihpi-fix-a-potential-double-fetch-bug-when-co.patch 78 | 79 | # 0001-fs-coda-ensure-the-header-peeked-at-is-the-same-in-t.patch 80 | git send-email \ 81 | --to "jaharkes@cs.cmu.edu" \ 82 | --to "coda@cs.cmu.edu" \ 83 | --to "codalist@coda.cs.cmu.edu" \ 84 | --to "linux-kernel@vger.kernel.org" \ 85 | --cc "meng.xu@gatech.edu" \ 86 | --cc "sanidhya@gatech.edu" \ 87 | --cc "taesoo@gatech.edu" \ 88 | 0001-fs-coda-ensure-the-header-peeked-at-is-the-same-in-t.patch 89 | 90 | # 0001-scsi-ensure-the-header-peeked-does-not-change-in-the.patch 91 | git send-email \ 92 | --to "axboe@kernel.dk" \ 93 | --to "linux-block@vger.kernel.org" \ 94 | --to "linux-block@vger.kernel.org" \ 95 | --cc "meng.xu@gatech.edu" \ 96 | --cc "sanidhya@gatech.edu" \ 97 | --cc "taesoo@gatech.edu" \ 98 | 0001-scsi-ensure-the-header-peeked-does-not-change-in-the.patch 99 | 100 | # 0001-scsi-skip-message-header-in-next-fetch.patch 101 | git send-email \ 102 | --to "hch@infradead.org" \ 103 | --to "axboe@kernel.dk" \ 104 | --to "linux-block@vger.kernel.org" \ 105 | --to "linux-block@vger.kernel.org" \ 106 | --cc "meng.xu@gatech.edu" \ 107 | --cc "sanidhya@gatech.edu" \ 108 | --cc "taesoo@gatech.edu" \ 109 | 0001-scsi-skip-message-header-in-next-fetch.patch 110 | 111 | # 0001-net-compat-assert-the-size-of-cmsg-copied-in-is-as-e.patch 112 | git send-email \ 113 | --to "davem@davemloft.net" \ 114 | --to "netdev@vger.kernel.org" \ 115 | --to "linux-kernel@vger.kernel.org" \ 116 | --cc "meng.xu@gatech.edu" \ 117 | --cc "sanidhya@gatech.edu" \ 118 | --cc "taesoo@gatech.edu" \ 119 | 0001-net-compat-assert-the-size-of-cmsg-copied-in-is-as-e.patch 120 | 121 | # 0001-isdn-i4l-check-the-message-proto-does-not-change-acr.patch 122 | git send-email \ 123 | --to "isdn@linux-pingi.de" \ 124 | --to "davem@davemloft.net" \ 125 | --to "johannes.berg@intel.com" \ 126 | --to "netdev@vger.kernel.org" \ 127 | --to "linux-kernel@vger.kernel.org" \ 128 | --cc "meng.xu@gatech.edu" \ 129 | --cc "sanidhya@gatech.edu" \ 130 | --cc "taesoo@gatech.edu" \ 131 | 0001-isdn-i4l-check-the-message-proto-does-not-change-acr.patch 132 | 133 | # 0001-isdn-i4l-fetch-the-ppp_write-buffer-in-one-shot.patch 134 | git send-email \ 135 | --to "isdn@linux-pingi.de" \ 136 | --to "davem@davemloft.net" \ 137 | --to "johannes.berg@intel.com" \ 138 | --to "netdev@vger.kernel.org" \ 139 | --to "linux-kernel@vger.kernel.org" \ 140 | --cc "meng.xu@gatech.edu" \ 141 | --cc "sanidhya@gatech.edu" \ 142 | --cc "taesoo@gatech.edu" \ 143 | 0001-isdn-i4l-fetch-the-ppp_write-buffer-in-one-shot.patch 144 | 145 | # 0001-mpt3sas-downgrade-full-copy_from_user-to-access_ok-c.patch 146 | git send-email \ 147 | --to "sathya.prakash@broadcom.com" \ 148 | --to "chaitra.basappa@broadcom.com" \ 149 | --to "suganath-prabu.subramani@broadcom.com" \ 150 | --to "jejb@linux.vnet.ibm.com" \ 151 | --to "martin.petersen@oracle.com" \ 152 | --to "MPT-FusionLinux.pdl@broadcom.com" \ 153 | --to "linux-scsi@vger.kernel.org" \ 154 | --to "linux-kernel@vger.kernel.org" \ 155 | --cc "meng.xu@gatech.edu" \ 156 | --cc "sanidhya@gatech.edu" \ 157 | --cc "taesoo@gatech.edu" \ 158 | 0001-mpt3sas-downgrade-full-copy_from_user-to-access_ok-c.patch 159 | 160 | # 0001-mpt3sas-remove-redundant-copy_from_user-in-_ctl_geti.patch 161 | git send-email \ 162 | --to "hch@infradead.org" \ 163 | --to "sathya.prakash@broadcom.com" \ 164 | --to "chaitra.basappa@broadcom.com" \ 165 | --to "suganath-prabu.subramani@broadcom.com" \ 166 | --to "jejb@linux.vnet.ibm.com" \ 167 | --to "martin.petersen@oracle.com" \ 168 | --to "MPT-FusionLinux.pdl@broadcom.com" \ 169 | --to "linux-scsi@vger.kernel.org" \ 170 | --to "linux-kernel@vger.kernel.org" \ 171 | --cc "meng.xu@gatech.edu" \ 172 | --cc "sanidhya@gatech.edu" \ 173 | --cc "taesoo@gatech.edu" \ 174 | 0001-mpt3sas-remove-redundant-copy_from_user-in-_ctl_geti.patch 175 | 176 | # 0001-aacraid-fix-potential-double-fetch-issue.patch 177 | git send-email \ 178 | --to "aacraid@microsemi.com" \ 179 | --to "jejb@linux.vnet.ibm.com" \ 180 | --to "martin.petersen@oracle.com" \ 181 | --to "linux-scsi@vger.kernel.org" \ 182 | --to "linux-kernel@vger.kernel.org" \ 183 | --cc "meng.xu@gatech.edu" \ 184 | --cc "sanidhya@gatech.edu" \ 185 | --cc "taesoo@gatech.edu" \ 186 | 0001-aacraid-fix-potential-double-fetch-issue.patch 187 | -------------------------------------------------------------------------------- /pass/KSym/DAG.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #ifndef DAG_H_ 3 | #define DAG_H_ 4 | 5 | #include "Project.h" 6 | 7 | // forward decleration 8 | class SliceOracle; 9 | 10 | // classes 11 | class DAItem { 12 | public: 13 | enum DAItemType { 14 | DA_BLOCK, 15 | DA_LOOP 16 | }; 17 | 18 | public: 19 | DAItem(DAItemType t) : type(t) {} 20 | 21 | ~DAItem() {} 22 | 23 | DAItemType getType() const { 24 | return type; 25 | } 26 | 27 | void addPred(DAItem *item) { 28 | preds.insert(item); 29 | } 30 | 31 | void addSucc(DAItem *item) { 32 | succs.insert(item); 33 | } 34 | 35 | typedef typename set::iterator iterator; 36 | 37 | iterator predBegin() { 38 | return preds.begin(); 39 | } 40 | 41 | iterator predEnd() { 42 | return preds.end(); 43 | } 44 | 45 | iterator succBegin() { 46 | return succs.begin(); 47 | } 48 | 49 | iterator succEnd() { 50 | return succs.end(); 51 | } 52 | 53 | unsigned numPreds() { 54 | return preds.size(); 55 | } 56 | 57 | unsigned numSuccs() { 58 | return succs.size(); 59 | } 60 | 61 | virtual void check() = 0; 62 | 63 | void verify(); 64 | 65 | virtual LLVMSliceBlock *entrance() = 0; 66 | 67 | protected: 68 | // rtti 69 | DAItemType type; 70 | 71 | // links 72 | set preds; 73 | set succs; 74 | }; 75 | 76 | class DABlock : public DAItem { 77 | public: 78 | DABlock(LLVMSliceBlock *b) : DAItem(DA_BLOCK), bb(b) {} 79 | 80 | ~DABlock() {} 81 | 82 | LLVMSliceBlock *getBlock() { 83 | return bb; 84 | } 85 | 86 | static bool classof(const DAItem *item) { 87 | return item->getType() == DA_BLOCK; 88 | } 89 | 90 | void check() override; 91 | 92 | LLVMSliceBlock *entrance() override { 93 | return bb; 94 | } 95 | 96 | protected: 97 | LLVMSliceBlock *bb; 98 | }; 99 | 100 | class DALoop : public DAItem { 101 | public: 102 | DALoop(LLVMSliceLoop *l) : DAItem(DA_LOOP), loop(l) {} 103 | 104 | ~DALoop() {} 105 | 106 | LLVMSliceLoop *getLoop() { 107 | return loop; 108 | } 109 | 110 | static bool classof(const DAItem *item) { 111 | return item->getType() == DA_LOOP; 112 | } 113 | 114 | void check () override; 115 | 116 | LLVMSliceBlock *entrance() override { 117 | return loop->getHeader(); 118 | } 119 | 120 | protected: 121 | LLVMSliceLoop *loop; 122 | }; 123 | 124 | class DATrace { 125 | public: 126 | DATrace(list &items) : steps(items) {} 127 | 128 | ~DATrace() {} 129 | 130 | // NOTE: internally the steps from A to B are stored in a reverse 131 | // order (i.e., B->...->A) so we flip the iterator to compendate this 132 | 133 | typedef typename list::reverse_iterator iterator; 134 | 135 | iterator begin() { 136 | return steps.rbegin(); 137 | } 138 | 139 | iterator end() { 140 | return steps.rend(); 141 | } 142 | 143 | typedef typename list::iterator reverse_iterator; 144 | 145 | reverse_iterator rbegin() { 146 | return steps.begin(); 147 | } 148 | 149 | reverse_iterator rend() { 150 | return steps.end(); 151 | } 152 | 153 | unsigned size() { 154 | return steps.size(); 155 | } 156 | 157 | protected: 158 | list steps; 159 | }; 160 | 161 | class DAPath { 162 | public: 163 | DAPath() {} 164 | 165 | ~DAPath() { 166 | for(DATrace *trace : traces){ 167 | delete trace; 168 | } 169 | } 170 | 171 | void add(DATrace *trace) { 172 | traces.push_back(trace); 173 | } 174 | 175 | unsigned size() { 176 | return traces.size(); 177 | } 178 | 179 | typedef typename list::iterator iterator; 180 | 181 | iterator begin() { 182 | return traces.begin(); 183 | } 184 | 185 | iterator end() { 186 | return traces.end(); 187 | } 188 | 189 | protected: 190 | list traces; 191 | }; 192 | 193 | class DAGraph { 194 | public: 195 | DAGraph() : root(nullptr) {} 196 | 197 | ~DAGraph() { 198 | for(auto const &i : subs){ 199 | delete i.second; 200 | } 201 | 202 | for(auto const &i : blocks){ 203 | delete i.second; 204 | } 205 | 206 | for(auto const &i : loops){ 207 | delete i.second; 208 | } 209 | 210 | for(auto const &i : paths){ 211 | delete i.second; 212 | } 213 | } 214 | 215 | void recalculate(LLVMSlice &s, SliceOracle *so); 216 | 217 | void recalculate(LLVMSliceLoop *l, SliceOracle *so); 218 | 219 | void verify(); 220 | 221 | DAItem *query(LLVMSliceBlock *b) { 222 | auto pos = table.find(b); 223 | if(pos == table.end()){ 224 | return nullptr; 225 | } 226 | 227 | if(pos->second == nullptr){ 228 | return get(b); 229 | } else { 230 | return get(pos->second); 231 | } 232 | } 233 | 234 | DAGraph *subgraph(DALoop *l) { 235 | auto pos = subs.find(l); 236 | if(pos == subs.end()){ 237 | return nullptr; 238 | } 239 | 240 | return pos->second; 241 | } 242 | 243 | unsigned size() { 244 | return items.size(); 245 | } 246 | 247 | typedef typename list::iterator iterator; 248 | 249 | iterator begin() { 250 | return items.begin(); 251 | } 252 | 253 | iterator end() { 254 | return items.end(); 255 | } 256 | 257 | DAPath *getPath(DAItem *elem) { 258 | auto i = paths.find(elem); 259 | if(i != paths.end()){ 260 | return i->second; 261 | } 262 | 263 | DAPath *res = flatten(elem); 264 | paths.insert(make_pair(elem, res)); 265 | return res; 266 | } 267 | 268 | DAPath *getPath(LLVMSliceBlock *bb) { 269 | DAItem *elem = query(bb); 270 | assert(elem != nullptr); 271 | return getPath(elem); 272 | } 273 | 274 | protected: 275 | void add(LLVMSliceBlock *b) { 276 | DABlock *block = new DABlock(b); 277 | blocks.insert(make_pair(b, block)); 278 | items.push_back(block); 279 | } 280 | 281 | void add(LLVMSliceLoop *l) { 282 | DALoop *loop = new DALoop(l); 283 | loops.insert(make_pair(l, loop)); 284 | items.push_back(loop); 285 | } 286 | 287 | void build(SliceOracle *so, LLVMSliceLoop *scope, LLVMSliceBlock *header, 288 | const vector &blocks); 289 | 290 | DABlock *get(LLVMSliceBlock *b) { 291 | auto pos = blocks.find(b); 292 | if(pos == blocks.end()){ 293 | return nullptr; 294 | } 295 | 296 | return pos->second; 297 | } 298 | 299 | DALoop *get(LLVMSliceLoop *l) { 300 | auto pos = loops.find(l); 301 | if(pos == loops.end()){ 302 | return nullptr; 303 | } 304 | 305 | return pos->second; 306 | } 307 | 308 | DAPath *flatten(DAItem *elem); 309 | 310 | protected: 311 | // basics 312 | DAItem *root; 313 | list items; 314 | 315 | // mappings 316 | map table; 317 | 318 | map blocks; 319 | map loops; 320 | 321 | // enclused graphs 322 | map subs; 323 | 324 | // path repository 325 | map paths; 326 | }; 327 | 328 | #endif /* DAG_H_ */ 329 | -------------------------------------------------------------------------------- /pass/KSym/Libcall.def: -------------------------------------------------------------------------------- 1 | //=== for enumeration -----------------------------------------------------===// 2 | #ifdef LIBCALL_ENUM 3 | 4 | #define LIBCALL_0(name) \ 5 | SE_Call_##name, 6 | 7 | #define LIBCALL_1(name, pos0, arg0) \ 8 | SE_Call_##name, 9 | 10 | #define LIBCALL_2(name, pos0, arg0, pos1, arg1) \ 11 | SE_Call_##name, 12 | 13 | #define LIBCALL_3(name, pos0, arg0, pos1, arg1, pos2, arg2) \ 14 | SE_Call_##name, 15 | 16 | #endif 17 | 18 | //=== for SEOpv definition ------------------------------------------------===// 19 | #ifdef LIBCALL_DOPV 20 | 21 | #define LIBCALL_0(name) \ 22 | OPV_0(Call_##name); 23 | 24 | #define LIBCALL_1(name, pos0, arg0) \ 25 | OPV_1(Call_##name, arg0); 26 | 27 | #define LIBCALL_2(name, pos0, arg0, pos1, arg1) \ 28 | OPV_2(Call_##name, arg0, arg1); 29 | 30 | #define LIBCALL_3(name, pos0, arg0, pos1, arg1, pos2, arg2) \ 31 | OPV_3(Call_##name, arg0, arg1, arg2); 32 | 33 | #endif 34 | 35 | //=== for Slice backtrace -------------------------------------------------===// 36 | #ifdef LIBCALL_BACK 37 | 38 | #define LIBCALL_0(name) \ 39 | else if(fn == #name){ \ 40 | } 41 | 42 | #define LIBCALL_1(name, pos0, arg0) \ 43 | else if(fn == #name){ \ 44 | if(btrace(i_call->getArgOperand(pos0))){ \ 45 | res = true; \ 46 | } \ 47 | } 48 | 49 | #define LIBCALL_2(name, pos0, arg0, pos1, arg1) \ 50 | else if(fn == #name){ \ 51 | if(btrace(i_call->getArgOperand(pos0))){ \ 52 | res = true; \ 53 | } \ 54 | if(btrace(i_call->getArgOperand(pos1))){ \ 55 | res = true; \ 56 | } \ 57 | } 58 | 59 | #define LIBCALL_3(name, pos0, arg0, pos1, arg1, pos2, arg2) \ 60 | else if(fn == #name){ \ 61 | if(btrace(i_call->getArgOperand(pos0))){ \ 62 | res = true; \ 63 | } \ 64 | if(btrace(i_call->getArgOperand(pos1))){ \ 65 | res = true; \ 66 | } \ 67 | if(btrace(i_call->getArgOperand(pos2))){ \ 68 | res = true; \ 69 | } \ 70 | } 71 | 72 | #endif 73 | 74 | //=== for SENode creation -------------------------------------------------===// 75 | #ifdef LIBCALL_NODE 76 | 77 | #define LIBCALL_0(name) \ 78 | if(fn == #name){ \ 79 | INIT_NODE(Call) { \ 80 | node->addOpv(new SEOpvCall_##name(seq, op)); \ 81 | } FINI_NODE; \ 82 | } 83 | 84 | #define LIBCALL_1(name, pos0, arg0) \ 85 | if(fn == #name){ \ 86 | INIT_NODE(Call) { \ 87 | SENode *a0 = getNodeOrBuild(seq, op->getArgOperand(pos0)); \ 88 | node->addOpv(new SEOpvCall_##name(seq, op, a0)); \ 89 | } FINI_NODE; \ 90 | } 91 | 92 | #define LIBCALL_2(name, pos0, arg0, pos1, arg1) \ 93 | if(fn == #name){ \ 94 | INIT_NODE(Call) { \ 95 | SENode *a0 = getNodeOrBuild(seq, op->getArgOperand(pos0)); \ 96 | SENode *a1 = getNodeOrBuild(seq, op->getArgOperand(pos1)); \ 97 | node->addOpv(new SEOpvCall_##name(seq, op, a0, a1)); \ 98 | } FINI_NODE; \ 99 | } 100 | 101 | #define LIBCALL_3(name, pos0, arg0, pos1, arg1, pos2, arg2) \ 102 | if(fn == #name){ \ 103 | INIT_NODE(Call) { \ 104 | SENode *a0 = getNodeOrBuild(seq, op->getArgOperand(pos0)); \ 105 | SENode *a1 = getNodeOrBuild(seq, op->getArgOperand(pos1)); \ 106 | SENode *a2 = getNodeOrBuild(seq, op->getArgOperand(pos2)); \ 107 | node->addOpv(new SEOpvCall_##name(seq, op, a0, a1, a2)); \ 108 | } FINI_NODE; \ 109 | } 110 | 111 | #endif 112 | 113 | //=== list of libcalls ----------------------------------------------------===// 114 | 115 | // intrinsics 116 | LIBCALL_1(llvm_objectsize_i64_p0i8, 0, obj) 117 | LIBCALL_1(llvm_bswap_i16, 0, iv) 118 | 119 | // malloc 120 | LIBCALL_1(__get_free_pages, 1, order) 121 | LIBCALL_1(__kmalloc, 0, size) 122 | LIBCALL_1(__kmalloc_track_caller, 0, size) 123 | LIBCALL_1(__vmalloc, 0, size) 124 | LIBCALL_0(get_zeroed_page) 125 | LIBCALL_0(kmem_cache_alloc) 126 | LIBCALL_1(krealloc, 1, size) 127 | LIBCALL_1(kvmalloc_node, 0, size) 128 | LIBCALL_1(vmalloc, 0, size) 129 | LIBCALL_1(vzalloc, 0, size) 130 | 131 | // memory 132 | LIBCALL_2(kmemdup, 0, src, 1, size) 133 | 134 | // fetch 135 | LIBCALL_3(__copy_user_flushcache, 0, dst, 1, src, 2, size) 136 | LIBCALL_3(__copy_user_nocache, 0, dst, 1, src, 2, size) 137 | LIBCALL_3(_copy_from_user, 0, dst, 1, src, 2, size) 138 | LIBCALL_2(memdup_user, 0, src, 1, size) 139 | 140 | // string 141 | LIBCALL_3(memcmp, 0, cs, 1, ct, 2, count) 142 | LIBCALL_2(strcmp, 0, cs, 1, ct) 143 | LIBCALL_1(strlen, 0, buf) 144 | LIBCALL_3(strncmp, 0, cs, 1, ct, 2, count) 145 | LIBCALL_2(strnlen, 0, buf, 1, count) 146 | LIBCALL_2(strnlen_user, 0, buf, 1, count) 147 | 148 | // transfer 149 | LIBCALL_0(_copy_to_user) 150 | LIBCALL_0(compat_alloc_user_space) 151 | 152 | // sync 153 | LIBCALL_0(init_wait_entry) 154 | LIBCALL_0(finish_wait) 155 | LIBCALL_0(mutex_lock_interruptible_nested) 156 | LIBCALL_0(mutex_trylock) 157 | LIBCALL_0(prepare_to_wait_event) 158 | 159 | // info 160 | LIBCALL_0(__dynamic_dev_dbg) 161 | LIBCALL_0(__dynamic_pr_debug) 162 | LIBCALL_0(printk) 163 | 164 | // checks 165 | LIBCALL_0(__check_object_size) 166 | LIBCALL_0(__inode_permission) 167 | LIBCALL_0(__list_add_valid) 168 | LIBCALL_0(__virt_addr_valid) 169 | LIBCALL_0(capable) 170 | LIBCALL_0(kasan_check_write) 171 | LIBCALL_0(mnt_want_write_file) 172 | LIBCALL_0(ns_capable) 173 | LIBCALL_0(security_capable) 174 | LIBCALL_0(security_file_fnctl) 175 | LIBCALL_0(security_file_ioctl) 176 | 177 | //=== undef all macros ----------------------------------------------------===// 178 | #undef LIBCALL_0 179 | #undef LIBCALL_1 180 | #undef LIBCALL_2 181 | #undef LIBCALL_3 182 | 183 | #undef INIT_CALL 184 | #undef FINI_CALL 185 | -------------------------------------------------------------------------------- /work/linux_stable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os.path import basename, dirname, splitext 4 | from fnmatch import fnmatch 5 | 6 | from conf import * 7 | from util import * 8 | from cmd import * 9 | from app import * 10 | 11 | # objects 12 | BUILDER = CMDBuilder( 13 | # defs and udef follow a black list approach 14 | defs = CMDChangeList(False, { 15 | "CC_HAVE_ASM_GOTO": CMDChange(None, False), 16 | }), 17 | udef = CMDChangeList(False, {}), 18 | # incs follow a black list approach 19 | incs = CMDChangeList(False, {}), 20 | ihdr = CMDChangeList(False, {}), 21 | isys = CMDChangeList(False, {}), 22 | # libs follow a white list approach 23 | libs = CMDChangeList(True, {}), 24 | elfs = CMDChangeList(True, {}), 25 | # flags follows a white list approach 26 | wset = CMDChangeList(True, {}), 27 | mset = CMDChangeList(True, {}), 28 | flag = CMDChangeList(True, { 29 | "nostdinc": CMDChange(None, None), 30 | "nostdlib": CMDChange(None, None), 31 | }), 32 | fset = CMDChangeList(True, {}), 33 | pars = CMDChangeList(True, {}), 34 | ) 35 | 36 | FILTER = [ 37 | "crypto/*", 38 | "init/*", 39 | "lib/*", 40 | "drivers/gpu/drm/vmwgfx/vmwgfx.o", 41 | ] 42 | 43 | # functions 44 | class APP_LINUX_STABLE(App): 45 | def __init__(self, tag = "4.13.2"): 46 | super(APP_LINUX_STABLE, self).__init__("linux-stable", tag, 47 | BUILDER, None) 48 | 49 | toks = self.tag.split(".") 50 | if int(toks[0]) < 4: 51 | self.grouper = CMDLinker() 52 | 53 | elif int(toks[1]) <= 12: 54 | self.grouper = CMDLinker() 55 | 56 | else: 57 | self.grouper = CMDArchiver() 58 | 59 | def convert(self): 60 | return "v%s" % self.tag 61 | 62 | def config_impl(self, psrc, pobj, pbin, pext): 63 | with cd(psrc): 64 | cmd = "make O=%s allyesconfig" % pobj 65 | if shell(cmd) == 0: 66 | LOG_INF("Config done") 67 | else: 68 | LOG_ERR("Config failed") 69 | return False 70 | 71 | return True 72 | 73 | def build_impl(self, psrc, pobj, pbin, pext, plog): 74 | with cd(pobj): 75 | cmd = "make V=1 vmlinux modules" 76 | with open(plog, "w") as f: 77 | if shell(cmd, out = f) == 0: 78 | LOG_INF("Build done") 79 | else: 80 | LOG_ERR("Build failed") 81 | return False 82 | 83 | cmd = "make INSTALL_HDR_PATH=%s headers_install" % pbin 84 | if shell(cmd) == 0: 85 | LOG_INF("Install done") 86 | else: 87 | LOG_ERR("Install failed") 88 | return False 89 | 90 | return True 91 | 92 | def parse_line(self, line): 93 | line = line.strip() 94 | toks = line.split(" ", 1) 95 | if toks[0] != "gcc": 96 | return (False, None) 97 | 98 | argv = shellsplit(toks[1]) 99 | try: 100 | i = argv.index("&&") 101 | argv = argv[:i] 102 | except: 103 | pass 104 | 105 | return (True, argv) 106 | 107 | def parse_opts(self, opts): 108 | if opts.mode != "c": 109 | return (False, None) 110 | 111 | if not "__KERNEL__" in opts.defs: 112 | return (False, None) 113 | 114 | srcs = [] 115 | for src in opts.srcs: 116 | base, ext = splitext(src) 117 | if ext not in [".c", ".o", ".S"]: 118 | return None 119 | 120 | if ext in [".c"]: 121 | srcs.append(src) 122 | 123 | return (True, srcs) 124 | 125 | def group_line_until_4_12(self, line): 126 | line = line.strip() 127 | toks = line.split(" ", 1) 128 | if toks[0] != "ld": 129 | return (False, None) 130 | 131 | argv = shellsplit(toks[1]) 132 | try: 133 | i = argv.index(";") 134 | argv = argv[:i] 135 | except: 136 | pass 137 | 138 | return (True, argv) 139 | 140 | def group_line_after_4_13(self, line): 141 | line = line.strip() 142 | if not "ar rcSTPD " in line: 143 | return (False, None) 144 | 145 | if not line.startswith("rm -f " ): 146 | return (False, None) 147 | 148 | argv = shellsplit(line.split(";")[1].strip()) 149 | 150 | return (True, argv[2:]) 151 | 152 | def group_line(self, line): 153 | toks = self.tag.split(".") 154 | if int(toks[0]) < 4: 155 | return self.group_line_until_4_12(line) 156 | 157 | elif int(toks[1]) <= 12: 158 | return self.group_line_until_4_12(line) 159 | 160 | else: 161 | return self.group_line_after_4_13(line) 162 | 163 | def group_opts_until_4_12(self, opts): 164 | # filtering 165 | if opts.args.m != "elf_x86_64": 166 | return (False, None) 167 | 168 | if opts.args.e is not None: 169 | return (False, None) 170 | 171 | if opts.args.z is not None: 172 | return (False, None) 173 | 174 | if opts.args.emit_relocs is not None: 175 | return (False, None) 176 | 177 | # ignore kernel modules 178 | if splitext(opts.outs)[1] == ".ko": 179 | return (False, None) 180 | 181 | # do not link drivers and staging drivers 182 | if opts.outs == "drivers/built-in.o": 183 | return (False, None) 184 | 185 | if opts.outs == "drivers/staging/built-in.o": 186 | return (False, None) 187 | 188 | # collect srcs 189 | srcs = [] 190 | for src in opts.srcs: 191 | base, ext = splitext(src) 192 | if ext not in [".o"]: 193 | return None 194 | 195 | srcs.append(src) 196 | 197 | return (True, srcs) 198 | 199 | def group_opts_after_4_13(self, opts): 200 | # filtering 201 | if len(opts.srcs) == 0: 202 | return (False, None) 203 | 204 | # ignore kernel modules 205 | if splitext(opts.outs)[1] == ".ko": 206 | return (False, None) 207 | 208 | # do not link drivers and staging drivers 209 | if opts.outs == "drivers/built-in.o": 210 | return (False, None) 211 | 212 | if opts.outs == "drivers/staging/built-in.o": 213 | return (False, None) 214 | 215 | # collect srcs 216 | srcs = [] 217 | for src in opts.srcs: 218 | base, ext = splitext(src) 219 | if ext not in [".o"]: 220 | return None 221 | 222 | srcs.append(src) 223 | 224 | return (True, srcs) 225 | 226 | def group_opts(self, opts): 227 | toks = self.tag.split(".") 228 | if int(toks[0]) < 4: 229 | return self.group_opts_until_4_12(opts) 230 | 231 | elif int(toks[1]) <= 12: 232 | return self.group_opts_until_4_12(opts) 233 | 234 | else: 235 | return self.group_opts_after_4_13(opts) 236 | 237 | def group_mark(self, tops): 238 | marks = [] 239 | for goal in tops: 240 | if basename(goal) != "built-in.o": 241 | continue 242 | 243 | toks = dirname(goal).split("/") 244 | if len(toks) > 3: 245 | continue 246 | 247 | if len(toks) == 3 and \ 248 | (toks[0] != "drivers" or toks[1] != "staging"): 249 | continue 250 | 251 | if len(toks) == 2 and toks[0] != "drivers": 252 | continue 253 | 254 | marks.append(goal) 255 | 256 | return marks 257 | 258 | def check_filter(self, fn): 259 | for filt in FILTER: 260 | if fnmatch(fn, filt): 261 | return True 262 | 263 | return False 264 | -------------------------------------------------------------------------------- /pass/KSym/Func.cpp: -------------------------------------------------------------------------------- 1 | #include "Project.h" 2 | #include "PA.h" 3 | 4 | // utils 5 | static inline Value *getCallArgOrRet(CallInst *ci, int idx) { 6 | return idx >= 0 ? ci->getArgOperand(idx) : ci; 7 | } 8 | 9 | // collection 10 | void FuncHandle::collectFetch() { 11 | for(BasicBlock &bb : func){ 12 | for(Instruction &i : bb){ 13 | if(CallInst *ci = dyn_cast(&i)){ 14 | const FetchDef *def; 15 | 16 | if(ci->isInlineAsm()){ 17 | InlineAsm *target = cast(ci->getCalledValue()); 18 | def = Fetch::findDefMatch(target->getAsmString()); 19 | } else { 20 | Function *target = ci->getCalledFunction(); 21 | if(target == nullptr){ 22 | // TODO handle indirect call 23 | def = nullptr; 24 | } else { 25 | def = Fetch::findDefMatch(target->getName().str()); 26 | } 27 | } 28 | 29 | if(def != nullptr){ 30 | Fetch *fetch = new Fetch(ci, 31 | getCallArgOrRet(ci, def->src), 32 | getCallArgOrRet(ci, def->len), 33 | getCallArgOrRet(ci, def->dst)); 34 | 35 | fts.insert(make_pair(ci, fetch)); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | 42 | Fetch *FuncHandle::getFetchFromInst(Instruction *inst) { 43 | auto i = fts.find(inst); 44 | if(i == fts.end()){ 45 | return nullptr; 46 | } else { 47 | return i->second; 48 | } 49 | } 50 | 51 | // result collection 52 | void FuncHandle::addResult(Fetch *f1, Fetch *f2, CheckResult res) { 53 | auto k = make_pair(f1, f2); 54 | auto i = result.find(k); 55 | 56 | // add if no result 57 | if(i == result.end()){ 58 | result.insert(make_pair(k, res)); 59 | } 60 | 61 | // if in higher rank, override 62 | else if(res < i->second){ 63 | result.insert(make_pair(k, res)); 64 | } 65 | 66 | // if error, also record 67 | if(res == CK_SYMERR){ 68 | failed.insert(k); 69 | } 70 | } 71 | 72 | unsigned FuncHandle::countResult(CheckResult res) { 73 | unsigned count = 0; 74 | 75 | for(auto &i : result){ 76 | if(i.second == res){ 77 | count++; 78 | } 79 | } 80 | 81 | return count; 82 | } 83 | 84 | // analysis 85 | void FuncHandle::analyzeFetch(Fetch &fetch) { 86 | #ifdef KSYM_DEBUG 87 | SLOG.log("inst", Helper::getValueRepr(fetch.inst)); 88 | SLOG.log("host", Helper::getValueName(fetch.inst->getParent())); 89 | #endif 90 | 91 | // collect reachable blocks 92 | set reach; 93 | fo.getReachBlocks(fetch.inst->getParent(), reach); 94 | 95 | // create a slice 96 | Slice slice(reach, fetch.inst); 97 | 98 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_DRAW) 99 | SLOG.map("slice"); 100 | Record::CFG(slice, SLOG); 101 | SLOG.pop(); 102 | #endif 103 | 104 | // construct analysis 105 | LLVMSlice wrap(&slice); 106 | SliceOracle oracle(wrap); 107 | 108 | // unroll 109 | UnrollPath *unrolled = oracle.getUnrolled(&wrap.getBasisBlock()); 110 | 111 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_STAT) 112 | errs() 113 | << func.getName() 114 | << "::" << "fetch " << &fetch 115 | << "::" << unrolled->size() << " traces" 116 | << "\n"; 117 | #endif 118 | 119 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_FUNC) 120 | SLOG.vec("check"); 121 | #endif 122 | 123 | // per-trace analysis 124 | UnrollPath::iterator it = unrolled->begin(), ie = unrolled->end(); 125 | for(; it != ie; ++it){ 126 | analyzeFetchPerTrace(fetch, oracle, *(*it)); 127 | } 128 | 129 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_FUNC) 130 | SLOG.pop(); 131 | #endif 132 | } 133 | 134 | static inline void blistToIlist(blist &bl, Instruction *stop, iseq &il) { 135 | for(LLVMSliceBlock *bb : bl){ 136 | LLVMSliceBlock::inst_iterator i = bb->inst_begin(), ie = bb->inst_end(); 137 | for(; i != ie; ++i){ 138 | il.push_back(*i); 139 | } 140 | } 141 | 142 | // trim off from stop inst 143 | while(il.back() != stop){ 144 | il.pop_back(); 145 | } 146 | } 147 | 148 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_SVAR) 149 | static inline void dumpValueSymbol(Value *val, SEGraph &seg, SymExec &sym) { 150 | SENode *node = seg.getNodeProbe(val); 151 | assert(node != nullptr); 152 | 153 | SymVar *svar = sym.getVar(node); 154 | if(svar == nullptr){ 155 | errs() << "\n"; 156 | } else { 157 | svar->dump(sym.getContext()); 158 | } 159 | } 160 | #endif 161 | 162 | void FuncHandle::analyzeFetchPerTrace(Fetch &fetch, SliceOracle &so, 163 | blist &blks) { 164 | 165 | // convert to inst list 166 | iseq trace; 167 | blistToIlist(blks, fetch.inst, trace); 168 | 169 | // CHENXIONG: start 170 | /* 171 | PA *pa = new PA(); 172 | pa->analyzePointTo(trace, so); 173 | delete pa; 174 | */ 175 | // CHENXIONG: end 176 | 177 | try { 178 | // create SEG 179 | SEGraph seg(so, trace); 180 | 181 | iseq filt; 182 | seg.filterTrace(filt); 183 | //Node::analyzePointTo(trace); 184 | 185 | // create symbol engine 186 | SymExec sym(mo); 187 | 188 | // symbolize the SEG 189 | seg.symbolize(sym); 190 | 191 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_SVAR) 192 | // dump the symbolic results for debugging 193 | errs() << "---------------- src ----------------\n"; 194 | dumpValueSymbol(fetch.src, seg, sym); 195 | 196 | errs() << "---------------- len ----------------\n"; 197 | dumpValueSymbol(fetch.len, seg, sym); 198 | #endif 199 | 200 | // fetch cross-checking 201 | int len = trace.size() - 1; 202 | 203 | SENode *fsrc = seg.getNodeOrFail(len, fetch.src); 204 | SENode *flen = seg.getNodeOrFail(len, fetch.len); 205 | 206 | Fetch *other; 207 | SENode *osrc, *olen; 208 | CheckResult res; 209 | 210 | for(int c = 0; c < len; c++){ 211 | Instruction *i = trace.at(c); 212 | 213 | // check if the inst is in SEG 214 | SENode *node = seg.getNodeOrNull(c, i); 215 | if(node == nullptr){ 216 | continue; 217 | } 218 | 219 | // check if the inst is another fetch 220 | other = getFetchFromInst(i); 221 | if(other == nullptr){ 222 | continue; 223 | } 224 | 225 | // check fetch correlation 226 | osrc = seg.getNodeOrFail(c, other->src); 227 | olen = seg.getNodeOrFail(c, other->len); 228 | 229 | res = sym.checkOverlap( 230 | sym.getVar(fsrc), sym.getVar(flen), 231 | sym.getVar(osrc), sym.getVar(olen)); 232 | 233 | addResult(&fetch, other, res); 234 | 235 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_DUMP) 236 | // log the trace for a potential bug 237 | if(res == CK_SAT){ 238 | SLOG.map(); 239 | 240 | SLOG.log("seq", c); 241 | SLOG.log("tar", Helper::getValueRepr(other->inst)); 242 | 243 | SLOG.map("dfbug"); 244 | SLOG.log("src1", Helper::getValueRepr(fsrc->getVal())); 245 | SLOG.log("len1", Helper::getValueRepr(flen->getVal())); 246 | SLOG.log("src2", Helper::getValueRepr(osrc->getVal())); 247 | SLOG.log("len2", Helper::getValueRepr(olen->getVal())); 248 | SLOG.pop(); 249 | 250 | SLOG.vec("trace"); 251 | seg.replay(sym); 252 | SLOG.pop(); 253 | 254 | SLOG.pop(); 255 | } 256 | #endif 257 | } 258 | } catch(KSymError &e) { 259 | EXCEPT.insert(fetch.inst->getParent()->getParent()); 260 | } 261 | } 262 | 263 | // entry point 264 | void FuncHandle::run() { 265 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_DRAW) 266 | SLOG.map("cfg"); 267 | Record::CFG(func, SLOG); 268 | SLOG.pop(); 269 | #endif 270 | 271 | // collect fetches first 272 | collectFetch(); 273 | 274 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_STAT) 275 | if(!fts.empty()){ 276 | errs() 277 | << func.getName() 278 | << "::" << fts.size() << " fetches" 279 | << "\n"; 280 | } 281 | #endif 282 | 283 | // analyze each fetch 284 | #ifdef KSYM_DEBUG 285 | SLOG.vec("fetch"); 286 | #endif 287 | 288 | for(auto &i : fts){ 289 | #ifdef KSYM_DEBUG 290 | SLOG.map(); 291 | #endif 292 | 293 | analyzeFetch(*i.second); 294 | 295 | #ifdef KSYM_DEBUG 296 | SLOG.pop(); 297 | #endif 298 | } 299 | 300 | #ifdef KSYM_DEBUG 301 | SLOG.pop(); 302 | #endif 303 | 304 | #if defined(KSYM_DEBUG) && defined(KSYM_DEBUG_FUNC) 305 | // log results 306 | SLOG.map("result"); 307 | 308 | SLOG.log("total", result.size()); 309 | SLOG.log("error", failed.size()); 310 | SLOG.log("sat", countResult(CK_SAT)); 311 | SLOG.log("uns", countResult(CK_UNSAT)); 312 | SLOG.log("udf", countResult(CK_UNDEF)); 313 | 314 | SLOG.pop(); 315 | #endif 316 | } 317 | -------------------------------------------------------------------------------- /work/draw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import json 5 | from os.path import exists, splitext 6 | 7 | from enum import Enum 8 | from collections import OrderedDict 9 | from argparse import ArgumentParser 10 | 11 | import networkx as nx 12 | import matplotlib.pyplot as plt 13 | 14 | # data parsing 15 | class NodeLabel(Enum): 16 | ROOT_CFG = 1 17 | ROOT_SLICE = 2 18 | HOST_FETCH = 3 19 | 20 | class FlowNode(object): 21 | def __init__(self, nid, info): 22 | self.nid = nid 23 | self.info = info 24 | 25 | self.pred = [] 26 | self.succ = [] 27 | 28 | self.label = set() 29 | 30 | def addPred(self, node): 31 | self.pred.append(node) 32 | 33 | def addSucc(self, node): 34 | self.succ.append(node) 35 | 36 | def addLabel(self, lab): 37 | self.label.add(lab) 38 | 39 | def hasPred(self, nid): 40 | for n in self.pred: 41 | if n.nid == nid: 42 | return True 43 | 44 | return False 45 | 46 | def hasSucc(self, nid): 47 | for n in self.succ: 48 | if n.nid == nid: 49 | return True 50 | 51 | return False 52 | 53 | class FlowItem(object): 54 | def __init__(self, nid, perm): 55 | self.nid = nid 56 | 57 | self.perm = perm 58 | self.temp = None 59 | 60 | def update(self, temp): 61 | self.temp = temp 62 | 63 | def restore(self): 64 | self.temp = None 65 | 66 | class FlowGraph(object): 67 | def __init__(self, cfg): 68 | self.repo = OrderedDict() 69 | 70 | # init all nodes 71 | for rec in cfg["item"]: 72 | nid = rec["name"] 73 | self.repo[nid] = FlowItem(nid, FlowNode(nid, rec["size"])) 74 | 75 | # add links 76 | for rec in cfg["item"]: 77 | node = self.repo[rec["name"]].perm 78 | 79 | for elem in rec["pred"]: 80 | node.addPred(self.repo[elem].perm) 81 | 82 | for elem in rec["succ"]: 83 | node.addSucc(self.repo[elem].perm) 84 | 85 | # set root 86 | self.repo[cfg["root"]].perm.addLabel(NodeLabel.ROOT_CFG) 87 | 88 | def update(self, cfg, host): 89 | # update all nodes 90 | for rec in cfg["item"]: 91 | nid = rec["name"] 92 | assert nid in self.repo 93 | self.repo[nid].update(FlowNode(nid, rec["size"])) 94 | 95 | # add links 96 | for rec in cfg["item"]: 97 | node = self.repo[rec["name"]].temp 98 | 99 | for elem in rec["pred"]: 100 | node.addPred(self.repo[elem].temp) 101 | 102 | for elem in rec["succ"]: 103 | node.addSucc(self.repo[elem].temp) 104 | 105 | # set root 106 | self.repo[cfg["root"]].temp.addLabel(NodeLabel.ROOT_SLICE) 107 | 108 | # set host 109 | self.repo[host].temp.addLabel(NodeLabel.HOST_FETCH) 110 | 111 | def restore(self): 112 | for k in self.repo: 113 | self.repo[k].restore() 114 | 115 | # visualizer 116 | class NodeFormat(object): 117 | def __init__(self, node): 118 | self.alpha = 0.75 119 | self.width = 1.0 120 | self.color = "red" 121 | self.shape = "o" 122 | 123 | if NodeLabel.ROOT_CFG in node.perm.label: 124 | self.shape = "s" 125 | 126 | if node.temp is not None: 127 | self.color = "blue" 128 | 129 | if NodeLabel.ROOT_SLICE in node.temp.label: 130 | self.shape = "s" 131 | 132 | if NodeLabel.HOST_FETCH in node.temp.label: 133 | self.color = "yellow" 134 | 135 | class EdgeFormat(object): 136 | def __init__(self, mode): 137 | self.alpha = 1.0 138 | self.width = 2.0 139 | 140 | if mode == 0: 141 | self.color = "black" 142 | self.style = "solid" 143 | elif mode == 1: 144 | self.color = "green" 145 | self.style = "dashed" 146 | elif mode == 2: 147 | self.color = "blue" 148 | self.style = "solid" 149 | 150 | class Grapher(object): 151 | def __init__(self): 152 | self.graph = nx.DiGraph() 153 | 154 | def addNode(self, nid, val, fmt): 155 | self.graph.add_node(nid, val = val, fmt = fmt) 156 | 157 | def addEdge(self, src, dst, val, fmt): 158 | self.graph.add_edge(src, dst, val = val, fmt = fmt) 159 | 160 | def draw(self): 161 | # layout 162 | try: 163 | pos = nx.nx_pydot.pydot_layout(self.graph, prog = "dot") 164 | except Exception as ex: 165 | print >> sys.stderr, "Unable to use DOT layout: %s" % ex 166 | pos = nx.spring_layout(self.graph) 167 | 168 | # draw nodes 169 | for n in self.graph.nodes(data = True): 170 | fmt = n[1]["fmt"] 171 | nx.draw_networkx_nodes(self.graph, pos, [n[0]], 172 | cmap = plt.cm.Oranges, vmin = 0.0, vmax = 1.0, 173 | node_color = fmt.color, 174 | node_shape = fmt.shape, 175 | alpha = fmt.alpha, 176 | linewidths = fmt.width) 177 | 178 | # draw node text 179 | node_labels = nx.get_node_attributes(self.graph, "val") 180 | nx.draw_networkx_labels(self.graph, pos, labels = node_labels) 181 | 182 | # draw edges 183 | for e in self.graph.edges(data = True): 184 | fmt = e[2]["fmt"] 185 | nx.draw_networkx_edges(self.graph, pos, [(e[0], e[1])], 186 | edge_color = fmt.color, 187 | style = fmt.style, 188 | alpha = fmt.alpha, 189 | width = fmt.width) 190 | 191 | # show 192 | plt.show() 193 | 194 | # analysis 195 | class Fetch(object): 196 | def __init__(self, rec): 197 | self.inst = rec["inst"] 198 | self.host = rec["host"] 199 | self.slice = rec["slice"] 200 | 201 | class Func(object): 202 | def __init__(self, fid, rec): 203 | self.fid = fid 204 | 205 | # flow graph 206 | self.graph = FlowGraph(rec["cfg"]) 207 | 208 | # fetches 209 | self.fetches = [] 210 | for v in rec["fetch"]: 211 | self.fetches.append(Fetch(v)) 212 | 213 | def draw(self, fetch): 214 | self.graph.restore() 215 | self.graph.update(fetch.slice, fetch.host) 216 | 217 | print >> sys.stdout, "Fetch: %s" % fetch.inst 218 | 219 | g = Grapher() 220 | 221 | for k, v in self.graph.repo.items(): 222 | if v.temp is None: 223 | info = "%d" % (v.perm.info) 224 | else: 225 | info = "%d/%d" % (v.temp.info, v.perm.info) 226 | 227 | g.addNode(k, info, NodeFormat(v)) 228 | 229 | for k, v in self.graph.repo.items(): 230 | for s in v.perm.succ: 231 | if v.temp is not None and v.temp.hasSucc(s.nid): 232 | g.addEdge(v.nid, s.nid, "", EdgeFormat(2)) 233 | else: 234 | g.addEdge(v.nid, s.nid, "", EdgeFormat(0)) 235 | 236 | for k, v in self.graph.repo.items(): 237 | if v.temp is None: 238 | continue 239 | 240 | for s in v.temp.succ: 241 | if not v.perm.hasSucc(s.nid): 242 | g.addEdge(v.nid, s.nid, "", EdgeFormat(1)) 243 | 244 | g.draw() 245 | 246 | def show(self): 247 | if len(self.fetches) == 0: 248 | return 249 | 250 | print >> sys.stdout, "Function: %s" % self.fid 251 | 252 | for fetch in self.fetches: 253 | self.draw(fetch) 254 | 255 | class Module(object): 256 | def __init__(self, fn): 257 | if not exists(fn): 258 | print >> sys.stderr, "%s does not exist" % fn 259 | sys.exit(-1) 260 | 261 | self.funcs = OrderedDict() 262 | 263 | with open(fn, "r") as fp: 264 | mrec = json.load(fp) 265 | for k in mrec: 266 | self.funcs[k] = Func(k, mrec[k]) 267 | 268 | # entry point 269 | if __name__ == "__main__": 270 | # init 271 | parser = ArgumentParser() 272 | parser.add_argument("files", action="append", default=[]) 273 | parser.add_argument("-f", "--func", action="append", default=None) 274 | 275 | # parse 276 | args = parser.parse_args() 277 | 278 | # exec 279 | for fn in args.files: 280 | toks = splitext(fn) 281 | if toks[1] == ".c" and "/srcs/" in toks[0]: 282 | fn = toks[0].replace("/srcs/", "/syms/") + ".sym" 283 | 284 | mod = Module(fn) 285 | for fid in mod.funcs: 286 | if args.func is not None and fid not in args.func: 287 | continue 288 | 289 | mod.funcs[fid].show() 290 | -------------------------------------------------------------------------------- /work/cmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from collections import OrderedDict 4 | from argparse import ArgumentParser 5 | 6 | from conf import * 7 | from util import * 8 | 9 | # cmd parsing 10 | def CMDParser(): 11 | parser = ArgumentParser(add_help=False) 12 | 13 | # output 14 | parser.add_argument("-o", type=str, default=None) 15 | 16 | # cstd 17 | parser.add_argument("-std", type=str, default=None) 18 | 19 | # optimization 20 | parser.add_argument("-O", type=str, default=None) 21 | 22 | # operation mode 23 | parser.add_argument("-c", action="store_true") 24 | parser.add_argument("-E", action="store_true") 25 | parser.add_argument("-S", action="store_true") 26 | 27 | # various flags 28 | parser.add_argument("-arch", type=str, default=None) 29 | 30 | parser.add_argument("-g", action="store_true") 31 | parser.add_argument("-ggdb3", action="store_true") 32 | parser.add_argument("-gdwarf-2", action="store_true") 33 | 34 | parser.add_argument("-C", action="store_true") 35 | parser.add_argument("-P", action="store_true") 36 | 37 | parser.add_argument("-nostdinc", action="store_true") 38 | parser.add_argument("-nostdlib", action="store_true") 39 | parser.add_argument("-shared", action="store_true") 40 | 41 | parser.add_argument("-pipe", action="store_true") 42 | parser.add_argument("-pg", action="store_true") 43 | 44 | parser.add_argument("-MD", action="store_true") 45 | parser.add_argument("-MG", action="store_true") 46 | parser.add_argument("-MM", action="store_true") 47 | parser.add_argument("-MP", action="store_true") 48 | parser.add_argument("-MF", type=str, default=None) 49 | 50 | # defines 51 | parser.add_argument("-D", type=str, action="append", default=[]) 52 | # undefs 53 | parser.add_argument("-U", type=str, action="append", default=[]) 54 | 55 | # includes 56 | parser.add_argument("-I", type=str, action="append", default=[]) 57 | # include headers 58 | parser.add_argument("-include", type=str, action="append", default=[]) 59 | # include system 60 | parser.add_argument("-isystem", type=str, action="append", default=[]) 61 | # include dirs 62 | parser.add_argument("-idirafter", type=str, action="append", default=[]) 63 | 64 | # libs 65 | parser.add_argument("-l", type=str, action="append", default=[]) 66 | # linked elfs 67 | parser.add_argument("-lelf", type=str, action="append", default=[]) 68 | 69 | # warnings 70 | parser.add_argument("-W", type=str, action="append", default=[]) 71 | # flags 72 | parser.add_argument("-f", type=str, action="append", default=[]) 73 | # machine 74 | parser.add_argument("-m", type=str, action="append", default=[]) 75 | 76 | # extra params 77 | parser.add_argument("--param", type=str, action="append", default=[]) 78 | 79 | # src file 80 | parser.add_argument("inputs", type=str, nargs="+") 81 | 82 | return parser 83 | 84 | class CMDChange(object): 85 | def __init__(self, val, mod): 86 | self.val = val 87 | self.mod = mod 88 | 89 | def change(self, flag): 90 | # mod is None ==> keep if in flags, do nothing otherwise 91 | # mod is True ==> add if not in flags 92 | # mos is False ==> drop if in flags 93 | if self.mod is None or self.mod: 94 | if self.val is None: 95 | return flag 96 | 97 | toks = flag.split("=", 1) 98 | return "%s-%s" % (toks[0], self.val) 99 | else: 100 | return None 101 | 102 | class CMDChangeList(object): 103 | def __init__(self, white, filters): 104 | self.white = white 105 | self.filters = filters 106 | 107 | def __filter(self, flags): 108 | result = set() 109 | 110 | for i in flags: 111 | if i not in self.filters: 112 | if not self.white: 113 | result.add(i) 114 | continue 115 | 116 | res = self.filters[i].change(i) 117 | if res is None: 118 | continue 119 | 120 | result.add(res) 121 | 122 | return result 123 | 124 | def scan(self, flags): 125 | result = self.__filter(flags) 126 | 127 | for i in self.filters: 128 | inst = self.filters[i] 129 | if inst.mod is not None and inst.mod: 130 | if inst.val is None: 131 | result.add(i) 132 | else: 133 | result.add("%s=%s" % (i, inst.val)) 134 | 135 | return sorted(result) 136 | 137 | class CMDBuilder(object): 138 | def __init__(self, defs=None, udef=None, 139 | incs=None, ihdr=None, isys=None, idir=None, 140 | libs=None, elfs=None, 141 | flag=None, fset=None, pars=None, 142 | wset=None, mset=None,): 143 | 144 | self.defs = defs 145 | self.udef = udef 146 | 147 | self.incs = incs 148 | self.ihdr = ihdr 149 | self.isys = isys 150 | 151 | self.libs = libs 152 | self.elfs = elfs 153 | 154 | self.wset = wset 155 | self.mset = mset 156 | 157 | self.flag = flag 158 | self.fset = fset 159 | self.pars = pars 160 | 161 | def build(self, opts): 162 | for k in self.__dict__: 163 | assert(k in opts.__dict__) 164 | 165 | filters = self.__dict__[k] 166 | if filters is None: 167 | # default to whitelist 168 | opts.__dict[k] = [] 169 | continue 170 | 171 | flags = opts.__dict__[k] 172 | opts.__dict__[k] = filters.scan(flags) 173 | 174 | class CMDResult(object): 175 | def __init__(self, parser, argv): 176 | # parse args 177 | args, rems = parser.parse_known_args(argv) 178 | 179 | if len(rems) != 0: 180 | for r in rems: 181 | print r 182 | 183 | LOG_ERR("Argument parse failed") 184 | sys.exit() 185 | 186 | # collect results 187 | self.srcs = args.inputs 188 | self.outs = args.o 189 | 190 | if args.c: 191 | self.mode = "c" 192 | elif args.S: 193 | self.mode = "S" 194 | elif args.E: 195 | self.mode = "E" 196 | else: 197 | self.mode = None 198 | 199 | self.arch = args.arch 200 | 201 | self.cstd = args.std 202 | self.optl = args.O 203 | 204 | self.flag = [] 205 | if args.nostdinc: 206 | self.flag.append("nostdinc") 207 | 208 | if args.nostdlib: 209 | self.flag.append("nostdlib") 210 | 211 | if args.C: 212 | self.flag.append("C") 213 | 214 | if args.P: 215 | self.flag.append("P") 216 | 217 | self.defs = args.D 218 | self.udef = args.U 219 | 220 | self.incs = args.I 221 | self.ihdr = args.include 222 | self.isys = args.isystem 223 | self.idir = args.idirafter 224 | 225 | self.libs = args.l 226 | self.elfs = args.lelf 227 | 228 | self.wset = args.W 229 | self.mset = args.m 230 | self.fset = args.f 231 | self.pars = args.param 232 | 233 | def organize(self): 234 | return " ".join([ 235 | ("-%s" % self.mode if self.mode is not None else ""), 236 | ("-std=%s" % self.cstd if self.cstd is not None else ""), 237 | ("-O%s" % self.optl if self.optl is not None else ""), 238 | ("-arch%s" % self.arch if self.arch is not None else ""), 239 | " ".join(["-%s" % i for i in self.flag]), 240 | " ".join(["-f%s" % i for i in self.fset]), 241 | " ".join(["--param %s" % i for i in self.pars]), 242 | " ".join(["-isystem %s" % i for i in self.isys]), 243 | " ".join(["-I%s" % i for i in self.incs]), 244 | " ".join(["-include %s" % i for i in self.ihdr]), 245 | " ".join(["-idirafter %s" % i for i in self.idir]), 246 | " ".join(["-D%s" % i for i in self.defs]), 247 | " ".join(["-U%s" % i for i in self.udef]), 248 | " ".join(["-W%s" % i for i in self.wset]), 249 | " ".join(["-m%s" % i for i in self.mset]) 250 | ]) 251 | 252 | class CMDCollect(object): 253 | def __init__(self): 254 | self.flag = set() 255 | self.pars = set() 256 | self.fset = set() 257 | self.wset = set() 258 | self.mset = set() 259 | 260 | def update(self, opts): 261 | self.flag.update(opts.flag) 262 | self.pars.update(opts.pars) 263 | self.fset.update(opts.fset) 264 | self.wset.update(opts.wset) 265 | self.mset.update(opts.mset) 266 | 267 | def show(self): 268 | print "====== %s ======" % "flag" 269 | for i in self.flag: 270 | print i 271 | 272 | print "====== %s ======" % "pars" 273 | for i in self.pars: 274 | print i 275 | 276 | print "====== %s ======" % "fset" 277 | for i in self.fset: 278 | print i 279 | 280 | print "====== %s ======" % "wset" 281 | for i in self.wset: 282 | if i.startswith("p,-MD"): 283 | continue 284 | 285 | if i.startswith("p,-MT"): 286 | continue 287 | 288 | print i 289 | 290 | print "====== %s ======" % "mset" 291 | for i in self.mset: 292 | print i 293 | 294 | 295 | def CMDLinker(): 296 | grouper = ArgumentParser(add_help=False) 297 | 298 | # output 299 | grouper.add_argument("-o", type=str, default=None) 300 | 301 | # flags 302 | grouper.add_argument("-r", action="store_true") 303 | 304 | grouper.add_argument("-nostdlib", action="store_true") 305 | grouper.add_argument("--no-undefined", action="store_true") 306 | 307 | grouper.add_argument("--emit-relocs", action="store_true") 308 | grouper.add_argument("--build-id", action="store_true") 309 | 310 | # configs 311 | grouper.add_argument("-e", type=str, default=None) 312 | grouper.add_argument("-z", type=str, default=None) 313 | grouper.add_argument("-m", type=str, default=None) 314 | grouper.add_argument("-T", type=str, default=None) 315 | 316 | # src files 317 | grouper.add_argument("inputs", type=str, nargs="+") 318 | 319 | return grouper 320 | 321 | def CMDArchiver(): 322 | grouper = ArgumentParser(add_help=False) 323 | 324 | # output 325 | grouper.add_argument("o", type=str, default=None) 326 | 327 | # src files 328 | grouper.add_argument("inputs", type=str, nargs="*") 329 | 330 | return grouper 331 | 332 | class CMDModule(object): 333 | def __init__(self, grouper, argv): 334 | # parse args 335 | args, rems = grouper.parse_known_args(argv) 336 | 337 | if len(rems) != 0: 338 | for r in rems: 339 | print r 340 | 341 | LOG_ERR("Argument group failed") 342 | sys.exit() 343 | 344 | # collect results 345 | self.srcs = args.inputs 346 | self.outs = args.o 347 | self.args = args 348 | 349 | class CMDLink(object): 350 | def __init__(self, goal): 351 | self.goal = goal 352 | self.subs = set() 353 | self.pars = set() 354 | 355 | def link(self, sub): 356 | self.subs.add(sub.goal) 357 | sub.pars.add(self.goal) 358 | -------------------------------------------------------------------------------- /pass/KSym/Libcall.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "Project.h" 3 | 4 | // expansion 5 | #define LIBCALL_DERIVE_0(name) \ 6 | SymVal *SEOpvCall_##name::derive(SymExec &sym) 7 | 8 | #define LIBCALL_DERIVE_1(name, arg0) \ 9 | SymVal *SEOpvCall_##name::derive(SymExec &sym, \ 10 | SymVal *arg0) 11 | 12 | #define LIBCALL_DERIVE_2(name, arg0, arg1) \ 13 | SymVal *SEOpvCall_##name::derive(SymExec &sym, \ 14 | SymVal *arg0, SymVal *arg1) 15 | 16 | #define LIBCALL_DERIVE_3(name, arg0, arg1, arg2) \ 17 | SymVal *SEOpvCall_##name::derive(SymExec &sym, \ 18 | SymVal *arg0, SymVal *arg1, SymVal *arg2) 19 | 20 | // helpers 21 | #define CHECK_INST \ 22 | assert(isa(inst)); \ 23 | CallInst *op = cast(inst); 24 | 25 | #define GET_ORACLE \ 26 | ModuleOracle &oracle = sym.getOracle(); 27 | 28 | #define INIT_EXPR \ 29 | SymExpr expr; \ 30 | SymCond cond; 31 | 32 | #define FINI_EXPR \ 33 | return new SymVal(expr, cond); 34 | 35 | #define IGNORED \ 36 | INIT_EXPR; \ 37 | FINI_EXPR; 38 | 39 | // libcall symbolization routines 40 | 41 | // intrinsics 42 | LIBCALL_DERIVE_1(llvm_objectsize_i64_p0i8, obj) { 43 | INIT_EXPR; 44 | 45 | CHECK_INST; 46 | 47 | Value *a0 = op->getArgOperand(0); 48 | if(GetElementPtrInst *gep = dyn_cast(a0)){ 49 | expr.expr = sym.createConstTypeSize(gep->getResultElementType()); 50 | } else { 51 | expr.expr = sym.createVarPointer(); 52 | 53 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 54 | expr.expr, sym.createConstPointer(HEAP_SLOT_LEN))); 55 | } 56 | 57 | FINI_EXPR; 58 | } 59 | 60 | LIBCALL_DERIVE_1(llvm_bswap_i16, iv) { 61 | INIT_EXPR; 62 | 63 | CHECK_INST; 64 | 65 | expr.expr = sym.castMachIntSwap(iv->getExpr().expr); 66 | 67 | FINI_EXPR; 68 | } 69 | 70 | // malloc 71 | LIBCALL_DERIVE_1(__get_free_pages, order) { 72 | INIT_EXPR; 73 | 74 | CHECK_INST; 75 | 76 | expr.expr = sym.createPtrHeap(); 77 | 78 | FINI_EXPR; 79 | } 80 | 81 | LIBCALL_DERIVE_1(__kmalloc, size) { 82 | INIT_EXPR; 83 | 84 | CHECK_INST; 85 | 86 | expr.expr = sym.createPtrHeap(); 87 | 88 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 89 | size->getExpr().expr, sym.createConstPointer(0))); 90 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 91 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 92 | 93 | FINI_EXPR; 94 | } 95 | 96 | LIBCALL_DERIVE_1(__kmalloc_track_caller, size) { 97 | INIT_EXPR; 98 | 99 | CHECK_INST; 100 | 101 | expr.expr = sym.createPtrHeap(); 102 | 103 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 104 | size->getExpr().expr, sym.createConstPointer(0))); 105 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 106 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 107 | 108 | FINI_EXPR; 109 | } 110 | 111 | LIBCALL_DERIVE_1(__vmalloc, size) { 112 | INIT_EXPR; 113 | 114 | CHECK_INST; 115 | 116 | expr.expr = sym.createPtrHeap(); 117 | 118 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 119 | size->getExpr().expr, sym.createConstPointer(0))); 120 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 121 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 122 | 123 | FINI_EXPR; 124 | } 125 | 126 | LIBCALL_DERIVE_0(get_zeroed_page) { 127 | INIT_EXPR; 128 | 129 | CHECK_INST; 130 | 131 | expr.expr = sym.createPtrHeap(); 132 | 133 | FINI_EXPR; 134 | } 135 | 136 | LIBCALL_DERIVE_0(kmem_cache_alloc) { 137 | INIT_EXPR; 138 | 139 | CHECK_INST; 140 | 141 | expr.expr = sym.createPtrHeap(); 142 | 143 | FINI_EXPR; 144 | } 145 | 146 | LIBCALL_DERIVE_1(krealloc, size) { 147 | INIT_EXPR; 148 | 149 | CHECK_INST; 150 | 151 | expr.expr = sym.createPtrHeap(); 152 | 153 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 154 | size->getExpr().expr, sym.createConstPointer(0))); 155 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 156 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 157 | 158 | FINI_EXPR; 159 | } 160 | 161 | LIBCALL_DERIVE_1(kvmalloc_node, size) { 162 | INIT_EXPR; 163 | 164 | CHECK_INST; 165 | 166 | expr.expr = sym.createPtrHeap(); 167 | 168 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 169 | size->getExpr().expr, sym.createConstPointer(0))); 170 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 171 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 172 | 173 | FINI_EXPR; 174 | } 175 | 176 | LIBCALL_DERIVE_1(vmalloc, size) { 177 | INIT_EXPR; 178 | 179 | CHECK_INST; 180 | 181 | expr.expr = sym.createPtrHeap(); 182 | 183 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 184 | size->getExpr().expr, sym.createConstPointer(0))); 185 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 186 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 187 | 188 | FINI_EXPR; 189 | } 190 | 191 | LIBCALL_DERIVE_1(vzalloc, size) { 192 | INIT_EXPR; 193 | 194 | CHECK_INST; 195 | 196 | expr.expr = sym.createPtrHeap(); 197 | 198 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 199 | size->getExpr().expr, sym.createConstPointer(0))); 200 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 201 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 202 | 203 | FINI_EXPR; 204 | } 205 | 206 | // memory 207 | LIBCALL_DERIVE_2(kmemdup, src, size) { 208 | INIT_EXPR; 209 | 210 | CHECK_INST; 211 | 212 | expr.expr = sym.createPtrHeap(); 213 | 214 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 215 | size->getExpr().expr, sym.createConstPointer(0))); 216 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 217 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 218 | 219 | FINI_EXPR; 220 | } 221 | 222 | // fetch 223 | LIBCALL_DERIVE_3(__copy_user_flushcache, dst, src, size) { 224 | INIT_EXPR; 225 | 226 | CHECK_INST; 227 | GET_ORACLE; 228 | 229 | expr.expr = sym.createConstMachIntUnsigned(0, 230 | oracle.getTypeWidth(op->getType())); 231 | 232 | // NOTE: special treatment on 32bit size 233 | Z3_ast se = size->getExpr().expr; 234 | if(!sym.isPointerSort(se)){ 235 | se = sym.castMachIntZExt(se, oracle.getPointerWidth()); 236 | } 237 | 238 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 239 | se, sym.createConstPointer(0))); 240 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 241 | se, sym.createConstPointer(HEAP_SLOT_LEN))); 242 | 243 | FINI_EXPR; 244 | } 245 | 246 | LIBCALL_DERIVE_3(__copy_user_nocache, dst, src, size) { 247 | INIT_EXPR; 248 | 249 | CHECK_INST; 250 | GET_ORACLE; 251 | 252 | expr.expr = sym.createConstMachIntUnsigned(0, 253 | oracle.getTypeWidth(op->getType())); 254 | 255 | // NOTE: special treatment on 32bit size 256 | Z3_ast se = size->getExpr().expr; 257 | if(!sym.isPointerSort(se)){ 258 | se = sym.castMachIntZExt(se, oracle.getPointerWidth()); 259 | } 260 | 261 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 262 | se, sym.createConstPointer(0))); 263 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 264 | se, sym.createConstPointer(HEAP_SLOT_LEN))); 265 | 266 | FINI_EXPR; 267 | } 268 | 269 | LIBCALL_DERIVE_3(_copy_from_user, dst, src, size) { 270 | INIT_EXPR; 271 | 272 | CHECK_INST; 273 | GET_ORACLE; 274 | 275 | expr.expr = sym.createConstMachIntUnsigned(0, 276 | oracle.getTypeWidth(op->getType())); 277 | 278 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 279 | size->getExpr().expr, sym.createConstPointer(0))); 280 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 281 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 282 | 283 | FINI_EXPR; 284 | } 285 | 286 | LIBCALL_DERIVE_2(memdup_user, src, size) { 287 | INIT_EXPR; 288 | 289 | CHECK_INST; 290 | 291 | expr.expr = sym.createPtrHeap(); 292 | 293 | sym.addAssert(Z3_mk_bvugt(sym.getContext(), 294 | size->getExpr().expr, sym.createConstPointer(0))); 295 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 296 | size->getExpr().expr, sym.createConstPointer(HEAP_SLOT_LEN))); 297 | 298 | FINI_EXPR; 299 | } 300 | 301 | // string 302 | LIBCALL_DERIVE_3(memcmp, cs, ct, count) { 303 | INIT_EXPR; 304 | 305 | CHECK_INST; 306 | GET_ORACLE; 307 | 308 | expr.expr = sym.createConstMachIntUnsigned(0, 309 | oracle.getTypeWidth(op->getType())); 310 | 311 | FINI_EXPR; 312 | } 313 | 314 | LIBCALL_DERIVE_2(strcmp, cs, ct) { 315 | INIT_EXPR; 316 | 317 | CHECK_INST; 318 | GET_ORACLE; 319 | 320 | expr.expr = sym.createConstMachIntUnsigned(0, 321 | oracle.getTypeWidth(op->getType())); 322 | 323 | FINI_EXPR; 324 | } 325 | 326 | LIBCALL_DERIVE_1(strlen, buf) { 327 | INIT_EXPR; 328 | 329 | expr.expr = sym.createVarPointer(); 330 | 331 | sym.addAssert(Z3_mk_bvult(sym.getContext(), 332 | expr.expr, sym.createConstPointer(HEAP_SLOT_LEN))); 333 | 334 | FINI_EXPR; 335 | } 336 | 337 | LIBCALL_DERIVE_3(strncmp, cs, ct, count) { 338 | INIT_EXPR; 339 | 340 | CHECK_INST; 341 | GET_ORACLE; 342 | 343 | expr.expr = sym.createConstMachIntUnsigned(0, 344 | oracle.getTypeWidth(op->getType())); 345 | 346 | FINI_EXPR; 347 | } 348 | 349 | LIBCALL_DERIVE_2(strnlen, buf, count) { 350 | INIT_EXPR; 351 | 352 | CHECK_INST; 353 | 354 | expr.expr = sym.createVarPointer(); 355 | sym.addAssert(Z3_mk_bvule(sym.getContext(), 356 | expr.expr, count->getExpr().expr)); 357 | 358 | FINI_EXPR; 359 | } 360 | 361 | LIBCALL_DERIVE_2(strnlen_user, buf, count) { 362 | INIT_EXPR; 363 | 364 | CHECK_INST; 365 | 366 | expr.expr = sym.createVarPointer(); 367 | sym.addAssert(Z3_mk_bvule(sym.getContext(), 368 | expr.expr, count->getExpr().expr)); 369 | 370 | FINI_EXPR; 371 | } 372 | 373 | // transfer 374 | LIBCALL_DERIVE_0(_copy_to_user) { 375 | IGNORED; 376 | } 377 | 378 | LIBCALL_DERIVE_0(compat_alloc_user_space) { 379 | IGNORED; 380 | } 381 | 382 | // sync 383 | LIBCALL_DERIVE_0(init_wait_entry) { 384 | IGNORED; 385 | } 386 | 387 | LIBCALL_DERIVE_0(finish_wait) { 388 | IGNORED; 389 | } 390 | 391 | LIBCALL_DERIVE_0(mutex_lock_interruptible_nested) { 392 | IGNORED; 393 | } 394 | 395 | LIBCALL_DERIVE_0(mutex_trylock) { 396 | IGNORED; 397 | } 398 | 399 | LIBCALL_DERIVE_0(prepare_to_wait_event) { 400 | IGNORED; 401 | } 402 | 403 | // info 404 | LIBCALL_DERIVE_0(__dynamic_dev_dbg) { 405 | IGNORED; 406 | } 407 | 408 | LIBCALL_DERIVE_0(__dynamic_pr_debug) { 409 | IGNORED; 410 | } 411 | 412 | LIBCALL_DERIVE_0(printk) { 413 | IGNORED; 414 | } 415 | 416 | // checks 417 | LIBCALL_DERIVE_0(__check_object_size) { 418 | IGNORED; 419 | } 420 | 421 | LIBCALL_DERIVE_0(__inode_permission) { 422 | IGNORED; 423 | } 424 | 425 | LIBCALL_DERIVE_0(__list_add_valid) { 426 | IGNORED; 427 | } 428 | 429 | LIBCALL_DERIVE_0(__virt_addr_valid) { 430 | IGNORED; 431 | } 432 | 433 | LIBCALL_DERIVE_0(capable) { 434 | IGNORED; 435 | } 436 | 437 | LIBCALL_DERIVE_0(kasan_check_write) { 438 | IGNORED; 439 | } 440 | 441 | LIBCALL_DERIVE_0(mnt_want_write_file) { 442 | IGNORED; 443 | } 444 | 445 | LIBCALL_DERIVE_0(ns_capable) { 446 | IGNORED; 447 | } 448 | 449 | LIBCALL_DERIVE_0(security_capable) { 450 | IGNORED; 451 | } 452 | 453 | LIBCALL_DERIVE_0(security_file_fnctl) { 454 | IGNORED; 455 | } 456 | 457 | LIBCALL_DERIVE_0(security_file_ioctl) { 458 | IGNORED; 459 | } 460 | -------------------------------------------------------------------------------- /pass/KSym/PA.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "PA.h" 3 | 4 | using namespace SEGUtil; 5 | 6 | void PA::summarizeFunctions(Module *module) { 7 | } 8 | 9 | void PA::analyzePointTo(vector iList, SliceOracle &so) { 10 | if (iList.empty()) 11 | llvm_unreachable("empty instruction path"); 12 | 13 | const Function *func = iList[0]->getParent()->getParent(); 14 | errs() << "----> " << func->getName() << '\n'; 15 | module = func->getParent(); 16 | dataLayout = &(module->getDataLayout()); 17 | 18 | // initialize global variable nodes 19 | initializeGlobals(module); 20 | 21 | errs() << "++++++++path begin++++++++\n"; 22 | for (Instruction *inst : iList) { 23 | errs() << *inst << '\n'; 24 | } 25 | errs() << "++++++++ path end ++++++++\n"; 26 | 27 | errs() << "=========================================\n"; 28 | 29 | unsigned currIdx = -1; 30 | for (Instruction *inst : iList) { 31 | currIdx += 1; 32 | errs() << *inst << '\n'; 33 | switch (inst->getOpcode()) { 34 | case Instruction::Alloca: 35 | { 36 | AllocaInst *aInst = dyn_cast(inst); 37 | Node *newNode = new Node(NodeType::ALLOC, InstType::INIT, aInst, aInst); 38 | AllNodes[aInst] = newNode; 39 | break; 40 | } 41 | case Instruction::Call: 42 | case Instruction::Invoke: 43 | { 44 | // TODO 45 | errs() << "TODO: Call/Invoke\n"; 46 | break; 47 | } 48 | case Instruction::Ret: 49 | { 50 | // TODO 51 | errs() << "TODO: Ret\n"; 52 | break; 53 | } 54 | case Instruction::Load: 55 | { 56 | LoadInst *lInst = dyn_cast(inst); 57 | const Value *src = lInst->getPointerOperand(); 58 | Node *srcNode = lookupNode(src); 59 | if (!srcNode) 60 | llvm_unreachable("cannod find source node"); 61 | Node *dstNode = lookupNode(lInst); 62 | if (!dstNode) { 63 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, lInst); 64 | AllNodes[lInst] = dstNode; 65 | } 66 | srcNode->getNode(srcNode->startOffset)->copy(dstNode); 67 | break; 68 | } 69 | case Instruction::Store: 70 | { 71 | StoreInst *sInst = dyn_cast(inst); 72 | const Value *src = sInst->getValueOperand(); 73 | const Value *dst = sInst->getPointerOperand(); 74 | Node *srcNode = lookupNode(src); 75 | if (!srcNode) 76 | llvm_unreachable("cannot find source node"); 77 | Node *dstNode = lookupNode(dst)->getNode(); 78 | if (!dstNode) 79 | llvm_unreachable("cannot find destination node"); 80 | srcNode->copy(dstNode->getNode(dstNode->startOffset)); 81 | break; 82 | } 83 | case Instruction::GetElementPtr: 84 | { 85 | GetElementPtrInst *gepInst = dyn_cast(inst); 86 | // calculate the offset 87 | SmallVector indexOps = SmallVector(gepInst->op_begin() + 1, gepInst->op_end()); 88 | int64_t elemOffset = 0; 89 | unsigned n = indexOps.size(); 90 | for (unsigned i = 0; i < n; ++i) { 91 | if (!isa(indexOps[i])) { 92 | //llvm_unreachable("non constant index for gep inst"); 93 | errs() << "WARN: " << "gep inst has non constant index\n"; 94 | indexOps[i] = ConstantInt::get(Type::getInt32Ty(module->getContext()), 0); 95 | } 96 | } 97 | 98 | PointerType *pTy = dyn_cast(gepInst->getPointerOperand()->getType()); 99 | elemOffset = dataLayout->getIndexedOffsetInType(pTy->getElementType(), indexOps); 100 | // get base node and element node, copy base node to dst node 101 | Node *dstNode = lookupNode(gepInst); 102 | if (!dstNode) { 103 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, gepInst); 104 | AllNodes[gepInst] = dstNode; 105 | } 106 | Node *baseNode = lookupNode(gepInst->getPointerOperand()); 107 | if (!baseNode) 108 | llvm_unreachable("cannot find source base node"); 109 | Node *elemNode = baseNode->getNode(elemOffset); 110 | if (!elemNode) 111 | llvm_unreachable("cannot find element node"); 112 | baseNode->copy(dstNode); 113 | dstNode->startOffset = baseNode->startOffset + elemOffset; 114 | break; 115 | } 116 | case Instruction::PHI: 117 | { 118 | // just pick up an existing source value, TODO: get latest source value 119 | PHINode *phiNode = dyn_cast(inst); 120 | Value *srcValue = SEGUtil::backtrace(currIdx, phiNode, iList, so); 121 | Node *dstNode = lookupNode(phiNode); 122 | if (!dstNode) { 123 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, phiNode); 124 | AllNodes[phiNode] = dstNode; 125 | } 126 | 127 | if (!srcValue) 128 | llvm_unreachable("cannot find source value for PHINode"); 129 | Node *srcNode = lookupNode(srcValue); 130 | if (!srcNode) 131 | llvm_unreachable("cannot find PHINode source node"); 132 | srcNode->copy(dstNode); 133 | 134 | /* 135 | unsigned n = phiNode->getNumIncomingValues(); 136 | bool found = false; 137 | for (unsigned i = 0; i < n; ++i) { 138 | Node *srcNode = lookupNode(phiNode->getIncomingValue(i)); 139 | if (srcNode) { 140 | srcNode->copy(dstNode); 141 | found = true; 142 | break; 143 | } 144 | } 145 | if (!found) 146 | llvm_unreachable("cannot find source value for PHINode"); 147 | */ 148 | break; 149 | } 150 | case Instruction::BitCast: 151 | { 152 | BitCastInst *bcInst = dyn_cast(inst); 153 | Node *dstNode = lookupNode(bcInst); 154 | if (!dstNode) { 155 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, bcInst); 156 | AllNodes[bcInst] = dstNode; 157 | } 158 | Node *srcNode = lookupNode(bcInst->getOperand(0)); 159 | if (!srcNode) 160 | llvm_unreachable("cannot find source node"); 161 | srcNode->copy(dstNode); 162 | break; 163 | } 164 | case Instruction::IntToPtr: 165 | { 166 | IntToPtrInst *intToPtrInst = dyn_cast(inst); 167 | // TODO 168 | Node *dstNode = lookupNode(intToPtrInst); 169 | if (!dstNode) { 170 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, intToPtrInst); 171 | AllNodes[intToPtrInst] = dstNode; 172 | } 173 | break; 174 | } 175 | case Instruction::PtrToInt: 176 | { 177 | PtrToIntInst *ptrToIntInst = dyn_cast(inst); 178 | // TODO 179 | Node *dstNode = lookupNode(ptrToIntInst); 180 | if (!dstNode) { 181 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, ptrToIntInst); 182 | AllNodes[ptrToIntInst] = dstNode; 183 | } 184 | break; 185 | } 186 | case Instruction::Select: 187 | { 188 | // randomly pick an existing value, TODO: get the latest source value 189 | SelectInst *sInst = dyn_cast(inst); 190 | Node *dstNode = lookupNode(sInst); 191 | if (!dstNode) { 192 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, sInst); 193 | AllNodes[sInst] = dstNode; 194 | } 195 | Node *srcNode = lookupNode(sInst->getTrueValue()); 196 | if (srcNode) 197 | srcNode->copy(dstNode); 198 | else { 199 | srcNode = lookupNode(sInst->getFalseValue()); 200 | if (!srcNode) 201 | llvm_unreachable("cannot find source value for SelectInst"); 202 | srcNode->copy(dstNode); 203 | } 204 | break; 205 | } 206 | case Instruction::ExtractElement: 207 | { 208 | ExtractElementInst *extractElementInst = dyn_cast(inst); 209 | const Value *indexOperand = extractElementInst->getIndexOperand(); 210 | if (!isa(indexOperand)) { 211 | //llvm_unreachable("extract element inst has non constant index"); 212 | errs() << "WARN: " << "extract element inst has non constant index\n"; 213 | indexOperand = ConstantInt::get(Type::getInt32Ty(module->getContext()), 0); 214 | } 215 | const ConstantInt *cInt = dyn_cast(indexOperand); 216 | int64_t elemOffset = cInt->getSExtValue(); 217 | Node *dstNode = lookupNode(extractElementInst); 218 | if (!dstNode) { 219 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, extractElementInst); 220 | AllNodes[extractElementInst] = dstNode; 221 | } 222 | Node *vectorNode = lookupNode(extractElementInst->getVectorOperand()); 223 | if (!vectorNode) 224 | llvm_unreachable("cannot find source vector node"); 225 | Node *elemNode = vectorNode->getNode(elemOffset); 226 | elemNode->copy(dstNode); 227 | break; 228 | } 229 | case Instruction::ExtractValue: 230 | { 231 | ExtractValueInst *extractValueInst = dyn_cast(inst); 232 | SmallVector indexOps = SmallVector(extractValueInst->op_begin() + 1, extractValueInst->op_end()); 233 | unsigned n = indexOps.size(); 234 | for (unsigned i = 0; i < n; ++i) { 235 | if (!isa(indexOps[i])) { 236 | //llvm_unreachable("extract value inst has non constant index"); 237 | errs() << "WARN: " << "extract value inst has non constant index\n"; 238 | indexOps[i] = ConstantInt::get(Type::getInt32Ty(module->getContext()), 0); 239 | } 240 | } 241 | PointerType *ptrTy = PointerType::get(extractValueInst->getAggregateOperand()->getType(), 0); 242 | int64_t elemOffset = dataLayout->getIndexedOffsetInType(ptrTy->getElementType(), indexOps); 243 | Node *dstNode = lookupNode(extractValueInst); 244 | if (!dstNode) { 245 | dstNode = new Node(NodeType::UNDEFINED, InstType::READ, nullptr, extractValueInst); 246 | AllNodes[extractValueInst] = dstNode; 247 | } 248 | Node *baseNode = lookupNode(extractValueInst->getAggregateOperand()); 249 | if (!baseNode) 250 | llvm_unreachable("cannot find base node"); 251 | Node *elemNode = baseNode->getNode(elemOffset); 252 | if (!elemNode) 253 | llvm_unreachable("cannot find element node"); 254 | elemNode->copy(dstNode); 255 | break; 256 | } 257 | case Instruction::InsertElement: 258 | { 259 | InsertElementInst *insertElementInst = dyn_cast(inst); 260 | const Value *indexOperand = insertElementInst->getOperand(2); 261 | if (!isa(indexOperand)) { 262 | //llvm_unreachable("insert element inst has non constant index"); 263 | errs() << "WARN: " << "insert element inst has non constant index\n"; 264 | indexOperand = ConstantInt::get(Type::getInt32Ty(module->getContext()), 0); 265 | } 266 | const ConstantInt *cInt = dyn_cast(indexOperand); 267 | int64_t elemOffset = cInt->getSExtValue(); 268 | Node *baseNode = lookupNode(insertElementInst->getOperand(0)); 269 | if (!baseNode) 270 | llvm_unreachable("cannot find base node"); 271 | Node *elemNode = baseNode->getNode(elemOffset); 272 | if (!elemNode) 273 | llvm_unreachable("cannot find element node"); 274 | Node *srcNode = lookupNode(insertElementInst->getOperand(1)); 275 | if (!srcNode) 276 | llvm_unreachable("cannot find source node"); 277 | srcNode->copy(elemNode); 278 | break; 279 | } 280 | case Instruction::InsertValue: 281 | { 282 | InsertValueInst *insertValueInst = dyn_cast(inst); 283 | SmallVector indexOps = SmallVector(insertValueInst->op_begin() + 2, insertValueInst->op_end()); 284 | for (unsigned i = 0; i < indexOps.size(); ++i) { 285 | if (!isa(indexOps[i])) { 286 | //llvm_unreachable("extract value inst has non constant index"); 287 | errs() << "WARN: " << "extract value inst has non constant index\n"; 288 | indexOps[i] = ConstantInt::get(Type::getInt32Ty(module->getContext()), 0); 289 | } 290 | } 291 | PointerType *ptrTy = PointerType::get(insertValueInst->getAggregateOperand()->getType(), 0); 292 | int64_t elemOffset = dataLayout->getIndexedOffsetInType(ptrTy->getElementType(), indexOps); 293 | Node *baseNode = lookupNode(insertValueInst->getAggregateOperand()); 294 | if (!baseNode) 295 | llvm_unreachable("cannot find base node"); 296 | Node *elemNode = baseNode->getNode(elemOffset); 297 | if (!elemNode) 298 | llvm_unreachable("cannot find element node"); 299 | Node *srcNode = lookupNode(insertValueInst->getInsertedValueOperand()); 300 | if (!srcNode) 301 | llvm_unreachable("cannot find source node"); 302 | srcNode->copy(elemNode); 303 | break; 304 | } 305 | default: 306 | { 307 | errs() << "WARN: unhandled instruction - " << *inst << '\n'; 308 | Node *newNode = new Node(NodeType::ALLOC, InstType::INIT, inst, inst); 309 | AllNodes[inst] = newNode; 310 | break; 311 | } 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /pass/KSym/Global.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | #include "PA.h" 3 | 4 | void PA::initializeGlobals(const Module *module) { 5 | // global variables 6 | for (auto const& globalVal : module->globals()) { 7 | Node *newNode = new Node(NodeType::GLOBAL, InstType::INIT, &globalVal, nullptr); 8 | AllNodes[&globalVal] = newNode; 9 | } 10 | 11 | // functions 12 | for (auto const& f : *module) { 13 | Node *newNode = new Node(NodeType::GLOBAL, InstType::INIT, &f, nullptr); 14 | (*(newNode->mem))[0] = new Node(NodeType::GLOBAL, InstType::NONE, &f, nullptr); 15 | AllNodes[&f] = newNode; 16 | 17 | // create nodes for arguments 18 | Function::const_arg_iterator fItr = f.arg_begin(); 19 | while (fItr != f.arg_end()) { 20 | const Argument *arg = &(*fItr); 21 | Node *newNode = new Node(NodeType::ALLOC, InstType::INIT, arg, nullptr); 22 | AllNodes[arg] = newNode; 23 | 24 | ++fItr; 25 | } 26 | } 27 | 28 | // global alias 29 | for (auto const& globalAlias : module->aliases()) { 30 | Node *gNode = lookupNode(globalAlias.getBaseObject()); 31 | AllNodes[&globalAlias] = gNode; 32 | } 33 | 34 | // initialize globals 35 | for (auto const& globalVal : module->globals()) { 36 | if (globalVal.hasDefinitiveInitializer()) { 37 | SmallVector indexOps; 38 | IntegerType * Int32Ty = IntegerType::getInt32Ty(module->getContext()); 39 | ConstantInt * CI = ConstantInt::get(Int32Ty, 0); 40 | indexOps.push_back(CI); 41 | initGlobal(&globalVal, globalVal.getInitializer(), indexOps); 42 | } 43 | } 44 | } 45 | 46 | Node* PA::getNodeForConstantExpr(Value *v) { 47 | if (GlobalValue *gVal = dyn_cast(v)) { 48 | Node *gNode = getGlobalNode(v); 49 | if (!gNode) 50 | llvm_unreachable("cannot get node for global value"); 51 | return gNode; 52 | } 53 | 54 | ConstantExpr *ce = dyn_cast(v); 55 | if (!ce) { 56 | llvm_unreachable("not constant expr"); 57 | } 58 | switch (ce->getOpcode()) { 59 | case Instruction::GetElementPtr: 60 | { 61 | Node *gNode = nullptr; 62 | 63 | GlobalVariable *gVal = dyn_cast(ce->getOperand(0)); 64 | if (gVal) { 65 | gNode = getGlobalNode(gVal); 66 | } else { 67 | gNode = getNodeForConstantExpr(ce->getOperand(0)); 68 | } 69 | 70 | if (!gNode) 71 | llvm_unreachable("cannot get node for gep"); 72 | 73 | SmallVector indexOps(ce->op_begin() + 1, ce->op_end()); 74 | PointerType *pTy = dyn_cast(ce->getOperand(0)->getType()); 75 | if (!pTy) 76 | llvm_unreachable("not a pointer type"); 77 | int64_t elemOffset = dataLayout->getIndexedOffsetInType(pTy->getElementType(), indexOps); 78 | // elemNode = gNode->mem[elemOffset] 79 | 80 | Node *newNode = new Node(NodeType::GLOBAL, InstType::INIT, nullptr, nullptr); 81 | newNode->mem = gNode->mem; 82 | newNode->startOffset = gNode->startOffset + elemOffset; 83 | 84 | Node *elemNode = gNode->getNode(elemOffset); 85 | if (elemNode == nullptr) { 86 | elemNode = new Node(NodeType::UNDEFINED, InstType::INIT, nullptr, nullptr); 87 | gNode->insertNode(elemOffset, elemNode); 88 | } 89 | 90 | return newNode; 91 | 92 | } 93 | case Instruction::BitCast: 94 | { 95 | Node *gNode = nullptr; 96 | GlobalVariable *gVal = dyn_cast(ce->getOperand(0)); 97 | if (gVal) { 98 | gNode = getGlobalNode(gVal); 99 | } else { 100 | gNode = getNodeForConstantExpr(ce->getOperand(0)); 101 | } 102 | return gNode; 103 | } 104 | case Instruction::PtrToInt: 105 | case Instruction::IntToPtr: 106 | { 107 | Node *newNode = new Node(NodeType::UNDEFINED, InstType::INIT, nullptr, nullptr); 108 | return newNode; 109 | } 110 | default: 111 | llvm_unreachable("not getelementptr/bitcast"); 112 | break; 113 | } 114 | llvm_unreachable("end of getNodeForConstantExpr"); 115 | return nullptr; 116 | } 117 | 118 | void PA::initGlobal(const Value *V, const Constant *C, 119 | SmallVector& indexOps) { 120 | int64_t offset = 0; 121 | if (const BlockAddress *bAddr = dyn_cast(C)) { 122 | llvm_unreachable("unhandled global block address"); 123 | } else if (const ConstantAggregateZero *caz = dyn_cast(C)) { 124 | // don't need to initialize zero initializer 125 | return; 126 | } else if (const ConstantArray *ca = dyn_cast(C)) { 127 | unsigned opNum = ca->getNumOperands(); 128 | for (unsigned i = 0; i < opNum; ++i) { 129 | IntegerType * Int32Ty = IntegerType::getInt32Ty(module->getContext()); 130 | ConstantInt * CI = ConstantInt::get(Int32Ty, i); 131 | indexOps.push_back(CI); 132 | initGlobal(V, ca->getOperand(i), indexOps); 133 | indexOps.pop_back(); 134 | } 135 | return; 136 | } else if (const ConstantDataSequential *cds = dyn_cast(C)) { 137 | unsigned elemNum = cds->getNumElements(); 138 | for (unsigned i = 0; i < elemNum; ++i) { 139 | IntegerType * Int32Ty = IntegerType::getInt32Ty(module->getContext()); 140 | ConstantInt * CI = ConstantInt::get(Int32Ty, i); 141 | indexOps.push_back(CI); 142 | initGlobal(V, cds->getElementAsConstant(i), indexOps); 143 | indexOps.pop_back(); 144 | } 145 | return; 146 | } else if (const ConstantExpr *ce = dyn_cast(C) ) { 147 | PointerType *pTy = dyn_cast(V->getType()); 148 | if (!pTy) 149 | llvm_unreachable("not pointer type"); 150 | offset = dataLayout->getIndexedOffsetInType(pTy->getElementType(), indexOps); 151 | switch (ce->getOpcode()) { 152 | case Instruction::GetElementPtr: 153 | { 154 | Node *elemNode = getNodeForConstantExpr(const_cast(ce)); 155 | addGlobalNode(V, offset, elemNode); 156 | break; 157 | } 158 | case Instruction::BitCast: 159 | { 160 | Node *gNode = getNodeForConstantExpr(const_cast(ce)); 161 | addGlobalNode(V, offset, gNode); 162 | break; 163 | } 164 | case Instruction::PtrToInt: 165 | case Instruction::IntToPtr: 166 | { 167 | Node *gNode = getNodeForConstantExpr(const_cast(ce)); 168 | addGlobalNode(V, offset, gNode); 169 | break; 170 | } 171 | default: 172 | llvm_unreachable("unhandled constant expession in global variable initialization"); 173 | } 174 | return; 175 | } else if (const ConstantFP *cfp = dyn_cast(C)) { 176 | // don't need to initialize float pointing number 177 | return; 178 | } else if (const ConstantInt *ci = dyn_cast(C)) { 179 | // don't need to initialize int 180 | return; 181 | } else if (const ConstantPointerNull *cpn = dyn_cast(C)) { 182 | // intialize to undefined node 183 | PointerType *pTy = dyn_cast(V->getType()); 184 | if (!pTy) 185 | llvm_unreachable("not a pointer type"); 186 | offset = dataLayout->getIndexedOffsetInType(pTy->getElementType(), indexOps); 187 | addGlobalNode(V, offset, new Node(NodeType::UNDEFINED, InstType::INIT, nullptr, nullptr)); 188 | return; 189 | } else if (const ConstantStruct *cs = dyn_cast(C)) { 190 | unsigned opNum = cs->getNumOperands(); 191 | for (unsigned i = 0; i < opNum; ++i) { 192 | IntegerType * Int32Ty = IntegerType::getInt32Ty(module->getContext()); 193 | ConstantInt * CI = ConstantInt::get(Int32Ty, i); 194 | indexOps.push_back(CI); 195 | initGlobal(V, cs->getOperand(i), indexOps); 196 | indexOps.pop_back(); 197 | } 198 | return; 199 | } else if (const ConstantVector *cv = dyn_cast(C)) { 200 | unsigned opNum = cv->getNumOperands(); 201 | for (unsigned i = 0; i < opNum; ++i) { 202 | IntegerType * Int32Ty = IntegerType::getInt32Ty(module->getContext()); 203 | ConstantInt * CI = ConstantInt::get(Int32Ty, i); 204 | indexOps.push_back(CI); 205 | initGlobal(V, cs->getOperand(i), indexOps); 206 | indexOps.pop_back(); 207 | } 208 | return; 209 | } else if (const GlobalValue *gv = dyn_cast(C)) { 210 | // add the node 211 | Node *gNode = getGlobalNode(gv); 212 | // GlobalNodes[V]->mem[offset] = gNode 213 | PointerType *pTy = dyn_cast(V->getType()); 214 | if (!pTy) 215 | llvm_unreachable("not a pointer type"); 216 | offset = dataLayout->getIndexedOffsetInType(pTy->getElementType(), indexOps); 217 | addGlobalNode(V, offset, gNode); 218 | return; 219 | } else if (const UndefValue *uv = dyn_cast(C)) { 220 | // intialize to undefined node 221 | PointerType *pTy = dyn_cast(V->getType()); 222 | if (!pTy) 223 | llvm_unreachable("not a pointer type"); 224 | offset = dataLayout->getIndexedOffsetInType(pTy->getElementType(), indexOps); 225 | addGlobalNode(V, offset, new Node(NodeType::UNDEFINED, InstType::INIT, nullptr, nullptr)); 226 | return; 227 | } 228 | 229 | llvm_unreachable("not handled global initialization"); 230 | return; 231 | } 232 | 233 | int64_t PA::initGlobal(const Value *V, const Constant *C, int64_t offset) { 234 | if (const BlockAddress *bAddr = dyn_cast(C)) { 235 | llvm_unreachable("unhandled global block address"); 236 | } else if (const ConstantAggregateZero *caz = dyn_cast(C)) { 237 | // don't need to initialize zero initializer 238 | int64_t tySize = dataLayout->getTypeAllocSize(caz->getType()); 239 | // update offset 240 | return (offset + tySize); 241 | } else if (const ConstantArray *ca = dyn_cast(C)) { 242 | unsigned opNum = ca->getNumOperands(); 243 | for (unsigned i = 0; i < opNum; ++i) { 244 | // update offset 245 | offset = initGlobal(V, ca->getOperand(i), offset); 246 | } 247 | return offset; 248 | } else if (const ConstantDataSequential *cds = dyn_cast(C)) { 249 | unsigned elemNum = cds->getNumElements(); 250 | for (unsigned i = 0; i < elemNum; ++i) { 251 | // update offset 252 | offset = initGlobal(V, cds->getElementAsConstant(i), offset); 253 | } 254 | return offset; 255 | } else if (const ConstantExpr *ce = dyn_cast(C) ) { 256 | switch (ce->getOpcode()) { 257 | case Instruction::GetElementPtr: 258 | { 259 | Node *elemNode = getNodeForConstantExpr(const_cast(ce)); 260 | addGlobalNode(V, offset, elemNode); 261 | // update offset 262 | offset += dataLayout->getTypeAllocSize(ce->getType()); 263 | break; 264 | } 265 | case Instruction::BitCast: 266 | { 267 | Node *gNode = getNodeForConstantExpr(const_cast(ce)); 268 | addGlobalNode(V, offset, gNode); 269 | // update offset 270 | offset += dataLayout->getTypeAllocSize(ce->getType()); 271 | break; 272 | } 273 | case Instruction::PtrToInt: 274 | { 275 | Node *gNode = getNodeForConstantExpr(const_cast(ce)); 276 | addGlobalNode(V, offset, gNode); 277 | // update offset 278 | offset += dataLayout->getTypeAllocSize(ce->getType()); 279 | break; 280 | } 281 | default: 282 | llvm_unreachable("unhandled constant expession in global variable initialization"); 283 | } 284 | return offset; 285 | } else if (const ConstantFP *cfp = dyn_cast(C)) { 286 | // don't need to initialize float pointing number 287 | int64_t tySize = dataLayout->getTypeAllocSize(cfp->getType()); 288 | // update offset 289 | return (offset + tySize); 290 | } else if (const ConstantInt *ci = dyn_cast(C)) { 291 | // don't need to initialize int 292 | int64_t tySize = dataLayout->getTypeAllocSize(ci->getType()); 293 | // update offset 294 | return (offset + tySize); 295 | } else if (const ConstantPointerNull *cpn = dyn_cast(C)) { 296 | // intialize to undefined node 297 | addGlobalNode(V, offset, new Node(NodeType::UNDEFINED, InstType::INIT, nullptr, nullptr)); 298 | int64_t tySize = dataLayout->getTypeAllocSize(cpn->getType()); 299 | // update offset 300 | return (offset + tySize); 301 | } else if (const ConstantStruct *cs = dyn_cast(C)) { 302 | unsigned opNum = cs->getNumOperands(); 303 | for (unsigned i = 0; i < opNum; ++i) { 304 | offset = initGlobal(V, cs->getOperand(i), offset); 305 | } 306 | return offset; 307 | } else if (const ConstantVector *cv = dyn_cast(C)) { 308 | unsigned opNum = cv->getNumOperands(); 309 | for (unsigned i = 0; i < opNum; ++i) { 310 | offset = initGlobal(V, cv->getOperand(i), offset); 311 | } 312 | return offset; 313 | } else if (const GlobalValue *gv = dyn_cast(C)) { 314 | // add the node 315 | Node *gNode = getGlobalNode(gv); 316 | // GlobalNodes[V]->mem[offset] = gNode 317 | addGlobalNode(V, offset, gNode); 318 | // update offset 319 | int64_t tySize = dataLayout->getTypeAllocSize(gv->getType()); 320 | return (offset + tySize); 321 | } else if (const UndefValue *uv = dyn_cast(C)) { 322 | // intialize to undefined node 323 | addGlobalNode(V, offset, new Node(NodeType::UNDEFINED, InstType::INIT, nullptr, nullptr)); 324 | int64_t tySize = dataLayout->getTypeAllocSize(uv->getType()); 325 | // update offset 326 | return (offset + tySize); 327 | } 328 | 329 | llvm_unreachable("not handled global initialization"); 330 | return 0; 331 | } 332 | --------------------------------------------------------------------------------