1 /* 2 * 3 * honggfuzz - architecture dependent code (LINUX/UNWIND) 4 * ----------------------------------------- 5 * 6 * Author: Robert Swiecki <swiecki (at) google.com> 7 * 8 * Copyright 2010-2018 by Google Inc. All Rights Reserved. 9 * 10 * Licensed under the Apache License, Version 2.0 (the "License"); you may 11 * not use this file except in compliance with the License. You may obtain 12 * 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 19 * implied. See the License for the specific language governing 20 * permissions and limitations under the License. 21 * 22 */ 23 24 #include "linux/unwind.h" 25 26 #include <endian.h> 27 #include <libunwind-ptrace.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 32 #include "honggfuzz.h" 33 #include "libhfcommon/common.h" 34 #include "libhfcommon/log.h" 35 36 /* 37 * WARNING: Ensure that _UPT-info structs are not shared between threads 38 * http://www.nongnu.org/libunwind/man/libunwind-ptrace(3).html 39 */ 40 41 // libunwind error codes used for debugging 42 static const char* UNW_ER[] = { 43 "UNW_ESUCCESS", /* no error */ 44 "UNW_EUNSPEC", /* unspecified (general) error */ 45 "UNW_ENOMEM", /* out of memory */ 46 "UNW_EBADREG", /* bad register number */ 47 "UNW_EREADONLYREG", /* attempt to write read-only register */ 48 "UNW_ESTOPUNWIND", /* stop unwinding */ 49 "UNW_EINVALIDIP", /* invalid IP */ 50 "UNW_EBADFRAME", /* bad frame */ 51 "UNW_EINVAL", /* unsupported operation or bad value */ 52 "UNW_EBADVERSION", /* unwind info has unsupported version */ 53 "UNW_ENOINFO" /* no unwind info found */ 54 }; 55 56 typedef struct { 57 unsigned long start; 58 unsigned long end; 59 char perms[6]; 60 unsigned long offset; 61 char dev[8]; 62 unsigned long inode; 63 char name[PATH_MAX]; 64 } procMap_t; 65 66 static procMap_t* arch_parsePidMaps(pid_t pid, size_t* mapsCount) { 67 FILE* f = NULL; 68 char fProcMaps[PATH_MAX] = {0}; 69 snprintf(fProcMaps, PATH_MAX, "/proc/%d/maps", pid); 70 71 if ((f = fopen(fProcMaps, "rb")) == NULL) { 72 PLOG_E("Couldn't open '%s' - R/O mode", fProcMaps); 73 return 0; 74 } 75 defer { 76 fclose(f); 77 }; 78 79 *mapsCount = 0; 80 procMap_t* mapsList = malloc(sizeof(procMap_t)); 81 if (mapsList == NULL) { 82 PLOG_W("malloc(size='%zu')", sizeof(procMap_t)); 83 return NULL; 84 } 85 86 while (!feof(f)) { 87 char buf[sizeof(procMap_t) + 1]; 88 if (fgets(buf, sizeof(buf), f) == 0) { 89 break; 90 } 91 92 mapsList[*mapsCount].name[0] = '\0'; 93 sscanf(buf, "%lx-%lx %5s %lx %7s %ld %s", &mapsList[*mapsCount].start, 94 &mapsList[*mapsCount].end, mapsList[*mapsCount].perms, &mapsList[*mapsCount].offset, 95 mapsList[*mapsCount].dev, &mapsList[*mapsCount].inode, mapsList[*mapsCount].name); 96 97 *mapsCount += 1; 98 if ((mapsList = realloc(mapsList, (*mapsCount + 1) * sizeof(procMap_t))) == NULL) { 99 PLOG_W("realloc failed (sz=%zu)", (*mapsCount + 1) * sizeof(procMap_t)); 100 free(mapsList); 101 return NULL; 102 } 103 } 104 105 return mapsList; 106 } 107 108 static char* arch_searchMaps(unsigned long addr, size_t mapsCnt, procMap_t* mapsList) { 109 for (size_t i = 0; i < mapsCnt; i++) { 110 if (addr >= mapsList[i].start && addr <= mapsList[i].end) { 111 return mapsList[i].name; 112 } 113 114 /* Benefit from maps being sorted by address */ 115 if (addr < mapsList[i].start) { 116 break; 117 } 118 } 119 return NULL; 120 } 121 122 #ifndef __ANDROID__ 123 size_t arch_unwindStack(pid_t pid, funcs_t* funcs) { 124 size_t num_frames = 0, mapsCnt = 0; 125 procMap_t* mapsList = arch_parsePidMaps(pid, &mapsCnt); 126 defer { 127 free(mapsList); 128 }; 129 130 unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER); 131 if (!as) { 132 LOG_E("[pid='%d'] unw_create_addr_space failed", pid); 133 return num_frames; 134 } 135 defer { 136 unw_destroy_addr_space(as); 137 }; 138 139 void* ui = _UPT_create(pid); 140 if (ui == NULL) { 141 LOG_E("[pid='%d'] _UPT_create failed", pid); 142 return num_frames; 143 } 144 defer { 145 _UPT_destroy(ui); 146 }; 147 148 unw_cursor_t c; 149 int ret = unw_init_remote(&c, as, ui); 150 if (ret < 0) { 151 LOG_E("[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]); 152 return num_frames; 153 } 154 155 for (num_frames = 0; unw_step(&c) > 0 && num_frames < _HF_MAX_FUNCS; num_frames++) { 156 unw_word_t ip; 157 char* mapName = NULL; 158 ret = unw_get_reg(&c, UNW_REG_IP, &ip); 159 if (ret < 0) { 160 LOG_E("[pid='%d'] [%zd] failed to read IP (%s)", pid, num_frames, UNW_ER[-ret]); 161 funcs[num_frames].pc = 0; 162 } else { 163 funcs[num_frames].pc = (void*)(uintptr_t)ip; 164 } 165 if (mapsCnt > 0 && (mapName = arch_searchMaps(ip, mapsCnt, mapsList)) != NULL) { 166 memcpy(funcs[num_frames].mapName, mapName, sizeof(funcs[num_frames].mapName)); 167 } else { 168 strncpy(funcs[num_frames].mapName, "UNKNOWN", sizeof(funcs[num_frames].mapName)); 169 } 170 } 171 172 return num_frames; 173 } 174 175 #else /* !defined(__ANDROID__) */ 176 size_t arch_unwindStack(pid_t pid, funcs_t* funcs) { 177 size_t num_frames = 0, mapsCnt = 0; 178 procMap_t* mapsList = arch_parsePidMaps(pid, &mapsCnt); 179 defer { 180 free(mapsList); 181 }; 182 183 unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER); 184 if (!as) { 185 LOG_E("[pid='%d'] unw_create_addr_space failed", pid); 186 return num_frames; 187 } 188 defer { 189 unw_destroy_addr_space(as); 190 }; 191 192 struct UPT_info* ui = (struct UPT_info*)_UPT_create(pid); 193 if (ui == NULL) { 194 LOG_E("[pid='%d'] _UPT_create failed", pid); 195 return num_frames; 196 } 197 defer { 198 _UPT_destroy(ui); 199 }; 200 201 unw_cursor_t cursor; 202 int ret = unw_init_remote(&cursor, as, ui); 203 if (ret < 0) { 204 LOG_E("[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]); 205 return num_frames; 206 } 207 208 do { 209 char* mapName = NULL; 210 unw_word_t pc = 0, offset = 0; 211 char buf[_HF_FUNC_NAME_SZ] = {0}; 212 213 ret = unw_get_reg(&cursor, UNW_REG_IP, &pc); 214 if (ret < 0) { 215 LOG_E("[pid='%d'] [%zd] failed to read IP (%s)", pid, num_frames, UNW_ER[-ret]); 216 // We don't want to try to extract info from an arbitrary IP 217 // TODO: Maybe abort completely (goto out)) 218 goto skip_frame_info; 219 } 220 221 unw_proc_info_t frameInfo; 222 ret = unw_get_proc_info(&cursor, &frameInfo); 223 if (ret < 0) { 224 LOG_D("[pid='%d'] [%zd] unw_get_proc_info (%s)", pid, num_frames, UNW_ER[-ret]); 225 // Not safe to keep parsing frameInfo 226 goto skip_frame_info; 227 } 228 229 ret = unw_get_proc_name(&cursor, buf, sizeof(buf), &offset); 230 if (ret < 0) { 231 LOG_D( 232 "[pid='%d'] [%zd] unw_get_proc_name() failed (%s)", pid, num_frames, UNW_ER[-ret]); 233 buf[0] = '\0'; 234 } 235 236 skip_frame_info: 237 // Compared to bfd, line var plays the role of offset from func_name 238 // Reports format is adjusted accordingly to reflect in saved file 239 funcs[num_frames].line = offset; 240 funcs[num_frames].pc = (void*)pc; 241 memcpy(funcs[num_frames].func, buf, sizeof(funcs[num_frames].func)); 242 if (mapsCnt > 0 && (mapName = arch_searchMaps(pc, mapsCnt, mapsList)) != NULL) { 243 memcpy(funcs[num_frames].mapName, mapName, sizeof(funcs[num_frames].mapName)); 244 } else { 245 strncpy(funcs[num_frames].mapName, "UNKNOWN", sizeof(funcs[num_frames].mapName)); 246 } 247 248 num_frames++; 249 250 ret = unw_step(&cursor); 251 } while (ret > 0 && num_frames < _HF_MAX_FUNCS); 252 253 return num_frames; 254 } 255 #endif /* defined(__ANDROID__) */ 256 257 /* 258 * Nested loop not most efficient approach, although it's assumed that list is 259 * usually target specific and thus small. 260 */ 261 char* arch_btContainsSymbol( 262 size_t symbolsListSz, char** symbolsList, size_t num_frames, funcs_t* funcs) { 263 for (size_t frame = 0; frame < num_frames; frame++) { 264 size_t len = strlen(funcs[frame].func); 265 266 /* Try only for frames that have symbol name from backtrace */ 267 if (strlen(funcs[frame].func) > 0) { 268 for (size_t i = 0; i < symbolsListSz; i++) { 269 /* Wildcard symbol string special case */ 270 char* wOff = strchr(symbolsList[i], '*'); 271 if (wOff) { 272 /* Length always > 3 as checked at input file parsing step */ 273 len = wOff - symbolsList[i] - 1; 274 } 275 276 if (strncmp(funcs[frame].func, symbolsList[i], len) == 0) { 277 return funcs[frame].func; 278 } 279 } 280 } 281 } 282 return NULL; 283 } 284