blob: 181005b1731036c4fa948d5cfce76458617324ef [file] [log] [blame]
Elliott Hughes28e98bc2018-06-14 16:59:04 -07001/*
2 * Copyright (c) 2013 Luca Clementi <luca.clementi@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"
Elliott Hughes03a418e2018-06-15 13:11:40 -070033#include "mmap_notify.h"
Elliott Hughes28e98bc2018-06-14 16:59:04 -070034#include "xstring.h"
35
36static unsigned int mmap_cache_generation;
Elliott Hughes28e98bc2018-06-14 16:59:04 -070037
Elliott Hughes03a418e2018-06-15 13:11:40 -070038static void
39mmap_cache_invalidate(struct tcb *tcp, void *unused)
Elliott Hughes28e98bc2018-06-14 16:59:04 -070040{
Elliott Hughes03a418e2018-06-15 13:11:40 -070041#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);
Elliott Hughes28e98bc2018-06-14 16:59:04 -070052}
53
Elliott Hughes03a418e2018-06-15 13:11:40 -070054void
55mmap_cache_enable(void)
Elliott Hughes28e98bc2018-06-14 16:59:04 -070056{
Elliott Hughes03a418e2018-06-15 13:11:40 -070057 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 */
66static void
67delete_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;
Elliott Hughes28e98bc2018-06-14 16:59:04 -070088}
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 */
Elliott Hughes03a418e2018-06-15 13:11:40 -070096extern enum mmap_cache_rebuild_result
97mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller)
Elliott Hughes28e98bc2018-06-14 16:59:04 -070098{
Elliott Hughes03a418e2018-06-15 13:11:40 -070099 if (tcp->mmap_cache
100 && tcp->mmap_cache->generation != mmap_cache_generation)
101 delete_mmap_cache(tcp, caller);
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700102
Elliott Hughes03a418e2018-06-15 13:11:40 -0700103 if (tcp->mmap_cache)
104 return MMAP_CACHE_REBUILD_READY;
105
106 char filename[sizeof("/proc/4294967296/maps")];
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700107 xsprintf(filename, "/proc/%u/maps", tcp->pid);
Elliott Hughes03a418e2018-06-15 13:11:40 -0700108
109 FILE *fp = fopen_stream(filename, "r");
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700110 if (!fp) {
111 perror_msg("fopen: %s", filename);
Elliott Hughes03a418e2018-06-15 13:11:40 -0700112 return MMAP_CACHE_REBUILD_NOCACHE;
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700113 }
114
Elliott Hughes03a418e2018-06-15 13:11:40 -0700115 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
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700124 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700125 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
Elliott Hughes03a418e2018-06-15 13:11:40 -0700156 struct mmap_cache_entry_t *entry;
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700157 /*
158 * sanity check to make sure that we're storing
159 * non-overlapping regions in ascending order
160 */
Elliott Hughes03a418e2018-06-15 13:11:40 -0700161 if (cache.size > 0) {
162 entry = &cache.entry[cache.size - 1];
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700163 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
Elliott Hughes03a418e2018-06-15 13:11:40 -0700180 if (cache.size >= allocated)
181 cache.entry = xgrowarray(cache.entry, &allocated,
182 sizeof(*cache.entry));
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700183
Elliott Hughes03a418e2018-06-15 13:11:40 -0700184 entry = &cache.entry[cache.size];
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700185 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);
Elliott Hughes03a418e2018-06-15 13:11:40 -0700198 cache.size++;
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700199 }
200 fclose(fp);
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700201
Elliott Hughes03a418e2018-06-15 13:11:40 -0700202 if (!cache.size)
203 return MMAP_CACHE_REBUILD_NOCACHE;
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700204
Elliott Hughes03a418e2018-06-15 13:11:40 -0700205 tcp->mmap_cache = xmalloc(sizeof(*tcp->mmap_cache));
206 memcpy(tcp->mmap_cache, &cache, sizeof(cache));
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700207
208 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
Elliott Hughes03a418e2018-06-15 13:11:40 -0700209 tcp->mmap_cache->generation, mmap_cache_generation,
210 tcp, tcp->mmap_cache->entry, caller);
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700211
Elliott Hughes03a418e2018-06-15 13:11:40 -0700212 return MMAP_CACHE_REBUILD_RENEWED;
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700213}
214
Elliott Hughes03a418e2018-06-15 13:11:40 -0700215struct mmap_cache_entry_t *
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700216mmap_cache_search(struct tcb *tcp, unsigned long ip)
217{
Elliott Hughes03a418e2018-06-15 13:11:40 -0700218 if (!tcp->mmap_cache)
219 return NULL;
220
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700221 int lower = 0;
Elliott Hughes03a418e2018-06-15 13:11:40 -0700222 int upper = (int) tcp->mmap_cache->size - 1;
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700223
224 while (lower <= upper) {
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700225 int mid = (upper + lower) / 2;
Elliott Hughes03a418e2018-06-15 13:11:40 -0700226 struct mmap_cache_entry_t *entry = &tcp->mmap_cache->entry[mid];
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700227
Elliott Hughes03a418e2018-06-15 13:11:40 -0700228 if (ip >= entry->start_addr &&
229 ip < entry->end_addr)
230 return entry;
231 else if (ip < entry->start_addr)
Elliott Hughes28e98bc2018-06-14 16:59:04 -0700232 upper = mid - 1;
233 else
234 lower = mid + 1;
235 }
236 return NULL;
237}
Elliott Hughesc1873762018-12-19 15:13:36 -0800238
239struct mmap_cache_entry_t *
240mmap_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}