├── lfi.creator ├── triggers ├── NetInspectorServer.c ├── PrintStackTrigger.h ├── NetInspector.h ├── AfterUnlockTrigger.h ├── PrintStackTrigger.cpp ├── ReadInspector.h ├── SingleTrigger.h ├── RandomTrigger.h ├── CallCountTrigger.h ├── SingleTrigger.cpp ├── TimerTrigger.h ├── SemTrigger.h ├── ReadInspector.cpp ├── StateTrigger.h ├── NetInspector.cpp ├── TimerTrigger.cpp ├── CallCountTrigger.cpp ├── RandomTrigger.cpp ├── SemTrigger.cpp ├── AfterUnlockTrigger.cpp └── StateTrigger.cpp ├── lfi.config ├── profiler ├── syscall_errors_head.h ├── toxml.awk ├── evaluator │ ├── Makefile │ └── errordiff.cpp ├── Makefile ├── README ├── listfn_eval.sh ├── BasicBlock.cpp ├── CFGBuilder.h ├── BasicBlock.h ├── CFGBuilderSPARC.h ├── TODO ├── CFGraph.h ├── CFGraph.cpp ├── syscalls_man.sh ├── listfn.sh ├── syscalls.sh ├── dasmfn.sh ├── libxml_profiler │ └── docparser.php ├── SEDetector.cpp ├── ProfilerMgr.cpp ├── CFGBuilder.cpp └── CFGBuilderSPARC.cpp ├── Makefile ├── lfi.includes ├── cs-analyzer ├── Makefile ├── README ├── BasicBlock.cpp ├── CFGBuilder.h ├── BasicBlock.h ├── CFGraph.h ├── CFGraph.cpp ├── CFGBuilder.cpp └── CFG.cpp ├── scenarios ├── pthread_read.xml ├── macos.simple.xml ├── pbft.xml └── sampleplan.xml ├── lfi.files ├── Trigger.cpp ├── Trigger.h ├── README.md ├── inter.cpp ├── inter.h └── libfi.cpp /lfi.creator: -------------------------------------------------------------------------------- 1 | [General] 2 | -------------------------------------------------------------------------------- /triggers/NetInspectorServer.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lfi.config: -------------------------------------------------------------------------------- 1 | // ADD PREDEFINED MACROS HERE! 2 | -------------------------------------------------------------------------------- /profiler/syscall_errors_head.h: -------------------------------------------------------------------------------- 1 | #include "errno.h" 2 | 3 | struct sys_errors { 4 | int sysno; 5 | int *error_values; 6 | }; 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ -Wall -o libfi libfi.cpp `xml2-config --cflags` `xml2-config --libs` 3 | 4 | clean: 5 | rm -f inter.c.* intercept.stub* -------------------------------------------------------------------------------- /lfi.includes: -------------------------------------------------------------------------------- 1 | /Users/zamf/code/lfi/lfi 2 | /Users/zamf/code/lfi/lfi/cs-analyzer 3 | /Users/zamf/code/lfi/lfi/profiler 4 | /Users/zamf/code/lfi/lfi/triggers -------------------------------------------------------------------------------- /profiler/toxml.awk: -------------------------------------------------------------------------------- 1 | 2 | { 3 | print "" 4 | print "0" 5 | print "" 6 | } -------------------------------------------------------------------------------- /profiler/evaluator/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ errordiff.cpp -o evaluator 3 | ./evaluator 4 | kernel: 5 | g++ -o evaluator -DSYSCALLS errordiff.cpp 6 | ./evaluator 7 | -------------------------------------------------------------------------------- /triggers/PrintStackTrigger.h: -------------------------------------------------------------------------------- 1 | 2 | #include "../Trigger.h" 3 | 4 | DEFINE_TRIGGER( PrintStackTrigger ) 5 | { 6 | public: 7 | PrintStackTrigger(); 8 | void Init(xmlNodePtr initData); 9 | bool Eval(const string* functionName, ...); 10 | private: 11 | FILE* file; 12 | }; 13 | -------------------------------------------------------------------------------- /triggers/NetInspector.h: -------------------------------------------------------------------------------- 1 | 2 | #include "../Trigger.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | DEFINE_TRIGGER( NetInspector ) 10 | { 11 | public: 12 | NetInspector(); 13 | bool Eval(const string* functionName, ...); 14 | private: 15 | int sockfd; 16 | char buffer[256]; 17 | char receiveBuf[10]; 18 | }; 19 | -------------------------------------------------------------------------------- /cs-analyzer/Makefile: -------------------------------------------------------------------------------- 1 | build: cfg graph builder 2 | g++ -g -O0 -o cs-analyzer CFG.o BasicBlock.o CFGraph.o CFGBuilder.o 3 | 4 | graph: BasicBlock.h BasicBlock.cpp CFGraph.h CFGraph.cpp 5 | g++ -g -O0 -c BasicBlock.cpp 6 | g++ -g -O0 -c CFGraph.cpp 7 | 8 | builder: CFGBuilder.h CFGBuilder.cpp 9 | g++ -g -O0 -c CFGBuilder.cpp 10 | 11 | cfg: CFG.cpp 12 | g++ -g -O0 -c CFG.cpp 13 | 14 | clean: 15 | rm -f *.o *.bak cs-analyzer -------------------------------------------------------------------------------- /profiler/Makefile: -------------------------------------------------------------------------------- 1 | build: graph builder mainx manager 2 | g++ -g -O0 -o profiler BasicBlock.o CFGraph.o CFGBuilder.o CFG.o 3 | 4 | graph: BasicBlock.h BasicBlock.cpp CFGraph.h CFGraph.cpp 5 | g++ -g -O0 -c BasicBlock.cpp 6 | g++ -g -O0 -c CFGraph.cpp 7 | 8 | builder: CFGBuilder.h CFGBuilder.cpp 9 | g++ -g -O0 -c CFGBuilder.cpp 10 | 11 | mainx: CFG.cpp 12 | g++ -g -O0 -c CFG.cpp 13 | 14 | 15 | manager: ProfilerMgr.cpp 16 | g++ -g -O0 -o profilermgr ProfilerMgr.cpp 17 | 18 | clean: 19 | rm -f *.o *.bak profiler profilermgr -------------------------------------------------------------------------------- /triggers/AfterUnlockTrigger.h: -------------------------------------------------------------------------------- 1 | 2 | #include "../Trigger.h" 3 | #include 4 | 5 | //#define exePath "/home/paul/mysql-5.1.44/sql/mysqld" 6 | 7 | DEFINE_TRIGGER( AfterUnlockTrigger ) 8 | { 9 | public: 10 | struct UnlockInfo { 11 | char file[256]; 12 | int line; 13 | }; 14 | 15 | public: 16 | AfterUnlockTrigger(); 17 | void Init(xmlNodePtr initData); 18 | bool Eval(const string* functionName, ...); 19 | 20 | private: 21 | int lineCount; 22 | string exePath; 23 | map lastUnlockInfo; 24 | }; 25 | -------------------------------------------------------------------------------- /cs-analyzer/README: -------------------------------------------------------------------------------- 1 | 2 | LIB-FI call site analyzer 3 | 4 | Purpose: take a binary program and determine potentially vulnerable places for fault injection 5 | 6 | Usage: 7 | make 8 | cs-analyzer |- [default retval] [default errno] > injection-scenarion.xml 9 | 10 | Where: 11 | is the path to the executable to be analyzed 12 | 13 | |- is the function to analyze. Use '-' in order to analyze a pre-defined set of functions 14 | 15 | [default retval] and [default errno] are the values used in the generated injection scenario for the injected return value and 16 | injected errno value. If unspecified, they default to -1, respectively EINVAL 17 | 18 | The call site analyzer outputs to stdout a XML injection scenario that can be fed directly to LIB-FI for execution 19 | The call site analyzer outputs to stderr human-readable information corresponding to the generated scenario 20 | 21 | See the online documentation at https://sourceforge.net/apps/trac/lfi/wiki/Documentation for details. 22 | -------------------------------------------------------------------------------- /triggers/PrintStackTrigger.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "PrintStackTrigger.h" 3 | #include 4 | #include 5 | #include 6 | 7 | PrintStackTrigger::PrintStackTrigger() 8 | { 9 | } 10 | 11 | void PrintStackTrigger::Init(xmlNodePtr initData) 12 | { 13 | xmlNodePtr nodeElement, textElement; 14 | 15 | nodeElement = initData->children; 16 | while (nodeElement) 17 | { 18 | if (XML_ELEMENT_NODE == nodeElement->type && 19 | !xmlStrcmp(nodeElement->name, (const xmlChar*)"file")) 20 | { 21 | textElement = nodeElement->children; 22 | if (XML_TEXT_NODE == textElement->type) 23 | { 24 | remove((char*)textElement->content); 25 | file = fopen((char*)textElement->content,"a"); 26 | } 27 | } 28 | nodeElement = nodeElement->next; 29 | } 30 | } 31 | 32 | bool PrintStackTrigger::Eval(const string*, ...) 33 | { 34 | void *array[10]; 35 | size_t size; 36 | 37 | size = backtrace(array, 10); 38 | backtrace_symbols_fd(array, size, fileno(file)); 39 | fclose(file); 40 | return true; 41 | } 42 | -------------------------------------------------------------------------------- /profiler/README: -------------------------------------------------------------------------------- 1 | 2 | LIB-FI Library Profiler 3 | 4 | Purpose: take a binary library and extract information regarding exported 5 | functions and possible retrun values for each function. 6 | 7 | Usage: 8 | make 9 | ./dasmfn.sh 10 | ./listfn.sh 11 | 12 | Special case: profiling linux system calls 13 | make 14 | ./dasmfn.sh 15 | ./syscalls.sh 16 | 17 | Note: 18 | The debug kernel can be downloaded in /boot with apt-get install linux-image-debug-... (apt-cache search kernel debug image) 19 | or a similar command depending on distribution 20 | The syscall_table.S is usually at /usr/src/linux-source/arch/i386/kernel/syscall_table.S 21 | 22 | 23 | 1. Disassembly for each function is created in the disassembly subfolder via objdump 24 | 2. The profiler manager (build using make above) is run on each disassembly file to generate 25 | the actual results in the profiles folder. It invokes the profiler for the actual file and then 26 | for all the relevant dependencies 27 | -------------------------------------------------------------------------------- /profiler/listfn_eval.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #profiles a entire library or a specific function 4 | #disassembly must already be available on disk in the ./disassembly folder 5 | # for the target function and all its dependencies. output is a C file 6 | #use dasmfn.sh to generate the disassembly for an entire library 7 | 8 | 9 | set +x 10 | 11 | if [[ x$1 == x ]] 12 | then 13 | echo "Usage: ./listfn_eval.sh libname.so" 14 | exit 1 15 | fi 16 | 17 | if [[ x$2 == x ]]; then 18 | TARGETFN="FUNC" 19 | else 20 | TARGETFN=" $2$" 21 | fi 22 | 23 | readelf -s --wide $1 | grep FUNC > exports.tmp 24 | FUNCTIONS=`cat exports.tmp |grep "$TARGETFN" | grep -v UND | awk '{ print $8 }'| grep -v '^_'` 25 | 26 | echo "#include \"std_errors_head.h\"" 27 | for function in $FUNCTIONS 28 | do 29 | 30 | echo "int $function""_errors[] = {" 31 | 32 | ./profilermgr "$function" 1>/dev/null 2>&1 33 | 34 | 35 | sort -u "profiles/$function" | awk '{ print $5 "," }' 36 | echo "12345" 37 | echo "};" 38 | 39 | 40 | rm -r -f profiles 41 | mkdir profiles 42 | 43 | done 44 | 45 | echo "struct sys_errors errors_profiler[] = {" 46 | 47 | for function in $FUNCTIONS 48 | do 49 | echo "{\"$function\", $function""_errors }," 50 | done 51 | 52 | echo "};" -------------------------------------------------------------------------------- /profiler/BasicBlock.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "BasicBlock.h" 28 | 29 | BasicBlock::BasicBlock(void) 30 | { 31 | } 32 | 33 | BasicBlock::~BasicBlock(void) 34 | { 35 | } 36 | -------------------------------------------------------------------------------- /cs-analyzer/BasicBlock.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "BasicBlock.h" 28 | 29 | BasicBlock::BasicBlock(void) 30 | { 31 | } 32 | 33 | BasicBlock::~BasicBlock(void) 34 | { 35 | } 36 | -------------------------------------------------------------------------------- /triggers/ReadInspector.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "../Trigger.h" 28 | 29 | DEFINE_TRIGGER( ReadInspector ) 30 | { 31 | public: 32 | ReadInspector(); 33 | bool Eval(const string* functionName, ...); 34 | }; 35 | -------------------------------------------------------------------------------- /triggers/SingleTrigger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "../Trigger.h" 28 | 29 | DEFINE_TRIGGER( SingleTrigger ) 30 | { 31 | public: 32 | SingleTrigger(); 33 | bool Eval(const string* functionName, ...); 34 | private: 35 | unsigned int triggered; 36 | }; 37 | -------------------------------------------------------------------------------- /profiler/CFGBuilder.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #pragma once 28 | 29 | #include "CFGraph.h" 30 | 31 | 32 | class CCFGBuilder 33 | { 34 | public: 35 | CCFGBuilder(void); 36 | ~CCFGBuilder(void); 37 | 38 | CFGraph* Parse(const char* text); 39 | }; 40 | -------------------------------------------------------------------------------- /cs-analyzer/CFGBuilder.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "CFGraph.h" 28 | 29 | 30 | class CCFGBuilder 31 | { 32 | public: 33 | CCFGBuilder(void); 34 | ~CCFGBuilder(void); 35 | 36 | CFGraph* Parse(const char* text, const char* targetfn, int callcount, long int* callAddress); 37 | }; 38 | -------------------------------------------------------------------------------- /triggers/RandomTrigger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "../Trigger.h" 28 | 29 | DEFINE_TRIGGER( RandomTrigger ) 30 | { 31 | public: 32 | RandomTrigger(); 33 | void Init(xmlNodePtr initData); 34 | bool Eval(const string* functionName, ...); 35 | private: 36 | int probability; 37 | static unsigned int seed; 38 | }; 39 | -------------------------------------------------------------------------------- /triggers/CallCountTrigger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "../Trigger.h" 28 | 29 | DEFINE_TRIGGER( CallCountTrigger ) 30 | { 31 | public: 32 | CallCountTrigger(); 33 | void Init(xmlNodePtr initData); 34 | bool Eval(const string* functionName, ...); 35 | private: 36 | int callCount; 37 | vector callCounts; 38 | }; 39 | -------------------------------------------------------------------------------- /profiler/BasicBlock.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #pragma once 28 | 29 | 30 | #include 31 | #include 32 | 33 | using namespace std; 34 | 35 | class BasicBlock 36 | { 37 | public: 38 | BasicBlock(void); 39 | ~BasicBlock(void); 40 | 41 | list m_listInstructions; 42 | }; 43 | -------------------------------------------------------------------------------- /scenarios/pthread_read.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 8 | 9 | 10 | 11 | 12 | 13 | 14 | /usr/lib/libsqlite3.so.0.8.6 15 | sqlite3.c 16 | 24805 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /cs-analyzer/BasicBlock.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #pragma once 28 | 29 | 30 | #include 31 | #include 32 | 33 | using namespace std; 34 | 35 | class BasicBlock 36 | { 37 | public: 38 | BasicBlock(void); 39 | ~BasicBlock(void); 40 | 41 | list m_listInstructions; 42 | }; 43 | -------------------------------------------------------------------------------- /profiler/CFGBuilderSPARC.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #pragma once 28 | 29 | #include "CFGraph.h" 30 | 31 | 32 | class CCFGBuilderSPARC 33 | { 34 | public: 35 | CCFGBuilderSPARC(void); 36 | ~CCFGBuilderSPARC(void); 37 | 38 | CFGraph* Parse(const char* text); 39 | int IsBranchInstruction(char*); 40 | }; 41 | -------------------------------------------------------------------------------- /triggers/SingleTrigger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "SingleTrigger.h" 28 | #include 29 | 30 | SingleTrigger::SingleTrigger() 31 | { 32 | triggered = 0; 33 | } 34 | 35 | bool SingleTrigger::Eval(const string*, ...) 36 | { 37 | if (triggered) { 38 | return false; 39 | } 40 | 41 | triggered = 1; 42 | return true; 43 | } 44 | -------------------------------------------------------------------------------- /triggers/TimerTrigger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "../Trigger.h" 28 | 29 | class StartTime 30 | { 31 | public: 32 | StartTime(); 33 | unsigned int st_time; 34 | }; 35 | 36 | DEFINE_TRIGGER( TimerTrigger ) 37 | { 38 | public: 39 | TimerTrigger(); 40 | void Init(xmlNodePtr initData); 41 | bool Eval(const string* functionName, ...); 42 | private: 43 | int wait; 44 | int go; 45 | static StartTime start; 46 | }; 47 | -------------------------------------------------------------------------------- /triggers/SemTrigger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "../Trigger.h" 28 | #include 29 | 30 | DEFINE_TRIGGER( SemTrigger ) 31 | { 32 | public: 33 | SemTrigger(); 34 | bool Eval(const string* functionName, ...); 35 | private: 36 | long get_lockCount(); 37 | void set_lockCount(long); 38 | #ifdef __APPLE__ 39 | static pthread_key_t lockCount_key; 40 | #else 41 | static __thread long lockCount; 42 | #endif 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /profiler/TODO: -------------------------------------------------------------------------------- 1 | 2 | LIB-FI Library Profiler 3 | 4 | -include errno information 5 | 6 | -mark a location as `dirty` if appropriate to avoid false positives 7 | i.e. 8 | mov eax, 0x1234 9 | or eax, ebx 10 | ret 11 | 12 | current behavior - or eax, ebx is ignored, 0x1234 is reported as a possible return value 13 | 14 | -add some `simulation` to the mix. 15 | e.g. 16 | mov eax, 0x0 17 | add eax, 0x1 18 | ret 19 | 20 | Reported possible return values: 0x0 (or none afer implementing the above `dirty` mark). 21 | Problem: the return value is build by using two instuctions 22 | Solution: keep a stack of operations to apply to the constant value found (we're going bottom-up) 23 | 24 | -break some complex instructions in more simple instruction 25 | e.g. (idea from ecb_crypt) 26 | mov edx, 0x2 27 | ... 28 | cmp something, something_else 29 | sete dl 30 | .... 31 | mov eax, edx 32 | ret 33 | 34 | The possible return values in this case are 1 and 2. (sete = set if equal) 35 | This could be handled by 36 | 1. transforming the sete in jne+mov, in the end generating a new BB and 37 | 2. implementing the `simulation` (above) 38 | 39 | More instructions can be handled this way (e.g. cmovxx family) 40 | 41 | 42 | 43 | -save information in the format expected by the injector [done] 44 | 45 | -generate more accurate disassembly. Currently the function boundaries can be inaccurate. 46 | The entry point is always known but the last instruction is not easy to determine. The 47 | current approach: disassemble until the next exported symbol is encountered [done] -------------------------------------------------------------------------------- /cs-analyzer/CFGraph.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include "BasicBlock.h" 29 | 30 | using namespace std; 31 | 32 | #define EXIT_NODE 499 33 | 34 | class CFGraph 35 | { 36 | public: 37 | struct AdjListElement { 38 | int m_bbIndex; 39 | AdjListElement* m_pNext; 40 | }; 41 | public: 42 | CFGraph(void); 43 | ~CFGraph(void); 44 | 45 | void Add(int what, int where); 46 | AdjListElement* GetHead(int where); 47 | vector m_vBasicBlocks; 48 | private: 49 | AdjListElement m_vectAdjLists[EXIT_NODE+1]; 50 | }; 51 | -------------------------------------------------------------------------------- /profiler/CFGraph.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #pragma once 28 | 29 | #include 30 | #include "BasicBlock.h" 31 | 32 | using namespace std; 33 | 34 | #define EXIT_NODE 499 35 | 36 | class CFGraph 37 | { 38 | public: 39 | struct AdjListElement { 40 | int m_bbIndex; 41 | AdjListElement* m_pNext; 42 | }; 43 | public: 44 | CFGraph(void); 45 | ~CFGraph(void); 46 | 47 | void Add(int what, int where); 48 | AdjListElement* GetHead(int where); 49 | vector m_vBasicBlocks; 50 | private: 51 | AdjListElement m_vectAdjLists[EXIT_NODE+1]; 52 | }; 53 | -------------------------------------------------------------------------------- /triggers/ReadInspector.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "ReadInspector.h" 28 | #include 29 | #include 30 | 31 | ReadInspector::ReadInspector() 32 | { 33 | } 34 | 35 | bool ReadInspector::Eval(const string* functionName, ...) 36 | { 37 | /* only intended to be used when intercepting the read function */ 38 | va_list ap; 39 | int fd; 40 | size_t size; 41 | 42 | va_start(ap, functionName); 43 | fd = va_arg(ap, int); 44 | va_arg(ap, void*); 45 | size = va_arg(ap, size_t); 46 | va_end(ap); 47 | 48 | /* inject only when reading 1024 bytes from stdin */ 49 | return (fd == 0 && size == 1024); 50 | } 51 | -------------------------------------------------------------------------------- /profiler/CFGraph.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "CFGraph.h" 28 | #include 29 | 30 | CFGraph::CFGraph(void) 31 | { 32 | memset(m_vectAdjLists, 0, sizeof(m_vectAdjLists)); 33 | } 34 | 35 | CFGraph::~CFGraph(void) 36 | { 37 | } 38 | 39 | void CFGraph::Add(int what, int where) 40 | { 41 | CFGraph::AdjListElement* newel = new CFGraph::AdjListElement(); 42 | 43 | newel->m_bbIndex = what; 44 | newel->m_pNext = m_vectAdjLists[where].m_pNext; 45 | m_vectAdjLists[where].m_pNext = newel; 46 | } 47 | 48 | CFGraph::AdjListElement* CFGraph::GetHead(int where) 49 | { 50 | return m_vectAdjLists[where].m_pNext; 51 | } 52 | -------------------------------------------------------------------------------- /cs-analyzer/CFGraph.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "CFGraph.h" 28 | #include 29 | #include 30 | 31 | CFGraph::CFGraph(void) 32 | { 33 | memset(m_vectAdjLists, 0, sizeof(m_vectAdjLists)); 34 | } 35 | 36 | CFGraph::~CFGraph(void) 37 | { 38 | } 39 | 40 | void CFGraph::Add(int what, int where) 41 | { 42 | CFGraph::AdjListElement* newel = new CFGraph::AdjListElement(); 43 | 44 | newel->m_bbIndex = what; 45 | newel->m_pNext = m_vectAdjLists[where].m_pNext; 46 | m_vectAdjLists[where].m_pNext = newel; 47 | } 48 | 49 | CFGraph::AdjListElement* CFGraph::GetHead(int where) 50 | { 51 | return m_vectAdjLists[where].m_pNext; 52 | } 53 | -------------------------------------------------------------------------------- /triggers/StateTrigger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "../Trigger.h" 28 | 29 | enum VarType { VAR_INT, VAR_STRING }; 30 | enum VarLocation { VAR_GLOBAL, VAR_LOCAL }; 31 | 32 | struct Variable 33 | { 34 | VarType type; 35 | VarLocation location; 36 | void* offset; 37 | int frame; 38 | union { 39 | int targetInt; 40 | char targetString[128]; 41 | } targetValue; 42 | 43 | bool resolved; /* unused */ 44 | string module; /* unused */ 45 | string fileName; /* unused */ 46 | int lineNumber; /* unused */ 47 | }; 48 | 49 | DEFINE_TRIGGER( StateTrigger ) 50 | { 51 | public: 52 | StateTrigger() { }; 53 | void Init(xmlNodePtr initData); 54 | bool Eval(const string* functionName, ...); 55 | private: 56 | Variable var; 57 | }; 58 | -------------------------------------------------------------------------------- /lfi.files: -------------------------------------------------------------------------------- 1 | cs-analyzer/BasicBlock.cpp 2 | cs-analyzer/BasicBlock.h 3 | cs-analyzer/CFG.cpp 4 | cs-analyzer/CFGBuilder.cpp 5 | cs-analyzer/CFGBuilder.h 6 | cs-analyzer/CFGraph.cpp 7 | cs-analyzer/CFGraph.h 8 | cs-analyzer/README 9 | profiler/evaluator/errordiff.cpp 10 | profiler/libxml_profiler/docparser.php 11 | profiler/libxml_profiler/libxml-all.html 12 | profiler/BasicBlock.cpp 13 | profiler/BasicBlock.h 14 | profiler/CFG.cpp 15 | profiler/CFGBuilder.cpp 16 | profiler/CFGBuilder.h 17 | profiler/CFGBuilderSPARC.cpp 18 | profiler/CFGBuilderSPARC.h 19 | profiler/CFGraph.cpp 20 | profiler/CFGraph.h 21 | profiler/dasmfn.sh 22 | profiler/listfn.sh 23 | profiler/listfn_eval.sh 24 | profiler/profiler_errors.h 25 | profiler/ProfilerMgr.cpp 26 | profiler/README 27 | profiler/SEDetector.cpp 28 | profiler/syscall_errors_head.h 29 | profiler/syscalls.sh 30 | profiler/syscalls_man.sh 31 | profiler/TODO 32 | profiler/toxml.awk 33 | scenarios/LibCprofile_noexec.xml 34 | scenarios/pbft.xml 35 | scenarios/pthread_read.xml 36 | scenarios/sampleplan.xml 37 | triggers/AfterUnlockTrigger.cpp 38 | triggers/AfterUnlockTrigger.h 39 | triggers/CallCountTrigger.cpp 40 | triggers/CallCountTrigger.h 41 | triggers/NetInspector.cpp 42 | triggers/NetInspector.h 43 | triggers/NetInspectorServer.c 44 | triggers/PrintStackTrigger.cpp 45 | triggers/PrintStackTrigger.h 46 | triggers/RandomTrigger.cpp 47 | triggers/RandomTrigger.h 48 | triggers/ReadInspector.cpp 49 | triggers/ReadInspector.h 50 | triggers/SemTrigger.cpp 51 | triggers/SemTrigger.h 52 | triggers/SingleTrigger.cpp 53 | triggers/SingleTrigger.h 54 | triggers/StateTrigger.cpp 55 | triggers/StateTrigger.h 56 | triggers/TimerTrigger.cpp 57 | triggers/TimerTrigger.h 58 | inter.c 59 | inter.h 60 | LibCprofile.xml 61 | libfi.cpp 62 | LICENSE 63 | README 64 | Trigger.cpp 65 | Trigger.h -------------------------------------------------------------------------------- /scenarios/macos.simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 1 12 | 13 | 14 | 15 | 16 | 20 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Trigger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include 29 | #include "Trigger.h" 30 | 31 | Class :: FactoryMethodMap* Class :: fmMap ; 32 | 33 | Trigger* Class :: newInstance() 34 | { 35 | /* 36 | if intercepting functions called by static initializers/global 37 | variables constructors, the class may not be yet registered 38 | */ 39 | if (NULL == fmMap) 40 | return NULL; 41 | FactoryMethodMap::iterator it = fmMap->find( name ) ; 42 | if( it != fmMap->end() ) 43 | return( it->second() ) ; 44 | else 45 | return( NULL ) ; 46 | } 47 | 48 | void Class :: Register( std::string s, FactoryMethod m ) 49 | { 50 | if( fmMap == NULL ) 51 | fmMap = new std::map< std::string, FactoryMethod > ; 52 | fmMap->insert( std::pair< std::string, FactoryMethod >( s, m ) ) ; 53 | } 54 | -------------------------------------------------------------------------------- /profiler/syscalls_man.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #get syscall errors from man pages (obsoleted by syscalls.sh) 4 | 5 | set +x 6 | 7 | if [[ x$1 == x ]] 8 | then 9 | echo "Usage: syscalls_errors /path/to/kernel/unistd.h" 10 | exit 1 11 | fi 12 | 13 | 14 | cat > test.xsl << EOF 15 | 17 | 18 | 19 | , 20 | 21 | 22 | 23 | EOF 24 | 25 | 26 | echo "#include \"syscall_errors_head.h\"" 27 | 28 | FUNCTIONS=`cat $1 | grep __NR_ | awk '{ print $2 }'| sed 's/__NR_//'` 29 | 30 | for function in $FUNCTIONS 31 | do 32 | CLEANFUNC=`echo $function|sed 's/64//'|sed 's/32//'` 33 | FILE=`find /usr/share/man/ -name "$CLEANFUNC.2.gz" | sort |head -n1` 34 | 35 | if [[ x$FILE == "x" ]] 36 | then 37 | #echo "unable to find man page for $function" 38 | continue 39 | fi 40 | 41 | CALLID=`cat $1 | grep "__NR_"$function"\\>" | awk '{ print $3 }'` 42 | if [[ $CALLID =~ ^[0-9]{1,3}$ ]]; then 43 | 44 | cp $FILE manpage.gz 45 | gunzip manpage.gz 46 | 47 | if doclifter manpage 2> /dev/null 48 | then 49 | echo "int "$function"_errors[] = { " 50 | xsltproc test.xsl manpage.xml 2> /dev/null | grep -v version 2> /dev/null 51 | echo "0 };" 52 | echo 53 | fi 54 | 55 | rm -f manpage.xml 56 | rm -f manpage 57 | 58 | fi 59 | done 60 | 61 | rm -f test.xsl 62 | 63 | echo "struct sys_errors errors_man[] = {" 64 | 65 | for function in $FUNCTIONS 66 | do 67 | CLEANFUNC=`echo $function|sed 's/64//'|sed 's/32//'` 68 | 69 | FILE=`find /usr/share/man/ -name "$CLEANFUNC.*.gz" | head -n1` 70 | 71 | if [[ x$FILE == "x" ]] 72 | then 73 | continue 74 | fi 75 | 76 | CALLID=`cat $1 | grep "__NR_"$function"\\>" | awk '{ print $3 }'` 77 | if [[ $CALLID =~ ^[0-9]{1,3}$ ]]; then 78 | echo "{"$CALLID, $function"_errors}," 79 | fi 80 | done 81 | 82 | echo "};" 83 | -------------------------------------------------------------------------------- /triggers/NetInspector.cpp: -------------------------------------------------------------------------------- 1 | #include "NetInspector.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define serverHostname "myServerName" 8 | #define serverPort 11111 9 | 10 | NetInspector::NetInspector() 11 | { 12 | int portno, n; 13 | struct sockaddr_in serv_addr; 14 | struct hostent *server; 15 | 16 | portno = serverPort; 17 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 18 | if (sockfd < 0) 19 | exit(-1); 20 | server = gethostbyname(serverHostname); 21 | if (server == NULL) { 22 | fprintf(stderr,"ERROR, no such host\n"); 23 | exit(0); 24 | } 25 | bzero((char *) &serv_addr, sizeof(serv_addr)); 26 | serv_addr.sin_family = AF_INET; 27 | bcopy((char *)server->h_addr, 28 | (char *)&serv_addr.sin_addr.s_addr, 29 | server->h_length); 30 | serv_addr.sin_port = htons(portno); 31 | if (connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0) 32 | exit(-2); 33 | } 34 | 35 | bool NetInspector::Eval(const string* functionName, ...) 36 | { 37 | /* only intended to be used when intercepting the read function */ 38 | va_list ap; 39 | int fd; 40 | size_t size; 41 | 42 | int socket_i; 43 | char *message_i; 44 | size_t length_i; 45 | int flags_i; 46 | struct sockaddr *dest_addr_i; 47 | socklen_t dest_len_i; 48 | 49 | va_start(ap, functionName); 50 | socket_i = va_arg(ap, int); 51 | message_i = va_arg(ap, char*); 52 | length_i = va_arg(ap, size_t); 53 | flags_i = va_arg(ap, int); 54 | dest_addr_i = va_arg(ap, struct sockaddr *); 55 | dest_len_i = va_arg(ap, socklen_t); 56 | va_end(ap); 57 | 58 | bzero(buffer,256); 59 | sprintf(buffer, "%s %d",functionName->c_str(), (int)length_i); 60 | int n = write(sockfd,buffer,strlen(buffer)); 61 | if (n < 0) 62 | exit(-3); 63 | bzero(receiveBuf,10); 64 | n = read(sockfd,receiveBuf,10); 65 | if (n < 0) 66 | exit(4); 67 | if ( receiveBuf[0] == '1' ) 68 | return 1; 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /profiler/listfn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Created by Paul Marinescu and George Candea 4 | # Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 5 | # 6 | # This file is part of LFI (Library-level Fault Injector). 7 | # 8 | # LFI is free software: you can redistribute it and/or modify it 9 | # under the terms of the GNU General Public License as published by the 10 | # Free Software Foundation, either version 3 of the License, or (at 11 | # your option) any later version. 12 | # 13 | # LFI is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public 19 | # License along with LFI. If not, see http://www.gnu.org/licenses/. 20 | # 21 | # EPFL 22 | # Dependable Systems Lab (DSLAB) 23 | # Room 330, Station 14 24 | # 1015 Lausanne 25 | # Switzerland 26 | 27 | 28 | 29 | #profiles a entire library or a specific function 30 | #disassembly must already be available on disk in the ./disassembly folder 31 | #for the target function and all its dependencies. Output is an XML description 32 | #use dasmfn.sh to generate the disassembly for an entire library 33 | 34 | 35 | set +x 36 | 37 | if [[ x$1 == x ]] 38 | then 39 | echo "Usage: listfn libname.so " 40 | exit 1 41 | fi 42 | 43 | if [[ x$2 == x ]]; then 44 | TARGETFN="FUNC" 45 | else 46 | TARGETFN=" $2$" 47 | fi 48 | 49 | readelf -s --wide $1 | grep FUNC > exports.tmp 50 | FUNCTIONS=`cat exports.tmp |grep "$TARGETFN" | grep -v UND | awk '{ print $8 }'| grep -v '^_'` 51 | 52 | echo "" 53 | for function in $FUNCTIONS 54 | do 55 | 56 | ./profilermgr "$function" "$1" # 1>/dev/null 2>&1 57 | 58 | echo "" 59 | sort -u "profiles/$function" | awk '{print $5;}' | awk -f toxml.awk 60 | echo "" 61 | 62 | rm -r -f profiles 63 | mkdir profiles 64 | 65 | done 66 | echo "" 67 | rm exports.tmp -------------------------------------------------------------------------------- /profiler/syscalls.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | # Created by Paul Marinescu and George Candea 5 | # Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 6 | # 7 | # This file is part of LFI (Library-level Fault Injector). 8 | # 9 | # LFI is free software: you can redistribute it and/or modify it 10 | # under the terms of the GNU General Public License as published by the 11 | # Free Software Foundation, either version 3 of the License, or (at 12 | # your option) any later version. 13 | # 14 | # LFI is distributed in the hope that it will be useful, but 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | # General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public 20 | # License along with LFI. If not, see http://www.gnu.org/licenses/. 21 | # 22 | # EPFL 23 | # Dependable Systems Lab (DSLAB) 24 | # Room 330, Station 14 25 | # 1015 Lausanne 26 | # Switzerland 27 | 28 | 29 | #get syscall errors from kernel disassembly 30 | #disassembly must already be available on disk in the ./disassembly folder 31 | #for all syscalls and their dependecies 32 | #use dasmfn.sh to generate the disassembly for the entire kernel 33 | 34 | set +x 35 | 36 | if [[ x$1 == x ]] 37 | then 38 | echo "Usage: syscalls.sh " 39 | exit 1 40 | fi 41 | 42 | FUNCTIONS=`cat $1 |grep long | awk '{ print $2 }'` 43 | 44 | echo "#include \"syscall_errors_head.h\"" 45 | for function in $FUNCTIONS 46 | do 47 | 48 | echo "int $function""_errors[] = {" 49 | 50 | ./profilermgr "$function" 1>/dev/null 2>&1 51 | 52 | 53 | sort -u "profiles/$function" | awk '{if (0 != $5) { print $5 "," };}' 54 | echo "0" 55 | echo "};" 56 | 57 | 58 | rm -r -f profiles 59 | mkdir profiles 60 | 61 | done 62 | 63 | echo "struct sys_errors profiler_errors[] = {" 64 | SYSCALLNO=0 65 | for function in $FUNCTIONS 66 | do 67 | echo "{$SYSCALLNO, $function""_errors }," 68 | SYSCALLNO=`expr $SYSCALLNO + 1` 69 | done 70 | 71 | echo "};" -------------------------------------------------------------------------------- /triggers/TimerTrigger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "TimerTrigger.h" 28 | #include 29 | 30 | StartTime TimerTrigger::start; 31 | 32 | StartTime::StartTime() 33 | { 34 | st_time = (unsigned)time(NULL); 35 | } 36 | 37 | TimerTrigger::TimerTrigger() 38 | : wait(0) 39 | , go(0) 40 | { 41 | } 42 | 43 | void TimerTrigger::Init(xmlNodePtr initData) 44 | { 45 | xmlNodePtr nodeElement, textElement; 46 | 47 | nodeElement = initData->children; 48 | while (nodeElement) 49 | { 50 | if (XML_ELEMENT_NODE == nodeElement->type && 51 | !xmlStrcmp(nodeElement->name, (const xmlChar*)"wait")) 52 | { 53 | textElement = nodeElement->children; 54 | if (XML_TEXT_NODE == textElement->type) 55 | wait = atoi((char*)textElement->content); 56 | } 57 | nodeElement = nodeElement->next; 58 | } 59 | } 60 | 61 | bool TimerTrigger::Eval(const string*, ...) 62 | { 63 | if (go) 64 | return true; 65 | 66 | if ((unsigned)time(NULL) - start.st_time >= wait) { 67 | go = 1; 68 | } 69 | 70 | if (go) 71 | return true; 72 | 73 | return false; 74 | } 75 | -------------------------------------------------------------------------------- /triggers/CallCountTrigger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "CallCountTrigger.h" 28 | #include 29 | 30 | CallCountTrigger::CallCountTrigger() 31 | : callCount(0) 32 | { 33 | } 34 | 35 | void CallCountTrigger::Init(xmlNodePtr initData) 36 | { 37 | xmlNodePtr nodeElement, textElement; 38 | 39 | nodeElement = initData->children; 40 | while (nodeElement) 41 | { 42 | if (XML_ELEMENT_NODE == nodeElement->type && 43 | !xmlStrcmp(nodeElement->name, (const xmlChar*)"callcount")) 44 | { 45 | textElement = nodeElement->children; 46 | if (XML_TEXT_NODE == textElement->type) 47 | callCounts.push_back(atoi((char*)textElement->content)); 48 | } 49 | nodeElement = nodeElement->next; 50 | } 51 | } 52 | 53 | bool CallCountTrigger::Eval(const string*, ...) 54 | { 55 | ++callCount; 56 | // binary search? not useful for a reasonably small number of call counts 57 | for (vector::iterator it = callCounts.begin(), itend = callCounts.end(); it != itend; ++it) 58 | { 59 | if (callCount == *it) { 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | -------------------------------------------------------------------------------- /triggers/RandomTrigger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "RandomTrigger.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | unsigned int RandomTrigger::seed = 0; 34 | 35 | RandomTrigger::RandomTrigger() 36 | : probability(0) 37 | { 38 | } 39 | 40 | void RandomTrigger::Init(xmlNodePtr initData) 41 | { 42 | xmlNodePtr nodeElement, textElement; 43 | time_t t; 44 | const char* seedFile = "rndtrigger.seed"; 45 | 46 | nodeElement = initData->children; 47 | while (nodeElement) 48 | { 49 | if (XML_ELEMENT_NODE == nodeElement->type && 50 | !xmlStrcmp(nodeElement->name, (const xmlChar*)"percent")) 51 | { 52 | textElement = nodeElement->children; 53 | if (XML_TEXT_NODE == textElement->type) 54 | probability = atoi((char*)textElement->content); 55 | } 56 | nodeElement = nodeElement->next; 57 | } 58 | 59 | if (!seed) 60 | { 61 | seed = (unsigned)time(&t); 62 | srand(seed); 63 | } 64 | } 65 | 66 | bool RandomTrigger::Eval(const string*, ...) 67 | { 68 | if (rand() % 100 < probability) 69 | return true; 70 | return false; 71 | } 72 | -------------------------------------------------------------------------------- /scenarios/pbft.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 6 | 7 | 8 | 9 | 10 | 11 | 2 12 | 13 | 14 | 15 | 16 | 17 | 40 18 | 19 | 20 | 21 | 22 | 23 | 0 24 | 25 | 26 | 27 | 28 | 29 | 10 30 | 31 | 32 | 33 | 37 | 38 | 39 | 10 40 | 41 | 42 | 43 | 44 | 45 | 10 46 | 47 | 48 | 49 | 52 | 53 | 54 | 55 | /usr/local/bin/ls 56 | ls.c 57 | 2405 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | /usr/local/bin/id 66 | id.c 67 | 235 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /triggers/SemTrigger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "SemTrigger.h" 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef __APPLE__ 33 | pthread_key_t SemTrigger::lockCount_key = 0; 34 | #else 35 | __thread long SemTrigger::lockCount = 0; 36 | #endif 37 | 38 | SemTrigger::SemTrigger() 39 | { 40 | #ifdef __APPLE__ 41 | if (!lockCount_key) 42 | pthread_key_create(&lockCount_key, NULL); 43 | #endif 44 | } 45 | 46 | long SemTrigger::get_lockCount() 47 | { 48 | #ifdef __APPLE__ 49 | return (long)pthread_getspecific(lockCount_key); 50 | #else 51 | return lockCount; 52 | #endif 53 | } 54 | 55 | void SemTrigger::set_lockCount(long v) 56 | { 57 | #ifdef __APPLE__ 58 | pthread_setspecific(lockCount_key, (void*)v); 59 | #else 60 | lockCount = v; 61 | #endif 62 | } 63 | 64 | 65 | bool SemTrigger::Eval(const string* functionName, ...) 66 | { 67 | long l; 68 | if (*functionName == "pthread_mutex_lock") 69 | { 70 | set_lockCount(get_lockCount()+1); 71 | } 72 | else if (*functionName == "pthread_mutex_unlock") 73 | { 74 | if ((l = get_lockCount())) // sanity check 75 | set_lockCount(l - 1); 76 | } 77 | else 78 | { 79 | if (get_lockCount() > 0) 80 | return true; 81 | } 82 | return false; 83 | } 84 | -------------------------------------------------------------------------------- /profiler/dasmfn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | # Created by Paul Marinescu and George Candea 5 | # Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 6 | # 7 | # This file is part of LFI (Library-level Fault Injector). 8 | # 9 | # LFI is free software: you can redistribute it and/or modify it 10 | # under the terms of the GNU General Public License as published by the 11 | # Free Software Foundation, either version 3 of the License, or (at 12 | # your option) any later version. 13 | # 14 | # LFI is distributed in the hope that it will be useful, but 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | # General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public 20 | # License along with LFI. If not, see http://www.gnu.org/licenses/. 21 | # 22 | # EPFL 23 | # Dependable Systems Lab (DSLAB) 24 | # Room 330, Station 14 25 | # 1015 Lausanne 26 | # Switzerland 27 | 28 | #disassembles the function specified as the second argument to disassembly/function_name 29 | #if no function is specified, disassembles all the FUNC symbols found in the library 30 | 31 | set +x 32 | 33 | if [[ x$1 == x ]] 34 | then 35 | echo "Usage: dasmfn libname.so " 36 | exit 1 37 | fi 38 | 39 | if [[ x$2 == x ]]; then 40 | TARGETFN="FUNC" 41 | else 42 | TARGETFN=" $2$" 43 | fi 44 | 45 | readelf -s --wide $1 | grep FUNC > exports.tmp 46 | cat exports.tmp | awk '{ print "0x" $2 }'| sort > exports_sort.tmp 47 | FUNCTIONS=`cat exports.tmp |grep "$TARGETFN" | grep -v UND | awk '{ print $8 }'` # | sed 's/@/_/g'` 48 | 49 | for function in $FUNCTIONS 50 | do 51 | 52 | GARG=" $function$" 53 | 54 | STARTOFFSET=0x`cat exports.tmp | grep -v '^_' | grep "$GARG" | awk '{ print $2 }' | head -1` 55 | 56 | CHCNT=`echo $STARTOFFSET|wc -m` 57 | 58 | 59 | if [ "$CHCNT" = "11" ]; then 60 | cleanfunction=`echo $function|sed 's/@@.*//g'` 61 | 62 | STOPOFFSET=`cat exports_sort.tmp | awk ' { if ( ( $0 ) > ("'$STARTOFFSET'") ) { print $0 ; exit } }'` 63 | objdump -d -M intel --start-address=$STARTOFFSET --stop-address=$STOPOFFSET $1 | egrep -v "(^Disassembly|^/|^$)" > "disassembly/$cleanfunction" 64 | 65 | echo $function $STARTOFFSET $STOPOFFSET 66 | fi 67 | 68 | done 69 | 70 | rm exports_sort.tmp 71 | rm exports.tmp 72 | -------------------------------------------------------------------------------- /scenarios/sampleplan.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 20 13 | 14 | 15 | 16 | 17 | 18 | 40 19 | 20 | 21 | 22 | 26 | 27 | 28 | 11 29 | 30 | 31 | 32 | 33 | 34 | 11 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | /usr/local/bin/ls 45 | ls.c 46 | 2405 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | /usr/local/bin/id 55 | id.c 56 | 235 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /profiler/evaluator/errordiff.cpp: -------------------------------------------------------------------------------- 1 | // errordiff.cpp : Defines the entry point for the console application. 2 | // 3 | 4 | #include 5 | #include 6 | #include "man_errors.h" 7 | #include "profiler_errors.h" 8 | 9 | #ifdef SYSCALLS 10 | #define MAGIC_END 0 11 | #else 12 | #define MAGIC_END 12345 13 | #endif 14 | using namespace std; 15 | 16 | int main(int argc, char* argv[]) 17 | { 18 | int i, j, k, cnt, sum, fnfound; 19 | int found, missing, fp, accuracy; 20 | set man_errors, profiler_errors; 21 | set::iterator it; 22 | 23 | cnt = sum = 0; 24 | 25 | for (i = 0; i < sizeof(errors_man)/sizeof(errors_man[0]); ++i) 26 | { 27 | man_errors.clear(); 28 | profiler_errors.clear(); 29 | j = 0; 30 | fnfound = 0; 31 | while (errors_man[i].error_values[j] != MAGIC_END) 32 | { 33 | man_errors.insert(errors_man[i].error_values[j]); 34 | ++j; 35 | } 36 | for (j = 0; j < sizeof(errors_profiler)/sizeof(errors_profiler[0]); ++j) 37 | { 38 | #ifdef SYSCALLS 39 | if (errors_man[i].sysno == errors_profiler[j].sysno) 40 | #else 41 | if (0 == strcmp(errors_man[i].name, errors_profiler[j].name)) 42 | #endif 43 | { 44 | fnfound = 1; 45 | k = 0; 46 | while (errors_profiler[j].error_values[k] != MAGIC_END) 47 | { 48 | #ifdef SYSCALLS 49 | if (errors_profiler[j].error_values[k] < -500) 50 | { 51 | profiler_errors.insert(EINTR); 52 | } 53 | else 54 | { 55 | profiler_errors.insert(-errors_profiler[j].error_values[k]); 56 | } 57 | #else 58 | profiler_errors.insert(errors_profiler[j].error_values[k]); 59 | #endif 60 | ++k; 61 | } 62 | break; 63 | } 64 | } 65 | if (fnfound) 66 | { 67 | found = missing = fp = 0; 68 | for (it = profiler_errors.begin(); it != profiler_errors.end(); ++it) 69 | { 70 | if (man_errors.find(*it) != man_errors.end()) 71 | { 72 | ++found; 73 | } 74 | else 75 | { 76 | ++fp; 77 | } 78 | } 79 | missing = man_errors.size() - found; 80 | accuracy = (found) * 100 / ((fp+man_errors.size() > 0 ) ? (fp+man_errors.size()) : 1); 81 | if (!found && !fp && !man_errors.size()) 82 | accuracy = 100; 83 | #ifdef SYSCALLS 84 | cout << "|-\n| " << errors_man[i].sysno << " || " << found << " || " << missing << " || " << fp << " || " << accuracy << "%" << endl; 85 | #else 86 | cout << "|-\n| " << errors_man[i].name << " || " << found << " || " << missing << " || " << fp << " || " << accuracy << "%" << endl; 87 | #endif 88 | ++cnt; 89 | sum += accuracy; 90 | } 91 | } 92 | cout << "Avg(accuracy): " << (float)sum/cnt << "% over " << cnt << " values" << endl; 93 | return 0; 94 | } 95 | 96 | -------------------------------------------------------------------------------- /profiler/libxml_profiler/docparser.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | \n"; 7 | } 8 | 9 | if (count($argv) < 2) 10 | { 11 | usage($argv[0]); 12 | exit; 13 | } 14 | $clean_tags_regex = '/<(a|\/a|br|table|\/table|tbody|\/tbody|td|\/td|tr|\/tr|tt|\/tt|span|\/span|i|\/i|col)[^>]*>/'; 15 | $regex = '/Function: ([^<]*)<\/h3>
([^ \t]*)[^<]*<\/pre>

