Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | // vim:ts=8:sw=2:et: |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "leaky.h" |
michael@0 | 7 | |
michael@0 | 8 | #ifdef USE_BFD |
michael@0 | 9 | #include <stdio.h> |
michael@0 | 10 | #include <string.h> |
michael@0 | 11 | #include <fcntl.h> |
michael@0 | 12 | #include <unistd.h> |
michael@0 | 13 | #include <libgen.h> |
michael@0 | 14 | #include <bfd.h> |
michael@0 | 15 | #include <cxxabi.h> |
michael@0 | 16 | |
michael@0 | 17 | static bfd *try_debug_file(const char *filename, unsigned long crc32) |
michael@0 | 18 | { |
michael@0 | 19 | int fd = open(filename, O_RDONLY); |
michael@0 | 20 | if (fd < 0) |
michael@0 | 21 | return nullptr; |
michael@0 | 22 | |
michael@0 | 23 | unsigned char buf[4*1024]; |
michael@0 | 24 | unsigned long crc = 0; |
michael@0 | 25 | |
michael@0 | 26 | while (1) { |
michael@0 | 27 | ssize_t count = read(fd, buf, sizeof(buf)); |
michael@0 | 28 | if (count <= 0) |
michael@0 | 29 | break; |
michael@0 | 30 | |
michael@0 | 31 | crc = bfd_calc_gnu_debuglink_crc32(crc, buf, count); |
michael@0 | 32 | } |
michael@0 | 33 | |
michael@0 | 34 | close(fd); |
michael@0 | 35 | |
michael@0 | 36 | if (crc != crc32) |
michael@0 | 37 | return nullptr; |
michael@0 | 38 | |
michael@0 | 39 | bfd *object = bfd_openr(filename, nullptr); |
michael@0 | 40 | if (!bfd_check_format(object, bfd_object)) { |
michael@0 | 41 | bfd_close(object); |
michael@0 | 42 | return nullptr; |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | return object; |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | static bfd *find_debug_file(bfd *lib, const char *aFileName) |
michael@0 | 49 | { |
michael@0 | 50 | // check for a separate debug file with symbols |
michael@0 | 51 | asection *sect = bfd_get_section_by_name(lib, ".gnu_debuglink"); |
michael@0 | 52 | |
michael@0 | 53 | if (!sect) |
michael@0 | 54 | return nullptr; |
michael@0 | 55 | |
michael@0 | 56 | bfd_size_type debuglinkSize = bfd_section_size (objfile->obfd, sect); |
michael@0 | 57 | |
michael@0 | 58 | char *debuglink = new char[debuglinkSize]; |
michael@0 | 59 | bfd_get_section_contents(lib, sect, debuglink, 0, debuglinkSize); |
michael@0 | 60 | |
michael@0 | 61 | // crc checksum is aligned to 4 bytes, and after the NUL. |
michael@0 | 62 | int crc_offset = (int(strlen(debuglink)) & ~3) + 4; |
michael@0 | 63 | unsigned long crc32 = bfd_get_32(lib, debuglink + crc_offset); |
michael@0 | 64 | |
michael@0 | 65 | // directory component |
michael@0 | 66 | char *dirbuf = strdup(aFileName); |
michael@0 | 67 | const char *dir = dirname(dirbuf); |
michael@0 | 68 | |
michael@0 | 69 | static const char debug_subdir[] = ".debug"; |
michael@0 | 70 | // This is gdb's default global debugging info directory, but gdb can |
michael@0 | 71 | // be instructed to use a different directory. |
michael@0 | 72 | static const char global_debug_dir[] = "/usr/lib/debug"; |
michael@0 | 73 | |
michael@0 | 74 | char *filename = |
michael@0 | 75 | new char[strlen(global_debug_dir) + strlen(dir) + crc_offset + 3]; |
michael@0 | 76 | |
michael@0 | 77 | // /path/debuglink |
michael@0 | 78 | sprintf(filename, "%s/%s", dir, debuglink); |
michael@0 | 79 | bfd *debugFile = try_debug_file(filename, crc32); |
michael@0 | 80 | if (!debugFile) { |
michael@0 | 81 | |
michael@0 | 82 | // /path/.debug/debuglink |
michael@0 | 83 | sprintf(filename, "%s/%s/%s", dir, debug_subdir, debuglink); |
michael@0 | 84 | debugFile = try_debug_file(filename, crc32); |
michael@0 | 85 | if (!debugFile) { |
michael@0 | 86 | |
michael@0 | 87 | // /usr/lib/debug/path/debuglink |
michael@0 | 88 | sprintf(filename, "%s/%s/%s", global_debug_dir, dir, debuglink); |
michael@0 | 89 | debugFile = try_debug_file(filename, crc32); |
michael@0 | 90 | } |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | delete[] filename; |
michael@0 | 94 | free(dirbuf); |
michael@0 | 95 | delete[] debuglink; |
michael@0 | 96 | |
michael@0 | 97 | return debugFile; |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | |
michael@0 | 101 | // Use an indirect array to avoid copying tons of objects |
michael@0 | 102 | Symbol ** leaky::ExtendSymbols(int num) |
michael@0 | 103 | { |
michael@0 | 104 | long n = numExternalSymbols + num; |
michael@0 | 105 | |
michael@0 | 106 | externalSymbols = (Symbol**) |
michael@0 | 107 | realloc(externalSymbols, |
michael@0 | 108 | (size_t) (sizeof(externalSymbols[0]) * n)); |
michael@0 | 109 | Symbol *new_array = new Symbol[n]; |
michael@0 | 110 | for (int i = 0; i < num; i++) { |
michael@0 | 111 | externalSymbols[i + numExternalSymbols] = &new_array[i]; |
michael@0 | 112 | } |
michael@0 | 113 | lastSymbol = externalSymbols + n; |
michael@0 | 114 | Symbol **sp = externalSymbols + numExternalSymbols; |
michael@0 | 115 | numExternalSymbols = n; |
michael@0 | 116 | return sp; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | #define NEXT_SYMBOL do { sp++; \ |
michael@0 | 120 | if (sp >= lastSymbol) { \ |
michael@0 | 121 | sp = ExtendSymbols(16384); \ |
michael@0 | 122 | } \ |
michael@0 | 123 | } while (0) |
michael@0 | 124 | |
michael@0 | 125 | void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress) |
michael@0 | 126 | { |
michael@0 | 127 | int initialSymbols = usefulSymbols; |
michael@0 | 128 | if (nullptr == externalSymbols) { |
michael@0 | 129 | externalSymbols = (Symbol**) calloc(sizeof(Symbol*),10000); |
michael@0 | 130 | Symbol *new_array = new Symbol[10000]; |
michael@0 | 131 | for (int i = 0; i < 10000; i++) { |
michael@0 | 132 | externalSymbols[i] = &new_array[i]; |
michael@0 | 133 | } |
michael@0 | 134 | numExternalSymbols = 10000; |
michael@0 | 135 | } |
michael@0 | 136 | Symbol** sp = externalSymbols + usefulSymbols; |
michael@0 | 137 | lastSymbol = externalSymbols + numExternalSymbols; |
michael@0 | 138 | |
michael@0 | 139 | // Create a dummy symbol for the library so, if it doesn't have any |
michael@0 | 140 | // symbols, we show it by library. |
michael@0 | 141 | (*sp)->Init(aFileName, aBaseAddress); |
michael@0 | 142 | NEXT_SYMBOL; |
michael@0 | 143 | |
michael@0 | 144 | bfd_boolean kDynamic = (bfd_boolean) false; |
michael@0 | 145 | |
michael@0 | 146 | static int firstTime = 1; |
michael@0 | 147 | if (firstTime) { |
michael@0 | 148 | firstTime = 0; |
michael@0 | 149 | bfd_init (); |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | bfd* lib = bfd_openr(aFileName, nullptr); |
michael@0 | 153 | if (nullptr == lib) { |
michael@0 | 154 | return; |
michael@0 | 155 | } |
michael@0 | 156 | if (!bfd_check_format(lib, bfd_object)) { |
michael@0 | 157 | bfd_close(lib); |
michael@0 | 158 | return; |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | bfd *symbolFile = find_debug_file(lib, aFileName); |
michael@0 | 162 | |
michael@0 | 163 | // read mini symbols |
michael@0 | 164 | PTR minisyms; |
michael@0 | 165 | unsigned int size; |
michael@0 | 166 | long symcount = 0; |
michael@0 | 167 | |
michael@0 | 168 | if (symbolFile) { |
michael@0 | 169 | symcount = bfd_read_minisymbols(symbolFile, kDynamic, &minisyms, &size); |
michael@0 | 170 | if (symcount == 0) { |
michael@0 | 171 | bfd_close(symbolFile); |
michael@0 | 172 | } else { |
michael@0 | 173 | bfd_close(lib); |
michael@0 | 174 | } |
michael@0 | 175 | } |
michael@0 | 176 | if (symcount == 0) { |
michael@0 | 177 | symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); |
michael@0 | 178 | if (symcount == 0) { |
michael@0 | 179 | // symtab is empty; try dynamic symbols |
michael@0 | 180 | kDynamic = (bfd_boolean) true; |
michael@0 | 181 | symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); |
michael@0 | 182 | } |
michael@0 | 183 | symbolFile = lib; |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | asymbol* store; |
michael@0 | 187 | store = bfd_make_empty_symbol(symbolFile); |
michael@0 | 188 | |
michael@0 | 189 | // Scan symbols |
michael@0 | 190 | size_t demangle_buffer_size = 128; |
michael@0 | 191 | char *demangle_buffer = (char*) malloc(demangle_buffer_size); |
michael@0 | 192 | bfd_byte* from = (bfd_byte *) minisyms; |
michael@0 | 193 | bfd_byte* fromend = from + symcount * size; |
michael@0 | 194 | for (; from < fromend; from += size) { |
michael@0 | 195 | asymbol *sym; |
michael@0 | 196 | sym = bfd_minisymbol_to_symbol(symbolFile, kDynamic, (const PTR) from, store); |
michael@0 | 197 | |
michael@0 | 198 | symbol_info syminfo; |
michael@0 | 199 | bfd_get_symbol_info (symbolFile, sym, &syminfo); |
michael@0 | 200 | |
michael@0 | 201 | // if ((syminfo.type == 'T') || (syminfo.type == 't')) { |
michael@0 | 202 | const char* nm = bfd_asymbol_name(sym); |
michael@0 | 203 | if (nm && nm[0]) { |
michael@0 | 204 | char* dnm = nullptr; |
michael@0 | 205 | if (strncmp("__thunk", nm, 7)) { |
michael@0 | 206 | dnm = |
michael@0 | 207 | abi::__cxa_demangle(nm, demangle_buffer, &demangle_buffer_size, 0); |
michael@0 | 208 | if (dnm) { |
michael@0 | 209 | demangle_buffer = dnm; |
michael@0 | 210 | } |
michael@0 | 211 | } |
michael@0 | 212 | (*sp)->Init(dnm ? dnm : nm, syminfo.value + aBaseAddress); |
michael@0 | 213 | NEXT_SYMBOL; |
michael@0 | 214 | } |
michael@0 | 215 | // } |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | free(demangle_buffer); |
michael@0 | 219 | demangle_buffer = nullptr; |
michael@0 | 220 | |
michael@0 | 221 | bfd_close(symbolFile); |
michael@0 | 222 | |
michael@0 | 223 | int interesting = sp - externalSymbols; |
michael@0 | 224 | if (!quiet) { |
michael@0 | 225 | printf("%s provided %d symbols\n", aFileName, |
michael@0 | 226 | interesting - initialSymbols); |
michael@0 | 227 | } |
michael@0 | 228 | usefulSymbols = interesting; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | #endif /* USE_BFD */ |