├── .gitignore ├── jni ├── Application.mk ├── fuzz.h ├── arch.h ├── files.h ├── util.h ├── log.h ├── Android.mk ├── common.h ├── util.c ├── log.c ├── files.c ├── arch_posix.c ├── fuzz.c ├── honggfuzz.c ├── arch_ptrace.c └── arch_mac.c ├── bin ├── honggfuzz_PIE └── honggfuzz_noPIE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | libs/ 2 | obj/ 3 | -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_PLATFORM := android-8 2 | -------------------------------------------------------------------------------- /bin/honggfuzz_PIE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anestisb/honggfuzz-android/HEAD/bin/honggfuzz_PIE -------------------------------------------------------------------------------- /bin/honggfuzz_noPIE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anestisb/honggfuzz-android/HEAD/bin/honggfuzz_noPIE -------------------------------------------------------------------------------- /jni/fuzz.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - fuzzing routines 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #ifndef _FUZZ_H_ 25 | #define _FUZZ_H_ 26 | 27 | extern void fuzz_main(honggfuzz_t * fuzz); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /jni/arch.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - architecture dependent code 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #ifndef _ARCH_H_ 25 | #define _ARCH_H_ 26 | 27 | extern bool arch_launchChild(honggfuzz_t * fuzz, char *fileName); 28 | 29 | extern pid_t arch_reapChild(honggfuzz_t * fuzz); 30 | 31 | extern bool arch_prepareParent(honggfuzz_t * hfuzz); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /jni/files.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - file operations 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #ifndef _FILES_H_ 25 | #define _FILES_H_ 26 | 27 | #include 28 | 29 | extern bool files_init(honggfuzz_t * hfuzz); 30 | 31 | extern uint8_t *files_mapFileToRead(char *fileName, off_t * fileSz, int *fd); 32 | 33 | extern bool files_writeToFd(int fd, uint8_t * buf, off_t fileSz); 34 | 35 | extern bool files_writePatternToFd(int fd, off_t size, unsigned char p); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /jni/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - utilities 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #ifndef _UTIL_H_ 25 | #define _UTIL_H_ 26 | 27 | #include 28 | 29 | extern void util_rndInit(void); 30 | 31 | extern uint32_t util_rndGet(uint32_t min, uint32_t max); 32 | 33 | extern void util_getLocalTime(const char *fmt, char *buf, size_t len); 34 | 35 | extern void util_nullifyStdio(void); 36 | 37 | extern bool util_redirectStdin(char *inputFile); 38 | 39 | extern void util_recoverStdio(void); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /jni/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - log messages 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #ifndef _LOG_H_ 25 | #define _LOG_H_ 26 | 27 | typedef enum { 28 | l_FATAL = 0, l_ERROR, l_WARN, l_INFO, l_DEBUG 29 | } log_level_t; 30 | 31 | extern void log_setMinLevel(log_level_t dl); 32 | 33 | extern void log_msg(log_level_t dl, bool perr, const char *file, const char *func, int line, 34 | const char *fmt, ...); 35 | 36 | #define LOGMSG(ll, ...) log_msg(ll, false, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); 37 | #define LOGMSG_P(ll, ...) log_msg(ll, true, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | # honggfuzz Android - Makefile 2 | # ----------------------------------------- 3 | # 4 | # Author: Robert Swiecki 5 | # Modifications: Anestis Bechtsoudis 6 | # 7 | # Original: Copyright 2010 by Google Inc. All Rights Reserved. 8 | # Modifications: Copyright 2014 by Census SA. All Rights Reserved. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | 22 | # Change to false if target API is < 16 (4.1 JellyBean) 23 | TARGET_IS_PIE = true 24 | 25 | LOCAL_PATH := $(call my-dir) 26 | include $(CLEAR_VARS) 27 | 28 | LOCAL_MODULE := honggfuzz 29 | LOCAL_SRC_FILES := honggfuzz.c log.c files.c fuzz.c util.c arch_posix.c 30 | LOCAL_CFLAGS := -O3 -g -ggdb -c -std=c99 -I. -D_GNU_SOURCE \ 31 | -Wall -Werror -Wimplicit -Wunused -Wcomment \ 32 | -Wchar-subscripts -Wuninitialized -Wcast-align \ 33 | -Wreturn-type -Wpointer-arith 34 | LOCAL_LDFLAGS := -lm 35 | 36 | ifeq ($(TARGET_IS_PIE),true) 37 | LOCAL_CFLAGS += -fPIE 38 | LOCAL_LDFLAGS += -fPIE -pie 39 | BIN_SUFFIX := _PIE 40 | else 41 | BIN_SUFFIX := _noPIE 42 | endif 43 | 44 | include $(BUILD_EXECUTABLE) 45 | 46 | all:POST_BUILD_EVENT 47 | POST_BUILD_EVENT: 48 | cp libs/armeabi/honggfuzz bin/honggfuzz$(BIN_SUFFIX) 49 | -------------------------------------------------------------------------------- /jni/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - core structures and macros 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #ifndef _COMMON_H_ 25 | #define _COMMON_H_ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define PROG_NAME "honggfuzz" 34 | #define PROG_VERSION "0.3.1" 35 | #define PROG_AUTHORS "Robert Swiecki , Copyright 2010 by Google Inc. All Rights Reserved.\n" \ 36 | "Anestis Bechtsoudis , Copyright 2014 by Census SA. All Rights Reserved." 37 | 38 | #define FILE_PLACEHOLDER "___FILE___" 39 | #define CON_FILE_PLACEHOLDER "=___FILE___" 40 | 41 | typedef struct { 42 | char **cmdline; 43 | char *inputFile; 44 | bool nullifyStdio; 45 | bool fuzzStdin; 46 | bool saveUnique; 47 | char *fileExtn; 48 | double flipRate; 49 | char flipMode; 50 | uint32_t fuzzStart; 51 | uint32_t fuzzEnd; 52 | char *externalCommand; 53 | long tmOut; 54 | long threadsMax; 55 | long threadsCnt; 56 | void *ignoreAddr; 57 | unsigned long asLimit; 58 | int pid; 59 | 60 | char **files; 61 | int fileCnt; 62 | struct { 63 | pid_t pid; 64 | char fileName[PATH_MAX]; 65 | } *fuzzers; 66 | } honggfuzz_t; 67 | 68 | static inline int HF_SLOT(honggfuzz_t * hfuzz, pid_t pid) 69 | { 70 | for (int x = 0; x < hfuzz->threadsMax; x++) { 71 | if (pid == hfuzz->fuzzers[x].pid) 72 | return x; 73 | } 74 | return -1; 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | honggfuzz android 2 | ================= 3 | 4 | **!!! Project is deprecated. Use [upstream mirror](https://github.com/anestisb/honggfuzz) instead.** 5 | 6 | An Android port of the [honggfuzz](https://code.google.com/p/honggfuzz/) general purpose fuzzer tool. 7 | 8 | ## General Info 9 | 10 | POSIX/SIGNAL architecture has been chosen in order to be compatible with most of the Android ecosystems. PTRACE arch interfaces probably should also work, although they require lot of crafting (feel free to tweak & test them). 11 | 12 | 13 | Pay extra attention to the number of parallel tasks based on your hardware & fuzzing target. 14 | 15 | ## ChangeLog 16 | 17 | ### version 3.1 (17 July 2014) 18 | * Add support for fuzz mangle area offsets relative to file begin pos for internal data generator 19 | * Add support for `--=___FILE___` type of arguments in fuzz target 20 | 21 | ### version 3.0 (14 July 2014) 22 | * Initial port from original repo 23 | * Edits in arch-posix.c to support Android NDK builds 24 | 25 | ## Building 26 | 27 | * Install Android NDK from official [source](https://developer.android.com/tools/sdk/ndk/index.html) (prefer latest version). 28 | * Tweak system path to include NDK root, `build/tools` & `prebuilt//bin` 29 | * If compiling for devices < API 16 (4.1 JellyBean), disable PIE in `jni/Android.mk:TARGET_IS_PIE` 30 | * Invoke ndk-build in repo root path 31 | * adb push libs/armeabi/honggfuzz to your device 32 | * Have fun :) 33 | 34 | 35 | Two sample ARM EABI5 executables are included in the bin directory for convenience. 36 | 37 | * `MD5 (bin/honggfuzz_PIE) = c571be57dc2d94d3eefe058faaf313da` 38 | * `MD5 (bin/honggfuzz_noPIE) = 13fba34bbad32a2bfd13af84f835eb84` 39 | 40 | ## License 41 | ``` 42 | honggfuzz Android port 43 | ----------------------------------------- 44 | 45 | Author: Robert Swiecki 46 | Modifications: Anestis Bechtsoudis 47 | 48 | Original: Copyright 2010 by Google Inc. All Rights Reserved. 49 | Modifications: Copyright 2014 by Census SA. All Rights Reserved. 50 | 51 | Licensed under the Apache License, Version 2.0 (the "License"); 52 | you may not use this file except in compliance with the License. 53 | You may obtain a copy of the License at 54 | 55 | http://www.apache.org/licenses/LICENSE-2.0 56 | 57 | Unless required by applicable law or agreed to in writing, software 58 | distributed under the License is distributed on an "AS IS" BASIS, 59 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 60 | See the License for the specific language governing permissions and 61 | limitations under the License. 62 | ``` 63 | -------------------------------------------------------------------------------- /jni/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - utilities 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "common.h" 35 | #include "log.h" 36 | 37 | void util_rndInit(void) 38 | { 39 | struct timeval tv; 40 | 41 | gettimeofday(&tv, NULL); 42 | uint64_t seed = ((uint64_t) tv.tv_sec << 24) ^ ((uint64_t) tv.tv_usec); 43 | 44 | LOGMSG(l_DEBUG, "srand48() reinitialized with %ld", (long)seed); 45 | srand48((long)seed); 46 | } 47 | 48 | /* 49 | * Yeah.. the distribution is not perfect, but it's not supposed to be 50 | * cryptographically secure 51 | */ 52 | uint32_t util_rndGet(uint32_t min, uint32_t max) 53 | { 54 | double rnd = drand48(); 55 | 56 | return (uint32_t) lrint(floor((rnd * (max - min + 1)) + min)); 57 | } 58 | 59 | void util_getLocalTime(const char *fmt, char *buf, size_t len) 60 | { 61 | struct tm ltime; 62 | 63 | time_t t = time(NULL); 64 | 65 | localtime_r(&t, <ime); 66 | strftime(buf, len, fmt, <ime); 67 | } 68 | 69 | void util_nullifyStdio(void) 70 | { 71 | int fd = open("/dev/null", O_RDWR); 72 | 73 | if (fd == -1) { 74 | LOGMSG_P(l_ERROR, "Couldn't open '/dev/null'"); 75 | return; 76 | } 77 | 78 | dup2(fd, 0); 79 | dup2(fd, 1); 80 | dup2(fd, 2); 81 | 82 | if (fd > 2) { 83 | close(fd); 84 | } 85 | 86 | return; 87 | } 88 | 89 | bool util_redirectStdin(char *inputFile) 90 | { 91 | int fd = open(inputFile, O_RDONLY); 92 | 93 | if (fd == -1) { 94 | LOGMSG_P(l_ERROR, "Couldn't open '%s'", inputFile); 95 | return false; 96 | } 97 | 98 | dup2(fd, 0); 99 | if (fd != 0) { 100 | close(fd); 101 | } 102 | 103 | return true; 104 | } 105 | 106 | void util_recoverStdio(void) 107 | { 108 | int fd = open("/dev/tty", O_RDWR); 109 | 110 | if (fd == -1) { 111 | LOGMSG_P(l_ERROR, "Couldn't open '/dev/tty'"); 112 | return; 113 | } 114 | 115 | dup2(fd, 0); 116 | dup2(fd, 1); 117 | dup2(fd, 2); 118 | 119 | if (fd > 2) { 120 | close(fd); 121 | } 122 | 123 | return; 124 | } 125 | -------------------------------------------------------------------------------- /jni/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - log messages 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "common.h" 35 | #include "log.h" 36 | 37 | int log_minLevel; 38 | bool log_isStdioTTY; 39 | 40 | #define STDOUT_FD 1 41 | __attribute__ ((constructor)) 42 | void log_init(void 43 | ) 44 | { 45 | log_minLevel = l_INFO; 46 | if (isatty(STDOUT_FD)) { 47 | log_isStdioTTY = true; 48 | } else { 49 | log_isStdioTTY = false; 50 | } 51 | } 52 | 53 | void log_setMinLevel(log_level_t dl) 54 | { 55 | log_minLevel = dl; 56 | } 57 | 58 | void log_msg(log_level_t dl, 59 | bool perr, const char *file, const char *func, int line, const char *fmt, ... 60 | ) 61 | { 62 | struct { 63 | char *descr; 64 | char *prefix; 65 | } logLevels[] = { 66 | { 67 | "[FATAL]", "\033[1;31m"}, { 68 | "[ERROR]", "\033[1;35m"}, { 69 | "[WARNING]", "\033[1;34m"}, { 70 | "[INFO]", "\033[1m"}, { 71 | "[DEBUG]", "\033[0;37m"} 72 | }; 73 | 74 | char strerr[512]; 75 | if (perr) { 76 | snprintf(strerr, sizeof(strerr), "%s", strerror(errno)); 77 | } 78 | 79 | if (dl > log_minLevel) 80 | return; 81 | 82 | struct tm tm; 83 | struct timeval tv; 84 | 85 | gettimeofday(&tv, NULL); 86 | localtime_r((const time_t *)&tv.tv_sec, &tm); 87 | 88 | if (log_isStdioTTY) { 89 | printf("%s", logLevels[dl].prefix); 90 | } 91 | 92 | if (log_minLevel >= l_DEBUG || !log_isStdioTTY) { 93 | printf 94 | ("%s %d/%02d/%02d %02d:%02d:%02d (%s:%s %d) ", 95 | logLevels[dl].descr, tm.tm_year + 1900, tm.tm_mon + 1, 96 | tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, file, func, line); 97 | } else { 98 | printf("%s ", logLevels[dl].descr); 99 | } 100 | 101 | va_list args; 102 | va_start(args, fmt); 103 | vprintf(fmt, args); 104 | va_end(args); 105 | 106 | if (perr) { 107 | printf(": %s", strerr); 108 | } 109 | 110 | if (log_isStdioTTY) { 111 | printf("\033[0m"); 112 | } 113 | 114 | printf("\n"); 115 | fflush(stdout); 116 | } 117 | -------------------------------------------------------------------------------- /jni/files.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - file operations 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "common.h" 37 | #include "files.h" 38 | #include "log.h" 39 | 40 | bool files_writeToFd(int fd, uint8_t * buf, off_t fileSz) 41 | { 42 | off_t written = 0; 43 | while (written < fileSz) { 44 | ssize_t sz = write(fd, &buf[written], fileSz - written); 45 | if (sz < 0 && errno == EINTR) 46 | continue; 47 | 48 | if (sz < 0) 49 | return false; 50 | 51 | written += sz; 52 | } 53 | 54 | return true; 55 | } 56 | 57 | bool files_writePatternToFd(int fd, off_t size, unsigned char p) 58 | { 59 | void *buf = malloc(size); 60 | if (!buf) { 61 | LOGMSG_P(l_WARN, "Couldn't allocate memory"); 62 | return false; 63 | } 64 | 65 | memset(buf, p, (size_t) size); 66 | int ret = files_writeToFd(fd, buf, size); 67 | free(buf); 68 | 69 | return ret; 70 | } 71 | 72 | uint8_t *files_mapFileToRead(char *fileName, off_t * fileSz, int *fd) 73 | { 74 | if ((*fd = open(fileName, O_RDONLY)) == -1) { 75 | LOGMSG_P(l_WARN, "Couldn't open() '%s' file in R/O mode", fileName); 76 | return NULL; 77 | } 78 | 79 | struct stat st; 80 | if (fstat(*fd, &st) == -1) { 81 | LOGMSG_P(l_WARN, "Couldn't stat() the '%s' file", fileName); 82 | close(*fd); 83 | return NULL; 84 | } 85 | 86 | uint8_t *buf; 87 | if ((buf = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, *fd, 0)) == MAP_FAILED) { 88 | LOGMSG_P(l_WARN, "Couldn't mmap() the '%s' file", fileName); 89 | close(*fd); 90 | return NULL; 91 | } 92 | 93 | *fileSz = st.st_size; 94 | return buf; 95 | } 96 | 97 | static bool files_readdir(honggfuzz_t * hfuzz) 98 | { 99 | DIR *dir = opendir(hfuzz->inputFile); 100 | if (!dir) { 101 | LOGMSG_P(l_ERROR, "Couldn't open dir '%s'", hfuzz->inputFile); 102 | return false; 103 | } 104 | 105 | int count = 0; 106 | 107 | for (;;) { 108 | struct dirent de, *res; 109 | if (readdir_r(dir, &de, &res) > 0) { 110 | LOGMSG_P(l_ERROR, "Couldn't read the '%s' dir", hfuzz->inputFile); 111 | closedir(dir); 112 | return false; 113 | } 114 | 115 | if (res == NULL && count > 0) { 116 | LOGMSG(l_INFO, "%d input files have been added to the list", hfuzz->fileCnt); 117 | closedir(dir); 118 | return true; 119 | } 120 | 121 | if (res == NULL && count == 0) { 122 | LOGMSG(l_ERROR, "Directory '%s' doesn't contain any regular files", hfuzz->inputFile); 123 | closedir(dir); 124 | return false; 125 | } 126 | 127 | char path[PATH_MAX]; 128 | snprintf(path, sizeof(path), "%s/%s", hfuzz->inputFile, res->d_name); 129 | 130 | struct stat st; 131 | if (stat(path, &st) == -1) { 132 | LOGMSG(l_WARN, "Couldn't stat() the '%s' file", path); 133 | continue; 134 | } 135 | 136 | if (!S_ISREG(st.st_mode)) { 137 | LOGMSG(l_DEBUG, "'%s' is not a regular file, skipping", path); 138 | continue; 139 | } 140 | 141 | if (st.st_size == 0) { 142 | LOGMSG(l_DEBUG, "'%s' is empty", path); 143 | continue; 144 | } 145 | 146 | if (!(hfuzz->files = realloc(hfuzz->files, sizeof(char *) * (count + 1)))) { 147 | LOGMSG_P(l_ERROR, "Couldn't allocate memory"); 148 | closedir(dir); 149 | return false; 150 | } 151 | 152 | hfuzz->files[count] = strdup(path); 153 | if (!hfuzz->files[count]) { 154 | LOGMSG_P(l_ERROR, "Couldn't allocate memory"); 155 | closedir(dir); 156 | return false; 157 | } 158 | hfuzz->fileCnt = ++count; 159 | 160 | LOGMSG(l_DEBUG, "Added '%s' to the list of input files", path); 161 | } 162 | 163 | abort(); /* NOTREACHED */ 164 | return false; 165 | } 166 | 167 | bool files_init(honggfuzz_t * hfuzz) 168 | { 169 | if (hfuzz->externalCommand && !hfuzz->inputFile) { 170 | LOGMSG(l_INFO, 171 | "No input file corpus specified, the external command '%s' is responsible for creating the fuzz files", 172 | hfuzz->externalCommand); 173 | return true; 174 | } 175 | 176 | hfuzz->files = malloc(sizeof(char *)); 177 | if (!hfuzz->files) { 178 | LOGMSG_P(l_ERROR, "Couldn't allocate memory"); 179 | return false; 180 | } 181 | 182 | if (!hfuzz->inputFile) { 183 | LOGMSG(l_ERROR, "No input file/dir specified"); 184 | return false; 185 | } 186 | 187 | struct stat st; 188 | if (stat(hfuzz->inputFile, &st) == -1) { 189 | LOGMSG_P(l_ERROR, "Couldn't stat the input file/dir '%s'", hfuzz->inputFile); 190 | return false; 191 | } 192 | 193 | if (S_ISDIR(st.st_mode)) { 194 | return files_readdir(hfuzz); 195 | } 196 | 197 | if (!S_ISREG(st.st_mode)) { 198 | LOGMSG(l_ERROR, "'%s' is not a regular file, nor a directory", hfuzz->inputFile); 199 | return false; 200 | } 201 | 202 | hfuzz->files[0] = hfuzz->inputFile; 203 | hfuzz->fileCnt = 1; 204 | 205 | return true; 206 | } 207 | -------------------------------------------------------------------------------- /jni/arch_posix.c: -------------------------------------------------------------------------------- 1 | /* 2 | honggfuzz Android - Makefile 3 | ----------------------------------------- 4 | 5 | Author: Robert Swiecki 6 | Modifications: Anestis Bechtsoudis 7 | 8 | Original: Copyright 2010 by Google Inc. All Rights Reserved. 9 | Modifications: Copyright 2014 by Census SA. All Rights Reserved. 10 | 11 | Licensed under the Apache License, Version 2.0 (the "License"); 12 | you may not use this file except in compliance with the License. 13 | You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, software 18 | distributed under the License is distributed on an "AS IS" BASIS, 19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | See the License for the specific language governing permissions and 21 | limitations under the License. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "common.h" 39 | #include "log.h" 40 | #include "arch.h" 41 | #include "util.h" 42 | 43 | /* 44 | * Small hack since most default Android NDKs 45 | * doesn't support it. 46 | */ 47 | #ifndef WIFCONTINUED 48 | #define WIFCONTINUED(x) WEXITSTATUS(0) 49 | #endif 50 | 51 | struct { 52 | bool important; 53 | const char *descr; 54 | } arch_sigs[NSIG]; 55 | 56 | __attribute__ ((constructor)) 57 | void arch_initSigs(void 58 | ) 59 | { 60 | for (int x = 0; x < NSIG; x++) 61 | arch_sigs[x].important = false; 62 | 63 | arch_sigs[SIGILL].important = true; 64 | arch_sigs[SIGILL].descr = "SIGILL"; 65 | arch_sigs[SIGFPE].important = true; 66 | arch_sigs[SIGFPE].descr = "SIGFPE"; 67 | arch_sigs[SIGSEGV].important = true; 68 | arch_sigs[SIGSEGV].descr = "SIGSEGV"; 69 | arch_sigs[SIGBUS].important = true; 70 | arch_sigs[SIGBUS].descr = "SIGBUS"; 71 | arch_sigs[SIGABRT].important = true; 72 | arch_sigs[SIGABRT].descr = "SIGABRT"; 73 | } 74 | 75 | /* 76 | * Returns true if a process exited (so, presumably, we can delete an input 77 | * file) 78 | */ 79 | static bool arch_analyzeSignal(honggfuzz_t * hfuzz, pid_t pid, int status) 80 | { 81 | /* 82 | * Resumed by delivery of SIGCONT 83 | */ 84 | if (WIFCONTINUED(status)) { 85 | return false; 86 | } 87 | 88 | /* 89 | * Boring, the process just exited 90 | */ 91 | if (WIFEXITED(status)) { 92 | LOGMSG(l_DEBUG, "Process (pid %d) exited normally with status %d", pid, 93 | WEXITSTATUS(status)); 94 | return true; 95 | } 96 | 97 | /* 98 | * Shouldn't really happen, but, well.. 99 | */ 100 | if (!WIFSIGNALED(status)) { 101 | LOGMSG(l_ERROR, 102 | "Process (pid %d) exited with the following status %d, please report that as a bug", 103 | pid, status); 104 | return true; 105 | } 106 | 107 | int termsig = WTERMSIG(status); 108 | LOGMSG(l_DEBUG, "Process (pid %d) killed by signal %d '%s'", pid, termsig, strsignal(termsig)); 109 | if (!arch_sigs[termsig].important) { 110 | LOGMSG(l_DEBUG, "It's not that important signal, skipping"); 111 | return true; 112 | } 113 | 114 | char localtmstr[PATH_MAX]; 115 | util_getLocalTime("%F.%H.%M.%S", localtmstr, sizeof(localtmstr)); 116 | 117 | char newname[PATH_MAX]; 118 | snprintf(newname, sizeof(newname), "%s.%d.%s.%s", arch_sigs[termsig].descr, pid, 119 | localtmstr, hfuzz->fileExtn); 120 | 121 | int idx = HF_SLOT(hfuzz, pid); 122 | LOGMSG(l_INFO, "Ok, that's interesting, saving the '%s' as '%s'", 123 | hfuzz->fuzzers[idx].fileName, newname); 124 | 125 | if (link(hfuzz->fuzzers[idx].fileName, newname) == -1) { 126 | LOGMSG_P(l_ERROR, "Couldn't save '%s' as '%s'", hfuzz->fuzzers[idx].fileName, newname); 127 | } 128 | return true; 129 | } 130 | 131 | bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName) 132 | { 133 | #define ARGS_MAX 512 134 | char *args[ARGS_MAX + 2]; 135 | 136 | int x; 137 | char *pCurArg = NULL; 138 | char argData[PATH_MAX] = { 0 }; 139 | 140 | for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) { 141 | pCurArg = hfuzz->cmdline[x]; 142 | if (!hfuzz->fuzzStdin && strcmp(pCurArg, FILE_PLACEHOLDER) == 0) { 143 | args[x] = fileName; 144 | } else if (!hfuzz->fuzzStdin && strstr(pCurArg, CON_FILE_PLACEHOLDER) != NULL) { 145 | const char *off = strstr(hfuzz->cmdline[x], CON_FILE_PLACEHOLDER); 146 | snprintf(argData, PATH_MAX, "%.*s=%s", (off - pCurArg), pCurArg, fileName); 147 | args[x] = argData; 148 | } else { 149 | args[x] = hfuzz->cmdline[x]; 150 | } 151 | } 152 | 153 | args[x++] = NULL; 154 | 155 | LOGMSG(l_DEBUG, "Launching '%s' on file '%s'", args[0], fileName); 156 | 157 | /* 158 | * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof) 159 | */ 160 | if (hfuzz->tmOut) { 161 | struct itimerval it; 162 | 163 | /* 164 | * The hfuzz->tmOut is real CPU usage time... 165 | */ 166 | it.it_value.tv_sec = hfuzz->tmOut; 167 | it.it_value.tv_usec = 0; 168 | it.it_interval.tv_sec = 0; 169 | it.it_interval.tv_usec = 0; 170 | if (setitimer(ITIMER_PROF, &it, NULL) == -1) { 171 | LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_PROF timer"); 172 | return false; 173 | } 174 | 175 | /* 176 | * ...so, if a process sleeps, this one should 177 | * trigger a signal... 178 | */ 179 | it.it_value.tv_sec = hfuzz->tmOut * 2UL; 180 | it.it_value.tv_usec = 0; 181 | it.it_interval.tv_sec = 0; 182 | it.it_interval.tv_usec = 0; 183 | if (setitimer(ITIMER_REAL, &it, NULL) == -1) { 184 | LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_REAL timer"); 185 | return false; 186 | } 187 | 188 | /* 189 | * ..if a process sleeps and catches SIGPROF/SIGALRM 190 | * rlimits won't help either 191 | */ 192 | struct rlimit rl; 193 | 194 | rl.rlim_cur = hfuzz->tmOut * 2; 195 | rl.rlim_max = hfuzz->tmOut * 2; 196 | if (setrlimit(RLIMIT_CPU, &rl) == -1) { 197 | LOGMSG_P(l_ERROR, "Couldn't enforce the RLIMIT_CPU resource limit"); 198 | return false; 199 | } 200 | } 201 | 202 | /* 203 | * The address space limit. If big enough - roughly the size of RAM used 204 | */ 205 | if (hfuzz->asLimit) { 206 | struct rlimit rl; 207 | 208 | rl.rlim_cur = hfuzz->asLimit * 1024UL * 1024UL; 209 | rl.rlim_max = hfuzz->asLimit * 1024UL * 1024UL; 210 | if (setrlimit(RLIMIT_AS, &rl) == -1) { 211 | LOGMSG_P(l_DEBUG, "Couldn't encforce the RLIMIT_AS resource limit, ignoring"); 212 | } 213 | } 214 | 215 | if (hfuzz->nullifyStdio) { 216 | util_nullifyStdio(); 217 | } 218 | 219 | if (hfuzz->fuzzStdin) { 220 | /* Uglyyyyyy ;) */ 221 | if (!util_redirectStdin(fileName)) { 222 | return false; 223 | } 224 | } 225 | 226 | execvp(args[0], args); 227 | 228 | util_recoverStdio(); 229 | LOGMSG(l_FATAL, "Failed to create new '%s' process", args[0]); 230 | return false; 231 | } 232 | 233 | pid_t arch_reapChild(honggfuzz_t * hfuzz) 234 | { 235 | int status; 236 | struct rusage ru; 237 | 238 | pid_t pid = wait3(&status, 0, &ru); 239 | if (pid <= 0) { 240 | return pid; 241 | } 242 | LOGMSG(l_DEBUG, "Process (pid %d) came back with status %d", pid, status); 243 | 244 | int ret = arch_analyzeSignal(hfuzz, pid, status); 245 | 246 | if (ret) { 247 | return pid; 248 | } else { 249 | return (-1); 250 | } 251 | } 252 | 253 | bool arch_prepareParent(honggfuzz_t * hfuzz) 254 | { 255 | return true; 256 | } 257 | -------------------------------------------------------------------------------- /jni/fuzz.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - fuzzing routines 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | Felix Gröbert 8 | 9 | Copyright 2010 by Google Inc. All Rights Reserved. 10 | 11 | Licensed under the Apache License, Version 2.0 (the "License"); 12 | you may not use this file except in compliance with the License. 13 | You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, software 18 | distributed under the License is distributed on an "AS IS" BASIS, 19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | See the License for the specific language governing permissions and 21 | limitations under the License. 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "common.h" 43 | #include "log.h" 44 | #include "arch.h" 45 | #include "util.h" 46 | #include "files.h" 47 | 48 | static void fuzz_mangleContent(honggfuzz_t * hfuzz, uint8_t * buf, off_t fileSz) 49 | { 50 | /* 51 | * Just copy the file if "-r 0" 52 | */ 53 | if (hfuzz->flipRate == 0.0) { 54 | return; 55 | } 56 | 57 | uint64_t changesCnt = fileSz * hfuzz->flipRate; 58 | 59 | if (hfuzz->flipMode == 'b') { 60 | changesCnt *= 8UL; 61 | } 62 | 63 | changesCnt = util_rndGet(1, changesCnt); 64 | 65 | uint32_t start = hfuzz->fuzzStart; 66 | uint32_t end = (hfuzz->fuzzEnd == UINT_MAX) ? fileSz : hfuzz->fuzzEnd; 67 | 68 | for (uint64_t x = 0; x < changesCnt; x++) { 69 | off_t pos = util_rndGet(start, end - 1); 70 | 71 | if (hfuzz->flipMode == 'b') { 72 | buf[pos] ^= (1 << util_rndGet(0, 7)); 73 | } else { 74 | buf[pos] = (uint8_t) util_rndGet(0, 255); 75 | } 76 | } 77 | } 78 | 79 | static void fuzz_getFileName(honggfuzz_t * hfuzz, char *fileName) 80 | { 81 | struct timeval tv; 82 | gettimeofday(&tv, NULL); 83 | 84 | snprintf(fileName, PATH_MAX, ".honggfuzz.%d.%lu.%lu.%lu.%s", (int)getpid(), 85 | (unsigned long int)tv.tv_sec, (unsigned long int)tv.tv_usec, 86 | (unsigned long int)util_rndGet(0, 1 << 30), hfuzz->fileExtn); 87 | 88 | return; 89 | } 90 | 91 | static bool fuzz_prepareFile(honggfuzz_t * hfuzz, char *fileName) 92 | { 93 | int rnd_index = util_rndGet(0, hfuzz->fileCnt - 1); 94 | off_t fileSz; 95 | int srcfd; 96 | 97 | uint8_t *buf = files_mapFileToRead(hfuzz->files[rnd_index], &fileSz, &srcfd); 98 | if (buf == NULL) { 99 | LOGMSG(l_ERROR, "Couldn't open and map '%s' in R/O mode", hfuzz->files[rnd_index]); 100 | return false; 101 | } 102 | 103 | LOGMSG(l_DEBUG, "Mmaped '%s' in R/O mode, size: %d", hfuzz->files[rnd_index], fileSz); 104 | 105 | int dstfd = open(fileName, O_CREAT | O_EXCL | O_RDWR, 0644); 106 | if (dstfd == -1) { 107 | LOGMSG_P(l_ERROR, "Couldn't create a temporary file '%s' in the current directory", 108 | fileName); 109 | munmap(buf, fileSz); 110 | close(srcfd); 111 | return false; 112 | } 113 | 114 | fuzz_mangleContent(hfuzz, buf, fileSz); 115 | 116 | if (!files_writeToFd(dstfd, buf, fileSz)) { 117 | munmap(buf, fileSz); 118 | close(srcfd); 119 | close(dstfd); 120 | return false; 121 | } 122 | 123 | munmap(buf, fileSz); 124 | close(srcfd); 125 | close(dstfd); 126 | return true; 127 | } 128 | 129 | static bool fuzz_prepareFileExternally(honggfuzz_t * hfuzz, char *fileName) 130 | { 131 | int rnd_index = util_rndGet(0, hfuzz->fileCnt - 1); 132 | off_t fileSz; 133 | int srcfd; 134 | 135 | int dstfd = open(fileName, O_CREAT | O_EXCL | O_RDWR, 0644); 136 | if (dstfd == -1) { 137 | LOGMSG_P(l_ERROR, "Couldn't create a temporary file '%s' in the current directory", 138 | fileName); 139 | return false; 140 | } 141 | 142 | LOGMSG(l_DEBUG, "Created '%f' as an input file", fileName); 143 | 144 | if (hfuzz->inputFile) { 145 | uint8_t *buf = files_mapFileToRead(hfuzz->files[rnd_index], &fileSz, &srcfd); 146 | if (buf == NULL) { 147 | LOGMSG(l_ERROR, "Couldn't open and map '%s' in R/O mode", hfuzz->files[rnd_index]); 148 | close(dstfd); 149 | return false; 150 | } 151 | 152 | LOGMSG(l_DEBUG, "Mmaped '%s' in R/O mode, size: %d", hfuzz->files[rnd_index], fileSz); 153 | 154 | bool ret = files_writeToFd(dstfd, buf, fileSz); 155 | munmap(buf, fileSz); 156 | close(srcfd); 157 | 158 | if (!ret) { 159 | close(dstfd); 160 | return false; 161 | } 162 | } 163 | 164 | close(dstfd); 165 | 166 | pid_t pid = fork(); 167 | if (pid == -1) { 168 | LOGMSG_P(l_ERROR, "Couldn't fork"); 169 | return false; 170 | } 171 | 172 | if (!pid) { 173 | /* 174 | * child does the external file modifications 175 | */ 176 | execl(hfuzz->externalCommand, hfuzz->externalCommand, fileName, NULL); 177 | LOGMSG_P(l_FATAL, "Couldn't execute '%s %s'", hfuzz->externalCommand, fileName); 178 | return false; 179 | } else { 180 | /* 181 | * parent waits until child is done fuzzing the input file 182 | */ 183 | 184 | int childStatus; 185 | pid_t terminatedPid; 186 | do { 187 | terminatedPid = wait(&childStatus); 188 | } while (terminatedPid != pid); 189 | 190 | if (WIFEXITED(childStatus)) { 191 | LOGMSG(l_DEBUG, "External command exited with status %d", WEXITSTATUS(childStatus)); 192 | return true; 193 | } else if (WIFSIGNALED(childStatus)) { 194 | LOGMSG(l_ERROR, "External command terminated with signal %d", WTERMSIG(childStatus)); 195 | return false; 196 | } 197 | LOGMSG(l_FATAL, "External command terminated abnormally, status: %d", childStatus); 198 | return false; 199 | } 200 | 201 | abort(); /* NOTREACHED */ 202 | } 203 | 204 | static void fuzz_reapChild(honggfuzz_t * hfuzz) 205 | { 206 | pid_t pid = arch_reapChild(hfuzz); 207 | if (pid <= 0) { 208 | return; 209 | } 210 | 211 | int idx = HF_SLOT(hfuzz, pid); 212 | // Might be a process we are attached to 213 | if (idx == -1) { 214 | return; 215 | } 216 | 217 | unlink(hfuzz->fuzzers[idx].fileName); 218 | hfuzz->fuzzers[idx].pid = 0; 219 | hfuzz->threadsCnt--; 220 | } 221 | 222 | static void fuzz_runNext(honggfuzz_t * hfuzz) 223 | { 224 | int i = HF_SLOT(hfuzz, 0); 225 | 226 | fuzz_getFileName(hfuzz, hfuzz->fuzzers[i].fileName); 227 | 228 | pid_t pid = fork(); 229 | 230 | if (pid == -1) { 231 | LOGMSG_P(l_FATAL, "Couldn't fork"); 232 | exit(EXIT_FAILURE); 233 | } 234 | 235 | if (!pid) { 236 | /* 237 | * We've forked, other pid's might have the same rnd seeds now, 238 | * reinitialize it 239 | */ 240 | util_rndInit(); 241 | 242 | hfuzz->fuzzers[i].pid = getpid(); 243 | 244 | if (hfuzz->externalCommand != NULL) { 245 | if (!fuzz_prepareFileExternally(hfuzz, hfuzz->fuzzers[i].fileName)) { 246 | exit(EXIT_FAILURE); 247 | } 248 | } else { 249 | if (!fuzz_prepareFile(hfuzz, hfuzz->fuzzers[i].fileName)) { 250 | exit(EXIT_FAILURE); 251 | } 252 | } 253 | 254 | /* 255 | * Ok, kill the parent 256 | */ 257 | if (!arch_launchChild(hfuzz, hfuzz->fuzzers[i].fileName)) { 258 | kill(getppid(), SIGTERM); 259 | exit(EXIT_FAILURE); 260 | } 261 | } 262 | 263 | hfuzz->threadsCnt++; 264 | hfuzz->fuzzers[i].pid = pid; 265 | 266 | LOGMSG(l_INFO, "Launched new process, pid: %d, (%d/%d)", pid, 267 | hfuzz->threadsCnt, hfuzz->threadsMax); 268 | return; 269 | } 270 | 271 | void fuzz_main(honggfuzz_t * hfuzz) 272 | { 273 | if (!arch_prepareParent(hfuzz)) { 274 | LOGMSG(l_FATAL, "Couldn't prepare parent for fuzzing"); 275 | exit(EXIT_FAILURE); 276 | } 277 | 278 | for (;;) { 279 | while (hfuzz->threadsCnt < hfuzz->threadsMax) { 280 | fuzz_runNext(hfuzz); 281 | } 282 | fuzz_reapChild(hfuzz); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /jni/honggfuzz.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - the main file 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | Felix Gröbert 8 | 9 | Copyright 2010 by Google Inc. All Rights Reserved. 10 | 11 | Licensed under the Apache License, Version 2.0 (the "License"); 12 | you may not use this file except in compliance with the License. 13 | You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, software 18 | distributed under the License is distributed on an "AS IS" BASIS, 19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | See the License for the specific language governing permissions and 21 | limitations under the License. 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "common.h" 33 | #include "log.h" 34 | #include "files.h" 35 | #include "fuzz.h" 36 | #include "util.h" 37 | 38 | #define AB ANSI_BOLD 39 | #define AC ANSI_CLEAR 40 | #define ANSI_BOLD "\033[1m" 41 | #define ANSI_CLEAR "\033[0m" 42 | 43 | static bool checkFor_FILE_PLACEHOLDER(char **args) 44 | { 45 | for (int x = 0; args[x]; x++) { 46 | if (!strcmp(args[x], FILE_PLACEHOLDER)) 47 | return true; 48 | if (strstr(args[x], CON_FILE_PLACEHOLDER)) 49 | return true; 50 | } 51 | return false; 52 | } 53 | 54 | static void usage(void 55 | ) 56 | { 57 | printf("%s", 58 | " <" AB "-f val" AC ">: input file (or input dir)\n" 59 | " [" AB "-h" AC "] : this help\n" 60 | " [" AB "-q" AC "] : null-ify children's stdin, stdout, stderr; make them quiet\n" 61 | " [" AB "-s" AC "] : standard input fuzz, instead of providing a file argument\n" 62 | " [" AB "-u" AC "] : save unique test-cases only, otherwise (if not used) append\n" 63 | " current timestamp to the output filenames\n" 64 | " [" AB "-d val" AC "]: debug level (0 - FATAL ... 4 - DEBUG), default: '" AB "3" AC "' (INFO)\n" 65 | " [" AB "-e val" AC "]: file extension (e.g swf), default: '" AB "fuzz" AC "'\n" 66 | " [" AB "-r val" AC "]: flip rate, default: '" AB "0.001" AC "'\n" 67 | " [" AB "-m val" AC "]: flip mode (-mB - byte, -mb - bit), default: '" AB "-mB" AC "'\n" 68 | " [" AB "-b val" AC "]: mangling range start offset from file begin (default: '" AB "0" AC "' Begin)\n" 69 | " [" AB "-w val" AC "]: mangling range end offset from file begin (default: '" AB "UINT_MAX" AC "' EOF)\n" 70 | " [" AB "-c val" AC "]: command modifying input files externally (instead of -r/-m)\n" 71 | " [" AB "-t val" AC "]: timeout (in secs), default: '" AB "3" AC "' (0 - no timeout)\n" 72 | " [" AB "-a val" AC "]: address limit (from si.si_addr) below which crashes\n" 73 | " are not reported, default: '" AB "0" AC "' (suggested: 65535)\n" 74 | " [" AB "-n val" AC "]: number of concurrent fuzzing processes, default: '" AB "5" AC "'\n" 75 | " [" AB "-l val" AC "]: per process memory limit in MiB, default: '" AB "0" AC "' (no limit)\n" 76 | #ifdef _HAVE_ARCH_PTRACE 77 | " [" AB "-p val" AC "]: attach to a pid (a group thread), instead of monitoring\n" 78 | " previously created process, default: '" AB "0" AC "' (none)\n" 79 | #endif /* _HAVE_ARCH_PTRACE */ 80 | "usage:" 81 | AB " " PROG_NAME " -f input_dir -- /usr/bin/tiffinfo -D " FILE_PLACEHOLDER AC "\n"); 82 | exit(EXIT_SUCCESS); 83 | } 84 | 85 | int main(int argc, char **argv) 86 | { 87 | int c; 88 | int ll = l_INFO; 89 | honggfuzz_t hfuzz; 90 | 91 | hfuzz.inputFile = NULL; 92 | hfuzz.nullifyStdio = false; 93 | hfuzz.fuzzStdin = false; 94 | hfuzz.saveUnique = false; 95 | hfuzz.fileExtn = "fuzz"; 96 | hfuzz.flipRate = 0.001f; 97 | hfuzz.flipMode = 'B'; 98 | hfuzz.fuzzStart = 0; 99 | hfuzz.fuzzEnd = UINT_MAX; 100 | hfuzz.externalCommand = NULL; 101 | hfuzz.tmOut = 3; 102 | hfuzz.ignoreAddr = (void *)0UL; 103 | hfuzz.threadsMax = 5; 104 | hfuzz.asLimit = 0UL; 105 | hfuzz.cmdline = NULL; 106 | hfuzz.pid = 0; 107 | 108 | hfuzz.files = NULL; 109 | hfuzz.threadsCnt = 0; 110 | 111 | printf(AB PROG_NAME " version " PROG_VERSION "\n" PROG_AUTHORS AC "\n"); 112 | if (argc < 2) { 113 | usage(); 114 | exit(EXIT_SUCCESS); 115 | } 116 | 117 | for (;;) { 118 | c = getopt(argc, argv, "hqsuf:d:e:r:m:c:t:a:n:l:p:b:w:"); 119 | if (c < 0) 120 | break; 121 | 122 | switch (c) { 123 | case 'f': 124 | hfuzz.inputFile = optarg; 125 | break; 126 | case 'h': 127 | usage(); 128 | break; 129 | case 'q': 130 | hfuzz.nullifyStdio = true; 131 | break; 132 | case 's': 133 | hfuzz.fuzzStdin = true; 134 | break; 135 | case 'u': 136 | hfuzz.saveUnique = true; 137 | break; 138 | case 'd': 139 | ll = atoi(optarg); 140 | break; 141 | case 'e': 142 | hfuzz.fileExtn = optarg; 143 | break; 144 | case 'r': 145 | hfuzz.flipRate = atof(optarg); 146 | break; 147 | case 'm': 148 | hfuzz.flipMode = optarg[0]; 149 | break; 150 | case 'c': 151 | hfuzz.externalCommand = optarg; 152 | break; 153 | case 't': 154 | hfuzz.tmOut = atol(optarg); 155 | break; 156 | case 'a': 157 | hfuzz.ignoreAddr = (void *)atol(optarg); 158 | break; 159 | case 'n': 160 | hfuzz.threadsMax = atol(optarg); 161 | break; 162 | case 'l': 163 | hfuzz.asLimit = strtoul(optarg, NULL, 10); 164 | break; 165 | case 'p': 166 | hfuzz.pid = atoi(optarg); 167 | break; 168 | case 'b': 169 | hfuzz.fuzzStart = strtoul(optarg, NULL, 10); 170 | break; 171 | case 'w': 172 | hfuzz.fuzzEnd = strtoul(optarg, NULL, 10); 173 | break; 174 | default: 175 | break; 176 | } 177 | } 178 | hfuzz.cmdline = &argv[optind]; 179 | 180 | util_rndInit(); 181 | log_setMinLevel(ll); 182 | 183 | if (!hfuzz.cmdline[0]) { 184 | LOGMSG(l_FATAL, "Please specify binary to fuzz"); 185 | usage(); 186 | } 187 | 188 | if (!hfuzz.fuzzStdin && !checkFor_FILE_PLACEHOLDER(hfuzz.cmdline)) { 189 | LOGMSG(l_FATAL, 190 | "You must specify '" FILE_PLACEHOLDER 191 | "' when the -s (stdin fuzzing) option is not set"); 192 | usage(); 193 | } 194 | 195 | if (hfuzz.pid) { 196 | LOGMSG(l_INFO, "External PID specified, concurrency disabled"); 197 | hfuzz.threadsMax = 1; 198 | } 199 | 200 | if (strchr(hfuzz.fileExtn, '/')) { 201 | LOGMSG(l_FATAL, "The file extension contains the '/' character: '%s'", hfuzz.fileExtn); 202 | usage(); 203 | } 204 | 205 | if (hfuzz.fuzzStart > hfuzz.fuzzEnd || hfuzz.fuzzStart == hfuzz.fuzzEnd) { 206 | LOGMSG(l_FATAL, "Invalid mangle fuzz area file offsets"); 207 | usage(); 208 | } 209 | 210 | LOGMSG(l_INFO, 211 | "debugLevel: %d, inputFile '%s', nullifyStdio: %d, fuzzStdin: %d, saveUnique: %d, flipRate: %lf, " 212 | "flipMode: '%c', externalCommand: '%s', tmOut: %ld, threadsMax: %ld, fileExtn '%s', ignoreAddr: %p, " 213 | "memoryLimit: %lu (MiB), fuzzExe: '%s', fuzzedPid: %d", 214 | ll, hfuzz.inputFile, hfuzz.nullifyStdio ? 1 : 0, 215 | hfuzz.fuzzStdin ? 1 : 0, hfuzz.saveUnique ? 1 : 0, hfuzz.flipRate, hfuzz.flipMode, 216 | hfuzz.externalCommand == NULL ? "NULL" : hfuzz.externalCommand, hfuzz.tmOut, 217 | hfuzz.threadsMax, hfuzz.fileExtn, hfuzz.ignoreAddr, hfuzz.asLimit, hfuzz.cmdline[0], 218 | hfuzz.pid); 219 | 220 | if (!(hfuzz.fuzzers = malloc(sizeof(hfuzz.fuzzers[0]) * hfuzz.threadsMax))) { 221 | LOGMSG_P(l_FATAL, "Couldn't allocate memory"); 222 | exit(EXIT_FAILURE); 223 | } 224 | memset(hfuzz.fuzzers, '\0', sizeof(hfuzz.fuzzers[0]) * hfuzz.threadsMax); 225 | 226 | if (!files_init(&hfuzz)) { 227 | LOGMSG(l_FATAL, "Couldn't load input files"); 228 | exit(EXIT_FAILURE); 229 | } 230 | 231 | /* 232 | * So far so good 233 | */ 234 | fuzz_main(&hfuzz); 235 | 236 | abort(); /* NOTREACHED */ 237 | return EXIT_SUCCESS; 238 | } 239 | -------------------------------------------------------------------------------- /jni/arch_ptrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - architecture dependent code (PTRACE) 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | 8 | Copyright 2010 by Google Inc. All Rights Reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #if defined(__i386__) || defined(__x86_64__) 41 | #include 42 | #endif 43 | 44 | #include "common.h" 45 | #include "log.h" 46 | #include "arch.h" 47 | #include "util.h" 48 | 49 | struct { 50 | bool important; 51 | const char *descr; 52 | } arch_sigs[NSIG]; 53 | 54 | __attribute__ ((constructor)) 55 | void arch_initSigs(void 56 | ) 57 | { 58 | for (int x = 0; x < NSIG; x++) 59 | arch_sigs[x].important = false; 60 | 61 | arch_sigs[SIGILL].important = true; 62 | arch_sigs[SIGILL].descr = "SIGILL"; 63 | arch_sigs[SIGFPE].important = true; 64 | arch_sigs[SIGFPE].descr = "SIGFPE"; 65 | arch_sigs[SIGSEGV].important = true; 66 | arch_sigs[SIGSEGV].descr = "SIGSEGV"; 67 | arch_sigs[SIGBUS].important = true; 68 | arch_sigs[SIGBUS].descr = "SIGBUS"; 69 | arch_sigs[SIGABRT].important = true; 70 | arch_sigs[SIGABRT].descr = "SIGABRT"; 71 | } 72 | 73 | static bool arch_enablePtrace(honggfuzz_t * hfuzz) 74 | { 75 | // We're fuzzing an external process, so just return true 76 | if (hfuzz->pid) { 77 | return true; 78 | } 79 | 80 | if (ptrace(PT_TRACE_ME, 0, 0, 0) == -1) { 81 | LOGMSG_P(l_FATAL, "Couldn't attach ptrace to pid %d", getpid()); 82 | return false; 83 | } 84 | 85 | return true; 86 | } 87 | 88 | static size_t arch_getProcMem(pid_t pid, uint8_t * buf, size_t len, void *pc) 89 | { 90 | /* 91 | * len must be aligned to the sizeof(long) 92 | */ 93 | int cnt = len / sizeof(long); 94 | size_t memsz = 0; 95 | 96 | for (int x = 0; x < cnt; x++) { 97 | uint8_t *addr = (uint8_t *) pc + (int)(x * sizeof(long)); 98 | long ret = ptrace(PT_READ_D, pid, addr, NULL); 99 | 100 | if (errno != 0) { 101 | LOGMSG_P(l_WARN, "Couldn't PT_READ_D on pid %d, addr: %p", pid, addr); 102 | break; 103 | } 104 | 105 | memsz += sizeof(long); 106 | memcpy(&buf[x * sizeof(long)], &ret, sizeof(long)); 107 | } 108 | return memsz; 109 | } 110 | 111 | #if defined(__i386__) || defined(__x86_64__) 112 | #ifndef MAX_OP_STRING 113 | #define MAX_OP_STRING 32 114 | #endif /* MAX_OP_STRING */ 115 | static void arch_getX86InstrStr(pid_t pid, char *instr, void *pc) 116 | { 117 | /* 118 | * MAX_INSN_LENGTH is actually 15, but we need a value aligned to 8 119 | * which is sizeof(long) on 64bit CPU archs (on most of them, I hope;) 120 | */ 121 | uint8_t buf[16]; 122 | size_t memsz; 123 | 124 | if ((memsz = arch_getProcMem(pid, buf, sizeof(buf), pc)) == 0) { 125 | snprintf(instr, MAX_OP_STRING, "%s", "[NOT_MMAPED]"); 126 | return; 127 | } 128 | 129 | ud_t ud_obj; 130 | ud_init(&ud_obj); 131 | ud_set_mode(&ud_obj, 64); 132 | ud_set_syntax(&ud_obj, UD_SYN_INTEL); 133 | ud_set_pc(&ud_obj, (uint64_t) (long)pc); 134 | ud_set_input_buffer(&ud_obj, buf, memsz); 135 | if (!ud_disassemble(&ud_obj)) { 136 | LOGMSG(l_WARN, "Couldn't disassemble the x86/x86-64 instruction stream"); 137 | return; 138 | } 139 | 140 | snprintf(instr, MAX_OP_STRING, "%s", ud_insn_asm(&ud_obj)); 141 | for (int x = 0; instr[x] && x < MAX_OP_STRING; x++) { 142 | if (instr[x] == '/' || instr[x] == '\\' || isspace(instr[x]) || !isprint(instr[x])) { 143 | instr[x] = '_'; 144 | } 145 | } 146 | } 147 | 148 | #endif /* defined(__i386__) || defined(__x86_64__) */ 149 | 150 | static void arch_savePtraceData(honggfuzz_t * hfuzz, pid_t pid, int status) 151 | { 152 | void *pc = NULL; 153 | 154 | char instr[MAX_OP_STRING] = "[UNKNOWN]"; 155 | siginfo_t si; 156 | 157 | if (ptrace(PT_GETSIGINFO, pid, 0, &si) == -1) { 158 | LOGMSG_P(l_WARN, "Couldn't get siginfo for pid %d", pid); 159 | return; 160 | } 161 | 162 | struct user_regs_struct regs; 163 | if (ptrace(PT_GETREGS, pid, NULL, ®s) == -1) { 164 | LOGMSG(l_ERROR, "Couldn't get CPU registers"); 165 | } 166 | #ifdef __i386__ 167 | pc = (void *)regs.eip; 168 | arch_getX86InstrStr(pid, instr, pc); 169 | #endif /* __i386__ */ 170 | #ifdef __x86_64__ 171 | pc = (void *)regs.rip; 172 | arch_getX86InstrStr(pid, instr, pc); 173 | #endif /* __x86_64__ */ 174 | 175 | LOGMSG(l_DEBUG, 176 | "Pid: %d, signo: %d, errno: %d, code: %d, addr: %p, pc: %p, instr: '%s'", 177 | pid, si.si_signo, si.si_errno, si.si_code, si.si_addr, pc, instr); 178 | 179 | int idx = HF_SLOT(hfuzz, pid); 180 | 181 | // If we're checkign state of an external process, then the idx is 0 (cause 182 | // there's no concurrency) 183 | if (hfuzz->pid) { 184 | idx = 0; 185 | } 186 | 187 | if (si.si_addr < hfuzz->ignoreAddr) { 188 | LOGMSG(l_INFO, 189 | "'%s' is interesting (%s), but the si.si_addr is %p (below %p), skipping", 190 | hfuzz->fuzzers[idx].fileName, arch_sigs[si.si_signo].descr, si.si_addr, 191 | hfuzz->ignoreAddr); 192 | return; 193 | } 194 | 195 | char newname[PATH_MAX]; 196 | if (hfuzz->saveUnique) { 197 | snprintf(newname, sizeof(newname), 198 | "%s.PC.%p.CODE.%d.ADDR.%p.INSTR.%s.%s.%s", 199 | arch_sigs[si.si_signo].descr, pc, si.si_code, si.si_addr, instr, 200 | hfuzz->fuzzers[idx].origFileName, hfuzz->fileExtn); 201 | } else { 202 | char localtmstr[PATH_MAX]; 203 | util_getLocalTime("%F.%H.%M.%S", localtmstr, sizeof(localtmstr)); 204 | snprintf(newname, sizeof(newname), "%s.PC.%p.CODE.%d.ADDR.%p.INSTR.%s.%s.%d.%s.%s", 205 | arch_sigs[si.si_signo].descr, pc, si.si_code, si.si_addr, instr, localtmstr, pid, 206 | hfuzz->fuzzers[idx].origFileName, hfuzz->fileExtn); 207 | } 208 | 209 | if (link(hfuzz->fuzzers[idx].fileName, newname) == 0) { 210 | LOGMSG(l_INFO, "Ok, that's interesting, saved '%s' as '%s'", 211 | hfuzz->fuzzers[idx].fileName, newname); 212 | } else { 213 | if (errno == EEXIST) { 214 | LOGMSG(l_INFO, "It seems that '%s' already exists, skipping", newname); 215 | } else { 216 | LOGMSG_P(l_ERROR, "Couldn't link '%s' to '%s'", hfuzz->fuzzers[idx].fileName, newname); 217 | } 218 | } 219 | } 220 | 221 | /* 222 | * Returns true if a process exited (so, presumably, we can delete an input 223 | * file) 224 | */ 225 | static bool arch_analyzePtrace(honggfuzz_t * hfuzz, pid_t pid, int status) 226 | { 227 | /* 228 | * It's our child which fuzzess our process (that we had attached to) finished 229 | */ 230 | int idx = HF_SLOT(hfuzz, pid); 231 | if (hfuzz->pid && idx != -1) { 232 | if (WIFEXITED(status) || WIFSIGNALED(status)) { 233 | LOGMSG_P(l_DEBUG, "Process pid: %d finished"); 234 | return true; 235 | } else { 236 | return false; 237 | } 238 | } 239 | 240 | /* 241 | * If it's an uninteresting signal (even SIGTRAP), let it run and relay the 242 | * signal (if not SIGTRAP) 243 | */ 244 | if (WIFSTOPPED(status) && !arch_sigs[WSTOPSIG(status)].important) { 245 | int sig = WSTOPSIG(status) == SIGTRAP ? 0 : WSTOPSIG(status); 246 | ptrace(PT_CONTINUE, pid, 0, sig); 247 | return false; 248 | } 249 | 250 | /* 251 | * If it's an interesting signal, save the testcase, and detach 252 | * the tracer (relay the signal as well) 253 | */ 254 | if (WIFSTOPPED(status) && arch_sigs[WSTOPSIG(status)].important) { 255 | arch_savePtraceData(hfuzz, pid, status); 256 | ptrace(PT_CONTINUE, pid, 0, WSTOPSIG(status)); 257 | return false; 258 | } 259 | 260 | /* 261 | * Resumed by delivery of SIGCONT 262 | */ 263 | if (WIFCONTINUED(status)) { 264 | return false; 265 | } 266 | 267 | /* 268 | * Process exited 269 | */ 270 | if (WIFEXITED(status) || WIFSIGNALED(status)) { 271 | if (hfuzz->pid && pid == hfuzz->pid) { 272 | LOGMSG(l_WARN, "Monitored process PID: %d finished", pid); 273 | exit(EXIT_SUCCESS); 274 | } 275 | return true; 276 | } 277 | 278 | abort(); /* NOTREACHED */ 279 | return true; 280 | } 281 | 282 | bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName) 283 | { 284 | if (!arch_enablePtrace(hfuzz)) { 285 | return false; 286 | } 287 | #ifdef __linux__ 288 | #include 289 | #include 290 | /* 291 | * Kill a process (with ABRT) which corrupts its own heap 292 | */ 293 | if (setenv("MALLOC_CHECK_", "3", 1) == -1) { 294 | LOGMSG_P(l_ERROR, "setenv(MALLOC_CHECK_=3) failed"); 295 | return false; 296 | } 297 | 298 | /* 299 | * Kill the children when fuzzer dies (e.g. due to Ctrl+C) 300 | */ 301 | if (prctl(PR_SET_PDEATHSIG, (long)SIGKILL, 0L, 0L, 0L) == -1) { 302 | LOGMSG_P(l_ERROR, "prctl(PR_SET_PDEATHSIG, SIGKILL) failed"); 303 | return false; 304 | } 305 | 306 | /* 307 | * Disable ASLR 308 | */ 309 | if (personality(ADDR_NO_RANDOMIZE) == -1) { 310 | LOGMSG_P(l_ERROR, "personality(ADDR_NO_RANDOMIZE) failed"); 311 | return false; 312 | } 313 | #endif /* __linux__ */ 314 | 315 | #define ARGS_MAX 512 316 | char *args[ARGS_MAX + 2]; 317 | 318 | int x; 319 | char *pCurArg = NULL; 320 | char argData[PATH_MAX] = { 0 }; 321 | 322 | for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) { 323 | pCurArg = hfuzz->cmdline[x]; 324 | if (!hfuzz->fuzzStdin && strcmp(pCurArg, FILE_PLACEHOLDER) == 0) { 325 | args[x] = fileName; 326 | } else if (!hfuzz->fuzzStdin && strstr(pCurArg, CON_FILE_PLACEHOLDER) != NULL) { 327 | const char *off = strstr(hfuzz->cmdline[x], CON_FILE_PLACEHOLDER); 328 | snprintf(argData, PATH_MAX, "%.*s=%s", (off - pCurArg), pCurArg, fileName); 329 | args[x] = argData; 330 | } else { 331 | args[x] = hfuzz->cmdline[x]; 332 | } 333 | } 334 | 335 | args[x++] = NULL; 336 | 337 | LOGMSG(l_DEBUG, "Launching '%s' on file '%s'", args[0], fileName); 338 | 339 | /* 340 | * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof) 341 | */ 342 | if (hfuzz->tmOut) { 343 | struct itimerval it; 344 | 345 | /* 346 | * The hfuzz->tmOut is real CPU usage time... 347 | */ 348 | it.it_value.tv_sec = hfuzz->tmOut; 349 | it.it_value.tv_usec = 0; 350 | it.it_interval.tv_sec = 0; 351 | it.it_interval.tv_usec = 0; 352 | if (setitimer(ITIMER_PROF, &it, NULL) == -1) { 353 | LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_PROF timer"); 354 | return false; 355 | } 356 | 357 | /* 358 | * ...so, if a process sleeps, this one should 359 | * trigger a signal... 360 | */ 361 | it.it_value.tv_sec = hfuzz->tmOut * 2UL; 362 | it.it_value.tv_usec = 0; 363 | it.it_interval.tv_sec = 0; 364 | it.it_interval.tv_usec = 0; 365 | if (setitimer(ITIMER_REAL, &it, NULL) == -1) { 366 | LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_REAL timer"); 367 | return false; 368 | } 369 | 370 | /* 371 | * ..if a process sleeps and catches SIGPROF/SIGALRM 372 | * rlimits won't help either 373 | */ 374 | struct rlimit rl; 375 | 376 | rl.rlim_cur = hfuzz->tmOut * 2; 377 | rl.rlim_max = hfuzz->tmOut * 2; 378 | if (setrlimit(RLIMIT_CPU, &rl) == -1) { 379 | LOGMSG_P(l_ERROR, "Couldn't enforce the RLIMIT_CPU resource limit"); 380 | return false; 381 | } 382 | } 383 | 384 | /* 385 | * The address space limit. If big enough - roughly the size of RAM used 386 | */ 387 | if (hfuzz->asLimit) { 388 | struct rlimit rl; 389 | 390 | rl.rlim_cur = hfuzz->asLimit * 1024UL * 1024UL; 391 | rl.rlim_max = hfuzz->asLimit * 1024UL * 1024UL; 392 | if (setrlimit(RLIMIT_AS, &rl) == -1) { 393 | LOGMSG_P(l_DEBUG, "Couldn't encforce the RLIMIT_AS resource limit, ignoring"); 394 | } 395 | } 396 | 397 | if (hfuzz->nullifyStdio) { 398 | util_nullifyStdio(); 399 | } 400 | 401 | if (hfuzz->fuzzStdin) { 402 | /* Uglyyyyyy ;) */ 403 | if (!util_redirectStdin(fileName)) { 404 | return false; 405 | } 406 | } 407 | 408 | execvp(args[0], args); 409 | 410 | util_recoverStdio(); 411 | LOGMSG(l_FATAL, "Failed to create new '%s' process", args[0]); 412 | return false; 413 | } 414 | 415 | pid_t arch_reapChild(honggfuzz_t * hfuzz) 416 | { 417 | int status; 418 | pid_t pid = waitpid(-1, &status, __WALL); 419 | if (pid <= 0) { 420 | return pid; 421 | } 422 | LOGMSG(l_DEBUG, "Process (pid %d) came back with status %d", pid, status); 423 | 424 | int ret = arch_analyzePtrace(hfuzz, pid, status); 425 | 426 | if (ret) { 427 | return pid; 428 | } else { 429 | return (-1); 430 | } 431 | } 432 | 433 | static bool arch_listThreads(int tasks[], size_t thrSz, int pid) 434 | { 435 | size_t count = 0; 436 | char path[512]; 437 | snprintf(path, sizeof(path), "/proc/%d/task", pid); 438 | DIR *dir = opendir(path); 439 | if (!dir) { 440 | LOGMSG_P(l_ERROR, "Couldn't open dir '%s'", path); 441 | return false; 442 | } 443 | 444 | for (;;) { 445 | struct dirent de, *res; 446 | if (readdir_r(dir, &de, &res) > 0) { 447 | LOGMSG_P(l_ERROR, "Couldn't read contents of '%s'", path); 448 | closedir(dir); 449 | return false; 450 | } 451 | 452 | if (res == NULL) { 453 | break; 454 | } 455 | 456 | pid_t pid = (pid_t) strtol(res->d_name, (char **)NULL, 10); 457 | if (pid == 0) { 458 | LOGMSG(l_DEBUG, "The following dir entry couldn't be converted to pid_t '%s'", 459 | res->d_name); 460 | continue; 461 | } 462 | 463 | tasks[count++] = pid; 464 | LOGMSG(l_DEBUG, "Added pid '%d' from '%s/%s'", pid, path, res->d_name); 465 | 466 | if (count >= thrSz) { 467 | break; 468 | } 469 | } 470 | closedir(dir); 471 | LOGMSG_P(l_DEBUG, "Total number of threads in pid '%d': '%d'", pid, count); 472 | tasks[count + 1] = 0; 473 | if (count < 1) { 474 | return false; 475 | } 476 | return true; 477 | } 478 | 479 | bool arch_prepareParent(honggfuzz_t * hfuzz) 480 | { 481 | if (!hfuzz->pid) { 482 | return true; 483 | } 484 | #define MAX_THREAD_IN_TASK 4096 485 | int tasks[MAX_THREAD_IN_TASK + 1]; 486 | tasks[MAX_THREAD_IN_TASK] = 0; 487 | if (!arch_listThreads(tasks, MAX_THREAD_IN_TASK, hfuzz->pid)) { 488 | LOGMSG(l_ERROR, "Couldn't read thread list for pid '%d'", hfuzz->pid); 489 | return false; 490 | } 491 | 492 | for (int i = 0; i < MAX_THREAD_IN_TASK && tasks[i]; i++) { 493 | if (ptrace(PT_ATTACH, tasks[i], NULL, NULL) == -1) { 494 | LOGMSG_P(l_ERROR, "Couldn't ptrace() ATTACH to pid: %d", tasks[i]); 495 | return false; 496 | } 497 | 498 | int status; 499 | while (waitpid(tasks[i], &status, WUNTRACED | __WALL) != tasks[i]) ; 500 | 501 | if (ptrace(PT_CONTINUE, tasks[i], NULL, NULL) == -1) { 502 | LOGMSG_P(l_ERROR, "Couldn't ptrace() CONTINUE pid: %d", tasks[i]); 503 | ptrace(PT_DETACH, tasks[i], 0, SIGCONT); 504 | return false; 505 | } 506 | 507 | LOGMSG(l_INFO, "Successfully attached to pid/tid: %d", tasks[i]); 508 | } 509 | return true; 510 | } 511 | -------------------------------------------------------------------------------- /jni/arch_mac.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | honggfuzz - architecture dependent code (MAC OS X) 4 | ----------------------------------------- 5 | 6 | Author: Robert Swiecki 7 | Felix Gröbert 8 | 9 | Copyright 2010 by Google Inc. All Rights Reserved. 10 | 11 | Licensed under the Apache License, Version 2.0 (the "License"); 12 | you may not use this file except in compliance with the License. 13 | You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, software 18 | distributed under the License is distributed on an "AS IS" BASIS, 19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | See the License for the specific language governing permissions and 21 | limitations under the License. 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "common.h" 43 | #include "log.h" 44 | #include "arch.h" 45 | #include "util.h" 46 | #include "files.h" 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #include "mach_exc.h" 57 | #include "mach_excServer.h" 58 | 59 | #import 60 | 61 | /* Interface to third_party/CrashReport_Mountain_Lion.o */ 62 | @ interface CrashReport: NSObject - (id) initWithTask:(task_t) 63 | task exceptionType:(exception_type_t) 64 | anExceptionType exceptionCode:(mach_exception_data_t) 65 | anExceptionCode exceptionCodeCount:(mach_msg_type_number_t) 66 | anExceptionCodeCount thread:(thread_t) 67 | thread threadStateFlavor:(thread_state_flavor_t) 68 | aThreadStateFlavor threadState:(thread_state_data_t) 69 | aThreadState threadStateCount:(mach_msg_type_number_t) aThreadStateCount; 70 | @end 71 | /* Global to have exception port available in the collection thread */ 72 | static mach_port_t g_exception_port = MACH_PORT_NULL; 73 | 74 | /* Global to have hfuzz avaiable in exception handler */ 75 | honggfuzz_t *g_hfuzz; 76 | 77 | /* Global to have a unique service name for each honggfuzz process */ 78 | char g_service_name[256]; 79 | 80 | struct { 81 | bool important; 82 | const char *descr; 83 | } arch_sigs[NSIG]; 84 | 85 | __attribute__ ((constructor)) 86 | void arch_initSigs(void) 87 | { 88 | for (int x = 0; x < NSIG; x++) 89 | arch_sigs[x].important = false; 90 | 91 | arch_sigs[SIGILL].important = true; 92 | arch_sigs[SIGILL].descr = "SIGILL"; 93 | arch_sigs[SIGFPE].important = true; 94 | arch_sigs[SIGFPE].descr = "SIGFPE"; 95 | arch_sigs[SIGSEGV].important = true; 96 | arch_sigs[SIGSEGV].descr = "SIGSEGV"; 97 | arch_sigs[SIGBUS].important = true; 98 | arch_sigs[SIGBUS].descr = "SIGBUS"; 99 | arch_sigs[SIGABRT].important = true; 100 | arch_sigs[SIGABRT].descr = "SIGABRT"; 101 | } 102 | 103 | const char *exception_to_string(int exception) 104 | { 105 | switch (exception) { 106 | case EXC_BAD_ACCESS: 107 | return "EXC_BAD_ACCESS"; 108 | case EXC_BAD_INSTRUCTION: 109 | return "EXC_BAD_INSTRUCTION"; 110 | case EXC_ARITHMETIC: 111 | return "EXC_ARITHMETIC"; 112 | case EXC_EMULATION: 113 | return "EXC_EMULATION"; 114 | case EXC_SOFTWARE: 115 | return "EXC_SOFTWARE"; 116 | case EXC_BREAKPOINT: 117 | return "EXC_BREAKPOINT"; 118 | case EXC_SYSCALL: 119 | return "EXC_SYSCALL"; 120 | case EXC_MACH_SYSCALL: 121 | return "EXC_MACH_SYSCALL"; 122 | case EXC_RPC_ALERT: 123 | return "EXC_RPC_ALERT"; 124 | case EXC_CRASH: 125 | return "EXC_CRASH"; 126 | } 127 | return "UNKNOWN"; 128 | } 129 | 130 | /* 131 | * Returns true if a process exited (so, presumably, we can delete an input 132 | * file) 133 | */ 134 | static bool arch_analyzeSignal(honggfuzz_t * hfuzz, pid_t pid, int status) 135 | { 136 | /* 137 | * Resumed by delivery of SIGCONT 138 | */ 139 | if (WIFCONTINUED(status)) { 140 | return false; 141 | } 142 | 143 | /* 144 | * Boring, the process just exited 145 | */ 146 | if (WIFEXITED(status)) { 147 | LOGMSG(l_DEBUG, "Process (pid %d) exited normally with status %d", pid, 148 | WEXITSTATUS(status)); 149 | return true; 150 | } 151 | 152 | /* 153 | * Shouldn't really happen, but, well.. 154 | */ 155 | if (!WIFSIGNALED(status)) { 156 | LOGMSG(l_ERROR, 157 | "Process (pid %d) exited with the following status %d, please report that as a bug", 158 | pid, status); 159 | return true; 160 | } 161 | 162 | int termsig = WTERMSIG(status); 163 | LOGMSG(l_DEBUG, "Process (pid %d) killed by signal %d '%s'", pid, termsig, strsignal(termsig)); 164 | if (!arch_sigs[termsig].important) { 165 | LOGMSG(l_DEBUG, "It's not that important signal, skipping"); 166 | return true; 167 | } 168 | 169 | /* 170 | * Signal is interesting 171 | */ 172 | 173 | int idx = HF_SLOT(hfuzz, pid); 174 | 175 | char newname[PATH_MAX]; 176 | 177 | if (hfuzz->saveUnique) { 178 | snprintf(newname, sizeof(newname), 179 | "%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.%s.%s", 180 | arch_sigs[termsig].descr, exception_to_string(hfuzz->fuzzers[idx].exception), 181 | hfuzz->fuzzers[idx].pc, hfuzz->fuzzers[idx].backtrace, hfuzz->fuzzers[idx].access, 182 | hfuzz->fuzzers[idx].origFileName, hfuzz->fileExtn); 183 | } else { 184 | 185 | char localtmstr[PATH_MAX]; 186 | util_getLocalTime("%F.%H.%M.%S", localtmstr, sizeof(localtmstr)); 187 | 188 | snprintf(newname, sizeof(newname), 189 | "%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.TIME.%s.PID.%.5d.%s.%s", 190 | arch_sigs[termsig].descr, exception_to_string(hfuzz->fuzzers[idx].exception), 191 | hfuzz->fuzzers[idx].pc, hfuzz->fuzzers[idx].backtrace, hfuzz->fuzzers[idx].access, 192 | localtmstr, pid, hfuzz->fuzzers[idx].origFileName, hfuzz->fileExtn); 193 | } 194 | 195 | if (link(hfuzz->fuzzers[idx].fileName, newname) == 0) { 196 | LOGMSG(l_INFO, "Ok, that's interesting, saved '%s' as '%s'", 197 | hfuzz->fuzzers[idx].fileName, newname); 198 | } else { 199 | if (errno == EEXIST) { 200 | LOGMSG(l_INFO, "It seems that '%s' already exists, skipping", newname); 201 | } else { 202 | LOGMSG_P(l_ERROR, "Couldn't link '%s' to '%s'", hfuzz->fuzzers[idx].fileName, newname); 203 | } 204 | } 205 | 206 | return true; 207 | } 208 | 209 | bool arch_launchChild(honggfuzz_t * hfuzz, char *fileName) 210 | { 211 | #define ARGS_MAX 512 212 | char *args[ARGS_MAX + 2]; 213 | 214 | int x; 215 | char *pCurArg = NULL; 216 | char argData[PATH_MAX] = { 0 }; 217 | 218 | for (x = 0; x < ARGS_MAX && hfuzz->cmdline[x]; x++) { 219 | pCurArg = hfuzz->cmdline[x]; 220 | if (!hfuzz->fuzzStdin && strcmp(pCurArg, FILE_PLACEHOLDER) == 0) { 221 | args[x] = fileName; 222 | } else if (!hfuzz->fuzzStdin && strstr(pCurArg, CON_FILE_PLACEHOLDER) != NULL) { 223 | const char *off = strstr(hfuzz->cmdline[x], CON_FILE_PLACEHOLDER); 224 | snprintf(argData, PATH_MAX, "%.*s=%s", (off - pCurArg), pCurArg, fileName); 225 | args[x] = argData; 226 | } else { 227 | args[x] = hfuzz->cmdline[x]; 228 | } 229 | } 230 | 231 | args[x++] = NULL; 232 | 233 | LOGMSG(l_DEBUG, "Launching '%s' on file '%s'", args[0], fileName); 234 | 235 | /* Get child's bootstrap port. */ 236 | mach_port_t child_bootstrap = MACH_PORT_NULL; 237 | if (task_get_bootstrap_port(mach_task_self(), &child_bootstrap) != KERN_SUCCESS) { 238 | return false; 239 | } 240 | 241 | /* Get exception port. */ 242 | mach_port_t exception_port = MACH_PORT_NULL; 243 | 244 | if (bootstrap_look_up(child_bootstrap, g_service_name, &exception_port) != KERN_SUCCESS) { 245 | return false; 246 | } 247 | 248 | /* Here we register the exception port in the child */ 249 | if (task_set_exception_ports(mach_task_self(), 250 | EXC_MASK_CRASH, 251 | exception_port, 252 | EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, 253 | MACHINE_THREAD_STATE) != KERN_SUCCESS) { 254 | return false; 255 | } 256 | 257 | /* 258 | * Set timeout (prof), real timeout (2*prof), and rlimit_cpu (2*prof) 259 | */ 260 | if (hfuzz->tmOut) { 261 | struct itimerval it; 262 | 263 | /* 264 | * The hfuzz->tmOut is real CPU usage time... 265 | */ 266 | it.it_value.tv_sec = hfuzz->tmOut; 267 | it.it_value.tv_usec = 0; 268 | it.it_interval.tv_sec = 0; 269 | it.it_interval.tv_usec = 0; 270 | if (setitimer(ITIMER_PROF, &it, NULL) == -1) { 271 | LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_PROF timer"); 272 | return false; 273 | } 274 | 275 | /* 276 | * ...so, if a process sleeps, this one should 277 | * trigger a signal... 278 | */ 279 | it.it_value.tv_sec = hfuzz->tmOut * 2UL; 280 | it.it_value.tv_usec = 0; 281 | it.it_interval.tv_sec = 0; 282 | it.it_interval.tv_usec = 0; 283 | if (setitimer(ITIMER_REAL, &it, NULL) == -1) { 284 | LOGMSG_P(l_ERROR, "Couldn't set the ITIMER_REAL timer"); 285 | return false; 286 | } 287 | 288 | /* 289 | * ..if a process sleeps and catches SIGPROF/SIGALRM 290 | * rlimits won't help either 291 | */ 292 | struct rlimit rl; 293 | 294 | rl.rlim_cur = hfuzz->tmOut * 2; 295 | rl.rlim_max = hfuzz->tmOut * 2; 296 | if (setrlimit(RLIMIT_CPU, &rl) == -1) { 297 | LOGMSG_P(l_ERROR, "Couldn't enforce the RLIMIT_CPU resource limit"); 298 | return false; 299 | } 300 | } 301 | 302 | /* 303 | * The address space limit. If big enough - roughly the size of RAM used 304 | */ 305 | if (hfuzz->asLimit) { 306 | struct rlimit rl; 307 | 308 | rl.rlim_cur = hfuzz->asLimit * 1024UL * 1024UL; 309 | rl.rlim_max = hfuzz->asLimit * 1024UL * 1024UL; 310 | if (setrlimit(RLIMIT_AS, &rl) == -1) { 311 | LOGMSG_P(l_DEBUG, "Couldn't encforce the RLIMIT_AS resource limit, ignoring"); 312 | } 313 | } 314 | 315 | if (hfuzz->nullifyStdio) { 316 | util_nullifyStdio(); 317 | } 318 | 319 | if (hfuzz->fuzzStdin) { 320 | /* Uglyyyyyy ;) */ 321 | if (!util_redirectStdin(fileName)) { 322 | return false; 323 | } 324 | } 325 | 326 | execvp(args[0], args); 327 | 328 | util_recoverStdio(); 329 | LOGMSG(l_FATAL, "Failed to create new '%s' process", args[0]); 330 | return false; 331 | } 332 | 333 | pid_t arch_reapChild(honggfuzz_t * hfuzz) 334 | { 335 | /* 336 | * First check manually if we have expired childs 337 | */ 338 | 339 | for (int idx = 0; idx < hfuzz->threadsMax; idx++) { 340 | double diff = difftime(time(NULL), hfuzz->fuzzers[idx].timeStarted); 341 | if (hfuzz->fuzzers[idx].pid != 0 && diff > (double)hfuzz->tmOut) { 342 | LOGMSG(l_WARN, 343 | "Process pid %d is overdue (%f seconds, max %f seconds %f), sending a SIGKILL", 344 | hfuzz->fuzzers[idx].pid, diff, (double)hfuzz->tmOut); 345 | kill(hfuzz->fuzzers[idx].pid, SIGKILL); 346 | } 347 | } 348 | 349 | /* 350 | * Now check for signals using wait3 351 | */ 352 | int status = 0; 353 | struct rusage ru; 354 | 355 | pid_t pid = wait3(&status, 0, &ru); 356 | if (pid <= 0) { 357 | return pid; 358 | } 359 | LOGMSG(l_DEBUG, "Process (pid %d) came back with status %d", pid, status); 360 | 361 | int ret = arch_analyzeSignal(hfuzz, pid, status); 362 | 363 | if (ret) { 364 | return pid; 365 | } else { 366 | return (-1); 367 | } 368 | } 369 | 370 | void *wait_for_exception() 371 | { 372 | while (1) { 373 | mach_msg_server_once(mach_exc_server, 4096, g_exception_port, MACH_MSG_OPTION_NONE); 374 | } 375 | } 376 | 377 | /* 378 | * Called once before fuzzing starts. Prepare mach ports for attaching crash reporter. 379 | */ 380 | bool arch_prepareParent(honggfuzz_t * hfuzz) 381 | { 382 | char plist[PATH_MAX]; 383 | snprintf(plist, sizeof(plist), "/Users/%s/Library/Preferences/com.apple.DebugSymbols.plist", 384 | getlogin()); 385 | 386 | if (files_exists(plist)) { 387 | LOGMSG(l_WARN, 388 | "honggfuzz won't work if DBGShellCommands are set in ~/Library/Preferences/com.apple.DebugSymbols.plist"); 389 | } 390 | 391 | /* Allocate exception port. */ 392 | if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &g_exception_port) != 393 | KERN_SUCCESS) { 394 | return false; 395 | } 396 | 397 | /* Insert exception receive port. */ 398 | if (mach_port_insert_right(mach_task_self(), g_exception_port, g_exception_port, 399 | MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 400 | return false; 401 | } 402 | 403 | /* Get bootstrap port. */ 404 | mach_port_t bootstrap = MACH_PORT_NULL; 405 | if (task_get_bootstrap_port(mach_task_self(), &bootstrap) != KERN_SUCCESS) { 406 | return false; 407 | } 408 | 409 | /* Generate and register exception port service. */ 410 | snprintf(g_service_name, sizeof(g_service_name), "com.google.code.honggfuzz.%d", 411 | util_rndGet(0, 999999)); 412 | if (bootstrap_check_in(bootstrap, g_service_name, &g_exception_port) != KERN_SUCCESS) { 413 | return false; 414 | } 415 | 416 | /* Create a collection thread to catch the exceptions from the children */ 417 | pthread_t exception_thread; 418 | 419 | if (pthread_create(&exception_thread, NULL, wait_for_exception, 0)) { 420 | LOGMSG(l_FATAL, "Parent: could not create thread to wait for child's exception"); 421 | return false; 422 | } 423 | 424 | if (pthread_detach(exception_thread)) { 425 | LOGMSG(l_FATAL, "Parent: could not detach thread to wait for child's exception"); 426 | return false; 427 | } 428 | 429 | /* Finally make hfuzz avaiable in the collection thread */ 430 | g_hfuzz = hfuzz; 431 | 432 | return true; 433 | } 434 | 435 | /* Write the crash report to DEBUG */ 436 | void write_crash_report(thread_port_t thread, 437 | task_port_t task, 438 | exception_type_t exception, 439 | mach_exception_data_t code, 440 | mach_msg_type_number_t code_count, 441 | int *flavor, thread_state_t in_state, mach_msg_type_number_t in_state_count) 442 | { 443 | 444 | NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init]; 445 | CrashReport *_crashReport = nil; 446 | 447 | _crashReport =[[CrashReport alloc] initWithTask: task exceptionType: exception exceptionCode: code exceptionCodeCount: code_count thread: thread threadStateFlavor: *flavor threadState: (thread_state_t) in_state threadStateCount:in_state_count]; 448 | 449 | NSString *crashDescription =[_crashReport description]; 450 | char *description = (char *)[crashDescription UTF8String]; 451 | 452 | LOGMSG(l_DEBUG, "CrashReport: %s", description); 453 | 454 | [_crashReport release]; 455 | [pool drain]; 456 | } 457 | 458 | /* Hash the callstack in an unique way */ 459 | uint64_t hash_callstack(thread_port_t thread, 460 | task_port_t task, 461 | exception_type_t exception, 462 | mach_exception_data_t code, 463 | mach_msg_type_number_t code_count, 464 | int *flavor, thread_state_t in_state, mach_msg_type_number_t in_state_count) 465 | { 466 | 467 | NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init]; 468 | CrashReport *_crashReport = nil; 469 | 470 | _crashReport =[[CrashReport alloc] initWithTask: task exceptionType: exception exceptionCode: code exceptionCodeCount: code_count thread: thread threadStateFlavor: *flavor threadState: (thread_state_t) in_state threadStateCount:in_state_count]; 471 | 472 | NSString *crashDescription =[_crashReport description]; 473 | char *description = (char *)[crashDescription UTF8String]; 474 | 475 | /* The callstack begins with the following word */ 476 | char *callstack = strstr(description, "Crashed:"); 477 | 478 | if (callstack == NULL) { 479 | LOGMSG(l_FATAL, "Could not find callstack in crash report %s", description); 480 | } 481 | 482 | /* Scroll forward to the next newline */ 483 | char *callstack_start = strstr(callstack, "\n"); 484 | 485 | if (callstack_start == NULL) { 486 | LOGMSG(l_FATAL, "Could not find callstack start in crash report %s", description); 487 | } 488 | 489 | /* Skip the newline */ 490 | callstack_start++; 491 | 492 | /* Determine the end of the callstack */ 493 | char *callstack_end = strstr(callstack, "\n\nThread"); 494 | 495 | if (callstack_end == NULL) { 496 | LOGMSG(l_FATAL, "Could not find callstack end in crash report %s", description); 497 | } 498 | 499 | /* Make sure it's NULL-terminated */ 500 | *callstack_end = '\0'; 501 | 502 | /* 503 | 504 | For each line, we only take the last three nibbles from the address. 505 | 506 | Sample output: 507 | 508 | 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10 509 | 1 libsystem_c.dylib 0x00007fff85731ec0 __abort + 193 510 | 2 libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195 511 | 3 stack_buffer_overflow64-stripped 0x000000010339def5 0x10339d000 + 3829 512 | 4 ??? 0x4141414141414141 0 + 4702111234474983745 513 | 514 | 0 libsystem_kernel.dylib 0x00007fff80514d46 __kill + 10 515 | 1 libsystem_c.dylib 0x00007fff85731ec0 __abort + 193 516 | 2 libsystem_c.dylib 0x00007fff85732d17 __stack_chk_fail + 195 517 | 3 stack_buffer_overflow64 0x0000000108f41ef5 main + 133 518 | 4 ??? 0x4141414141414141 0 + 4702111234474983745 519 | 520 | 0 libsystem_kernel.dylib 0x940023ba __kill + 10 521 | 1 libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32 522 | 2 libsystem_c.dylib 0x926f362e __abort + 246 523 | 3 libsystem_c.dylib 0x926c2b60 __chk_fail + 49 524 | 4 libsystem_c.dylib 0x926c2bf9 __memset_chk + 53 525 | 5 stack_buffer_overflow32-stripped 0x00093ee5 0x93000 + 3813 526 | 6 libdyld.dylib 0x978c6725 start + 1 527 | 528 | 0 libsystem_kernel.dylib 0x940023ba __kill + 10 529 | 1 libsystem_kernel.dylib 0x940014bc kill$UNIX2003 + 32 530 | 2 libsystem_c.dylib 0x926f362e __abort + 246 531 | 3 libsystem_c.dylib 0x926c2b60 __chk_fail + 49 532 | 4 libsystem_c.dylib 0x926c2bf9 __memset_chk + 53 533 | 5 stack_buffer_overflow32 0x0003cee5 main + 117 534 | 6 libdyld.dylib 0x978c6725 start + 1 535 | 536 | */ 537 | 538 | uint64_t hash = 0; 539 | char *pos = callstack_start; 540 | 541 | /* Go through each line until we run out of lines */ 542 | while (strstr(pos, "\t") != NULL) { 543 | /* 544 | * Format: dylib spaces tab address space symbol space plus space offset 545 | * Scroll pos forward to the last three nibbles of the address. 546 | */ 547 | if ((pos = strstr(pos, "\t")) == NULL) 548 | break; 549 | if ((pos = strstr(pos, " ")) == NULL) 550 | break; 551 | pos = pos - 3; 552 | /* Hash the last three nibbles */ 553 | hash ^= util_hash(pos, 3); 554 | /* Scroll pos one forward to skip the current tab */ 555 | pos++; 556 | } 557 | 558 | LOGMSG(l_DEBUG, "callstack hash %u", hash); 559 | 560 | [_crashReport release]; 561 | [pool drain]; 562 | 563 | return hash; 564 | } 565 | 566 | kern_return_t catch_mach_exception_raise 567 | (mach_port_t exception_port, 568 | mach_port_t thread, 569 | mach_port_t task, 570 | exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt) { 571 | LOGMSG(l_FATAL, "This function should never get called"); 572 | return KERN_SUCCESS; 573 | } 574 | 575 | kern_return_t catch_mach_exception_raise_state 576 | (mach_port_t exception_port, 577 | exception_type_t exception, 578 | const mach_exception_data_t code, 579 | mach_msg_type_number_t codeCnt, 580 | int *flavor, 581 | const thread_state_t old_state, 582 | mach_msg_type_number_t old_stateCnt, 583 | thread_state_t new_state, mach_msg_type_number_t * new_stateCnt) { 584 | LOGMSG(l_FATAL, "This function should never get called"); 585 | return KERN_SUCCESS; 586 | } 587 | 588 | kern_return_t catch_mach_exception_raise_state_identity( __attribute__ ((unused)) exception_port_t 589 | exception_port, thread_port_t thread, 590 | task_port_t task, 591 | exception_type_t exception, 592 | mach_exception_data_t code, 593 | mach_msg_type_number_t code_count, 594 | int *flavor, thread_state_t in_state, 595 | mach_msg_type_number_t in_state_count, 596 | thread_state_t out_state, 597 | mach_msg_type_number_t * out_state_count) 598 | { 599 | if (exception != EXC_CRASH) { 600 | LOGMSG(l_FATAL, "Got non EXC_CRASH! This should not happen."); 601 | } 602 | 603 | /* We will save our results to the honggfuzz_t global */ 604 | pid_t pid; 605 | pid_for_task(task, &pid); 606 | LOGMSG(l_DEBUG, "Crash of pid %d", pid); 607 | 608 | int idx = HF_SLOT(g_hfuzz, pid); 609 | 610 | /* 611 | * Get program counter. 612 | * Cast to void* in order to silence the alignment warnings 613 | */ 614 | 615 | x86_thread_state_t *platform_in_state = ((x86_thread_state_t *) (void *)in_state); 616 | 617 | if (x86_THREAD_STATE32 == platform_in_state->tsh.flavor) { 618 | g_hfuzz->fuzzers[idx].pc = platform_in_state->uts.ts32.__eip; 619 | } else { 620 | g_hfuzz->fuzzers[idx].pc = platform_in_state->uts.ts64.__rip; 621 | } 622 | 623 | /* Get the exception type */ 624 | 625 | exception_type_t exception_type = ((code[0] >> 20) & 0x0F); 626 | 627 | if (exception_type == 0) { 628 | exception_type = EXC_CRASH; 629 | } 630 | 631 | g_hfuzz->fuzzers[idx].exception = exception_type; 632 | 633 | /* Get the access address. TODO: check whether there is a better way to do this. */ 634 | 635 | mach_exception_data_type_t exception_data[2]; 636 | memcpy(exception_data, code, sizeof(exception_data)); 637 | exception_data[0] = (code[0] & ~(0x00000000FFF00000)); 638 | exception_data[1] = code[1]; 639 | 640 | mach_exception_data_type_t access_address = exception_data[1]; 641 | g_hfuzz->fuzzers[idx].access = (uint64_t) access_address; 642 | 643 | /* Get a hash of the callstack */ 644 | 645 | uint64_t hash = 646 | hash_callstack(thread, task, exception, code, code_count, flavor, in_state, in_state_count); 647 | 648 | g_hfuzz->fuzzers[idx].backtrace = hash; 649 | 650 | /* Cleanup */ 651 | 652 | if (mach_port_deallocate(mach_task_self(), task) != KERN_SUCCESS) { 653 | LOGMSG(l_WARN, "Exception Handler: Could not deallocate task"); 654 | } 655 | 656 | if (mach_port_deallocate(mach_task_self(), thread) != KERN_SUCCESS) { 657 | LOGMSG(l_WARN, "Exception Handler: Could not deallocate thread"); 658 | } 659 | 660 | return KERN_SUCCESS; //KERN_SUCCESS indicates that this should not be forwarded to other handlers 661 | } 662 | --------------------------------------------------------------------------------