[^<]*<\/p>\n()?/'; 16 | $constants = '/(?: |:)(-?[\d]+)\b/'; 17 | 18 | $contents = @file_get_contents($argv[1]); 19 | $results = array(); 20 | $vv = 0; 21 | if (false === $contents) 22 | { 23 | echo "Invalid file/address provided: {$argv[1]}\n"; 24 | } else { 25 | $contents = preg_replace($clean_tags_regex, '', $contents); 26 | $r = preg_match_all($regex, $contents, $matches); 27 | if ($r) { 28 | for ($i = 0; $i < count($matches[1]); $i++) 29 | { 30 | if (!empty($matches[3][$i]) && false === strpos($matches[1][$i], "UCSI")) 31 | { 32 | $r = strstr($matches[3][$i], "Returns"); 33 | } else { 34 | $r = false; 35 | } 36 | $return_text = $r; 37 | // echo $matches[1][$i] . " " . $matches[2][$i] . "\n"; 38 | if ($r) 39 | { 40 | if (false == strpos($r, "a positive error code")) 41 | { 42 | $r = preg_match_all($constants, $r, $matches2); 43 | if ($r) 44 | { 45 | for ($j = 0; $j < count($matches2[1]); $j++) 46 | { 47 | // echo $matches2[1][$j] . ", "; 48 | $results[$matches[1][$i]][] = $matches2[1][$j]; 49 | } 50 | if (1 == count($matches2[1]) && (false !== strpos($matches[1][$i], 'Has') || false !== strpos($matches[1][$i], 'Is'))) 51 | { 52 | // echo "0"; 53 | $results[$matches[1][$i]][] = 0; 54 | } 55 | } else { 56 | if (substr($matches[2][$i], -3) == "Ptr") 57 | { 58 | // echo "NULL"; 59 | $results[$matches[1][$i]][] = "NULL"; 60 | } 61 | if (false !== strpos($return_text, "negative value on fail")) 62 | { 63 | $results[$matches[1][$i]][] = "-1"; 64 | } 65 | } 66 | } 67 | } else { 68 | if ('void' == $matches[2][$i]) 69 | { 70 | $vv++; 71 | } 72 | else 73 | { 74 | // shouldn't happen 75 | } 76 | } 77 | } 78 | } 79 | echo '#include "std_errors_head.h"' . "\n"; 80 | 81 | foreach ($results as $fn => $values) 82 | { 83 | echo "int $fn" . "_man_errors[] = {\n"; 84 | foreach ($values as $value) 85 | { 86 | echo $value . ","; 87 | } 88 | echo "12345};\n"; 89 | } 90 | 91 | echo "\n\nstruct sys_errors errors_man[] = {"; 92 | foreach ($results as $fn => $values) 93 | { 94 | echo '{ "' . $fn . '", ' . $fn . "_man_errors },\n"; 95 | } 96 | echo "};\n"; 97 | echo "hahh $vv"; 98 | } 99 | 100 | ?> 101 | -------------------------------------------------------------------------------- /Trigger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | using namespace std; 34 | 35 | class Trigger 36 | { 37 | public: 38 | virtual void Init(xmlNodePtr initData) {} 39 | virtual bool Eval(const string* functionName, ...) = 0; 40 | }; 41 | 42 | typedef Trigger* (*FactoryMethod)() ; 43 | 44 | class RegEntry ; 45 | 46 | class Class 47 | { 48 | public : 49 | static Class forName( std::string s ) 50 | { 51 | return( Class( s ) ) ; 52 | } 53 | Trigger* newInstance() ; 54 | 55 | static Trigger* newI(std::string s) 56 | { 57 | return (Class(s).newInstance()); 58 | } 59 | private : 60 | std::string name ; 61 | typedef std::map< std::string, FactoryMethod > FactoryMethodMap ; 62 | static FactoryMethodMap* fmMap ; 63 | friend class RegEntry ; 64 | static void Register( std::string s, FactoryMethod m ) ; 65 | Class( std::string s ) : name( s ) 66 | { 67 | } 68 | }; 69 | 70 | 71 | class RegEntry 72 | { 73 | public : 74 | RegEntry( const char s[], FactoryMethod f ) 75 | { 76 | Class::Register( s, f ) ; 77 | } 78 | }; 79 | 80 | 81 | template< class T, const char S[] > class Registered 82 | { 83 | public : 84 | static Trigger* newInstance() 85 | { 86 | return( new T() ) ; 87 | } 88 | protected : 89 | Registered() 90 | { 91 | const RegEntry& dummy = r ; 92 | } 93 | private : 94 | static const RegEntry r ; 95 | } ; 96 | 97 | template< class T, const char S[] > const RegEntry 98 | Registered< T, S > :: r = RegEntry( S, Registered< T, S >::newInstance ) ; 99 | 100 | #define DEFINE_TRIGGER( C ) \ 101 | char C##Name__[] = #C ; \ 102 | class C : public Registered< C, C##Name__ >, public Trigger 103 | -------------------------------------------------------------------------------- /triggers/AfterUnlockTrigger.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "AfterUnlockTrigger.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | AfterUnlockTrigger::AfterUnlockTrigger() 12 | { 13 | } 14 | 15 | void AfterUnlockTrigger::Init(xmlNodePtr initData) 16 | { 17 | xmlNodePtr nodeElement, textElement; 18 | 19 | nodeElement = initData->children; 20 | while (nodeElement) 21 | { 22 | if (XML_ELEMENT_NODE == nodeElement->type) 23 | { 24 | textElement = nodeElement->children; 25 | if (XML_TEXT_NODE == textElement->type) { 26 | if (!xmlStrcmp(nodeElement->name, (const xmlChar*)"lines")) 27 | lineCount = atoi((char*)textElement->content); 28 | else if (!xmlStrcmp(nodeElement->name, (const xmlChar*)"module")) 29 | exePath = (char*)textElement->content; 30 | } 31 | } 32 | nodeElement = nodeElement->next; 33 | } 34 | } 35 | 36 | struct layout 37 | { 38 | struct layout *ebp; 39 | void *ret; 40 | }; 41 | 42 | 43 | bool get_file_line(const char* binary, unsigned long address, char* src, int* line) 44 | { 45 | int pipefd[2]; 46 | char addr2linecmd[1024]; 47 | 48 | char buffer[1024]; 49 | ssize_t readc; 50 | 51 | char* colon; 52 | 53 | src[0] = 0; 54 | if (-1 != pipe(pipefd)) { 55 | sprintf(addr2linecmd, "addr2line -e %s %lx 1>&%d", binary, address, pipefd[1]); 56 | system(addr2linecmd); 57 | close(pipefd[1]); 58 | 59 | readc = read(pipefd[0], buffer, sizeof(buffer)); 60 | buffer[readc] = 0; 61 | close(pipefd[0]); 62 | 63 | colon = strchr(buffer, ':'); 64 | if (colon) { 65 | colon[0] = 0; 66 | strcpy(src, buffer); 67 | *line = atoi(colon+1); 68 | } 69 | } 70 | 71 | if (0 == src[0] || 0 == strcmp(src, "??")) 72 | return false; 73 | return true; 74 | } 75 | 76 | 77 | bool AfterUnlockTrigger::Eval(const string* functionName, ...) 78 | { 79 | map::iterator it; 80 | UnlockInfo ui; 81 | pthread_t self; 82 | 83 | // XXX assuming stack frames & arch dependent 84 | register void *ebpx __asm__ ("ebp"); 85 | struct layout *ebp = (struct layout *)ebpx; 86 | 87 | // reaching to the function calling the interceptor 88 | ebp = ebp->ebp->ebp; 89 | 90 | // for mysql, go one more time to skip the wrappers 91 | ebp = ebp->ebp; 92 | 93 | if (*functionName == "pthread_exit") 94 | { 95 | it = lastUnlockInfo.find(self); 96 | if (it != lastUnlockInfo.end()) 97 | lastUnlockInfo.erase(it); 98 | } 99 | // run addr2line -e executable ebp->ret 100 | else if (get_file_line(exePath.c_str(), (unsigned long)ebp->ret, ui.file, &ui.line)) { 101 | self = pthread_self(); 102 | 103 | if (*functionName == "pthread_mutex_unlock") 104 | { 105 | lastUnlockInfo[self] = ui; 106 | } 107 | else 108 | { 109 | it = lastUnlockInfo.find(self); 110 | /* 111 | if (it != lastUnlockInfo.end()) 112 | { ofstream outf("/home/paul/kk"); 113 | outf << "uTrigger called for " << ui.file << ":" << ui.line << endl; 114 | 115 | outf << "last unlock at " << it->second.file << ":" << it->second.line << endl; 116 | outf.close(); 117 | } 118 | */ 119 | if (it != lastUnlockInfo.end()) { 120 | if (0 == strcmp(it->second.file, ui.file) && 121 | ui.line - it->second.line < lineCount) 122 | return true; 123 | } 124 | } 125 | } 126 | 127 | return false; 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Library-level Fault Injection Toolkit 3 | ===================================== 4 | 5 | LFI automatically identifies the errors exposed by shared libraries, finds potentially buggy error recovery code in program binaries, and produces corresponding injection scenarios. LFI injects the desired faults – in the form of error return codes and corresponding side effects – at the boundary between shared libraries and applications. 6 | 7 | Installation 8 | ------------ 9 | git clone https://github.com/dslab-epfl/lfi.git 10 | cd lfi && make 11 | 12 | ###Dependencies 13 | * gcc/clang 14 | * libxml-dev (or libxml-devel) 15 | 16 | ###Operating system 17 | LFI was tested on 64-bit MacOS and Linux systems. It *should* also work on their 32-bit counterparts. 18 | 19 | Usage 20 | ----- 21 | 22 | ./libfi [-t ] 23 | 24 | LFI comes with several example fault injection plans; we can use this one for a quick test: 25 | 26 | ###Basic scenario #1 27 | ./libfi scenarios/sampleplan.xml -t /bin/ls 28 | 29 | If everything is OK you should see ls running and failing with a "Bad file descriptor" error. 30 | 31 | ###Basic scenario #2 (MacOS) 32 | 33 | ./libfi -t /Applications/Safari.app/Contents/MacOS/Safari scenarios/macos.simple.xml 34 | 35 | ###Scenarion #3 36 | 37 | Next we will try to inject a fault in the communication between the PostgreSQL client and the database. First, install PostgreSQL if you don't have it already (e.g., apt-get install postgresql on Ubuntu) and start the server. Start the PostgreSQL client by typing "psql" and set up a database with some data: 38 | 39 |

 40 | CREATE TABLE friends ( name VARCHAR(30), age INT );
 41 | INSERT INTO friends VALUES ('John', 24), ('Mary', 33), ('Bob', 42);
 42 | SELECT * FROM friends;
 43 |  name | age 
 44 | ------+-----
 45 |  John |  24
 46 |  Mary |  33
 47 |  Bob  |  42
 48 | (3 rows)
 49 | \q
 50 | 
