Home | History | Annotate | Download | only in strace
      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