/* * * honggfuzz - architecture dependent code (LINUX/BFD) * ----------------------------------------- * * Author: Robert Swiecki <swiecki@google.com> * * Copyright 2010-2018 by Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * */ #include "linux/bfd.h" #include <bfd.h> #include <dis-asm.h> #include <inttypes.h> #include <pthread.h> #include <stdarg.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "honggfuzz.h" #include "libhfcommon/common.h" #include "libhfcommon/files.h" #include "libhfcommon/log.h" #include "libhfcommon/util.h" typedef struct { bfd* bfdh; asection* section; asymbol** syms; } bfd_t; /* * This is probably the only define which was added with binutils 2.29, so we us * it, do decide which disassembler() prototype from dis-asm.h to use */ #if defined(FOR_EACH_DISASSEMBLER_OPTION) #define _HF_BFD_GE_2_29 #endif static pthread_mutex_t arch_bfd_mutex = PTHREAD_MUTEX_INITIALIZER; static bool arch_bfdInit(pid_t pid, bfd_t* bfdParams) { char fname[PATH_MAX]; snprintf(fname, sizeof(fname), "/proc/%d/exe", pid); if ((bfdParams->bfdh = bfd_openr(fname, 0)) == NULL) { LOG_E("bfd_openr(%s) failed", fname); return false; } if (!bfd_check_format(bfdParams->bfdh, bfd_object)) { LOG_E("bfd_check_format() failed"); return false; } int storage_needed = bfd_get_symtab_upper_bound(bfdParams->bfdh); if (storage_needed <= 0) { LOG_E("bfd_get_symtab_upper_bound() returned '%d'", storage_needed); return false; } bfdParams->syms = (asymbol**)util_Malloc(storage_needed); bfd_canonicalize_symtab(bfdParams->bfdh, bfdParams->syms); if ((bfdParams->section = bfd_get_section_by_name(bfdParams->bfdh, ".text")) == NULL) { LOG_E("bfd_get_section_by_name('.text') failed"); return false; } return true; } static void arch_bfdDestroy(bfd_t* bfdParams) { if (bfdParams->syms) { free(bfdParams->syms); } if (bfdParams->bfdh) { bfd_close(bfdParams->bfdh); } } void arch_bfdResolveSyms(pid_t pid, funcs_t* funcs, size_t num) { /* Guess what? libbfd is not multi-threading safe */ MX_SCOPED_LOCK(&arch_bfd_mutex); bfd_init(); __block bfd_t bfdParams = { .bfdh = NULL, .section = NULL, .syms = NULL, }; if (arch_bfdInit(pid, &bfdParams) == false) { return; } const char* func; const char* file; unsigned int line; for (unsigned int i = 0; i < num; i++) { snprintf(funcs[i].func, sizeof(funcs->func), "[UNKNOWN]"); if (funcs[i].pc == NULL) { continue; } long offset = (long)funcs[i].pc - bfdParams.section->vma; if ((offset < 0 || (unsigned long)offset > bfdParams.section->size)) { continue; } if (bfd_find_nearest_line( bfdParams.bfdh, bfdParams.section, bfdParams.syms, offset, &file, &func, &line)) { snprintf(funcs[i].func, sizeof(funcs->func), "%s", func); funcs[i].line = line; } } arch_bfdDestroy(&bfdParams); } static int arch_bfdFPrintF(void* buf, const char* fmt, ...) { va_list args; va_start(args, fmt); int ret = util_vssnprintf(buf, _HF_INSTR_SZ, fmt, args); va_end(args); return ret; } void arch_bfdDisasm(pid_t pid, uint8_t* mem, size_t size, char* instr) { MX_SCOPED_LOCK(&arch_bfd_mutex); bfd_init(); char fname[PATH_MAX]; snprintf(fname, sizeof(fname), "/proc/%d/exe", pid); bfd* bfdh = bfd_openr(fname, NULL); if (bfdh == NULL) { LOG_W("bfd_openr('/proc/%d/exe') failed", pid); return; } if (!bfd_check_format(bfdh, bfd_object)) { LOG_W("bfd_check_format() failed"); bfd_close(bfdh); return; } #if defined(_HF_BFD_GE_2_29) disassembler_ftype disassemble = disassembler(bfd_get_arch(bfdh), bfd_little_endian(bfdh) ? FALSE : TRUE, 0, NULL); #else disassembler_ftype disassemble = disassembler(bfdh); #endif // defined(_HD_BFD_GE_2_29) if (disassemble == NULL) { LOG_W("disassembler() failed"); bfd_close(bfdh); return; } struct disassemble_info info; init_disassemble_info(&info, instr, arch_bfdFPrintF); info.arch = bfd_get_arch(bfdh); info.mach = bfd_get_mach(bfdh); info.buffer = mem; info.buffer_length = size; info.section = NULL; info.endian = bfd_little_endian(bfdh) ? BFD_ENDIAN_LITTLE : BFD_ENDIAN_BIG; disassemble_init_for_target(&info); strcpy(instr, ""); if (disassemble(0, &info) <= 0) { snprintf(instr, _HF_INSTR_SZ, "[DIS-ASM_FAILURE]"); } bfd_close(bfdh); }