Home | History | Annotate | Download | only in linux
      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