(root)/
strace-6.5/
src/
mmap_cache.c
       1  /*
       2   * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
       3   * Copyright (c) 2013-2021 The strace developers.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   */
       7  
       8  #include "defs.h"
       9  #include <limits.h>
      10  
      11  #include "largefile_wrappers.h"
      12  #include "mmap_cache.h"
      13  #include "mmap_notify.h"
      14  #include "xstring.h"
      15  
      16  static unsigned int mmap_cache_generation;
      17  
      18  static void
      19  mmap_cache_invalidate(struct tcb *tcp, void *unused)
      20  {
      21  	mmap_cache_generation++;
      22  	debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
      23  		       tcp->mmap_cache ? tcp->mmap_cache->generation : 0,
      24  		       mmap_cache_generation, tcp,
      25  		       tcp->mmap_cache ? tcp->mmap_cache->entry : 0);
      26  }
      27  
      28  void
      29  mmap_cache_enable(void)
      30  {
      31  	static bool use_mmap_cache;
      32  
      33  	if (!use_mmap_cache) {
      34  		mmap_notify_register_client(mmap_cache_invalidate, NULL);
      35  		use_mmap_cache = true;
      36  	}
      37  }
      38  
      39  /* deleting the cache */
      40  static void
      41  delete_mmap_cache(struct tcb *tcp, const char *caller)
      42  {
      43  	debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
      44  		       tcp->mmap_cache ? tcp->mmap_cache->generation : 0,
      45  		       mmap_cache_generation, tcp,
      46  		       tcp->mmap_cache ? tcp->mmap_cache->entry : 0, caller);
      47  
      48  	if (!tcp->mmap_cache)
      49  		return;
      50  
      51  	while (tcp->mmap_cache->size) {
      52  		unsigned int i = --tcp->mmap_cache->size;
      53  		free(tcp->mmap_cache->entry[i].binary_filename);
      54  		tcp->mmap_cache->entry[i].binary_filename = NULL;
      55  	}
      56  
      57  	free(tcp->mmap_cache->entry);
      58  	tcp->mmap_cache->entry = NULL;
      59  
      60  	free(tcp->mmap_cache);
      61  	tcp->mmap_cache = NULL;
      62  }
      63  
      64  /*
      65   * caching of /proc/ID/maps for each process to speed up stack tracing
      66   *
      67   * The cache must be refreshed after syscalls that affect memory mappings,
      68   * e.g. mmap, mprotect, munmap, execve.
      69   */
      70  extern enum mmap_cache_rebuild_result
      71  mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller)
      72  {
      73  	if (tcp->mmap_cache
      74  	    && tcp->mmap_cache->generation != mmap_cache_generation)
      75  		delete_mmap_cache(tcp, caller);
      76  
      77  	if (tcp->mmap_cache)
      78  		return MMAP_CACHE_REBUILD_READY;
      79  
      80  	char filename[sizeof("/proc/4294967296/maps")];
      81  	xsprintf(filename, "/proc/%u/maps", get_proc_pid(tcp->pid));
      82  
      83  	FILE *fp = fopen_stream(filename, "r");
      84  	if (!fp) {
      85  		perror_msg("fopen: %s", filename);
      86  		return MMAP_CACHE_REBUILD_NOCACHE;
      87  	}
      88  
      89  	struct mmap_cache_t cache = {
      90  		.free_fn = delete_mmap_cache,
      91  		.generation = mmap_cache_generation
      92  	};
      93  
      94  	/* start with a small dynamically-allocated array and then expand it */
      95  	size_t allocated = 0;
      96  	char buffer[PATH_MAX + 80];
      97  
      98  	while (fgets(buffer, sizeof(buffer), fp) != NULL) {
      99  		unsigned long start_addr, end_addr, mmap_offset;
     100  		char read_bit;
     101  		char write_bit;
     102  		char exec_bit;
     103  		char shared_bit;
     104  		unsigned long major, minor;
     105  		char binary_path[sizeof(buffer)];
     106  
     107  		if (sscanf(buffer, "%lx-%lx %c%c%c%c %lx %lx:%lx %*d %[^\n]",
     108  			   &start_addr, &end_addr,
     109  			   &read_bit, &write_bit, &exec_bit, &shared_bit,
     110  			   &mmap_offset,
     111  			   &major, &minor,
     112  			   binary_path) != 10)
     113  			continue;
     114  
     115  		/* skip mappings that have unknown protection */
     116  		if (!(read_bit == '-' || read_bit == 'r'))
     117  			continue;
     118  		if (!(write_bit == '-' || write_bit == 'w'))
     119  			continue;
     120  		if (!(exec_bit == '-' || exec_bit == 'x'))
     121  			continue;
     122  		if (!(shared_bit == 'p' || shared_bit == 's'))
     123  			continue;
     124  
     125  		if (end_addr < start_addr) {
     126  			error_msg("%s: unrecognized file format", filename);
     127  			break;
     128  		}
     129  
     130  		struct mmap_cache_entry_t *entry;
     131  		/*
     132  		 * sanity check to make sure that we're storing
     133  		 * non-overlapping regions in ascending order
     134  		 */
     135  		if (cache.size > 0) {
     136  			entry = &cache.entry[cache.size - 1];
     137  			if (entry->start_addr == start_addr &&
     138  			    entry->end_addr == end_addr) {
     139  				/* duplicate entry, e.g. [vsyscall] */
     140  				continue;
     141  			}
     142  			if (start_addr <= entry->start_addr ||
     143  			    start_addr < entry->end_addr) {
     144  				debug_msg("%s: overlapping memory region: "
     145  					  "\"%s\" [%08lx-%08lx] overlaps with "
     146  					  "\"%s\" [%08lx-%08lx]",
     147  					  filename, binary_path, start_addr,
     148  					  end_addr, entry->binary_filename,
     149  					  entry->start_addr, entry->end_addr);
     150  				continue;
     151  			}
     152  		}
     153  
     154  		if (cache.size >= allocated)
     155  			cache.entry = xgrowarray(cache.entry, &allocated,
     156  						 sizeof(*cache.entry));
     157  
     158  		entry = &cache.entry[cache.size];
     159  		entry->start_addr = start_addr;
     160  		entry->end_addr = end_addr;
     161  		entry->mmap_offset = mmap_offset;
     162  		entry->protections = (
     163  			0
     164  			| ((read_bit   == 'r')? MMAP_CACHE_PROT_READABLE  : 0)
     165  			| ((write_bit  == 'w')? MMAP_CACHE_PROT_WRITABLE  : 0)
     166  			| ((exec_bit   == 'x')? MMAP_CACHE_PROT_EXECUTABLE: 0)
     167  			| ((shared_bit == 's')? MMAP_CACHE_PROT_SHARED    : 0)
     168  			);
     169  		entry->major = major;
     170  		entry->minor = minor;
     171  		entry->binary_filename = xstrdup(binary_path);
     172  		cache.size++;
     173  	}
     174  	fclose(fp);
     175  
     176  	if (!cache.size)
     177  		return MMAP_CACHE_REBUILD_NOCACHE;
     178  
     179  	tcp->mmap_cache = xobjdup(&cache);
     180  
     181  	debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
     182  		       tcp->mmap_cache->generation, mmap_cache_generation,
     183  		       tcp, tcp->mmap_cache->entry, caller);
     184  
     185  	return MMAP_CACHE_REBUILD_RENEWED;
     186  }
     187  
     188  struct mmap_cache_entry_t *
     189  mmap_cache_search(struct tcb *tcp, unsigned long ip)
     190  {
     191  	if (!tcp->mmap_cache)
     192  		return NULL;
     193  
     194  	int lower = 0;
     195  	int upper = (int) tcp->mmap_cache->size - 1;
     196  
     197  	while (lower <= upper) {
     198  		int mid = (upper + lower) / 2;
     199  		struct mmap_cache_entry_t *entry = &tcp->mmap_cache->entry[mid];
     200  
     201  		if (ip >= entry->start_addr &&
     202  		    ip < entry->end_addr)
     203  			return entry;
     204  		else if (ip < entry->start_addr)
     205  			upper = mid - 1;
     206  		else
     207  			lower = mid + 1;
     208  	}
     209  	return NULL;
     210  }
     211  
     212  struct mmap_cache_entry_t *
     213  mmap_cache_search_custom(struct tcb *tcp, mmap_cache_search_fn fn, void *data)
     214  {
     215  	for (unsigned int i = 0; i < tcp->mmap_cache->size; i++) {
     216  		if (fn(tcp->mmap_cache->entry + i, data))
     217  			return tcp->mmap_cache->entry + i;
     218  	}
     219  	return NULL;
     220  }