Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com> |
| 3 | * Copyright (c) 2013-2018 The strace developers. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. The name of the author may not be used to endorse or promote products |
| 14 | * derived from this software without specific prior written permission. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | |
| 28 | #include "defs.h" |
| 29 | #include <limits.h> |
| 30 | |
| 31 | #include "largefile_wrappers.h" |
| 32 | #include "mmap_cache.h" |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 33 | #include "mmap_notify.h" |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 34 | #include "xstring.h" |
| 35 | |
| 36 | static unsigned int mmap_cache_generation; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 37 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 38 | static void |
| 39 | mmap_cache_invalidate(struct tcb *tcp, void *unused) |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 40 | { |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 41 | #if SUPPORTED_PERSONALITIES > 1 |
| 42 | if (tcp->currpers != DEFAULT_PERSONALITY) { |
| 43 | /* disable stack trace */ |
| 44 | return; |
| 45 | } |
| 46 | #endif |
| 47 | mmap_cache_generation++; |
| 48 | debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p", |
| 49 | tcp->mmap_cache ? tcp->mmap_cache->generation : 0, |
| 50 | mmap_cache_generation, tcp, |
| 51 | tcp->mmap_cache ? tcp->mmap_cache->entry : 0); |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 52 | } |
| 53 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 54 | void |
| 55 | mmap_cache_enable(void) |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 56 | { |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 57 | static bool use_mmap_cache; |
| 58 | |
| 59 | if (!use_mmap_cache) { |
| 60 | mmap_notify_register_client(mmap_cache_invalidate, NULL); |
| 61 | use_mmap_cache = true; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | /* deleting the cache */ |
| 66 | static void |
| 67 | delete_mmap_cache(struct tcb *tcp, const char *caller) |
| 68 | { |
| 69 | debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s", |
| 70 | tcp->mmap_cache ? tcp->mmap_cache->generation : 0, |
| 71 | mmap_cache_generation, tcp, |
| 72 | tcp->mmap_cache ? tcp->mmap_cache->entry : 0, caller); |
| 73 | |
| 74 | if (!tcp->mmap_cache) |
| 75 | return; |
| 76 | |
| 77 | while (tcp->mmap_cache->size) { |
| 78 | unsigned int i = --tcp->mmap_cache->size; |
| 79 | free(tcp->mmap_cache->entry[i].binary_filename); |
| 80 | tcp->mmap_cache->entry[i].binary_filename = NULL; |
| 81 | } |
| 82 | |
| 83 | free(tcp->mmap_cache->entry); |
| 84 | tcp->mmap_cache->entry = NULL; |
| 85 | |
| 86 | free(tcp->mmap_cache); |
| 87 | tcp->mmap_cache = NULL; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | /* |
| 91 | * caching of /proc/ID/maps for each process to speed up stack tracing |
| 92 | * |
| 93 | * The cache must be refreshed after syscalls that affect memory mappings, |
| 94 | * e.g. mmap, mprotect, munmap, execve. |
| 95 | */ |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 96 | extern enum mmap_cache_rebuild_result |
| 97 | mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller) |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 98 | { |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 99 | if (tcp->mmap_cache |
| 100 | && tcp->mmap_cache->generation != mmap_cache_generation) |
| 101 | delete_mmap_cache(tcp, caller); |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 102 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 103 | if (tcp->mmap_cache) |
| 104 | return MMAP_CACHE_REBUILD_READY; |
| 105 | |
| 106 | char filename[sizeof("/proc/4294967296/maps")]; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 107 | xsprintf(filename, "/proc/%u/maps", tcp->pid); |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 108 | |
| 109 | FILE *fp = fopen_stream(filename, "r"); |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 110 | if (!fp) { |
| 111 | perror_msg("fopen: %s", filename); |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 112 | return MMAP_CACHE_REBUILD_NOCACHE; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 113 | } |
| 114 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 115 | struct mmap_cache_t cache = { |
| 116 | .free_fn = delete_mmap_cache, |
| 117 | .generation = mmap_cache_generation |
| 118 | }; |
| 119 | |
| 120 | /* start with a small dynamically-allocated array and then expand it */ |
| 121 | size_t allocated = 0; |
| 122 | char buffer[PATH_MAX + 80]; |
| 123 | |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 124 | while (fgets(buffer, sizeof(buffer), fp) != NULL) { |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 125 | unsigned long start_addr, end_addr, mmap_offset; |
| 126 | char read_bit; |
| 127 | char write_bit; |
| 128 | char exec_bit; |
| 129 | char shared_bit; |
| 130 | unsigned long major, minor; |
| 131 | char binary_path[sizeof(buffer)]; |
| 132 | |
| 133 | if (sscanf(buffer, "%lx-%lx %c%c%c%c %lx %lx:%lx %*d %[^\n]", |
| 134 | &start_addr, &end_addr, |
| 135 | &read_bit, &write_bit, &exec_bit, &shared_bit, |
| 136 | &mmap_offset, |
| 137 | &major, &minor, |
| 138 | binary_path) != 10) |
| 139 | continue; |
| 140 | |
| 141 | /* skip mappings that have unknown protection */ |
| 142 | if (!(read_bit == '-' || read_bit == 'r')) |
| 143 | continue; |
| 144 | if (!(write_bit == '-' || write_bit == 'w')) |
| 145 | continue; |
| 146 | if (!(exec_bit == '-' || exec_bit == 'x')) |
| 147 | continue; |
| 148 | if (!(shared_bit == 'p' || shared_bit == 's')) |
| 149 | continue; |
| 150 | |
| 151 | if (end_addr < start_addr) { |
| 152 | error_msg("%s: unrecognized file format", filename); |
| 153 | break; |
| 154 | } |
| 155 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 156 | struct mmap_cache_entry_t *entry; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 157 | /* |
| 158 | * sanity check to make sure that we're storing |
| 159 | * non-overlapping regions in ascending order |
| 160 | */ |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 161 | if (cache.size > 0) { |
| 162 | entry = &cache.entry[cache.size - 1]; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 163 | if (entry->start_addr == start_addr && |
| 164 | entry->end_addr == end_addr) { |
| 165 | /* duplicate entry, e.g. [vsyscall] */ |
| 166 | continue; |
| 167 | } |
| 168 | if (start_addr <= entry->start_addr || |
| 169 | start_addr < entry->end_addr) { |
| 170 | debug_msg("%s: overlapping memory region: " |
| 171 | "\"%s\" [%08lx-%08lx] overlaps with " |
| 172 | "\"%s\" [%08lx-%08lx]", |
| 173 | filename, binary_path, start_addr, |
| 174 | end_addr, entry->binary_filename, |
| 175 | entry->start_addr, entry->end_addr); |
| 176 | continue; |
| 177 | } |
| 178 | } |
| 179 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 180 | if (cache.size >= allocated) |
| 181 | cache.entry = xgrowarray(cache.entry, &allocated, |
| 182 | sizeof(*cache.entry)); |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 183 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 184 | entry = &cache.entry[cache.size]; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 185 | entry->start_addr = start_addr; |
| 186 | entry->end_addr = end_addr; |
| 187 | entry->mmap_offset = mmap_offset; |
| 188 | entry->protections = ( |
| 189 | 0 |
| 190 | | ((read_bit == 'r')? MMAP_CACHE_PROT_READABLE : 0) |
| 191 | | ((write_bit == 'w')? MMAP_CACHE_PROT_WRITABLE : 0) |
| 192 | | ((exec_bit == 'x')? MMAP_CACHE_PROT_EXECUTABLE: 0) |
| 193 | | ((shared_bit == 's')? MMAP_CACHE_PROT_SHARED : 0) |
| 194 | ); |
| 195 | entry->major = major; |
| 196 | entry->minor = minor; |
| 197 | entry->binary_filename = xstrdup(binary_path); |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 198 | cache.size++; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 199 | } |
| 200 | fclose(fp); |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 201 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 202 | if (!cache.size) |
| 203 | return MMAP_CACHE_REBUILD_NOCACHE; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 204 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 205 | tcp->mmap_cache = xmalloc(sizeof(*tcp->mmap_cache)); |
| 206 | memcpy(tcp->mmap_cache, &cache, sizeof(cache)); |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 207 | |
| 208 | debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s", |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 209 | tcp->mmap_cache->generation, mmap_cache_generation, |
| 210 | tcp, tcp->mmap_cache->entry, caller); |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 211 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 212 | return MMAP_CACHE_REBUILD_RENEWED; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 213 | } |
| 214 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 215 | struct mmap_cache_entry_t * |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 216 | mmap_cache_search(struct tcb *tcp, unsigned long ip) |
| 217 | { |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 218 | if (!tcp->mmap_cache) |
| 219 | return NULL; |
| 220 | |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 221 | int lower = 0; |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 222 | int upper = (int) tcp->mmap_cache->size - 1; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 223 | |
| 224 | while (lower <= upper) { |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 225 | int mid = (upper + lower) / 2; |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 226 | struct mmap_cache_entry_t *entry = &tcp->mmap_cache->entry[mid]; |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 227 | |
Elliott Hughes | 03a418e | 2018-06-15 13:11:40 -0700 | [diff] [blame] | 228 | if (ip >= entry->start_addr && |
| 229 | ip < entry->end_addr) |
| 230 | return entry; |
| 231 | else if (ip < entry->start_addr) |
Elliott Hughes | 28e98bc | 2018-06-14 16:59:04 -0700 | [diff] [blame] | 232 | upper = mid - 1; |
| 233 | else |
| 234 | lower = mid + 1; |
| 235 | } |
| 236 | return NULL; |
| 237 | } |
Elliott Hughes | c187376 | 2018-12-19 15:13:36 -0800 | [diff] [blame] | 238 | |
| 239 | struct mmap_cache_entry_t * |
| 240 | mmap_cache_search_custom(struct tcb *tcp, mmap_cache_search_fn fn, void *data) |
| 241 | { |
| 242 | for (unsigned int i = 0; i < tcp->mmap_cache->size; i++) { |
| 243 | if (fn(tcp->mmap_cache->entry + i, data)) |
| 244 | return tcp->mmap_cache->entry + i; |
| 245 | } |
| 246 | return NULL; |
| 247 | } |