1 /* 2 * Copyright (c) 2013 Luca Clementi <luca.clementi (at) 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" 33 #include "mmap_notify.h" 34 #include "xstring.h" 35 36 static unsigned int mmap_cache_generation; 37 38 static void 39 mmap_cache_invalidate(struct tcb *tcp, void *unused) 40 { 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); 52 } 53 54 void 55 mmap_cache_enable(void) 56 { 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; 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 */ 96 extern enum mmap_cache_rebuild_result 97 mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller) 98 { 99 if (tcp->mmap_cache 100 && tcp->mmap_cache->generation != mmap_cache_generation) 101 delete_mmap_cache(tcp, caller); 102 103 if (tcp->mmap_cache) 104 return MMAP_CACHE_REBUILD_READY; 105 106 char filename[sizeof("/proc/4294967296/maps")]; 107 xsprintf(filename, "/proc/%u/maps", tcp->pid); 108 109 FILE *fp = fopen_stream(filename, "r"); 110 if (!fp) { 111 perror_msg("fopen: %s", filename); 112 return MMAP_CACHE_REBUILD_NOCACHE; 113 } 114 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 124 while (fgets(buffer, sizeof(buffer), fp) != NULL) { 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 156 struct mmap_cache_entry_t *entry; 157 /* 158 * sanity check to make sure that we're storing 159 * non-overlapping regions in ascending order 160 */ 161 if (cache.size > 0) { 162 entry = &cache.entry[cache.size - 1]; 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 180 if (cache.size >= allocated) 181 cache.entry = xgrowarray(cache.entry, &allocated, 182 sizeof(*cache.entry)); 183 184 entry = &cache.entry[cache.size]; 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); 198 cache.size++; 199 } 200 fclose(fp); 201 202 if (!cache.size) 203 return MMAP_CACHE_REBUILD_NOCACHE; 204 205 tcp->mmap_cache = xmalloc(sizeof(*tcp->mmap_cache)); 206 memcpy(tcp->mmap_cache, &cache, sizeof(cache)); 207 208 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s", 209 tcp->mmap_cache->generation, mmap_cache_generation, 210 tcp, tcp->mmap_cache->entry, caller); 211 212 return MMAP_CACHE_REBUILD_RENEWED; 213 } 214 215 struct mmap_cache_entry_t * 216 mmap_cache_search(struct tcb *tcp, unsigned long ip) 217 { 218 if (!tcp->mmap_cache) 219 return NULL; 220 221 int lower = 0; 222 int upper = (int) tcp->mmap_cache->size - 1; 223 224 while (lower <= upper) { 225 int mid = (upper + lower) / 2; 226 struct mmap_cache_entry_t *entry = &tcp->mmap_cache->entry[mid]; 227 228 if (ip >= entry->start_addr && 229 ip < entry->end_addr) 230 return entry; 231 else if (ip < entry->start_addr) 232 upper = mid - 1; 233 else 234 lower = mid + 1; 235 } 236 return NULL; 237 } 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 } 248