51 | 52 | Quit the client and create a fault injection plan as follows (you can name it *myplan.xml*): 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | libpq.so.5.1 61 | 62 | 63 | 64 | 65 | 66 | 3 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | This plan tells LFI to intercept the recv() function (which is a libc API call) and, on the 3rd call made by libpq to the function, inject a fault that returns value -1 and sets errno to EBADF. The scenario uses two triggers: 77 | 78 | * The callstack trigger *module_libpq* that makes the fault be injected only if the call is made from the libpq module. We use the libpq.so library here because the PostreSQL client uses libpq to communicate with the database. 79 | * The call count trigger *cc1* that allows the injection to occur only at the 3rd call to the recv() function. 80 | 81 | Now run LFI as follows 82 | 83 | ./libfi myplan.xml -t /usr/bin/psql 84 | 85 | You should witness something like this 86 | 87 |

 88 | % psql
 89 | Welcome to psql 8.3.7, the PostgreSQL interactive terminal.
 90 | 
 91 | Type:  \copyright for distribution terms
 92 |        \h for help with SQL commands
 93 |        \? for help with psql commands
 94 |        \g or terminate with semicolon to execute query
 95 |        \q to quit
 96 | 
 97 | postgres=# select * from friends;
 98 |  name | age 
 99 | ------+-----
