You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
7.1 KiB
247 lines
7.1 KiB
/* |
|
* Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com> |
|
* 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; |
|
}
|
|
|