| /* |
| * Copyright (c) 2013 Luca Clementi <[email protected]> |
| * Copyright (c) 2013-2018 The strace developers. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "defs.h" |
| #include <limits.h> |
| |
| #include "largefile_wrappers.h" |
| #include "mmap_cache.h" |
| #include "mmap_notify.h" |
| #include "xstring.h" |
| |
| static unsigned int mmap_cache_generation; |
| |
| static void |
| mmap_cache_invalidate(struct tcb *tcp, void *unused) |
| { |
| #if SUPPORTED_PERSONALITIES > 1 |
| if (tcp->currpers != DEFAULT_PERSONALITY) { |
| /* disable stack trace */ |
| return; |
| } |
| #endif |
| mmap_cache_generation++; |
| debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p", |
| tcp->mmap_cache ? tcp->mmap_cache->generation : 0, |
| mmap_cache_generation, tcp, |
| tcp->mmap_cache ? tcp->mmap_cache->entry : 0); |
| } |
| |
| void |
| mmap_cache_enable(void) |
| { |
| static bool use_mmap_cache; |
| |
| if (!use_mmap_cache) { |
| mmap_notify_register_client(mmap_cache_invalidate, NULL); |
| use_mmap_cache = true; |
| } |
| } |
| |
| /* deleting the cache */ |
| static void |
| delete_mmap_cache(struct tcb *tcp, const char *caller) |
| { |
| debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s", |
| tcp->mmap_cache ? tcp->mmap_cache->generation : 0, |
| mmap_cache_generation, tcp, |
| tcp->mmap_cache ? tcp->mmap_cache->entry : 0, caller); |
| |
| if (!tcp->mmap_cache) |
| return; |
| |
| while (tcp->mmap_cache->size) { |
| unsigned int i = --tcp->mmap_cache->size; |
| free(tcp->mmap_cache->entry[i].binary_filename); |
| tcp->mmap_cache->entry[i].binary_filename = NULL; |
| } |
| |
| free(tcp->mmap_cache->entry); |
| tcp->mmap_cache->entry = NULL; |
| |
| free(tcp->mmap_cache); |
| tcp->mmap_cache = NULL; |
| } |
| |
| /* |
| * caching of /proc/ID/maps for each process to speed up stack tracing |
| * |
| * The cache must be refreshed after syscalls that affect memory mappings, |
| * e.g. mmap, mprotect, munmap, execve. |
| */ |
| extern enum mmap_cache_rebuild_result |
| mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller) |
| { |
| if (tcp->mmap_cache |
| && tcp->mmap_cache->generation != mmap_cache_generation) |
| delete_mmap_cache(tcp, caller); |
| |
| if (tcp->mmap_cache) |
| return MMAP_CACHE_REBUILD_READY; |
| |
| char filename[sizeof("/proc/4294967296/maps")]; |
| xsprintf(filename, "/proc/%u/maps", tcp->pid); |
| |
| FILE *fp = fopen_stream(filename, "r"); |
| if (!fp) { |
| perror_msg("fopen: %s", filename); |
| return MMAP_CACHE_REBUILD_NOCACHE; |
| } |
| |
| struct mmap_cache_t cache = { |
| .free_fn = delete_mmap_cache, |
| .generation = mmap_cache_generation |
| }; |
| |
| /* start with a small dynamically-allocated array and then expand it */ |
| size_t allocated = 0; |
| char buffer[PATH_MAX + 80]; |
| |
| while (fgets(buffer, sizeof(buffer), fp) != NULL) { |
| unsigned long start_addr, end_addr, mmap_offset; |
| char read_bit; |
| char write_bit; |
| char exec_bit; |
| char shared_bit; |
| unsigned long major, minor; |
| char binary_path[sizeof(buffer)]; |
| |
| if (sscanf(buffer, "%lx-%lx %c%c%c%c %lx %lx:%lx %*d %[^\n]", |
| &start_addr, &end_addr, |
| &read_bit, &write_bit, &exec_bit, &shared_bit, |
| &mmap_offset, |
| &major, &minor, |
| binary_path) != 10) |
| continue; |
| |
| /* skip mappings that have unknown protection */ |
| if (!(read_bit == '-' || read_bit == 'r')) |
| continue; |
| if (!(write_bit == '-' || write_bit == 'w')) |
| continue; |
| if (!(exec_bit == '-' || exec_bit == 'x')) |
| continue; |
| if (!(shared_bit == 'p' || shared_bit == 's')) |
| continue; |
| |
| if (end_addr < start_addr) { |
| error_msg("%s: unrecognized file format", filename); |
| break; |
| } |
| |
| struct mmap_cache_entry_t *entry; |
| /* |
| * sanity check to make sure that we're storing |
| * non-overlapping regions in ascending order |
| */ |
| if (cache.size > 0) { |
| entry = &cache.entry[cache.size - 1]; |
| if (entry->start_addr == start_addr && |
| entry->end_addr == end_addr) { |
| /* duplicate entry, e.g. [vsyscall] */ |
| continue; |
| } |
| if (start_addr <= entry->start_addr || |
| start_addr < entry->end_addr) { |
| debug_msg("%s: overlapping memory region: " |
| "\"%s\" [%08lx-%08lx] overlaps with " |
| "\"%s\" [%08lx-%08lx]", |
| filename, binary_path, start_addr, |
| end_addr, entry->binary_filename, |
| entry->start_addr, entry->end_addr); |
| continue; |
| } |
| } |
| |
| if (cache.size >= allocated) |
| cache.entry = xgrowarray(cache.entry, &allocated, |
| sizeof(*cache.entry)); |
| |
| entry = &cache.entry[cache.size]; |
| entry->start_addr = start_addr; |
| entry->end_addr = end_addr; |
| entry->mmap_offset = mmap_offset; |
| entry->protections = ( |
| 0 |
| | ((read_bit == 'r')? MMAP_CACHE_PROT_READABLE : 0) |
| | ((write_bit == 'w')? MMAP_CACHE_PROT_WRITABLE : 0) |
| | ((exec_bit == 'x')? MMAP_CACHE_PROT_EXECUTABLE: 0) |
| | ((shared_bit == 's')? MMAP_CACHE_PROT_SHARED : 0) |
| ); |
| entry->major = major; |
| entry->minor = minor; |
| entry->binary_filename = xstrdup(binary_path); |
| cache.size++; |
| } |
| fclose(fp); |
| |
| if (!cache.size) |
| return MMAP_CACHE_REBUILD_NOCACHE; |
| |
| tcp->mmap_cache = xmalloc(sizeof(*tcp->mmap_cache)); |
| memcpy(tcp->mmap_cache, &cache, sizeof(cache)); |
| |
| debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s", |
| tcp->mmap_cache->generation, mmap_cache_generation, |
| tcp, tcp->mmap_cache->entry, caller); |
| |
| return MMAP_CACHE_REBUILD_RENEWED; |
| } |
| |
| struct mmap_cache_entry_t * |
| mmap_cache_search(struct tcb *tcp, unsigned long ip) |
| { |
| if (!tcp->mmap_cache) |
| return NULL; |
| |
| int lower = 0; |
| int upper = (int) tcp->mmap_cache->size - 1; |
| |
| while (lower <= upper) { |
| int mid = (upper + lower) / 2; |
| struct mmap_cache_entry_t *entry = &tcp->mmap_cache->entry[mid]; |
| |
| if (ip >= entry->start_addr && |
| ip < entry->end_addr) |
| return entry; |
| else if (ip < entry->start_addr) |
| upper = mid - 1; |
| else |
| lower = mid + 1; |
| } |
| return NULL; |
| } |
| |
| struct mmap_cache_entry_t * |
| mmap_cache_search_custom(struct tcb *tcp, mmap_cache_search_fn fn, void *data) |
| { |
| for (unsigned int i = 0; i < tcp->mmap_cache->size; i++) { |
| if (fn(tcp->mmap_cache->entry + i, data)) |
| return tcp->mmap_cache->entry + i; |
| } |
| return NULL; |
| } |