100 |  John |  24
101 |  Mary |  33
102 |  Bob  |  42
103 | (3 rows)
104 | 
105 | postgres=# select * from friends;
106 | could not receive data from server: Bad file descriptor
107 | 
108 | postgres=# select * from friends;
109 |  name | age 
110 | ------+-----
111 |  John |  24
112 |  Mary |  33
113 |  Bob  |  42
114 |  
115 | 116 | What is seen here is a fault that interferes with the PostgreSQL's client ability to receive the data from the server. 117 | 118 | If you're wondering why the fault occurs on the 2nd call, it's because psql makes one call to recv() during startup; if you add that in, you will see that it is the 3rd time we use recv() that the fault is observed. 119 | 120 | For further information about LFI, the available triggers and how to write your own, see the [documentation](https://github.com/dslab-epfl/lfi/wiki/User-Manual). Also see the [executive summary and publication list](https://github.com/dslab-epfl/lfi/wiki). 121 | -------------------------------------------------------------------------------- /triggers/StateTrigger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "StateTrigger.h" 28 | #include 29 | #include 30 | #include 31 | #ifdef __APPLE__ 32 | #include 33 | #endif 34 | 35 | #ifdef __x86_64__ 36 | #define REG_BP "rbp" 37 | #else 38 | #define REG_BP "ebp" 39 | #endif 40 | 41 | using namespace std; 42 | void StateTrigger::Init(xmlNodePtr initData) 43 | { 44 | xmlNodePtr nodeElement, nodeElementLvl2, textElement; 45 | 46 | nodeElement = initData->children; 47 | while (nodeElement) 48 | { 49 | if (XML_ELEMENT_NODE == nodeElement->type && 50 | (!xmlStrcmp(nodeElement->name, (const xmlChar*)"local") || !xmlStrcmp(nodeElement->name, (const xmlChar*)"global"))) 51 | { 52 | var.offset = 0; 53 | var.frame = 1; 54 | var.location = (!xmlStrcmp(nodeElement->name, (const xmlChar*)"local") ? VAR_LOCAL : VAR_GLOBAL ); 55 | 56 | nodeElementLvl2 = nodeElement->children; 57 | while (nodeElementLvl2) 58 | { 59 | textElement = nodeElementLvl2->children; 60 | if (textElement && XML_TEXT_NODE == textElement->type) 61 | { 62 | if (!xmlStrcmp(nodeElementLvl2->name, (const xmlChar*)"offset")) 63 | var.offset = (char*)strtoul((char*)textElement->content, NULL, 0); 64 | else if (!xmlStrcmp(nodeElementLvl2->name, (const xmlChar*)"type")) { 65 | if (!xmlStrcmp(textElement->content, (const xmlChar*)"int")) { 66 | var.type = VAR_INT; 67 | } else if (!xmlStrcmp(textElement->content, (const xmlChar*)"string")) { 68 | var.type = VAR_STRING; 69 | } else { 70 | cerr << "[StateTrigger] Unknown variable type: " << (char*)nodeElementLvl2->content << endl; 71 | } 72 | } else if (!xmlStrcmp(nodeElementLvl2->name, (const xmlChar*)"value")) { 73 | if (VAR_INT == var.type) 74 | var.targetValue.targetInt = atoi((char*)textElement->content); 75 | else if (VAR_STRING == var.type) { 76 | // XXX - check string size 77 | strcpy(var.targetValue.targetString, (char*)textElement->content); 78 | } 79 | } else if (!xmlStrcmp(nodeElementLvl2->name, (const xmlChar*)"frame")) { 80 | var.frame = atoi((char*)textElement->content); 81 | } else if (!xmlStrcmp(nodeElementLvl2->name, (const xmlChar*)"value")) { 82 | var.frame = atoi((char*)textElement->content); 83 | } 84 | } 85 | nodeElementLvl2 = nodeElementLvl2->next; 86 | } 87 | break; 88 | } 89 | nodeElement = nodeElement->next; 90 | } 91 | } 92 | 93 | struct layout 94 | { 95 | struct layout *bp; 96 | void *ret; 97 | // void *args[8]; 98 | }; 99 | 100 | #ifdef __APPLE__ 101 | void *__libc_stack_end; 102 | #else 103 | extern void *__libc_stack_end; 104 | #endif 105 | 106 | bool StateTrigger::Eval(const string*, ...) 107 | { 108 | int i; 109 | #ifdef __APPLE__ 110 | __libc_stack_end = pthread_get_stackaddr_np(pthread_self()); 111 | #endif 112 | if (VAR_GLOBAL == var.location) { 113 | if (VAR_INT == var.type) return (var.targetValue.targetInt == *(int*)var.offset); 114 | return !strcmp(var.targetValue.targetString, (char*)var.offset); 115 | } 116 | 117 | register void *bpx __asm__ ( REG_BP ); 118 | 119 | struct layout *bp = (struct layout *)bpx; 120 | 121 | for (i = (int)var.frame+2; i; --i) { 122 | if ((void*)bp > __libc_stack_end || ((long)bp & 3)) 123 | return false; 124 | bp = bp->bp; 125 | } 126 | if (VAR_INT == var.type) { 127 | if (var.targetValue.targetInt == *(int*)((char*)bp + (long)var.offset)) 128 | return true; 129 | return false; 130 | } 131 | char* target = *(char**)((char*)(bp) + (long)var.offset); 132 | if (!strcmp(var.targetValue.targetString, target)) 133 | return true; 134 | return false; 135 | } 136 | -------------------------------------------------------------------------------- /profiler/SEDetector.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "CFGBuilder.h" 34 | #include "CFGBuilderSPARC.h" 35 | 36 | #define MAGIC_FNPTR_CALL "call 00000000 " 37 | 38 | using namespace std; 39 | 40 | struct BFPathElement 41 | { 42 | int bbId; 43 | string target; 44 | }; 45 | 46 | bool operator<(const BFPathElement& lhs, const BFPathElement& rhs) 47 | { 48 | if (lhs.bbId < rhs.bbId) 49 | return true; 50 | if (lhs.bbId == rhs.bbId && lhs.target < rhs.target) 51 | return true; 52 | 53 | return false; 54 | } 55 | 56 | typedef vector BFPath; 57 | 58 | string GetText(char* pszAsmPath) 59 | { 60 | string line, contents; 61 | ifstream inf(pszAsmPath); 62 | 63 | if (inf.is_open()) 64 | { 65 | while (!inf.eof() && inf.good()) 66 | { 67 | getline(inf, line); 68 | contents += line; 69 | contents += "\n"; 70 | } 71 | } 72 | return contents; 73 | } 74 | 75 | int IsRegisterx86(string target) 76 | { 77 | return (target[0] != 'D' && target[0] != '['); 78 | } 79 | 80 | 81 | void BFWalkx86(CFGraph* graph, int start, char* reffile) 82 | { 83 | CFGraph::AdjListElement* head; 84 | queue bfQueue; 85 | 86 | BFPathElement anElement; 87 | 88 | BasicBlock* aBB; 89 | list::const_reverse_iterator itr; 90 | set setVisited; 91 | ofstream outref; 92 | int directCalls = 0, indirectCalls = 0; 93 | 94 | string target, aTarget, instruction; 95 | 96 | int i, j; 97 | int retcall; 98 | 99 | if (reffile) 100 | { 101 | outref.open(reffile); 102 | if (!outref.is_open()) 103 | { 104 | cerr << "Unable to open " << reffile << endl; 105 | reffile = NULL; 106 | } 107 | } 108 | 109 | if (head = graph->GetHead(start)) 110 | { 111 | anElement.target = "eax"; 112 | do { 113 | anElement.bbId = head->m_bbIndex; 114 | bfQueue.push(anElement); 115 | } while (head = head->m_pNext); 116 | } 117 | while (!bfQueue.empty()) 118 | { 119 | anElement = bfQueue.front(); 120 | bfQueue.pop(); 121 | 122 | target = anElement.target; 123 | 124 | aBB = graph->m_vBasicBlocks[anElement.bbId]; 125 | 126 | retcall = 0; 127 | for (itr = aBB->m_listInstructions.rbegin(); 128 | itr != aBB->m_listInstructions.rend(); 129 | ++itr) 130 | { 131 | i = 0; 132 | while (' ' != (*itr)[++i]); 133 | instruction = itr->substr(0, i); 134 | 135 | if ("mov" == instruction) 136 | { 137 | /* moving to `target`? */ 138 | i = 3; 139 | while(' ' == (*itr)[++i]); 140 | j = i; 141 | while(',' != (*itr)[++j]); 142 | aTarget = itr->substr(i, j-i); 143 | 144 | if (target == aTarget) 145 | { 146 | /* 147 | Simple method: change the target and continue. 148 | */ 149 | target = itr->substr(j+1); 150 | } 151 | } 152 | else if (IsRegisterx86(target)) 153 | { 154 | if ("or" == instruction) 155 | { 156 | /* could be `or reg,0xffffffff` */ 157 | i = 3; 158 | while(' ' == (*itr)[++i]); 159 | 160 | j = i; 161 | while(',' != (*itr)[++j]); 162 | aTarget = itr->substr(i, j-i); 163 | 164 | if (target == aTarget && "0xffffffff" == itr->substr(j+1)) 165 | { 166 | target = "0xffffffff"; 167 | } 168 | } 169 | else if ("xor" == instruction) 170 | { 171 | /* could be `xor reg,reg` */ 172 | i = 3; 173 | while(' ' == (*itr)[++i]); 174 | 175 | j = i; 176 | while(',' != (*itr)[++j]); 177 | aTarget = itr->substr(i, j-i); 178 | 179 | if (target == aTarget && target == itr->substr(j+1)) 180 | { 181 | target = "0x0"; 182 | } 183 | } 184 | else if ("eax" == target) 185 | { 186 | if ("call" == instruction) 187 | { 188 | if (reffile) { 189 | if (strstr(itr->c_str(), "DWORD PTR") || 190 | *itr == "eax" || *itr == "ebx" || *itr == "ecx" || *itr == "edx" || *itr == "edi" || *itr == "esi") 191 | { 192 | outref << MAGIC_FNPTR_CALL << endl; 193 | ++indirectCalls; 194 | } 195 | else 196 | { 197 | outref << *itr << endl; 198 | ++directCalls; 199 | } 200 | } else { 201 | cout << "new return value: " << *itr << endl; 202 | } 203 | retcall = 1; 204 | break; 205 | } 206 | else if ("int" == instruction && string::npos != itr->find("0x80")) 207 | { 208 | cout << "new return value: " << *itr << endl; 209 | retcall = 1; 210 | break; 211 | } 212 | } 213 | } 214 | } 215 | 216 | /* is this a literal? */ 217 | if (target.size() > 2 && '0' == target[0] && 'x' == target[1]) 218 | { 219 | cout << "new return value: " << target << " " << (int)strtoul(target.c_str(), NULL, 16) << endl; 220 | } 221 | else if (!retcall && (head = graph->GetHead(anElement.bbId))) 222 | { 223 | anElement.target = target; 224 | do { 225 | anElement.bbId = head->m_bbIndex; 226 | if (setVisited.insert(anElement).second) 227 | { 228 | bfQueue.push(anElement); 229 | } 230 | } while (head = head->m_pNext); 231 | } 232 | } 233 | } 234 | 235 | 236 | int main(int argc, char* argv[]) 237 | { 238 | CFGraph *graph, *tgraph; 239 | char *infile = "x.asm"; 240 | char *reffile = NULL; 241 | CCFGBuilder builder; 242 | string asma; 243 | 244 | if (argc > 1) 245 | infile = argv[1]; 246 | if (argc > 2) 247 | reffile = argv[2]; 248 | 249 | asma = GetText(infile); 250 | if (asma.empty()) 251 | { 252 | cerr << "Invalid or non-existing file: " << infile << endl; 253 | return -1; 254 | } 255 | graph = builder.Parse(asma.c_str()); 256 | 257 | CFGraph::AdjListElement* head; 258 | int i = 0; 259 | list::const_iterator its; 260 | 261 | tgraph = new CFGraph(); 262 | tgraph->m_vBasicBlocks = graph->m_vBasicBlocks; 263 | 264 | while (head = graph->GetHead(i)) 265 | { 266 | for (;head; head = head->m_pNext) 267 | { 268 | tgraph->Add(i, head->m_bbIndex); 269 | } 270 | ++i; 271 | } 272 | 273 | BFWalkx86(graph, 0, reffile); 274 | 275 | return 0; 276 | } 277 | -------------------------------------------------------------------------------- /profiler/ProfilerMgr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | using namespace std; 40 | 41 | #define PROFILEREX "profiler" 42 | #define REFFILE "function_references" 43 | #define DASMPATH "disassembly/" 44 | #define PROFILEPATH "profiles/" 45 | #define MAGIC_FNPTR_CALL "function_pointer_call" 46 | 47 | char targetLibrary[1024]; 48 | 49 | void usage(char* me) 50 | { 51 | cout << "Usage: "; 52 | cout << me << " " << endl; 53 | } 54 | 55 | void getReturnValues(const char* function); 56 | 57 | 58 | int isIntrinsic(string function) 59 | { 60 | return 0; 61 | } 62 | 63 | void appendInstrinsic(const ostream& out, string function) 64 | { 65 | } 66 | 67 | void handleReference(const char* function, const char* address, const char* profile) 68 | { 69 | /* 70 | Logic: 71 | 1. PROFILEPATH/function exists? 72 | 1.1 No -> { 73 | - generate the DASMPATH/function (or assume it already exists in the current implementation) 74 | - call getReturnValues to generate PROFILEPATH/function 75 | } 76 | 2. Append PROFILEPATH/function to profile 77 | */ 78 | 79 | string dasmPath(DASMPATH), profilePath(PROFILEPATH), line; 80 | char dasmCmd[1024]; 81 | ifstream infProfile, infDasm; 82 | ofstream outfProfile; 83 | size_t index; 84 | int startOffset, endOffset; 85 | string actual_function = function; 86 | 87 | /* check if this is a call to a @plt function and doesn't have an offset 88 | * strip the @plt part 89 | */ 90 | if (string::npos == actual_function.find("+") && string::npos != (index = actual_function.find("@plt"))) 91 | { 92 | actual_function = actual_function.substr(0, index); 93 | } 94 | 95 | 96 | dasmPath += actual_function; 97 | profilePath += actual_function; 98 | 99 | cerr << "handleReferece to " << function << ". appending to " << profile << endl; 100 | if (isIntrinsic(actual_function)) 101 | { 102 | outfProfile.open(profile, ios_base::out|ios_base::app); 103 | appendInstrinsic(outfProfile, actual_function); 104 | } 105 | else 106 | { 107 | infProfile.open(profilePath.c_str()); 108 | if (!infProfile.is_open()) 109 | { 110 | infDasm.open(dasmPath.c_str()); 111 | if (!infDasm.is_open()) 112 | { 113 | cerr << "WARNING: Disassembly not found for " << function << " trying to disassemble code. Return values set may be incomplete" << endl; 114 | 115 | startOffset = strtol(address, (char**)NULL, 16); 116 | /* assume function is at most 30K */ 117 | endOffset = startOffset + 30720; 118 | 119 | sprintf(dasmCmd, "objdump -d -M intel --start-address=%d --stop-address=%d %s | egrep -v \"(^Disassembly|^/|^$|efi-app-ia32)\" > %s%d.tmp", startOffset, endOffset, targetLibrary, DASMPATH, startOffset); 120 | cerr << "Executing " << dasmCmd << endl; 121 | system(dasmCmd); 122 | 123 | sprintf(dasmCmd, "%d.tmp", startOffset); 124 | actual_function = dasmCmd; 125 | profilePath = PROFILEPATH; 126 | profilePath += actual_function; 127 | 128 | sprintf(dasmCmd, "%s%d.tmp", DASMPATH, startOffset); 129 | 130 | infDasm.open(dasmCmd); 131 | } 132 | if (infDasm.is_open()) 133 | { 134 | getReturnValues(actual_function.c_str()); 135 | infProfile.clear(); 136 | infProfile.open(profilePath.c_str()); 137 | } 138 | } 139 | 140 | if (infProfile.is_open()) 141 | { 142 | outfProfile.open(profile, ios_base::out|ios_base::app); 143 | while (1) 144 | { 145 | getline(infProfile, line); 146 | if (infProfile.eof()) 147 | break; 148 | outfProfile << line << endl; 149 | } 150 | } 151 | } 152 | cerr << "handleReferece to " << function << "(" << actual_function << ") - done" << endl; 153 | } 154 | 155 | void getReturnValues(const char* function) 156 | { 157 | /* 158 | we're now asssuming DASMPATH/function exists (dasmfn.sh can be run to generate the files) 159 | on-demand disassembly is to be implemented in a future version 160 | */ 161 | /* 162 | We're now 163 | 1. running PROFILEREX "DASMPATH/function" "REFFILE" > "PROFILEPATH/function" 164 | 2. foreach (referenced_function in REFFILE) { 165 | getReturnValues(referenced_function); 166 | PROFILEPATH/function += PROFILEPATH/referenced_function 167 | } 168 | 3. handle circular references (if a circular reference exists, 169 | all functions involved have the same possible return values) - TO BE IMPLEMENTED 170 | */ 171 | 172 | string dasmPath(DASMPATH), profilePath(PROFILEPATH), reference, reference_address; 173 | pid_t pid; 174 | char buf, profiler_path[512], reffile[512]; 175 | int pfd[2]; 176 | int out_file; 177 | 178 | dasmPath += function; 179 | profilePath += function; 180 | 181 | sprintf(reffile, "%s_%s", REFFILE, function); 182 | 183 | if (-1 == pipe(pfd)) 184 | { 185 | perror("pipe"); 186 | exit(EXIT_FAILURE); 187 | } 188 | if (-1 == (pid = fork())) 189 | { 190 | perror("fork"); 191 | exit(EXIT_FAILURE); 192 | } 193 | 194 | if (0 == pid) 195 | { 196 | cerr << "Child starting..." << endl; 197 | /* close reading end */ 198 | close(pfd[0]); 199 | /* redirect stdout to pfd[1] */ 200 | dup2(pfd[1], STDOUT_FILENO); 201 | /* no need for pfd[1] anymore */ 202 | close(pfd[1]); 203 | 204 | if (getcwd(profiler_path, sizeof(profiler_path) - strlen(PROFILEREX))) 205 | { 206 | strcat(profiler_path, "/"); 207 | strcat(profiler_path, PROFILEREX); 208 | cerr << "Executing " << profiler_path << " " << dasmPath << endl; 209 | execlp(profiler_path, PROFILEREX, dasmPath.c_str(), reffile, (char*)NULL); 210 | perror("execlp"); 211 | cerr << "ERROR: profiler could not be executed" << endl; 212 | } 213 | _exit(EXIT_FAILURE); 214 | } else { 215 | /* close write end */ 216 | close(pfd[1]); 217 | 218 | out_file = open(profilePath.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644); 219 | while (read(pfd[0], &buf, 1) > 0) 220 | { 221 | write(out_file, &buf, 1); 222 | } 223 | 224 | close(pfd[0]); 225 | close(out_file); 226 | 227 | wait(NULL); 228 | 229 | cerr << "Profiler done" << endl; 230 | } 231 | 232 | ifstream inf(reffile); 233 | 234 | if (inf.is_open()) 235 | { 236 | while (1) { 237 | inf >> reference >> reference_address >> reference; 238 | if (!inf.eof()) 239 | { 240 | reference = reference.substr(1, reference.size()-2); 241 | cerr << reference_address << " " << function << endl; 242 | if (0 == strcmp(reference.c_str(), MAGIC_FNPTR_CALL)) 243 | { 244 | cerr << "WARNING. fnptr call detected referencing " << reference << endl; 245 | } else if (0 == strcmp(reference.c_str(), function) || 246 | (strtol(reference_address.c_str(), (char**)NULL, 16) == atoi(function) && atoi(function) != 0)) 247 | { 248 | /* recursive call. ignore */ 249 | } else { 250 | cerr << "referencing " << reference << endl; 251 | handleReference(reference.c_str(), reference_address.c_str(), profilePath.c_str()); 252 | } 253 | } else 254 | break; 255 | } 256 | inf.close(); 257 | unlink(reffile); 258 | } else { 259 | cerr << "WARNING: reference file " << reffile << " not found" << endl; 260 | } 261 | cerr << "after profiler done" << endl; 262 | } 263 | 264 | int main(int argc, char* argv[], char* envp[]) 265 | { 266 | int c; 267 | 268 | opterr = 0; 269 | 270 | while ((c = getopt (argc, argv, "rf:t:")) != -1) 271 | switch (c) 272 | { 273 | case 'r': 274 | break; 275 | case 'f': 276 | break; 277 | case 't': 278 | break; 279 | case '?': 280 | if (optopt == 'f') 281 | fprintf (stderr, "Option -f requires an argument.\n"); 282 | if (optopt == 't') 283 | fprintf (stderr, "Option -t requires an argument.\n"); 284 | else if (isprint (optopt)) 285 | fprintf (stderr, "Unknown option `-%c'.\n", optopt); 286 | else 287 | fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); 288 | return 1; 289 | default: 290 | abort (); 291 | } 292 | 293 | if (argc > 2) 294 | { 295 | strncpy(targetLibrary, argv[2], sizeof(targetLibrary)); 296 | getReturnValues(argv[1]); 297 | } 298 | else 299 | usage(argv[0]); 300 | return 0; 301 | } 302 | -------------------------------------------------------------------------------- /inter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "Trigger.h" 38 | #include "inter.h" 39 | 40 | 41 | #ifdef __x86_64__ 42 | #define REG_BP "rbp" 43 | #else 44 | #define REG_BP "ebp" 45 | #endif 46 | 47 | /* 48 | these will be defined in the automatically generated .c file 49 | (intercept.stub.c) 50 | */ 51 | 52 | extern int log_fd, replay_fd; 53 | extern int init_done; 54 | 55 | /* stores the return address across the original library function call */ 56 | #ifdef __APPLE__ 57 | pthread_key_t return_address_key; 58 | #else 59 | static __thread long return_address; 60 | #endif 61 | 62 | /* avoid intercepting our function calls */ 63 | #ifdef __APPLE__ 64 | pthread_key_t no_intercept_key; 65 | #else 66 | static __thread int no_intercept; 67 | #endif 68 | 69 | uint64_t tsc() { 70 | uint32_t low, high; 71 | __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)); 72 | uint64_t r = ((uint64_t) high<<32) | low; 73 | return r; 74 | } 75 | 76 | void lfi_segv_handler(int, siginfo_t *, void *) 77 | { 78 | #ifdef WITH_LOGS 79 | struct timespec t = {0, 0}; 80 | char message[256]; 81 | 82 | clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t); 83 | 84 | sprintf(message, "[ %lld %ld %ld] SIGSEGV received\n", tsc(), (long)t.tv_sec, t.tv_nsec); 85 | write(log_fd, message, strlen(message)); 86 | fdatasync(log_fd); 87 | #endif 88 | abort(); 89 | } 90 | 91 | void __attribute__ ((constructor)) 92 | my_init(void) 93 | { 94 | #ifdef WITH_LOGS 95 | log_fd = open(LOGFILE, 577, 0644); 96 | replay_fd = open(REPLAYFILE, 577, 0644); 97 | 98 | write(replay_fd, "\n", 7); 99 | #endif 100 | #ifdef WITH_SIGHANDLER 101 | struct sigaction sa; 102 | memset(&sa, 0, sizeof(sa)); 103 | sa.sa_flags = SA_SIGINFO; 104 | sa.sa_sigaction = lfi_segv_handler; 105 | sigaction(SIGSEGV, &sa, NULL); 106 | #endif 107 | #ifdef __APPLE__ 108 | int err; 109 | err = pthread_key_create(&return_address_key, NULL); 110 | err |= pthread_key_create(&no_intercept_key, NULL); 111 | if (err) 112 | write(2, "Failed to create thread keys\n", 29); 113 | #endif 114 | init_done = 1; 115 | } 116 | 117 | void __attribute__ ((destructor)) 118 | my_fini(void) 119 | { 120 | #ifdef WITH_LOGS 121 | write(replay_fd, "\n", 8); 122 | close(replay_fd); 123 | close(log_fd); 124 | #endif 125 | } 126 | 127 | long get_return_address() 128 | { 129 | long r; 130 | #ifdef __APPLE__ 131 | r = (long)pthread_getspecific(return_address_key); 132 | #else 133 | r = return_address; 134 | #endif 135 | return r; 136 | } 137 | 138 | long get_no_intercept() 139 | { 140 | long r; 141 | #ifdef __APPLE__ 142 | r = (long)pthread_getspecific(no_intercept_key); 143 | #else 144 | r = no_intercept; 145 | #endif 146 | return r; 147 | } 148 | 149 | void set_return_address(long value) 150 | { 151 | #ifdef __APPLE__ 152 | pthread_setspecific(return_address_key, (void*)value); 153 | #else 154 | return_address = value; 155 | #endif 156 | } 157 | 158 | void set_no_intercept(long value) 159 | { 160 | #ifdef __APPLE__ 161 | pthread_setspecific(no_intercept_key, (void*)value); 162 | #else 163 | no_intercept = value; 164 | #endif 165 | } 166 | 167 | 168 | /************************************************************************/ 169 | /* returns the action that should be taken based on the triggers */ 170 | /* associated with the function fn when running an injection scenario */ 171 | /************************************************************************/ 172 | void determine_action(struct fninfov2 fn_details[], 173 | __in const char* function_name, 174 | __in void* arg1, 175 | __in void* arg2, 176 | __in void* arg3, 177 | __in void* arg4, 178 | __in void* arg5, 179 | __in void* arg6, 180 | __out int* call_original, 181 | __out int* return_error, 182 | __out int* return_code, 183 | __out int* return_errno) 184 | { 185 | int err_index, i, j; 186 | bool ev; 187 | char message[256]; 188 | static long c; 189 | 190 | xmlDocPtr initDataDoc; 191 | xmlNodePtr initData; 192 | 193 | *call_original = 1; 194 | *return_error = 0; 195 | *return_code = 0; 196 | *return_errno = 0; 197 | TriggerDesc **triggers; 198 | 199 | /* 200 | you can think of the triggers for a function as a jagged array - fn_details[].triggers[] 201 | if all the triggers on one line (fn_details[i]) of the array evaluate to true, 202 | the error associated with that line is injected 203 | */ 204 | for (i = 0; fn_details[i].function_name[0]; ++i) 205 | { 206 | triggers = fn_details[i].triggers; 207 | // if no triggers are defined the default behavior is to inject 208 | ev = true; 209 | for (j = 0; triggers[j]; ++j) { 210 | /* 211 | c++; 212 | if (0 == c % 100000) 213 | printf("%d funcs\n", c); 214 | */ 215 | 216 | if (!triggers[j]->trigger) 217 | { 218 | /* TODO: make operation atomic i.e. never instantiate twice */ 219 | triggers[j]->trigger = Class::newI(triggers[j]->tclass); 220 | 221 | if (!triggers[j]->trigger) 222 | { 223 | printf( "Trigger class %s not found or not yet registered while intercepting %s\n", 224 | triggers[j]->tclass, fn_details[i].function_name); 225 | return; 226 | } 227 | else 228 | { 229 | if (triggers[j]->init[0]) 230 | { 231 | initDataDoc = xmlParseDoc((xmlChar*)triggers[j]->init); 232 | 233 | if (initDataDoc) 234 | initData = xmlDocGetRootElement(initDataDoc); 235 | } else { 236 | initData = NULL; 237 | } 238 | triggers[j]->trigger->Init(initData); 239 | } 240 | } 241 | 242 | #if defined(__i386) 243 | /* 244 | considering first arg to be at prev_ebp+2xsizeof(long) 245 | (not always the case. not really portable) 246 | */ 247 | void* _ebp; 248 | __asm__ __volatile__ ("movl %%ebp, %0" : "=m"(_ebp) : ); 249 | 250 | long* prev_ebp = *((long**)_ebp); 251 | switch(fn_details[i].argc) 252 | { 253 | case 6: 254 | arg6 = *(prev_ebp+7); 255 | case 5: 256 | arg5 = *(prev_ebp+6); 257 | case 4: 258 | arg4 = *(prev_ebp+5); 259 | case 3: 260 | arg3 = *(prev_ebp+4); 261 | case 2: 262 | arg2 = *(prev_ebp+3); 263 | case 1: 264 | arg1 = *(prev_ebp+2); 265 | } 266 | #endif 267 | /* consider using a char* */ 268 | const string fn = fn_details[i].function_name; 269 | switch (fn_details[i].argc) 270 | { 271 | case -1: 272 | case 0: 273 | ev = triggers[j]->trigger->Eval(&fn); 274 | break; 275 | case 1: 276 | ev = triggers[j]->trigger->Eval(&fn, arg1); 277 | break; 278 | case 2: 279 | ev = triggers[j]->trigger->Eval(&fn, arg1, arg2); 280 | break; 281 | case 3: 282 | ev = triggers[j]->trigger->Eval(&fn, arg1, arg2, arg3); 283 | break; 284 | case 4: 285 | ev = triggers[j]->trigger->Eval(&fn, arg1, arg2, arg3, arg4); 286 | break; 287 | case 5: 288 | ev = triggers[j]->trigger->Eval(&fn, arg1, arg2, arg3, arg4, arg5); 289 | break; 290 | case 6: 291 | ev = triggers[j]->trigger->Eval(&fn, arg1, arg2, arg3, arg4, arg5, arg6); 292 | break; 293 | default: 294 | printf("A maximum of 6 arguments are supported in a trigger call\n"); 295 | ev = false; 296 | } 297 | if (!ev) { 298 | break; 299 | } 300 | } 301 | if (ev) 302 | { 303 | *return_error = 1; 304 | *return_code = fn_details->return_value; 305 | *return_errno = fn_details->errno_value; 306 | *call_original = fn_details->call_original; 307 | break; 308 | } 309 | } 310 | 311 | /* the writes should be serialized (between threads) to avoid corruption */ 312 | 313 | if (*return_error) 314 | { 315 | #ifdef WITH_LOGS 316 | struct timespec t = {0, 0}; 317 | clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t); 318 | 319 | sprintf(message, "[ %s, %lld %ld %ld] Returning code %d; setting errno to %d\n", function_name, tsc(), (long)t.tv_sec, t.tv_nsec, *return_code, *return_errno); 320 | write(log_fd, message, strlen(message)); 321 | fdatasync(log_fd); 322 | 323 | sprintf(message, "\n", function_name, call_count, *return_code, *return_errno); 324 | write(replay_fd, message, strlen(message)); 325 | #endif 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /profiler/CFGBuilder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "CFGBuilder.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | using namespace std; 35 | 36 | #define _CRT_SECURE_NO_DEPRECATE 1 37 | 38 | CCFGBuilder::CCFGBuilder(void) 39 | { 40 | } 41 | 42 | CCFGBuilder::~CCFGBuilder(void) 43 | { 44 | } 45 | unsigned int GetLastOffset(const char* text) 46 | { 47 | const char *tab1, *tab2, *lineend; 48 | unsigned int lastoffset, coffset; 49 | 50 | lastoffset = 0; 51 | lineend = text; 52 | 53 | do { 54 | tab1 = strchr(lineend, '\t'); 55 | if (!tab1) 56 | break; 57 | 58 | coffset = strtoul(lineend, NULL, 16); 59 | if (coffset > lastoffset) 60 | lastoffset = coffset; 61 | 62 | lineend = strchr(lineend+1, '\n'); 63 | 64 | tab2 = strchr(tab1+1, '\t'); 65 | if (!tab2) 66 | break; 67 | /* handle null bytes in the instruction stream */ 68 | if (tab2 > lineend) 69 | continue; 70 | ++tab2; 71 | } while(tab1 && lineend); 72 | 73 | return lastoffset; 74 | } 75 | 76 | /* 77 | text should be of the form 78 | address:[ ]*\topcode[ ]*\tinstruction\n 79 | 80 | parsing is done in two phases: 81 | 1. determine jump targets within the code - O(n) 82 | 2. linear pass and construct the CFG - O(n) 83 | */ 84 | CFGraph* CCFGBuilder::Parse(const char* text) 85 | { 86 | const char *tab1, *tab2, *lineend; 87 | char address[16], instruction[256], jumptarget[16], buffer[256]; 88 | set jumptargets; 89 | unsigned int cjump, lastjumptarget, didjump, lastoffset, startaddress, endaddress; 90 | int indirectJumps, regularJumps; 91 | 92 | /* structures used to add the jxx-induced edges */ 93 | map startToBlockId; /* maps the jump targets to a block id (graph node id) */ 94 | vector fromBlockId; 95 | vector toAddress; 96 | 97 | int i, j; 98 | 99 | lastjumptarget = 0; 100 | startaddress = 0; 101 | endaddress = GetLastOffset(text); 102 | didjump = 0; 103 | indirectJumps = 0; 104 | regularJumps = 0; 105 | lineend = text; 106 | 107 | 108 | do { 109 | tab1 = strchr(lineend, '\t'); 110 | if (!tab1) 111 | break; 112 | 113 | lastoffset = strtoul(lineend, NULL, 16); 114 | if (!startaddress) 115 | { 116 | startaddress = lastoffset; 117 | #ifdef DODEBUG 118 | cerr << "last off: " << hex << startaddress << " " << endaddress << endl; 119 | #endif 120 | } 121 | lineend = strchr(lineend+1, '\n'); 122 | 123 | tab2 = strchr(tab1+1, '\t'); 124 | if (!tab2) 125 | break; 126 | /* handle null bytes in the instruction stream */ 127 | if (tab2 > lineend) 128 | continue; 129 | ++tab2; 130 | 131 | if (lineend) 132 | { 133 | if (lineend-tab2 < sizeof(instruction)) 134 | { 135 | strncpy(instruction, tab2, lineend-tab2); 136 | instruction[lineend-tab2] = 0; 137 | } else { 138 | cerr << "what an instruction: " << tab2 << endl; 139 | continue; 140 | } 141 | 142 | if ('j' == instruction[0]) 143 | { 144 | /* determine the jump target */ 145 | i = 0; 146 | while (' ' != instruction[++i]); 147 | while (' ' == instruction[++i]); 148 | 149 | j = i; 150 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 151 | 152 | if (j-i < sizeof(jumptarget)) 153 | { 154 | strncpy(jumptarget, &instruction[i], j-i); 155 | jumptarget[j-i] = 0; 156 | 157 | 158 | cjump = strtoul(jumptarget, NULL, 16); 159 | if (cjump >= startaddress && cjump <= endaddress) 160 | { 161 | if (cjump > lastjumptarget) 162 | lastjumptarget = cjump; 163 | jumptargets.insert(jumptarget); 164 | } else { 165 | if (strncmp(instruction, "jmp", 3)) 166 | cerr << "WARNING: tail call detected in non-jmp instruction (" << instruction << "). Is this the entire function?" << endl; 167 | } 168 | } 169 | else 170 | { 171 | cerr << "what a jump target: " << instruction << " " << i << " " << j << endl; 172 | } 173 | } 174 | 175 | if (0 == strncmp(instruction, "jmp", 3) || 0 == strncmp(instruction, "ret", 3) || 0 == strncmp(instruction, "repz ret", 8)) 176 | { 177 | /* yes, ge */ 178 | if (lastoffset >= lastjumptarget) 179 | { 180 | #ifdef DODEBUG 181 | cerr << "stopped parsing at offset " << hex << lastoffset << endl; 182 | #endif 183 | lastjumptarget = lastoffset+1; 184 | break; 185 | } 186 | } 187 | } 188 | } while(tab1 && lineend); 189 | 190 | if (!lastjumptarget) 191 | { 192 | cerr << "WARNING: no jump or ret instructions encountered. Is this the entire function?" << endl; 193 | lastjumptarget = 0xffffffff; 194 | } 195 | 196 | /* and the second pass */ 197 | int cut, ret, prev, jmp; 198 | lineend = text; 199 | cut = 0; 200 | ret = 0; 201 | jmp = 0; 202 | prev = 0; 203 | BasicBlock* currentBB = new BasicBlock(); 204 | CFGraph* graph = new CFGraph(); 205 | /* node 0 is the virtual start node - no instructions */ 206 | graph->m_vBasicBlocks.push_back(new BasicBlock()); 207 | 208 | do { 209 | while (' ' == *lineend) ++lineend; 210 | 211 | tab1 = strchr(lineend, ':'); 212 | if (!tab1) 213 | break; 214 | 215 | cjump = strtoul(lineend, NULL, 16); 216 | if (cjump >= lastjumptarget) 217 | { 218 | #ifdef DODEBUG 219 | cerr << "stopped parsing (again) at offset " << hex << cjump << endl; 220 | #endif 221 | break; 222 | } 223 | 224 | if (tab1-lineend < sizeof(address)) 225 | { 226 | strncpy(address, lineend, tab1-lineend); 227 | address[tab1-lineend] = 0; 228 | } 229 | else { 230 | lineend = strchr(lineend+1, '\n'); 231 | continue; 232 | } 233 | 234 | tab1 = strchr(lineend, '\t'); 235 | 236 | lineend = strchr(lineend+1, '\n'); 237 | 238 | tab2 = strchr(tab1+1, '\t'); 239 | if (!tab2) 240 | break; 241 | /* handle null bytes in the instruction stream */ 242 | if (tab2 > lineend) 243 | continue; 244 | 245 | ++tab2; 246 | if (lineend) 247 | { 248 | if (cut || ret || jumptargets.find(address) != jumptargets.end()) 249 | { 250 | /* if a ret was encountered add an extra edge to -1 (exit node) */ 251 | if (ret) 252 | { 253 | graph->Add(EXIT_NODE, prev+1); 254 | } 255 | if (!jmp) 256 | graph->Add(prev+1, prev); 257 | 258 | ++prev; 259 | graph->m_vBasicBlocks.push_back(currentBB); 260 | currentBB = new BasicBlock(); 261 | if (!currentBB) 262 | { 263 | cerr << "Out of memory!" << endl; 264 | exit(-1); 265 | } 266 | 267 | 268 | if (jumptargets.find(address) != jumptargets.end()) 269 | { 270 | startToBlockId.insert(map::value_type(address, prev+1)); 271 | } 272 | if (ret || (cut && 'j' == instruction[0] && 'm' == instruction[1] && 'p' == instruction[2])) 273 | { 274 | jmp = 1; 275 | } 276 | else 277 | { 278 | jmp = 0; 279 | } 280 | cut = ret = 0; 281 | } 282 | 283 | strncpy(instruction, tab2, lineend-tab2); 284 | instruction[lineend-tab2] = 0; 285 | 286 | if ('j' == instruction[0]) 287 | { 288 | /* determine the jump target */ 289 | i = 0; 290 | while (' ' != instruction[++i]); 291 | while (' ' == instruction[++i]); 292 | 293 | j = i; 294 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 295 | strncpy(jumptarget, &instruction[i], j-i); 296 | jumptarget[j-i] = 0; 297 | 298 | cjump = strtoul(jumptarget, NULL, 16); 299 | if (0 == strcmp(jumptarget, "eax") || 300 | 0 == strcmp(jumptarget, "ebx") || 301 | 0 == strcmp(jumptarget, "ecx") || 302 | 0 == strcmp(jumptarget, "edx") || 303 | 0 == strcmp(jumptarget, "esi") || 304 | 0 == strcmp(jumptarget, "edi")) 305 | { 306 | /* skip it. there's nothing we can do right now */ 307 | ++indirectJumps; 308 | } 309 | /* 310 | check for tail call 311 | replace jmp with call + ret 312 | */ 313 | else if (cjump < startaddress || cjump > endaddress) 314 | { 315 | sprintf(buffer, "call %s", &instruction[i]); 316 | currentBB->m_listInstructions.push_back(buffer); 317 | strcpy(instruction, "ret"); 318 | ret = 1; 319 | } else { 320 | ++regularJumps; 321 | fromBlockId.push_back(prev+1); 322 | toAddress.push_back(jumptarget); 323 | cut = 1; 324 | } 325 | } 326 | else if (0 == strncmp(instruction, "ret", 3) || /* handle a gcc optimization */ 0 == strncmp(instruction, "repz ret", 8)) 327 | { 328 | ret = 1; 329 | } 330 | 331 | currentBB->m_listInstructions.push_back(instruction); 332 | ++lineend; 333 | } 334 | } while(tab1 && lineend); 335 | 336 | /* if a ret is encountered add an extra edge to -1 (exit node) */ 337 | if (ret) 338 | { 339 | graph->Add(EXIT_NODE, prev+1); 340 | } 341 | if (!jmp) 342 | { 343 | graph->Add(prev+1, prev); 344 | } 345 | graph->m_vBasicBlocks.push_back(currentBB); 346 | 347 | 348 | /* add the jxx-induced edges */ 349 | map::iterator it; 350 | vector::const_iterator it1; 351 | vector::const_iterator it2; 352 | for (it1 = fromBlockId.begin(), it2 = toAddress.begin(); 353 | it1 != fromBlockId.end() && it2 != toAddress.end(); 354 | ++it1, ++it2) 355 | { 356 | it = startToBlockId.find(*it2); 357 | if (it != startToBlockId.end()) 358 | { 359 | graph->Add(it->second, *it1); 360 | } 361 | else 362 | { 363 | /* BUG. should never happen */ 364 | } 365 | } 366 | /* cerr << regularJumps << ";" << indirectJumps << ";" << endl; */ 367 | return graph; 368 | } 369 | -------------------------------------------------------------------------------- /inter.h: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #ifndef _GNU_SOURCE 31 | #define _GNU_SOURCE 32 | #endif 33 | 34 | #ifndef __USE_GNU 35 | #define __USE_GNU 36 | #endif 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | class Trigger; 43 | 44 | /* the maximum number of frames in a stack trace */ 45 | #define TRACE_SIZE 100 46 | /* human readable log file (overwritten at each run) */ 47 | #define LOGFILE "inject.log" 48 | #define LOGGING 0 49 | /* machine readable log file used for injection replay (overwritten at each run) */ 50 | #define REPLAYFILE "replay.xml" 51 | 52 | #define MAXINJECT 2000000 53 | 54 | /* only used to enhance readbility */ 55 | #ifndef __in 56 | #define __in 57 | #endif 58 | 59 | #ifndef __out 60 | #define __out 61 | #endif 62 | 63 | #ifndef NULL 64 | #define NULL (void*)0 65 | #endif 66 | 67 | struct TriggerDesc 68 | { 69 | char id[128]; 70 | char tclass[128]; 71 | Trigger* trigger; 72 | char init[4096]; 73 | }; 74 | 75 | struct fninfov2 76 | { 77 | char function_name[256]; 78 | int return_value; 79 | int errno_value; 80 | int call_original; 81 | int argc; 82 | 83 | /* custom triggers */ 84 | TriggerDesc **triggers; 85 | }; 86 | 87 | /* stores the return address across the original library function call 88 | #ifdef __APPLE__ 89 | pthread_key_t return_address_key; 90 | #else 91 | static __thread long return_address; 92 | #endif 93 | */ 94 | /* avoid intercepting our function calls 95 | #ifdef __APPLE__ 96 | pthread_key_t no_intercept_key; 97 | #else 98 | static __thread int no_intercept; 99 | #endif 100 | */ 101 | 102 | long get_return_address(); 103 | void set_return_address(long); 104 | long get_no_intercept(); 105 | void set_no_intercept(long); 106 | 107 | /* 108 | avoid including the standard headers because the compiler will likely 109 | generate warnings when injecting faults in an already declared function. 110 | However, this won't allow us to inject faults in these functions 111 | ALTERNATIVE: use dlsym to get pointers to the functions 112 | */ 113 | 114 | extern "C" { 115 | int printf(const char * _Format, ...); 116 | } 117 | 118 | void determine_action(struct fninfov2 fn_details[], 119 | __in const char* function_name, 120 | __in void* arg1, 121 | __in void* arg2, 122 | __in void* arg3, 123 | __in void* arg4, 124 | __in void* arg5, 125 | __in void* arg6, 126 | __out int* call_original, 127 | __out int *return_error, 128 | __out int* return_code, 129 | __out int* return_errno); 130 | 131 | void print_backtrace(void* bt[], int nptrs, int log_fd); 132 | 133 | /************************************************************************/ 134 | /* GENERATE_STUBv2 - macro to generate stub functions targeted for x86 */ 135 | /* UNSAFE: clobbered non-volatile registeres may not be restored */ 136 | /* when `forcing' an exit (i.e. when doing a leave/ret or leave/jmp) */ 137 | /* SOLUTION: manually push/pop all non-volatile registers OR */ 138 | /* check assembly listing generated by the compiler to determine if */ 139 | /* any registers need to be `pop`-ed (per-compiler solution) */ 140 | /************************************************************************/ 141 | #define GENERATE_STUBv2(FUNCTION_NAME) \ 142 | void FUNCTION_NAME (void) \ 143 | { \ 144 | int call_original, return_error; \ 145 | int return_code, return_errno; \ 146 | int initial_no_intercept; \ 147 | static void * (*original_fn_ptr)(); \ 148 | \ 149 | /* defaults */ \ 150 | call_original = 1; \ 151 | return_error = 0; \ 152 | return_code = 0; \ 153 | return_errno = 0; \ 154 | \ 155 | initial_no_intercept = get_no_intercept(); \ 156 | if (0 == initial_no_intercept && init_done /* don't hook open or write in the constructor */) { \ 157 | set_no_intercept(1); /* allow all determine_action-called functions to pass-through */ \ 158 | determine_action(function_info_ ## FUNCTION_NAME, #FUNCTION_NAME, \ 159 | 0, 0, 0, 0, 0, 0, \ 160 | &call_original, &return_error, &return_code, &return_errno); \ 161 | } \ 162 | \ 163 | if(!original_fn_ptr) { \ 164 | original_fn_ptr = (void *(*)()) dlsym(RTLD_NEXT, #FUNCTION_NAME); \ 165 | if(!original_fn_ptr) \ 166 | printf("Unable to get address for function %s\n", #FUNCTION_NAME); \ 167 | } \ 168 | \ 169 | set_no_intercept(initial_no_intercept); \ 170 | if (return_error) \ 171 | { \ 172 | errno = return_errno; \ 173 | __asm__ ("" : : "a"(return_code)); \ 174 | return; \ 175 | } \ 176 | else if (call_original) \ 177 | { \ 178 | /* this must correspond to the compiler-generated prologue for this function */ \ 179 | __asm__ ("nop" : : "a"(original_fn_ptr)); \ 180 | __asm__ ("mov %ebp, %esp"); \ 181 | __asm__ ("sub $0x4, %esp"); /* assuming the compiler-generated prologue saves only ebx */ \ 182 | __asm__ ("pop %ebx"); \ 183 | __asm__ ("pop %ebp"); \ 184 | __asm__ ("jmp *%eax"); \ 185 | } \ 186 | } 187 | 188 | struct reg_backup { 189 | void* r15; 190 | void* r14; 191 | void* r13; 192 | void* r12; 193 | void* rdi; 194 | void* rsi; 195 | void* rbx; 196 | void* rcx; 197 | void* rdx; 198 | void* r8; 199 | void* r9; 200 | }; 201 | 202 | /***********************************************************************/ 203 | /* GENERATE_STUB_x64 - macro to generate stub functions on x64 */ 204 | /* for Linux/MacOS x86_64 */ 205 | /***********************************************************************/ 206 | #ifdef __APPLE__ 207 | #define GENERATE_STUB_x64(FUNCTION_NAME, SYMBOL_NAME) \ 208 | void FUNCTION_NAME (void) __asm__ ( "_" #SYMBOL_NAME ); \ 209 | FUNCTION_BODY_x64(FUNCTION_NAME, SYMBOL_NAME) 210 | #else 211 | #define GENERATE_STUB_x64(FUNCTION_NAME, SYMBOL_NAME) \ 212 | void FUNCTION_NAME (void) __asm__ ( #SYMBOL_NAME ); \ 213 | FUNCTION_BODY_x64(FUNCTION_NAME, SYMBOL_NAME) 214 | #endif 215 | 216 | #define FUNCTION_BODY_x64(FUNCTION_NAME, SYMBOL_NAME) \ 217 | void FUNCTION_NAME (void) \ 218 | { \ 219 | int nptrs; \ 220 | void* buffer[TRACE_SIZE]; \ 221 | int call_original, return_error; \ 222 | int return_code, return_errno; \ 223 | reg_backup regs; \ 224 | static void * (*original_fn_ptr)(); \ 225 | /* we can't call write directly because it would prevent us for injecting faults in `write` 226 | (injecting requires the creation of a function with the same name but the prototype is 227 | different). We, use dlsym instead 228 | */ \ 229 | static int * (*original_write_ptr)(int, const void*, int); \ 230 | int initial_no_intercept; \ 231 | \ 232 | /* save non-volatiles */ \ 233 | __asm__ __volatile__ ("movq %%r9, %0" : "=m"(regs.r9) :); \ 234 | __asm__ __volatile__ ("movq %%r8, %0" : "=m"(regs.r8) :); \ 235 | __asm__ __volatile__ ("movq %%rdx, %0" : "=m"(regs.rdx) :); \ 236 | __asm__ __volatile__ ("movq %%rcx, %0" : "=m"(regs.rcx) :); \ 237 | /* restore non-volatiles */ \ 238 | __asm__ __volatile__ ("movq %%rbx, %0" : "=m"(regs.rbx) :); \ 239 | __asm__ __volatile__ ("movq %%rsi, %0" : "=m"(regs.rsi) :); \ 240 | __asm__ __volatile__ ("movq %%rdi, %0" : "=m"(regs.rdi) :); \ 241 | __asm__ __volatile__ ("movq %%r12, %0" : "=m"(regs.r12) :); \ 242 | __asm__ __volatile__ ("movq %%r13, %0" : "=m"(regs.r13) :); \ 243 | __asm__ __volatile__ ("movq %%r14, %0" : "=m"(regs.r14) :); \ 244 | __asm__ __volatile__ ("movq %%r15, %0" : "=m"(regs.r15) :); \ 245 | \ 246 | /* defaults */ \ 247 | call_original = 1; \ 248 | return_error = 0; \ 249 | return_code = 0; \ 250 | return_errno = 0; \ 251 | nptrs = 0; \ 252 | /* printf("intercepted %s\n", #FUNCTION_NAME); */ \ 253 | \ 254 | if (init_done) { \ 255 | initial_no_intercept = get_no_intercept(); \ 256 | if (0 == initial_no_intercept) { \ 257 | set_no_intercept(1); \ 258 | determine_action(function_info_ ## FUNCTION_NAME, #FUNCTION_NAME, \ 259 | regs.rdi, regs.rsi, regs.rcx, regs.rdx, regs.r8, regs.r9, \ 260 | &call_original, &return_error, &return_code, &return_errno); \ 261 | } \ 262 | } \ 263 | \ 264 | \ 265 | if(!original_fn_ptr) { \ 266 | original_fn_ptr = (void *(*)()) dlsym(RTLD_NEXT, #SYMBOL_NAME); \ 267 | if(!original_fn_ptr) \ 268 | printf("Unable to get address for function %s\n", #SYMBOL_NAME); \ 269 | } \ 270 | \ 271 | if (init_done) \ 272 | set_no_intercept(initial_no_intercept); \ 273 | \ 274 | if (return_error) \ 275 | { \ 276 | errno = return_errno; \ 277 | __asm__ ("" : : "a"(return_code)); \ 278 | return; \ 279 | } \ 280 | else if (call_original) \ 281 | { \ 282 | /* restore function arguments */ \ 283 | __asm__ __volatile__ ("movq %0, %%r9" : : "m"(regs.r9)); \ 284 | __asm__ __volatile__ ("movq %0, %%r8" : : "m"(regs.r8)); \ 285 | __asm__ __volatile__ ("movq %0, %%rdx" : : "m"(regs.rdx)); \ 286 | __asm__ __volatile__ ("movq %0, %%rcx" : : "m"(regs.rcx)); \ 287 | /* restore non-volatiles */ \ 288 | __asm__ __volatile__ ("movq %0, %%rbx" : : "m"(regs.rbx)); \ 289 | __asm__ __volatile__ ("movq %0, %%rsi" : : "m"(regs.rsi)); \ 290 | __asm__ __volatile__ ("movq %0, %%rdi" : : "m"(regs.rdi)); \ 291 | __asm__ __volatile__ ("movq %0, %%r12" : : "m"(regs.r12)); \ 292 | __asm__ __volatile__ ("movq %0, %%r13" : : "m"(regs.r13)); \ 293 | __asm__ __volatile__ ("movq %0, %%r14" : : "m"(regs.r14)); \ 294 | __asm__ __volatile__ ("movq %0, %%r15" : : "m"(regs.r15)); \ 295 | \ 296 | __asm__ ("leave"); \ 297 | __asm__ ("jmp *%%rax" : : "a"(original_fn_ptr)); \ 298 | } \ 299 | } 300 | 301 | 302 | #define STUB_VAR_DECL \ 303 | int log_fd, replay_fd; \ 304 | int init_done; 305 | -------------------------------------------------------------------------------- /cs-analyzer/CFGBuilder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include "CFGBuilder.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | 38 | #define _CRT_SECURE_NO_DEPRECATE 1 39 | 40 | CCFGBuilder::CCFGBuilder(void) 41 | { 42 | } 43 | 44 | CCFGBuilder::~CCFGBuilder(void) 45 | { 46 | } 47 | unsigned int GetLastOffset(const char* text) 48 | { 49 | const char *tab1, *tab2, *lineend; 50 | unsigned int lastoffset, coffset; 51 | 52 | lastoffset = 0; 53 | lineend = text; 54 | 55 | do { 56 | tab1 = strchr(lineend, '\t'); 57 | if (!tab1) 58 | break; 59 | 60 | coffset = strtoul(lineend, NULL, 16); 61 | if (coffset > lastoffset) 62 | lastoffset = coffset; 63 | 64 | lineend = strchr(lineend+1, '\n'); 65 | 66 | tab2 = strchr(tab1+1, '\t'); 67 | if (!tab2) 68 | break; 69 | /* handle null bytes in the instruction stream */ 70 | if (tab2 > lineend) 71 | continue; 72 | ++tab2; 73 | } while(tab1 && lineend); 74 | 75 | return lastoffset; 76 | } 77 | 78 | /* 79 | text should be of the form 80 | address:[ ]*\topcode[ ]*\tinstruction\n 81 | 82 | parsing is done in three phases: 83 | 1. finding the callcount-th function call to targetfn 84 | 2. determine jump targets within the code - O(n) 85 | 3. linear pass and construct the CFG - O(n) 86 | */ 87 | CFGraph* CCFGBuilder::Parse(const char* text, const char* targetfn, int callcount, long int* callAddress) 88 | { 89 | const char *tab1, *tab2, *lineend, *olineend; 90 | char address[16], instruction[256], jumptarget[16], buffer[256], function[256]; 91 | set jumptargets; 92 | unsigned int cjump, lastjumptarget, didjump, lastoffset, startaddress, endaddress; 93 | int indirectJumps, regularJumps; 94 | 95 | /* structures used to add the jxx-induced edges */ 96 | map startToBlockId; /* maps the jump targets to a block id (graph node id) */ 97 | vector fromBlockId; 98 | vector toAddress; 99 | 100 | int i, j; 101 | 102 | lastjumptarget = 0; 103 | startaddress = 0; 104 | lastoffset = 0; 105 | endaddress = GetLastOffset(text); 106 | didjump = 0; 107 | indirectJumps = 0; 108 | regularJumps = 0; 109 | lineend = text; 110 | 111 | do { 112 | while (' ' == *lineend) ++lineend; 113 | 114 | *callAddress = strtoul(lineend, NULL, 16); 115 | 116 | tab1 = strchr(lineend, '\t'); 117 | if (!tab1) 118 | break; 119 | 120 | lineend = strchr(lineend+1, '\n'); 121 | 122 | tab2 = strchr(tab1+1, '\t'); 123 | if (!tab2) 124 | break; 125 | /* handle null bytes in the instruction stream */ 126 | if (tab2 > lineend) 127 | continue; 128 | ++tab2; 129 | 130 | if (lineend) 131 | { 132 | if (lineend-tab2 < sizeof(instruction)) 133 | { 134 | strncpy(instruction, tab2, lineend-tab2); 135 | instruction[lineend-tab2] = 0; 136 | } else { 137 | cerr << "what a long instruction: " << tab2 << endl; 138 | continue; 139 | } 140 | 141 | // cerr << instruction << endl; 142 | if (0 == strncmp(instruction, "call", 4)) 143 | { 144 | /* determine the call target */ 145 | i = 0; 146 | while (' ' != instruction[++i]); 147 | while (' ' == instruction[++i]); 148 | 149 | j = i; 150 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 151 | if (j-i < sizeof(jumptarget)) 152 | { 153 | strncpy(jumptarget, &instruction[i], j-i); 154 | jumptarget[j-i] = 0; 155 | /* the call target can be many things. we're only interested in direct calls */ 156 | i = j; 157 | while (' ' == instruction[i]) ++i; 158 | if (instruction[i] == '<') { 159 | j = ++i; 160 | while (instruction[j] != '@' && instruction[j] != '>' && /* sanity check */ instruction[j] != '\0') ++j; 161 | if (j-i < sizeof(function)) { 162 | strncpy(function, &instruction[i], j-i); 163 | function[j-i] = 0; 164 | if (0 == strcmp(function, targetfn)) 165 | --callcount; 166 | } 167 | } 168 | } else { 169 | cerr << "jump target too big in " << instruction << endl; 170 | } 171 | } 172 | } 173 | } while(tab1 && lineend && callcount); 174 | 175 | if (callcount) /* no more calls to the function */ 176 | return NULL; 177 | 178 | olineend = lineend; 179 | 180 | // find the jump targets to construct the CFG 181 | do { 182 | while (' ' == *lineend) ++lineend; 183 | if (!startaddress) { 184 | tab1 = strchr(lineend, ':'); 185 | startaddress = strtoul(lineend, NULL, 16); 186 | endaddress = startaddress + 400; // semi-random, ~100 instructions are taken 187 | } 188 | tab1 = strchr(lineend, ':'); 189 | if (!tab1) 190 | break; 191 | 192 | cjump = strtoul(lineend, NULL, 16); 193 | if (cjump >= endaddress) 194 | { 195 | #ifdef DODEBUG 196 | cerr << "stopped parsing (again) at offset " << hex << cjump << endl; 197 | #endif 198 | break; 199 | } 200 | 201 | tab1 = strchr(lineend, '\t'); 202 | if (!tab1) 203 | break; 204 | 205 | lineend = strchr(lineend+1, '\n'); 206 | tab2 = strchr(tab1+1, '\t'); 207 | if (!tab2) 208 | break; 209 | 210 | /* handle null bytes in the instruction stream */ 211 | if (tab2 > lineend) 212 | continue; 213 | 214 | ++tab2; 215 | if (lineend) 216 | { 217 | if (lineend-tab2 < sizeof(instruction)) 218 | { 219 | strncpy(instruction, tab2, lineend-tab2); 220 | instruction[lineend-tab2] = 0; 221 | } else { 222 | cerr << "what an instruction: " << tab2 << endl; 223 | continue; 224 | } 225 | 226 | if ('j' == instruction[0]) 227 | { 228 | /* determine the jump target */ 229 | i = 0; 230 | while (' ' != instruction[++i]); 231 | while (' ' == instruction[++i]); 232 | 233 | j = i; 234 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 235 | strncpy(jumptarget, &instruction[i], j-i); 236 | jumptarget[j-i] = 0; 237 | 238 | cjump = strtoul(jumptarget, NULL, 16); 239 | if (0 == strcmp(jumptarget, "eax") || 240 | 0 == strcmp(jumptarget, "ebx") || 241 | 0 == strcmp(jumptarget, "ecx") || 242 | 0 == strcmp(jumptarget, "edx") || 243 | 0 == strcmp(jumptarget, "esi") || 244 | 0 == strcmp(jumptarget, "edi")) 245 | { 246 | /* skip it. there's nothing we can do right now */ 247 | } else { 248 | jumptargets.insert(jumptarget); 249 | } 250 | } 251 | ++lineend; 252 | } 253 | } while(tab1 && lineend); 254 | // done finding the jump targets 255 | 256 | 257 | lineend = olineend; 258 | 259 | /* and the second pass */ 260 | int cut, ret, prev, jmp; 261 | // lineend = text; 262 | cut = 0; 263 | ret = 0; 264 | jmp = 0; 265 | prev = 0; 266 | BasicBlock* currentBB = new BasicBlock(); 267 | CFGraph* graph = new CFGraph(); 268 | /* node 0 is the virtual start node - no instructions */ 269 | graph->m_vBasicBlocks.push_back(new BasicBlock()); 270 | 271 | do { 272 | while (' ' == *lineend) ++lineend; 273 | 274 | tab1 = strchr(lineend, ':'); 275 | if (!tab1) 276 | break; 277 | 278 | cjump = strtoul(lineend, NULL, 16); 279 | if (cjump >= endaddress) 280 | { 281 | #ifdef DODEBUG 282 | cerr << "stopped parsing (again) at offset " << hex << cjump << endl; 283 | #endif 284 | break; 285 | } 286 | 287 | if (tab1-lineend < sizeof(address)) 288 | { 289 | strncpy(address, lineend, tab1-lineend); 290 | address[tab1-lineend] = 0; 291 | } 292 | else { 293 | lineend = strchr(lineend+1, '\n'); 294 | continue; 295 | } 296 | 297 | tab1 = strchr(lineend, '\t'); 298 | 299 | lineend = strchr(lineend+1, '\n'); 300 | 301 | tab2 = strchr(tab1+1, '\t'); 302 | if (!tab2) 303 | break; 304 | /* handle null bytes in the instruction stream */ 305 | if (tab2 > lineend) 306 | continue; 307 | 308 | ++tab2; 309 | if (lineend) 310 | { 311 | if (cut || ret || jumptargets.find(address) != jumptargets.end()) 312 | { 313 | /* if a ret was encountered add an extra edge to -1 (exit node) */ 314 | if (ret) 315 | { 316 | graph->Add(EXIT_NODE, prev+1); 317 | } 318 | if (!jmp) 319 | graph->Add(prev+1, prev); 320 | 321 | ++prev; 322 | graph->m_vBasicBlocks.push_back(currentBB); 323 | currentBB = new BasicBlock(); 324 | if (!currentBB) 325 | { 326 | cerr << "Out of memory!" << endl; 327 | exit(-1); 328 | } 329 | 330 | if (jumptargets.find(address) != jumptargets.end()) 331 | { 332 | startToBlockId.insert(map::value_type(address, prev+1)); 333 | } 334 | if (ret || (cut && 'j' == instruction[0] && 'm' == instruction[1] && 'p' == instruction[2])) 335 | { 336 | jmp = 1; 337 | } 338 | else 339 | { 340 | jmp = 0; 341 | } 342 | cut = ret = 0; 343 | } 344 | 345 | strncpy(instruction, tab2, lineend-tab2); 346 | instruction[lineend-tab2] = 0; 347 | 348 | if ('j' == instruction[0]) 349 | { 350 | /* determine the jump target */ 351 | i = 0; 352 | while (' ' != instruction[++i]); 353 | while (' ' == instruction[++i]); 354 | 355 | j = i; 356 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 357 | strncpy(jumptarget, &instruction[i], j-i); 358 | jumptarget[j-i] = 0; 359 | 360 | cjump = strtoul(jumptarget, NULL, 16); 361 | if (0 == strcmp(jumptarget, "eax") || 362 | 0 == strcmp(jumptarget, "ebx") || 363 | 0 == strcmp(jumptarget, "ecx") || 364 | 0 == strcmp(jumptarget, "edx") || 365 | 0 == strcmp(jumptarget, "esi") || 366 | 0 == strcmp(jumptarget, "edi")) 367 | { 368 | /* skip it. there's nothing we can do right now */ 369 | ++indirectJumps; 370 | } 371 | /* 372 | check for tail call 373 | replace jmp with call + ret 374 | */ 375 | else if (cjump < startaddress || cjump > endaddress) 376 | { 377 | /* jump goes outside the chunk of code we analyze. 378 | discarding... */ 379 | 380 | /* ret = 1; */ 381 | cut = 1; 382 | } else { 383 | #ifdef DODEBUG 384 | cerr << "found a jump to " << jumptarget << endl; 385 | #endif 386 | ++regularJumps; 387 | fromBlockId.push_back(prev+1); 388 | toAddress.push_back(jumptarget); 389 | cut = 1; 390 | } 391 | } 392 | else if (0 == strncmp(instruction, "ret", 3) || /* handle a gcc optimization */ 0 == strncmp(instruction, "repz ret", 8)) 393 | { 394 | ret = 1; 395 | } 396 | 397 | currentBB->m_listInstructions.push_back(instruction); 398 | ++lineend; 399 | } 400 | } while(tab1 && lineend); 401 | 402 | /* if a ret is encountered add an extra edge to the exit node */ 403 | if (ret) 404 | { 405 | graph->Add(EXIT_NODE, prev+1); 406 | } 407 | if (!jmp) 408 | { 409 | graph->Add(prev+1, prev); 410 | } 411 | graph->m_vBasicBlocks.push_back(currentBB); 412 | 413 | 414 | /* add the jxx-induced edges */ 415 | map::iterator it; 416 | vector::const_iterator it1; 417 | vector::const_iterator it2; 418 | for (it1 = fromBlockId.begin(), it2 = toAddress.begin(); 419 | it1 != fromBlockId.end() && it2 != toAddress.end(); 420 | ++it1, ++it2) 421 | { 422 | it = startToBlockId.find(*it2); 423 | if (it != startToBlockId.end()) 424 | { 425 | graph->Add(it->second, *it1); 426 | } 427 | else 428 | { 429 | /* BUG. should never happen */ 430 | } 431 | } 432 | /* cerr << regularJumps << ";" << indirectJumps << ";" << endl; */ 433 | return graph; 434 | } 435 | -------------------------------------------------------------------------------- /profiler/CFGBuilderSPARC.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | 28 | /* support for SPARC shared objects is still in beta */ 29 | 30 | #include "CFGBuilderSPARC.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | using namespace std; 38 | 39 | #define _CRT_SECURE_NO_DEPRECATE 1 40 | #define DODEBUG 41 | 42 | CCFGBuilderSPARC::CCFGBuilderSPARC(void) 43 | { 44 | } 45 | 46 | CCFGBuilderSPARC::~CCFGBuilderSPARC(void) 47 | { 48 | } 49 | 50 | int CCFGBuilderSPARC::IsBranchInstruction(char* instruction) 51 | { 52 | char jumps[][8] = { "b", "ba", "bn", "bu", "bg", "bug", "bl", "bul", "blg", "bne", "be", "bue", "bge", "buge", "ble", "bule", "bo", "bcc", "bleu", "brz", "brlez", "brlz", "brgez", "brnz", "brgz", 53 | "bcs", "bpos", "bneg", "bvc", "bvs", "bpa", "bpn", "bpne", "bpe", "bpg", "bple", "bpge", "bpl", "bpgu", "bpleu", "bpcc", "bpcs", "bppos", "bpneg", "bpvc", "bpvs"}; 54 | int i; 55 | 56 | for (i = 0; i < sizeof(jumps)/sizeof(jumps[0]); ++i) 57 | if (0 == strncmp(instruction, jumps[i], strlen(jumps[i])) && (' ' == instruction[strlen(jumps[i])] || ',' == instruction[strlen(jumps[i])])) 58 | return 1; 59 | return 0; 60 | } 61 | 62 | 63 | unsigned int GetLastOffsetSPARC(const char* text) 64 | { 65 | const char *tab1, *tab2, *lineend; 66 | unsigned int lastoffset, coffset; 67 | 68 | lastoffset = 0; 69 | lineend = text; 70 | 71 | do { 72 | tab1 = strchr(lineend, '\t'); 73 | if (!tab1) 74 | break; 75 | 76 | coffset = strtoul(lineend, NULL, 16); 77 | if (coffset > lastoffset) 78 | lastoffset = coffset; 79 | 80 | lineend = strchr(lineend+1, '\n'); 81 | 82 | tab2 = strchr(tab1+1, '\t'); 83 | if (!tab2) 84 | break; 85 | /* handle null bytes in the instruction stream */ 86 | if (tab2 > lineend) 87 | continue; 88 | ++tab2; 89 | } while(tab1 && lineend); 90 | 91 | return lastoffset; 92 | } 93 | 94 | /* 95 | text should be of the form 96 | address:[ ]*\topcode[ ]*\tinstruction\n 97 | 98 | parsing is done in two phases: 99 | 1. determine jump targets within the code - O(n) 100 | 2. linear pass and construct the CFG - O(n) 101 | */ 102 | CFGraph* CCFGBuilderSPARC::Parse(const char* text) 103 | { 104 | const char *tab1, *tab2, *lineend; 105 | char address[16], instruction[256], branch_instruction[256], jumptarget[16], buffer[256]; 106 | set jumptargets; 107 | unsigned int cjump, lastjumptarget, didjump, lastoffset, startaddress, endaddress; 108 | int indirectJumps, regularJumps; 109 | 110 | /* structures used to add the jxx-induced edges */ 111 | map startToBlockId; /* maps the jump targets to a block id (graph node id) */ 112 | vector fromBlockId; 113 | vector toAddress; 114 | 115 | int i, j; 116 | 117 | lastjumptarget = 0; 118 | startaddress = 0; 119 | endaddress = GetLastOffsetSPARC(text); 120 | didjump = 0; 121 | indirectJumps = 0; 122 | regularJumps = 0; 123 | lineend = text; 124 | 125 | 126 | do { 127 | tab1 = strchr(lineend, '\t'); 128 | if (!tab1) 129 | break; 130 | 131 | lastoffset = strtoul(lineend, NULL, 16); 132 | if (!startaddress) 133 | { 134 | startaddress = lastoffset; 135 | #ifdef DODEBUG 136 | cerr << "last off: " << hex << startaddress << " " << endaddress << endl; 137 | #endif 138 | } 139 | lineend = strchr(lineend+1, '\n'); 140 | 141 | tab2 = strchr(tab1+1, '\t'); 142 | if (!tab2) 143 | break; 144 | /* handle null bytes in the instruction stream */ 145 | if (tab2 > lineend) 146 | continue; 147 | ++tab2; 148 | 149 | if (lineend) 150 | { 151 | if (lineend-tab2 < sizeof(instruction)) 152 | { 153 | strncpy(instruction, tab2, lineend-tab2); 154 | instruction[lineend-tab2] = 0; 155 | 156 | } else { 157 | cerr << "what an instruction: " << tab2 << endl; 158 | continue; 159 | } 160 | 161 | if (IsBranchInstruction(instruction) || 0 == strncmp(instruction, "call", 4)) 162 | { 163 | /* determine the jump target */ 164 | i = 0; 165 | while (' ' != instruction[++i]); 166 | while (' ' == instruction[++i]); 167 | 168 | j = i; 169 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 170 | if (j-i < sizeof(jumptarget)) 171 | { 172 | strncpy(jumptarget, &instruction[i], j-i); 173 | jumptarget[j-i] = 0; 174 | if (jumptarget[0] != '%') 175 | { 176 | cjump = strtoul(jumptarget, NULL, 16); 177 | if (cjump >= startaddress && cjump <= endaddress) 178 | { 179 | if (cjump > lastjumptarget) 180 | lastjumptarget = cjump; 181 | jumptargets.insert(jumptarget); 182 | } else { 183 | if (strncmp(instruction, "ba", 3) && strncmp(instruction, "call", 4)) 184 | cerr << "WARNING: tail call detected in non-ba instruction (" << instruction << "). Is this the entire function?" << endl; 185 | } 186 | } 187 | } 188 | else 189 | { 190 | cerr << "what a jump target: " << instruction << " !" << endl; 191 | } 192 | } 193 | 194 | if (0 == strncmp(instruction, "ret", 3)) 195 | { 196 | /* yes, ge */ 197 | if (lastoffset >= lastjumptarget) 198 | { 199 | #ifdef DODEBUG 200 | cerr << "stopped parsing at offset " << hex << lastoffset << endl; 201 | #endif 202 | /* also process the instruction immediately after this due to SPARC's pipelined execution */ 203 | lastjumptarget = lastoffset+5; 204 | break; 205 | } 206 | } 207 | } 208 | } while(tab1 && lineend); 209 | 210 | if (!lastjumptarget) 211 | { 212 | cerr << "WARNING: no jump or ret instructions encountered. Is this the entire function?" << endl; 213 | lastjumptarget = 0xffffffff; 214 | } 215 | 216 | /* and the second pass */ 217 | int cut, ret, prev, jmp, ignore_current, delay_slot; 218 | lineend = text; 219 | cut = 0; 220 | ret = 0; 221 | jmp = 0; 222 | prev = 0; 223 | delay_slot = 0; 224 | BasicBlock* currentBB = new BasicBlock(); 225 | CFGraph* graph = new CFGraph(); 226 | /* node 0 is the virtual start node - no instructions */ 227 | graph->m_vBasicBlocks.push_back(new BasicBlock()); 228 | 229 | do { 230 | while (' ' == *lineend) ++lineend; 231 | 232 | tab1 = strchr(lineend, ':'); 233 | if (!tab1) 234 | break; 235 | 236 | cjump = strtoul(lineend, NULL, 16); 237 | if (cjump >= lastjumptarget) 238 | { 239 | #ifdef DODEBUG 240 | cerr << "stopped parsing (again) at offset " << hex << cjump << endl; 241 | #endif 242 | break; 243 | } 244 | 245 | if (tab1-lineend < sizeof(address)) 246 | { 247 | strncpy(address, lineend, tab1-lineend); 248 | address[tab1-lineend] = 0; 249 | } 250 | else { 251 | lineend = strchr(lineend+1, '\n'); 252 | continue; 253 | } 254 | 255 | tab1 = strchr(lineend, '\t'); 256 | 257 | lineend = strchr(lineend+1, '\n'); 258 | 259 | tab2 = strchr(tab1+1, '\t'); 260 | if (!tab2) 261 | break; 262 | /* handle null bytes in the instruction stream */ 263 | if (tab2 > lineend) 264 | continue; 265 | 266 | ++tab2; 267 | if (lineend) 268 | { 269 | ignore_current = 0; 270 | if (cut || ret || jumptargets.find(address) != jumptargets.end()) 271 | { 272 | if (delay_slot) 273 | { 274 | ignore_current = 1; 275 | 276 | strncpy(instruction, tab2, lineend-tab2); 277 | instruction[lineend-tab2] = 0; 278 | currentBB->m_listInstructions.push_back(instruction); 279 | currentBB->m_listInstructions.push_back(branch_instruction); 280 | delay_slot = 0; 281 | } 282 | 283 | /* if a ret was encountered add an extra edge to -1 (exit node) */ 284 | if (ret) 285 | { 286 | graph->Add(EXIT_NODE, prev+1); 287 | } 288 | if (!jmp) 289 | graph->Add(prev+1, prev); 290 | 291 | ++prev; 292 | graph->m_vBasicBlocks.push_back(currentBB); 293 | currentBB = new BasicBlock(); 294 | if (!currentBB) 295 | { 296 | cerr << "Out of memory!" << endl; 297 | exit(-1); 298 | } 299 | 300 | if (jumptargets.find(address) != jumptargets.end()) 301 | { 302 | /* unlikely that the instruction after a branch is a jump target */ 303 | /* however, if it is, it will be copyied to both BBs */ 304 | startToBlockId.insert(map::value_type(address, prev+1)); 305 | } 306 | if (ret || (cut && 'b' == instruction[0] && 'a' == instruction[1])) 307 | { 308 | /* TODO calls can be jmps in SPARC */ 309 | jmp = 1; 310 | } 311 | else 312 | { 313 | jmp = 0; 314 | } 315 | cut = ret = 0; 316 | } 317 | 318 | if (!ignore_current) { 319 | strncpy(instruction, tab2, lineend-tab2); 320 | instruction[lineend-tab2] = 0; 321 | 322 | delay_slot = 1; 323 | if (IsBranchInstruction(instruction)) 324 | { 325 | /* determine the jump target */ 326 | i = 0; 327 | while (' ' != instruction[++i]); 328 | while (' ' == instruction[++i]); 329 | 330 | j = i; 331 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 332 | strncpy(jumptarget, &instruction[i], j-i); 333 | jumptarget[j-i] = 0; 334 | 335 | cjump = strtoul(jumptarget, NULL, 16); 336 | if (jumptarget[0] == '%') 337 | { 338 | /* skip it. there's nothing we can do right now */ 339 | ++indirectJumps; 340 | } 341 | /* 342 | check for tail call 343 | replace jmp with call + ret 344 | */ 345 | else if (cjump < startaddress || cjump > endaddress) 346 | { 347 | sprintf(buffer, "call %s", &instruction[i]); 348 | currentBB->m_listInstructions.push_back(buffer); 349 | strcpy(instruction, "ret"); 350 | ret = 1; 351 | } else { 352 | ++regularJumps; 353 | fromBlockId.push_back(prev+1); 354 | toAddress.push_back(jumptarget); 355 | cut = 1; 356 | } 357 | } 358 | else if (0 == strncmp(instruction, "call", 4)) 359 | { 360 | /* determine the jump target */ 361 | i = 0; 362 | while (' ' != instruction[++i]); 363 | while (' ' == instruction[++i]); 364 | 365 | j = i; 366 | while (' ' != instruction[++j] && /* sanity check */ instruction[j] != 0); 367 | strncpy(jumptarget, &instruction[i], j-i); 368 | jumptarget[j-i] = 0; 369 | 370 | cjump = strtoul(jumptarget, NULL, 16); 371 | if (cjump > startaddress && cjump <= endaddress) 372 | { 373 | /* actually a jump */ 374 | fromBlockId.push_back(prev+1); 375 | toAddress.push_back(jumptarget); 376 | cut = 1; 377 | /* discard %o7 for now */ 378 | sprintf(instruction, "ba %s", &instruction[i]); 379 | } 380 | 381 | } 382 | else if (0 == strncmp(instruction, "ret", 3)) 383 | { 384 | ret = 1; 385 | } 386 | else 387 | { 388 | delay_slot = 0; 389 | } 390 | 391 | if (delay_slot) 392 | { 393 | strcpy(branch_instruction, instruction); 394 | } 395 | else 396 | { 397 | currentBB->m_listInstructions.push_back(instruction); 398 | } 399 | } 400 | ++lineend; 401 | } 402 | } while(tab1 && lineend); 403 | 404 | /* if a ret is encountered add an extra edge to EXIT_NODE */ 405 | if (ret) 406 | { 407 | graph->Add(EXIT_NODE, prev+1); 408 | } 409 | if (!jmp) 410 | { 411 | graph->Add(prev+1, prev); 412 | } 413 | graph->m_vBasicBlocks.push_back(currentBB); 414 | 415 | 416 | /* add the jxx-induced edges */ 417 | map::iterator it; 418 | vector::const_iterator it1; 419 | vector::const_iterator it2; 420 | for (it1 = fromBlockId.begin(), it2 = toAddress.begin(); 421 | it1 != fromBlockId.end() && it2 != toAddress.end(); 422 | ++it1, ++it2) 423 | { 424 | it = startToBlockId.find(*it2); 425 | if (it != startToBlockId.end()) 426 | { 427 | graph->Add(it->second, *it1); 428 | } 429 | else 430 | { 431 | /* BUG. should never happen */ 432 | } 433 | } 434 | return graph; 435 | } 436 | -------------------------------------------------------------------------------- /cs-analyzer/CFG.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "CFGBuilder.h" 34 | #include 35 | #include 36 | 37 | #define MAGIC_FNPTR_CALL "call 00000000 " 38 | 39 | #ifdef __x86_64__ 40 | #error "x86_64 target not supported" 41 | #endif 42 | 43 | using namespace std; 44 | 45 | struct BFPathElement 46 | { 47 | int bbId; 48 | string target; 49 | 50 | int bbIdParent; 51 | string targetParent; 52 | }; 53 | 54 | struct RetCheckElement 55 | { 56 | int bbId; 57 | set retLocations; 58 | set errnoLocations; 59 | }; 60 | 61 | bool operator<(const BFPathElement& lhs, const BFPathElement& rhs) 62 | { 63 | if (lhs.bbId < rhs.bbId) 64 | return true; 65 | if (lhs.bbId == rhs.bbId && lhs.target < rhs.target) 66 | return true; 67 | 68 | return false; 69 | } 70 | 71 | struct SideEffectElement 72 | { 73 | int bbId; 74 | int type; /* 0 - global variable, 1 - argument */ 75 | set targets; 76 | }; 77 | 78 | 79 | int CompareSEElements(const SideEffectElement& e1, const SideEffectElement& e2) 80 | { 81 | set::const_iterator it1, it2; 82 | 83 | for (it1 = e1.targets.begin(),it2 = e2.targets.begin(); 84 | it1 != e1.targets.end() && it2 != e2.targets.end(); 85 | ++it1, ++it2) 86 | { 87 | if (*it1 < *it2) 88 | return -1; 89 | if (*it1 > *it2) 90 | return 1; 91 | } 92 | 93 | return 0; 94 | } 95 | 96 | bool operator<(const SideEffectElement& lhs, const SideEffectElement& rhs) 97 | { 98 | if (lhs.bbId < rhs.bbId) 99 | return true; 100 | if (lhs.bbId == rhs.bbId && lhs.targets.size() < rhs.targets.size()) 101 | return true; 102 | 103 | if (lhs.bbId == rhs.bbId && lhs.targets.size() == rhs.targets.size()) 104 | return (-1 == CompareSEElements(lhs, rhs)); 105 | 106 | return false; 107 | } 108 | void prel(ostream& outf, const SideEffectElement& e); 109 | 110 | void prel(const SideEffectElement& e) 111 | { 112 | return prel(cerr, e); 113 | } 114 | 115 | void prel(ostream& outf, const SideEffectElement& e) 116 | { 117 | outf << "id " << e.bbId << endl; 118 | outf << "size " << e.targets.size() << endl; 119 | for (set::const_iterator it = e.targets.begin(); 120 | it != e.targets.end(); 121 | ++it) 122 | { 123 | outf << *it << " " << endl; 124 | } 125 | } 126 | 127 | bool IsIncluded(SideEffectElement& s, const SideEffectElement& e) 128 | { 129 | set::const_iterator it1; 130 | bool rv = true; 131 | 132 | it1 = e.targets.begin(); 133 | 134 | for (; it1 != e.targets.end(); ++it1) 135 | { 136 | if (s.targets.insert(*it1).second) 137 | rv = false; 138 | } 139 | 140 | return rv; 141 | } 142 | 143 | typedef vector BFPath; 144 | 145 | string GetText(const char* pszAsmPath) 146 | { 147 | string line, contents; 148 | ifstream inf(pszAsmPath); 149 | 150 | if (inf.is_open()) 151 | { 152 | while (!inf.eof() && inf.good()) 153 | { 154 | getline(inf, line); 155 | contents += line; 156 | contents += "\n"; 157 | } 158 | } 159 | return contents; 160 | } 161 | 162 | int IsRegisterx86(string target) 163 | { 164 | return (target[0] != 'D' && target[0] != 'W' && target[0] != '['); 165 | } 166 | 167 | void CheckEax(CFGraph* graph, int start, vector& checkedAgainst, vector& errnoCheckedAgainst) 168 | { 169 | CFGraph::AdjListElement* head; 170 | queue bfQueue; 171 | 172 | RetCheckElement anElement; 173 | 174 | BasicBlock* aBB; 175 | list::const_iterator itr; 176 | set setVisited; 177 | 178 | ofstream outref; 179 | int directCalls = 0, indirectCalls = 0; 180 | 181 | string aTarget, anotherTarget, instruction; 182 | 183 | int i, j, ival; 184 | int retcall; 185 | 186 | checkedAgainst.clear(); 187 | errnoCheckedAgainst.clear(); 188 | if (head = graph->GetHead(start)) 189 | { 190 | anElement.retLocations.insert("eax"); 191 | do { 192 | anElement.bbId = head->m_bbIndex; 193 | bfQueue.push(anElement); 194 | setVisited.insert(anElement.bbId); 195 | } while (head = head->m_pNext); 196 | } 197 | while (!bfQueue.empty()) 198 | { 199 | anElement = bfQueue.front(); 200 | bfQueue.pop(); 201 | 202 | aBB = graph->m_vBasicBlocks[anElement.bbId]; 203 | 204 | retcall = 0; 205 | for (itr = aBB->m_listInstructions.begin(); 206 | itr != aBB->m_listInstructions.end(); 207 | ++itr) 208 | { 209 | i = 0; 210 | while (' ' != (*itr)[++i]); 211 | instruction = itr->substr(0, i); 212 | 213 | if ("mov" == instruction) 214 | { 215 | /* moving to `target`? */ 216 | i = 3; 217 | while(' ' == (*itr)[++i]); 218 | j = i; 219 | while(',' != (*itr)[++j]); 220 | aTarget = itr->substr(i, j-i); 221 | 222 | if (anElement.retLocations.find(aTarget) != anElement.retLocations.end()) 223 | { 224 | anElement.retLocations.erase(aTarget); 225 | /* cerr << "removing " << aTarget << endl; */ 226 | } 227 | 228 | /* moving a target somewhere? */ 229 | while(' ' == (*itr)[++j]); 230 | i = j; 231 | while(/* ' ' != (*itr)[j] && */ '\0' != (*itr)[j]) ++j; 232 | anotherTarget = itr->substr(i, j-i); 233 | if (anElement.retLocations.find(anotherTarget) != anElement.retLocations.end()) 234 | { 235 | anElement.retLocations.insert(aTarget); 236 | /* cerr << "adding " << aTarget << " due to " << anotherTarget << endl; */ 237 | } else { 238 | /* cerr << "NOT adding " << aTarget << " due to " << anotherTarget << endl; */ 239 | } 240 | if (anElement.errnoLocations.find(anotherTarget) != anElement.errnoLocations.end()) 241 | { 242 | anElement.errnoLocations.insert(aTarget); 243 | /* cerr << "adding errno" << aTarget << " due to " << anotherTarget << endl; */ 244 | } else { 245 | /* cerr << "NOT adding errno" << aTarget << " due to " << anotherTarget << endl; */ 246 | } 247 | } else if ("lea" == instruction) { 248 | /* loading to `target`? */ 249 | /* may also want to check if a watched location is loaded to target */ 250 | i = 3; 251 | while(' ' == (*itr)[++i]); 252 | j = i; 253 | while(',' != (*itr)[++j]); 254 | aTarget = itr->substr(i, j-i); 255 | 256 | if (anElement.retLocations.find(aTarget) != anElement.retLocations.end()) 257 | { 258 | anElement.retLocations.erase(aTarget); 259 | } 260 | } else if ("call" == instruction) { 261 | if (anElement.retLocations.find("eax") != anElement.retLocations.end()) 262 | { 263 | anElement.retLocations.erase("eax"); 264 | } 265 | // check for errno 266 | i = 4; 267 | while(' ' == (*itr)[++i]); 268 | while(' ' != (*itr)[i++]); 269 | j = i+1; 270 | while('\0' != (*itr)[++j]); 271 | aTarget = itr->substr(i, j-i); 272 | 273 | if ("<__errno_location@plt>" == aTarget) { 274 | // errno is retrieved 275 | anElement.errnoLocations.insert("DWORD PTR [eax]"); 276 | } 277 | } else if ("ret" == instruction) { 278 | if (anElement.retLocations.find("eax") != anElement.retLocations.end()) 279 | { 280 | checkedAgainst.push_back(-667); 281 | } 282 | } else if ("cmp" == instruction || "test" == instruction) { 283 | /* moving to `target`? */ 284 | i = 3; 285 | if ("test" == instruction) 286 | ++i; 287 | while(' ' == (*itr)[++i]); 288 | j = i; 289 | while(',' != (*itr)[++j]); 290 | aTarget = itr->substr(i, j-i); 291 | 292 | /* moving a target somewhere? */ 293 | while(' ' == (*itr)[++j]); 294 | i = j; 295 | while(' ' != (*itr)[j] && '\0' != (*itr)[j]) ++j; 296 | anotherTarget = itr->substr(i, j-i); 297 | 298 | /* cerr << instruction << " " << aTarget << " " << anotherTarget << endl;*/ 299 | 300 | /* test eax, eax for example */ 301 | if ("test" == instruction && 302 | aTarget == anotherTarget && 303 | anElement.retLocations.find(aTarget) != anElement.retLocations.end()) { 304 | // cerr << "comparing to 0" << endl; 305 | checkedAgainst.push_back(0); 306 | } else { 307 | if (anElement.retLocations.find(aTarget) != anElement.retLocations.end()) 308 | { 309 | // cerr << "comparing to " << anotherTarget << endl; 310 | if (anotherTarget.size() < 3 || anotherTarget[0] != '0') 311 | cerr << "Comparing to non-const val: " << anotherTarget << endl; 312 | else { 313 | checkedAgainst.push_back(strtoul(anotherTarget.c_str(), NULL, 16)); 314 | // cerr << "pushing " << anotherTarget << endl; 315 | } 316 | } 317 | if (anElement.retLocations.find(anotherTarget) != anElement.retLocations.end()) 318 | { 319 | // cerr << "comparing to " << aTarget << endl; 320 | if (aTarget.size() < 3 || aTarget[0] != '0') 321 | cerr << "Comparing to non-const val: " << aTarget << endl; 322 | else { 323 | checkedAgainst.push_back(strtoul(aTarget.c_str(), NULL, 16)); 324 | // cerr << "pushing " << aTarget << endl; 325 | } 326 | } 327 | 328 | //same thing for errno 329 | if (anElement.errnoLocations.find(aTarget) != anElement.errnoLocations.end()) 330 | { 331 | // cerr << "comparing to " << anotherTarget << endl; 332 | if (anotherTarget.size() < 3 || anotherTarget[0] != '0') 333 | cerr << "Comparing to non-const val: " << anotherTarget << endl; 334 | else { 335 | errnoCheckedAgainst.push_back(strtoul(anotherTarget.c_str(), NULL, 16)); 336 | // cerr << "pushing " << anotherTarget << endl; 337 | } 338 | } 339 | if (anElement.errnoLocations.find(anotherTarget) != anElement.errnoLocations.end()) 340 | { 341 | // cerr << "comparing to " << aTarget << endl; 342 | if (aTarget.size() < 3 || aTarget[0] != '0') 343 | cerr << "Comparing to non-const val: " << aTarget << endl; 344 | else { 345 | errnoCheckedAgainst.push_back(strtoul(aTarget.c_str(), NULL, 16)); 346 | // cerr << "pushing " << aTarget << endl; 347 | } 348 | } 349 | 350 | } 351 | } 352 | } 353 | 354 | if (head = graph->GetHead(anElement.bbId)) 355 | { 356 | do { 357 | anElement.bbId = head->m_bbIndex; 358 | if (anElement.bbId != EXIT_NODE && setVisited.insert(anElement.bbId).second) 359 | { 360 | bfQueue.push(anElement); 361 | } 362 | } while (head = head->m_pNext); 363 | } 364 | } 365 | } 366 | 367 | bool get_file_line(char* binary, unsigned long address, char* src, int* line) 368 | { 369 | int pipefd[2]; 370 | char addr2linecmd[1024]; 371 | 372 | char buffer[1024]; 373 | ssize_t readc; 374 | 375 | char* colon; 376 | 377 | src[0] = 0; 378 | if (-1 != pipe(pipefd)) { 379 | sprintf(addr2linecmd, "addr2line -e %s %lx 1>&%d", binary, address, pipefd[1]); 380 | system(addr2linecmd); 381 | close(pipefd[1]); 382 | 383 | readc = read(pipefd[0], buffer, sizeof(buffer)); 384 | buffer[readc] = 0; 385 | close(pipefd[0]); 386 | 387 | colon = strchr(buffer, ':'); 388 | if (colon) { 389 | colon[0] = 0; 390 | strcpy(src, buffer); 391 | *line = atoi(colon+1); 392 | } 393 | } 394 | 395 | if (0 == src[0] || 0 == strcmp(src, "??")) 396 | return false; 397 | return true; 398 | } 399 | 400 | int get_times_executed(char* file, int line) 401 | { 402 | char* slash; 403 | char gcov[1024], dir[1024] = ""; 404 | 405 | if ('/' == file[0]) { 406 | // good, absolute path 407 | slash = strrchr(file, '/'); 408 | slash[0] = '\0'; 409 | strcpy(dir, file); 410 | slash[0] = '/'; 411 | 412 | strcpy(gcov, file); 413 | } else { 414 | 415 | } 416 | 417 | strcpy(gcov, file); 418 | strcat(gcov, ".gcov"); 419 | 420 | char cmdline[1024]; 421 | int pipefd[2]; 422 | char buffer[1024]; 423 | ssize_t readc; 424 | 425 | char *colon; 426 | int execcountidx; 427 | int execcount = -1; 428 | 429 | if (-1 != pipe(pipefd)) { 430 | sprintf(cmdline, "cd %s; gcov %s > /dev/null", dir, file); 431 | system(cmdline); 432 | 433 | sprintf(cmdline, "cat %s 2>/dev/null | grep %d: 1>&%d", gcov, line, pipefd[1]); 434 | system(cmdline); 435 | close(pipefd[1]); 436 | 437 | readc = read(pipefd[0], buffer, sizeof(buffer)); 438 | buffer[readc] = 0; 439 | close(pipefd[0]); 440 | 441 | colon = strchr(buffer, ':'); 442 | if (colon) { 443 | if ('#' == *(colon-1)) { 444 | // not executed 445 | execcount = 0; 446 | } else if ('-' == *(colon-1)) { 447 | // not executable - shouldn't happen 448 | } else { 449 | execcountidx = 1; 450 | while (colon-execcountidx > &buffer[0] && isdigit(*(colon-execcountidx))) 451 | execcountidx++; 452 | execcount = atoi(colon-execcountidx); 453 | } 454 | } 455 | } else { 456 | perror("pipe"); 457 | } 458 | return execcount; 459 | } 460 | 461 | int main(int argc, char* argv[]) 462 | { 463 | CFGraph *graph, *tgraph; 464 | char *targetfn = NULL; 465 | char *infile = NULL; 466 | const char *defretval = "-1"; 467 | const char *deferrno = "EINVAL"; 468 | const char* fninterestdefault[] = { 469 | "opendir", 470 | "getcwd", 471 | "fdopen", 472 | "popen", 473 | "getlogin", 474 | "cuserid", 475 | "getspnam", 476 | "getspent" 477 | }; 478 | vector fninterest; 479 | 480 | CCFGBuilder builder; 481 | string asma; 482 | 483 | if (argc <= 2) { 484 | cerr << argv[0] << " |- [default retval] [default errno]" << endl; 485 | exit(1); 486 | } 487 | 488 | infile = argv[1]; 489 | targetfn = argv[2]; 490 | if (targetfn[0] != '-') { 491 | fninterest.push_back(targetfn); 492 | } else { 493 | int i; 494 | for (i = 0; i < sizeof(fninterestdefault)/sizeof(fninterestdefault[0]); ++i) 495 | fninterest.push_back(fninterestdefault[i]); 496 | } 497 | if (argc > 3) 498 | defretval = argv[3]; 499 | if (argc > 4) 500 | deferrno = argv[4]; 501 | char objdumpcmd[1024]; 502 | sprintf(objdumpcmd, "objdump -d -M intel %s > x.asm", infile); 503 | 504 | system(objdumpcmd); 505 | 506 | asma = GetText("x.asm"); 507 | if (asma.empty()) 508 | { 509 | cerr << "Invalid or non-existing file: " << infile << endl; 510 | return -1; 511 | } 512 | system("rm -f x.asm"); 513 | int cc = 1; 514 | long int callAddress = 0; 515 | int triggerc = 0; 516 | vector checkedAgainst, errnoCheckedAgainst; 517 | 518 | cout << "" << endl; 519 | for (vector::const_iterator it = fninterest.begin(), itend = fninterest.end(); 520 | it != itend; ++it) { 521 | cc = 1; 522 | cerr << "Analyzing calls to function " << *it << endl; 523 | while(1) { 524 | graph = builder.Parse(asma.c_str(), it->c_str(), cc, &callAddress); 525 | if (!graph) { 526 | cerr << "function " << *it << " referenced " << dec << cc-1 << " times" << endl << endl; 527 | break; 528 | } 529 | cc++; 530 | 531 | CFGraph::AdjListElement* head; 532 | int i = 0; 533 | set targetBBs; 534 | list::const_iterator its; 535 | 536 | char* fn = strrchr(infile, '/'); 537 | if (!fn) fn = infile; 538 | else fn = fn + 1; 539 | 540 | ofstream outf; 541 | int a, b; 542 | char addr2linecmd[1024]; 543 | char src[1024]; 544 | int line, lineexec; 545 | 546 | CheckEax(graph, 0, checkedAgainst, errnoCheckedAgainst); 547 | if (checkedAgainst.size()) { 548 | 549 | cerr << "Call at address " << hex << callAddress << " ("; 550 | if (get_file_line(infile, callAddress, src, &line)) { 551 | cerr << src << ":" << dec << line; 552 | lineexec = get_times_executed(src, line); 553 | if (-1 == lineexec) { 554 | cerr << " - failed to retrieve line execution count. gcov file missing?"; 555 | } else { 556 | cerr << " - exec " << lineexec << " times"; 557 | } 558 | } else 559 | cerr << "no line number information"; 560 | 561 | cerr << ") checks return value against: "; 562 | 563 | for (vector::const_iterator it = checkedAgainst.begin(), itend = checkedAgainst.end(); 564 | it != itend; ++it) 565 | if (*it == -667) 566 | cerr << "[ret] "; 567 | else 568 | cerr << dec << *it << " "; 569 | cerr << endl; 570 | 571 | if (errnoCheckedAgainst.size()) { 572 | cerr << "errno is checked against: "; 573 | for (vector::const_iterator it = errnoCheckedAgainst.begin(), itend = errnoCheckedAgainst.end(); 574 | it != itend; ++it) 575 | cerr << dec << *it << " "; 576 | cerr << endl; 577 | } 578 | } else { 579 | cerr << "call at address " << hex << callAddress << " ("; 580 | if (get_file_line(infile, callAddress, src, &line)) { 581 | cerr << src << ":" << dec << line; 582 | lineexec = get_times_executed(src, line); 583 | if (-1 == lineexec) { 584 | cerr << " - failed to retrieve line execution count. gcov file missing?"; 585 | } else { 586 | cerr << " - exec " << lineexec << " times"; 587 | } 588 | } else { 589 | cerr << "no line number information"; 590 | lineexec = -1; // force trigger 591 | } 592 | 593 | cerr << ") does not check the return value" << endl; 594 | 595 | if (lineexec) { 596 | cout << " " << endl; 597 | cout << " " << endl; 598 | cout << " " << endl; 599 | cout << " " << infile << "" << endl; 600 | cout << " " << hex << callAddress << "" << endl; 601 | cout << " " << endl; 602 | cout << " " << endl; 603 | cout << " " << endl; 604 | 605 | cout << " c_str() << "\" retval=\"" << defretval << "\" errno=\"" << deferrno << "\">" << endl; 606 | cout << " " << endl; 607 | cout << " " << endl; 608 | } 609 | } 610 | } // while(1) 611 | } // for 612 | cout << "" << endl; 613 | 614 | return 0; 615 | } 616 | 617 | -------------------------------------------------------------------------------- /libfi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Created by Paul Marinescu and George Candea 3 | Copyright (C) 2009 EPFL (Ecole Polytechnique Federale de Lausanne) 4 | 5 | This file is part of LFI (Library-level Fault Injector). 6 | 7 | LFI is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by the 9 | Free Software Foundation, either version 3 of the License, or (at 10 | your option) any later version. 11 | 12 | LFI is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public 18 | License along with LFI. If not, see http://www.gnu.org/licenses/. 19 | 20 | EPFL 21 | Dependable Systems Lab (DSLAB) 22 | Room 330, Station 14 23 | 1015 Lausanne 24 | Switzerland 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | using namespace std; 49 | 50 | #define STUBC ((char *) "intercept.stub.cpp") 51 | #ifdef __APPLE__ 52 | #define STUBEX ((char *) "intercept.stub.dylib") 53 | #else 54 | #define STUBEX ((char *) "intercept.stub.so") 55 | #endif 56 | 57 | #define LOGFILE "inject.log" 58 | #define REPLAYFILE "replay.xml" 59 | 60 | 61 | #define CRASH_METRIC (int)1e8 62 | #define FAILURE_METRIC (int)1e6 63 | #define TIME_MULTIPLIER 1 64 | 65 | static void 66 | usage(char* me) 67 | { 68 | cout << "Usage: "; 69 | cout << me << " [-t ] " << endl; 70 | } 71 | 72 | static void 73 | print_attribute_names(xmlAttrPtr a_node, ofstream& out) 74 | { 75 | for (; a_node; a_node = a_node->next) { 76 | out << " " << a_node->name << "=\\\"" << a_node->children->content << "\\\""; 77 | if (a_node->next) 78 | out << " "; 79 | } 80 | } 81 | 82 | static void 83 | print_element_names(xmlNodePtr a_node, ofstream& out) 84 | { 85 | xmlNodePtr cur_node = NULL; 86 | xmlChar ch; 87 | int i; 88 | 89 | for (cur_node = a_node; cur_node; cur_node = cur_node->next) { 90 | if (cur_node->type == XML_ELEMENT_NODE) { 91 | out << "<" << cur_node->name; 92 | print_attribute_names(cur_node->properties, out); 93 | out << ">"; 94 | } else if (cur_node->type == XML_TEXT_NODE) { 95 | i = 0; 96 | while (0 != (ch = cur_node->content[i++])) 97 | { 98 | if (ch == '\r' || ch == '\n') 99 | out << "\\"; 100 | out << ch; 101 | } 102 | } 103 | 104 | print_element_names(cur_node->children, out); 105 | 106 | if (cur_node->type == XML_ELEMENT_NODE) { 107 | out << "name << ">"; 108 | } 109 | } 110 | } 111 | 112 | static void 113 | print_triggers(xmlNodeSetPtr nodes, ofstream& out) 114 | { 115 | xmlNodePtr cur; 116 | xmlChar *triggerId; 117 | xmlChar *triggerClass; 118 | 119 | int size; 120 | int i, fn_count; 121 | set functionsUsed; 122 | 123 | size = (nodes) ? nodes->nodeNr : 0; 124 | fn_count = 0; 125 | 126 | for(i = 0; i < size; ++i) 127 | { 128 | assert(nodes->nodeTab[i]); 129 | 130 | cur = nodes->nodeTab[i]; 131 | if(cur->type == XML_ELEMENT_NODE) 132 | { 133 | triggerId = xmlGetProp(cur, (xmlChar*)"id"); 134 | triggerClass = xmlGetProp(cur, (xmlChar*)"class"); 135 | 136 | if (triggerId && triggerClass) 137 | { 138 | out << "struct TriggerDesc trigger_" << triggerId << " = { \"" << triggerId << "\", "; 139 | out << "\"" << triggerClass << "\", NULL, "; 140 | 141 | xmlFree(triggerId); 142 | xmlFree(triggerClass); 143 | 144 | cur = cur->children; 145 | while (cur && xmlStrcmp(cur->name, (const xmlChar *)"args")) 146 | { 147 | cur = cur->next; 148 | } 149 | if (cur) 150 | { 151 | out << "\""; 152 | print_element_names(cur, out); 153 | out << "\""; 154 | } else { 155 | out << "\"\""; 156 | } 157 | out << " };" << endl; 158 | } 159 | } 160 | } 161 | } 162 | 163 | static void 164 | print_function(xmlNodePtr fn, int triggerListId, ofstream& out) 165 | { 166 | const char defErrno[] = "0"; 167 | const char defCallOriginal[] = "0"; 168 | const char defArgc[] = "0"; 169 | 170 | xmlChar* functionName, *return_value, 171 | *errno_value, *call_original, *argc; 172 | 173 | functionName = xmlGetProp(fn, (xmlChar*)"name"); 174 | errno_value = xmlGetProp(fn, (xmlChar*)"errno"); 175 | return_value = xmlGetProp(fn, (xmlChar*)"retval"); 176 | call_original = xmlGetProp(fn, (xmlChar*)"calloriginal"); 177 | argc = xmlGetProp(fn, (xmlChar*)"argc"); 178 | 179 | if (functionName && return_value) 180 | { 181 | out << "\t{ \"" << functionName << "\", "; 182 | out << return_value << ", "; 183 | out << (errno_value ? (char*)errno_value : defErrno) << ", "; 184 | out << (call_original ? (char*)call_original : defCallOriginal) << ", "; 185 | out << (argc ? (char*)argc : defArgc) << ", "; 186 | out << "triggerList_" << triggerListId; 187 | out << " }," << endl; 188 | } 189 | 190 | if (functionName) 191 | xmlFree(functionName); 192 | if (errno_value) 193 | xmlFree(errno_value); 194 | if (return_value) 195 | xmlFree(return_value); 196 | if (call_original) 197 | xmlFree(call_original); 198 | if (argc) 199 | xmlFree(argc); 200 | } 201 | 202 | static void 203 | print_trigger_list(xmlNodePtr fn, int triggerListId, ofstream& out) 204 | { 205 | xmlNodePtr cur; 206 | xmlChar *triggerId; 207 | 208 | cur = fn->children; 209 | out << "TriggerDesc* triggerList_" << triggerListId << "[] = { "; 210 | 211 | while (cur) 212 | { 213 | if (cur->type == XML_ELEMENT_NODE && 214 | 0 == xmlStrcmp(cur->name, (const xmlChar *)"triggerx")) 215 | { 216 | triggerId = xmlGetProp(cur, (xmlChar*)"ref"); 217 | if (triggerId) 218 | { 219 | out << "&trigger_" << triggerId << ", "; 220 | xmlFree(triggerId); 221 | } 222 | } 223 | cur = cur->next; 224 | } 225 | out << "NULL };" << endl; 226 | } 227 | 228 | static void 229 | print_stubs(xmlNodeSetPtr nodes, ofstream& out) 230 | { 231 | xmlNodePtr cur; 232 | xmlChar *functionName; 233 | xmlChar *functionName2; 234 | int size; 235 | int i, j, triggerListId, triggerListIdBase; 236 | set functionsUsed; 237 | 238 | size = (nodes) ? nodes->nodeNr : 0; 239 | 240 | triggerListId = 1; 241 | for(i = 0; i < size; ++i) 242 | { 243 | assert(nodes->nodeTab[i]); 244 | 245 | if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) 246 | { 247 | cur = nodes->nodeTab[i]; 248 | functionName = xmlGetProp(cur, (xmlChar*)"name"); 249 | if (!functionName || functionsUsed.find((char*)functionName) != functionsUsed.end()) 250 | continue; 251 | 252 | functionsUsed.insert((char*)functionName); 253 | 254 | triggerListIdBase = triggerListId; 255 | print_trigger_list(cur, triggerListId++, out); 256 | for(j = i+1; j < size; ++j) 257 | { 258 | assert(nodes->nodeTab[j]); 259 | if(nodes->nodeTab[j]->type == XML_ELEMENT_NODE) 260 | { 261 | cur = nodes->nodeTab[j]; 262 | functionName2 = xmlGetProp(cur, (xmlChar*)"name"); 263 | if (functionName2) 264 | { 265 | if (0 == strcmp((char*)functionName, (char*)functionName2)) 266 | { 267 | print_trigger_list(cur, triggerListId++, out); 268 | } 269 | xmlFree(functionName2); 270 | } 271 | } 272 | } 273 | 274 | out << "struct fninfov2 function_info_" << functionName << "[] = {\n"; 275 | 276 | triggerListId = triggerListIdBase; 277 | 278 | cur = nodes->nodeTab[i]; 279 | print_function(cur, triggerListId++, out); 280 | for(j = i+1; j < size; ++j) 281 | { 282 | assert(nodes->nodeTab[j]); 283 | if(nodes->nodeTab[j]->type == XML_ELEMENT_NODE) 284 | { 285 | cur = nodes->nodeTab[j]; 286 | functionName2 = xmlGetProp(cur, (xmlChar*)"name"); 287 | if (functionName2) 288 | { 289 | if (0 == strcmp((char*)functionName, (char*)functionName2)) 290 | { 291 | print_function(cur, triggerListId++, out); 292 | } 293 | xmlFree(functionName2); 294 | } 295 | } 296 | } 297 | 298 | out << "\t{ \"\", 0, 0, 0, 0, NULL }" << endl; 299 | out << "};\n"; 300 | 301 | xmlFree(functionName); 302 | } 303 | } 304 | ofstream symbols("symbols"); 305 | out << "extern \"C\" {" << endl; 306 | set generated_stubs; 307 | for(i = 0; i < size; ++i) 308 | { 309 | assert(nodes->nodeTab[i]); 310 | 311 | if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) 312 | { 313 | cur = nodes->nodeTab[i]; 314 | functionName = xmlGetProp(cur, (xmlChar*)"name"); 315 | if (functionName && generated_stubs.end() == generated_stubs.find((char*)functionName)) 316 | { 317 | xmlChar* aliasName = xmlGetProp(cur, (xmlChar*)"alias"); 318 | out << "#ifdef __x86_64__" << endl; 319 | if (aliasName) { 320 | out << "GENERATE_STUB_x64(" << (char*)functionName << ", " << (char*)aliasName << ")" << endl; 321 | symbols << "_" << aliasName << endl; 322 | } else { 323 | out << "GENERATE_STUB_x64(" << (char*)functionName << ", " << (char*)functionName << ")" << endl; 324 | symbols << "_" << functionName << endl; 325 | } 326 | out << "#else" << endl; 327 | out << "GENERATE_STUBv2(" << (char*)functionName << ")" << endl; 328 | out << "#endif" << endl << endl; 329 | generated_stubs.insert((char*)functionName); 330 | xmlFree(functionName); 331 | if (aliasName) 332 | xmlFree(aliasName); 333 | } 334 | } 335 | } 336 | out << "}" << endl; 337 | } 338 | 339 | 340 | /************************************************************************/ 341 | /* int compile_file(char* cfile, char* outfile) */ 342 | /* */ 343 | /* Compiles cfile (dynamically generated) along with its dependencies */ 344 | /* to outfile as a shared object using appropriate flags */ 345 | /************************************************************************/ 346 | int compile_file(char* cfile, char* outfile) 347 | { 348 | char cmd[1024]; 349 | int status; 350 | #ifdef __APPLE__ 351 | sprintf(cmd, "g++ -g -o %s %s inter.cpp Trigger.cpp triggers/*.cpp `xml2-config --cflags` `xml2-config --libs` -O0 -shared -Xlinker -exported_symbols_list -Xlinker symbols", outfile, cfile); 352 | #else 353 | sprintf(cmd, "g++ -g -o %s %s inter.cpp Trigger.cpp triggers/*.cpp `xml2-config --cflags` `xml2-config --libs` -O0 -shared -fPIC -lrt -ldl", outfile, cfile); 354 | #endif 355 | // use the following line instead if you need debug information support (after installing libelf, libdwarf and the appropriate trigger) 356 | //sprintf(cmd, "g++ -g -o %s %s inter.cpp Trigger.cpp triggers/*.cpp `xml2-config --cflags` `xml2-config --libs` -O0 -shared -fPIC -lrt -ldl -ldwarf -lelf", outfile, cfile); 357 | 358 | cerr << "Compiling stub library " << outfile << " from " << cfile << endl; 359 | cerr << cmd << " ..." << endl; 360 | 361 | status = system(cmd); 362 | if (0 != WEXITSTATUS(status)) 363 | { 364 | cerr << "Compile failed" << endl; 365 | return -1; 366 | } 367 | else 368 | { 369 | cerr << "Compiled successfully..." << endl; 370 | } 371 | return 0; 372 | } 373 | 374 | int generate_stub(char* config) 375 | { 376 | xmlDocPtr doc; 377 | xmlXPathContextPtr xpathCtx; 378 | xmlXPathObjectPtr xpathObjTriggers; 379 | xmlXPathObjectPtr xpathObj; 380 | xmlChar *xpathExpr = (xmlChar*)"//function"; 381 | xmlChar *xpathExprTriggers = (xmlChar*)"//trigger"; 382 | 383 | cerr << "Generating stub file " << STUBC << " from " << config << endl; 384 | 385 | /* Print results */ 386 | ofstream outf(STUBC); 387 | 388 | outf << "#include \"inter.h\"" << endl; 389 | outf << "STUB_VAR_DECL" << endl << endl; 390 | 391 | doc = xmlParseFile(config); 392 | if (doc == NULL) { 393 | cerr << "Unable to open " << config << endl; 394 | return -1; 395 | } 396 | 397 | xpathCtx = xmlXPathNewContext(doc); 398 | if(xpathCtx == NULL) { 399 | cerr << "Error: unable to create new XPath context" << endl; 400 | xmlFreeDoc(doc); 401 | return(-1); 402 | } 403 | 404 | xpathObjTriggers = xmlXPathEvalExpression(xpathExprTriggers, xpathCtx); 405 | if(xpathObjTriggers == NULL) { 406 | cerr << "Error: unable to evaluate xpath expression \"" << xpathExprTriggers << "\"" << endl; 407 | xmlXPathFreeContext(xpathCtx); 408 | xmlFreeDoc(doc); 409 | return(-1); 410 | } 411 | 412 | xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); 413 | if(xpathObj == NULL) { 414 | cerr << "Error: unable to evaluate xpath expression \"" << xpathExpr << "\"" << endl; 415 | xmlXPathFreeContext(xpathCtx); 416 | xmlFreeDoc(doc); 417 | return(-1); 418 | } 419 | 420 | print_triggers(xpathObjTriggers->nodesetval, outf); 421 | print_stubs(xpathObj->nodesetval, outf); 422 | 423 | /* Cleanup */ 424 | xmlXPathFreeObject(xpathObj); 425 | xmlXPathFreeObject(xpathObjTriggers); 426 | xmlXPathFreeContext(xpathCtx); 427 | xmlFreeDoc(doc); 428 | 429 | return compile_file(STUBC, STUBEX); 430 | } 431 | 432 | /***************************************************************************/ 433 | /* run_subject(int argc, char** argv, */ 434 | /* char* preload_library, char *envp[]) */ 435 | /* */ 436 | /* Runs subject program defined by (argc, argv[]) in the parent's */ 437 | /* (our) environment + LD_PRELOAD */ 438 | /* */ 439 | /* Returns: */ 440 | /* -1 - failed to start program */ 441 | /* 0 - child program exited normally with a 0 return code */ 442 | /* FAILURE_METRIC - child program exited normally with a non 0 */ 443 | /* return code */ 444 | /* CRASH_METRIC - child program was terminated by a signal */ 445 | /***************************************************************************/ 446 | int run_subject(int argc, char** argv, char* preload_library, char *envp[]) 447 | { 448 | #ifdef __APPLE__ 449 | const char *preload = "DYLD_INSERT_LIBRARIES"; 450 | const char *apple_env_flat = "DYLD_FORCE_FLAT_NAMESPACE"; 451 | const char *apple_no_dyldcache = "DYLD_SHARED_REGION"; 452 | /* this needs to be specified explictly in the flat namespace */ 453 | const char *apple_explicit_libs = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib"; 454 | #else 455 | const char *preload = "LD_PRELOAD"; 456 | #endif 457 | char preload_path[1024]; 458 | 459 | char **newarg; 460 | int i, return_value; 461 | int fd; 462 | 463 | pid_t monitor; 464 | int status, exit_status, exit_signal; 465 | struct timeval tvstart, tvend; 466 | 467 | key_t key = getpid(); 468 | int* runstatus; 469 | int shmid = -1; 470 | 471 | if ((shmid = shmget( key, 1024, IPC_CREAT | 0666 )) < 0 ) 472 | perror("shmget"); 473 | if ((runstatus = (int*)shmat( shmid, NULL, 0 )) == (int*) -1) 474 | perror("shmat"); 475 | 476 | return_value = CRASH_METRIC; 477 | if (getcwd(preload_path, sizeof(preload_path) - strlen(preload_library) - 1)) 478 | { 479 | strcat(preload_path, "/"); 480 | strcat(preload_path, preload_library); 481 | #if __APPLE__ 482 | strlcat(preload_path, ":", sizeof(preload_path)); 483 | strlcat(preload_path, apple_explicit_libs, sizeof(preload_path)); 484 | #endif 485 | cerr << "[LFI] Preloading " << preload_path << endl; 486 | 487 | newarg = (char**)malloc((argc+1)*sizeof(char*)); 488 | for (i = 0; i < argc; ++i) 489 | newarg[i] = argv[i]; 490 | newarg[argc] = NULL; 491 | 492 | gettimeofday(&tvstart, NULL); 493 | if (0 == (monitor = fork())) 494 | { 495 | #ifdef __APPLE__ 496 | setenv(apple_env_flat, "", 1); 497 | setenv(apple_no_dyldcache, "avoid", 1); 498 | #endif 499 | setenv(preload, preload_path, 1); 500 | execv(argv[0], newarg); 501 | *runstatus = 1; 502 | shmdt(runstatus); 503 | _exit(0); 504 | } 505 | else 506 | { 507 | /* monitor */ 508 | if (-1 == monitor) 509 | { 510 | cerr << "Unable to fork" << endl; 511 | } 512 | else 513 | { 514 | waitpid(monitor, &status, 0); 515 | gettimeofday(&tvend, NULL); 516 | if (0 != *runstatus) 517 | { 518 | return_value = -1; 519 | } 520 | else 521 | { 522 | if (WIFEXITED(status)) 523 | { 524 | exit_status = WEXITSTATUS(status); 525 | cerr << "Process exited normally. Exit status: " << exit_status << endl; 526 | if (exit_status) 527 | { 528 | return_value = exit_status; 529 | } 530 | else 531 | { 532 | /* leave the timing to the external sensor 533 | return_value = (tvend.tv_sec-tvstart.tv_sec)*100 + (tvend.tv_usec - tvstart.tv_usec)/10000; 534 | */ 535 | return_value = 0; 536 | } 537 | 538 | } 539 | else if (WIFSIGNALED(status)) 540 | { 541 | exit_signal = WTERMSIG(status); 542 | cerr << "Process terminated by signal " << exit_signal << endl; 543 | 544 | fd = open(REPLAYFILE, O_WRONLY|O_APPEND); 545 | write(fd, "\n", 8); 546 | close(fd); 547 | 548 | return_value = 128+WTERMSIG(status); 549 | } 550 | } 551 | } 552 | } 553 | } 554 | if (shmdt(runstatus) < 0) 555 | { 556 | perror("shmdt"); 557 | } 558 | 559 | if ( shmctl( shmid, IPC_RMID, NULL ) < 0 ) 560 | { 561 | perror("shmctl"); 562 | } 563 | return return_value; 564 | } 565 | 566 | int main(int argc, char* argv[], char* envp[]) 567 | { 568 | char *crash_create, *run_target, *token; 569 | char *run_argv[64]; 570 | int run_argc; 571 | int status, crash_check, test_score; 572 | 573 | int c; 574 | 575 | crash_check = 0; 576 | run_target = NULL; 577 | 578 | opterr = 0; 579 | while ((c = getopt (argc, argv, "t:f:")) != -1) 580 | { 581 | switch (c) 582 | { 583 | case 'f': 584 | crash_check = 1; 585 | crash_create = optarg; 586 | break; 587 | case 't': 588 | run_target = optarg; 589 | break; 590 | case '?': 591 | if (optopt == 'f') 592 | fprintf (stderr, "Option -f requires an argument.\n"); 593 | else if (isprint (optopt)) 594 | fprintf (stderr, "Unknown option `-%c'.\n", optopt); 595 | else 596 | fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); 597 | return 1; 598 | default: 599 | abort (); 600 | } 601 | } 602 | if (optind >= argc) 603 | { 604 | usage(argv[0]); 605 | return -1; 606 | } 607 | 608 | //++optind 609 | run_argc = 0; 610 | token = strtok(run_target, "\t "); 611 | while (token) 612 | { 613 | run_argv[run_argc++] = token; 614 | token = strtok(NULL, "\t "); 615 | } 616 | 617 | LIBXML_TEST_VERSION 618 | status = generate_stub(argv[optind]); 619 | xmlCleanupParser(); 620 | test_score = 0; 621 | if (run_target) { 622 | if (0 == status) { 623 | if ((test_score = run_subject(run_argc, run_argv, STUBEX, envp)) < 0) 624 | cerr << "A problem occurred starting the target" << endl; 625 | } 626 | } 627 | 628 | return test_score; 629 | } 630 | --------------------------------------------------------------------------------