1 /*
2 *
3 * honggfuzz - architecture dependent code (LINUX/BFD)
4 * -----------------------------------------
5 *
6 * Author: Robert Swiecki <swiecki@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/bfd.h"
25
26 #include <bfd.h>
27 #include <dis-asm.h>
28 #include <inttypes.h>
29 #include <pthread.h>
30 #include <stdarg.h>
31 #include <stddef.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36
37 #include "honggfuzz.h"
38 #include "libhfcommon/common.h"
39 #include "libhfcommon/files.h"
40 #include "libhfcommon/log.h"
41 #include "libhfcommon/util.h"
42
43 typedef struct {
44 bfd* bfdh;
45 asection* section;
46 asymbol** syms;
47 } bfd_t;
48
49 /*
50 * This is probably the only define which was added with binutils 2.29, so we us
51 * it, do decide which disassembler() prototype from dis-asm.h to use
52 */
53 #if defined(FOR_EACH_DISASSEMBLER_OPTION)
54 #define _HF_BFD_GE_2_29
55 #endif
56
57 static pthread_mutex_t arch_bfd_mutex = PTHREAD_MUTEX_INITIALIZER;
58
arch_bfdInit(pid_t pid,bfd_t * bfdParams)59 static bool arch_bfdInit(pid_t pid, bfd_t* bfdParams) {
60 char fname[PATH_MAX];
61 snprintf(fname, sizeof(fname), "/proc/%d/exe", pid);
62 if ((bfdParams->bfdh = bfd_openr(fname, 0)) == NULL) {
63 LOG_E("bfd_openr(%s) failed", fname);
64 return false;
65 }
66
67 if (!bfd_check_format(bfdParams->bfdh, bfd_object)) {
68 LOG_E("bfd_check_format() failed");
69 return false;
70 }
71
72 int storage_needed = bfd_get_symtab_upper_bound(bfdParams->bfdh);
73 if (storage_needed <= 0) {
74 LOG_E("bfd_get_symtab_upper_bound() returned '%d'", storage_needed);
75 return false;
76 }
77
78 bfdParams->syms = (asymbol**)util_Malloc(storage_needed);
79 bfd_canonicalize_symtab(bfdParams->bfdh, bfdParams->syms);
80
81 if ((bfdParams->section = bfd_get_section_by_name(bfdParams->bfdh, ".text")) == NULL) {
82 LOG_E("bfd_get_section_by_name('.text') failed");
83 return false;
84 }
85
86 return true;
87 }
88
arch_bfdDestroy(bfd_t * bfdParams)89 static void arch_bfdDestroy(bfd_t* bfdParams) {
90 if (bfdParams->syms) {
91 free(bfdParams->syms);
92 }
93 if (bfdParams->bfdh) {
94 bfd_close(bfdParams->bfdh);
95 }
96 }
97
arch_bfdResolveSyms(pid_t pid,funcs_t * funcs,size_t num)98 void arch_bfdResolveSyms(pid_t pid, funcs_t* funcs, size_t num) {
99 /* Guess what? libbfd is not multi-threading safe */
100 MX_SCOPED_LOCK(&arch_bfd_mutex);
101
102 bfd_init();
103
104 __block bfd_t bfdParams = {
105 .bfdh = NULL,
106 .section = NULL,
107 .syms = NULL,
108 };
109
110 if (arch_bfdInit(pid, &bfdParams) == false) {
111 return;
112 }
113
114 const char* func;
115 const char* file;
116 unsigned int line;
117 for (unsigned int i = 0; i < num; i++) {
118 snprintf(funcs[i].func, sizeof(funcs->func), "[UNKNOWN]");
119 if (funcs[i].pc == NULL) {
120 continue;
121 }
122 long offset = (long)funcs[i].pc - bfdParams.section->vma;
123 if ((offset < 0 || (unsigned long)offset > bfdParams.section->size)) {
124 continue;
125 }
126 if (bfd_find_nearest_line(
127 bfdParams.bfdh, bfdParams.section, bfdParams.syms, offset, &file, &func, &line)) {
128 snprintf(funcs[i].func, sizeof(funcs->func), "%s", func);
129 funcs[i].line = line;
130 }
131 }
132
133 arch_bfdDestroy(&bfdParams);
134 }
135
arch_bfdFPrintF(void * buf,const char * fmt,...)136 static int arch_bfdFPrintF(void* buf, const char* fmt, ...) {
137 va_list args;
138 va_start(args, fmt);
139 int ret = util_vssnprintf(buf, _HF_INSTR_SZ, fmt, args);
140 va_end(args);
141
142 return ret;
143 }
144
arch_bfdDisasm(pid_t pid,uint8_t * mem,size_t size,char * instr)145 void arch_bfdDisasm(pid_t pid, uint8_t* mem, size_t size, char* instr) {
146 MX_SCOPED_LOCK(&arch_bfd_mutex);
147
148 bfd_init();
149
150 char fname[PATH_MAX];
151 snprintf(fname, sizeof(fname), "/proc/%d/exe", pid);
152 bfd* bfdh = bfd_openr(fname, NULL);
153 if (bfdh == NULL) {
154 LOG_W("bfd_openr('/proc/%d/exe') failed", pid);
155 return;
156 }
157
158 if (!bfd_check_format(bfdh, bfd_object)) {
159 LOG_W("bfd_check_format() failed");
160 bfd_close(bfdh);
161 return;
162 }
163 #if defined(_HF_BFD_GE_2_29)
164 disassembler_ftype disassemble =
165 disassembler(bfd_get_arch(bfdh), bfd_little_endian(bfdh) ? FALSE : TRUE, 0, NULL);
166 #else
167 disassembler_ftype disassemble = disassembler(bfdh);
168 #endif // defined(_HD_BFD_GE_2_29)
169 if (disassemble == NULL) {
170 LOG_W("disassembler() failed");
171 bfd_close(bfdh);
172 return;
173 }
174
175 struct disassemble_info info;
176 init_disassemble_info(&info, instr, arch_bfdFPrintF);
177 info.arch = bfd_get_arch(bfdh);
178 info.mach = bfd_get_mach(bfdh);
179 info.buffer = mem;
180 info.buffer_length = size;
181 info.section = NULL;
182 info.endian = bfd_little_endian(bfdh) ? BFD_ENDIAN_LITTLE : BFD_ENDIAN_BIG;
183 disassemble_init_for_target(&info);
184
185 strcpy(instr, "");
186 if (disassemble(0, &info) <= 0) {
187 snprintf(instr, _HF_INSTR_SZ, "[DIS-ASM_FAILURE]");
188 }
189
190 bfd_close(bfdh);
191 }
192