]> granicus.if.org Git - strace/blob - mmap_cache.c
mmap_cache: add function to enable mmap_cache
[strace] / mmap_cache.c
1 /*
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 "xstring.h"
32
33 #ifdef _LARGEFILE64_SOURCE
34 # ifdef HAVE_FOPEN64
35 #  define fopen_for_input fopen64
36 # else
37 #  define fopen_for_input fopen
38 # endif
39 #else
40 # define fopen_for_input fopen
41 #endif
42
43 static unsigned int mmap_cache_generation;
44 static bool use_mmap_cache;
45
46 extern void mmap_cache_enable(void)
47 {
48         use_mmap_cache = true;
49 }
50
51 extern bool mmap_cache_is_enabled(void)
52 {
53         return use_mmap_cache;
54 }
55
56 /*
57  * caching of /proc/ID/maps for each process to speed up stack tracing
58  *
59  * The cache must be refreshed after syscalls that affect memory mappings,
60  * e.g. mmap, mprotect, munmap, execve.
61  */
62 static void
63 build_mmap_cache(struct tcb *tcp)
64 {
65         FILE *fp;
66         struct mmap_cache_t *cache_head = NULL;
67         /* start with a small dynamically-allocated array and then expand it */
68         size_t cur_array_size = 0;
69         char filename[sizeof("/proc/4294967296/maps")];
70         char buffer[PATH_MAX + 80];
71
72         xsprintf(filename, "/proc/%u/maps", tcp->pid);
73         fp = fopen_for_input(filename, "r");
74         if (!fp) {
75                 perror_msg("fopen: %s", filename);
76                 return;
77         }
78
79         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
80                 struct mmap_cache_t *entry;
81                 unsigned long start_addr, end_addr, mmap_offset;
82                 char read_bit;
83                 char write_bit;
84                 char exec_bit;
85                 char shared_bit;
86                 char binary_path[sizeof(buffer)];
87
88                 if (sscanf(buffer, "%lx-%lx %c%c%c%c %lx %*x:%*x %*d %[^\n]",
89                            &start_addr, &end_addr,
90                            &read_bit, &write_bit, &exec_bit, &shared_bit,
91                            &mmap_offset, binary_path) != 8)
92                         continue;
93
94                 /* skip mappings that have unknown protection */
95                 if (!(read_bit == '-' || read_bit == 'r'))
96                         continue;
97                 if (!(write_bit == '-' || write_bit == 'w'))
98                         continue;
99                 if (!(exec_bit == '-' || exec_bit == 'x'))
100                         continue;
101                 if (!(shared_bit == 'p' || shared_bit == 's'))
102                         continue;
103
104                 if (end_addr < start_addr) {
105                         error_msg("%s: unrecognized file format", filename);
106                         break;
107                 }
108
109                 /*
110                  * sanity check to make sure that we're storing
111                  * non-overlapping regions in ascending order
112                  */
113                 if (tcp->mmap_cache_size > 0) {
114                         entry = &cache_head[tcp->mmap_cache_size - 1];
115                         if (entry->start_addr == start_addr &&
116                             entry->end_addr == end_addr) {
117                                 /* duplicate entry, e.g. [vsyscall] */
118                                 continue;
119                         }
120                         if (start_addr <= entry->start_addr ||
121                             start_addr < entry->end_addr) {
122                                 debug_msg("%s: overlapping memory region: "
123                                           "\"%s\" [%08lx-%08lx] overlaps with "
124                                           "\"%s\" [%08lx-%08lx]",
125                                           filename, binary_path, start_addr,
126                                           end_addr, entry->binary_filename,
127                                           entry->start_addr, entry->end_addr);
128                                 continue;
129                         }
130                 }
131
132                 if (tcp->mmap_cache_size >= cur_array_size)
133                         cache_head = xgrowarray(cache_head, &cur_array_size,
134                                                 sizeof(*cache_head));
135
136                 entry = &cache_head[tcp->mmap_cache_size];
137                 entry->start_addr = start_addr;
138                 entry->end_addr = end_addr;
139                 entry->mmap_offset = mmap_offset;
140                 entry->protections = (
141                         0
142                         | ((read_bit   == 'r')? MMAP_CACHE_PROT_READABLE  : 0)
143                         | ((write_bit  == 'w')? MMAP_CACHE_PROT_WRITABLE  : 0)
144                         | ((exec_bit   == 'x')? MMAP_CACHE_PROT_EXECUTABLE: 0)
145                         | ((shared_bit == 's')? MMAP_CACHE_PROT_SHARED    : 0)
146                         );
147                 entry->binary_filename = xstrdup(binary_path);
148                 tcp->mmap_cache_size++;
149         }
150         fclose(fp);
151         tcp->mmap_cache = cache_head;
152         tcp->mmap_cache_generation = mmap_cache_generation;
153
154         debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
155                        tcp->mmap_cache_generation,
156                        mmap_cache_generation,
157                        tcp, tcp->mmap_cache);
158 }
159
160 /* deleting the cache */
161 extern void
162 mmap_cache_delete(struct tcb *tcp, const char *caller)
163 {
164         unsigned int i;
165
166         debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
167                        tcp->mmap_cache_generation,
168                        mmap_cache_generation,
169                        tcp, tcp->mmap_cache, caller);
170
171         for (i = 0; i < tcp->mmap_cache_size; i++) {
172                 free(tcp->mmap_cache[i].binary_filename);
173                 tcp->mmap_cache[i].binary_filename = NULL;
174         }
175         free(tcp->mmap_cache);
176         tcp->mmap_cache = NULL;
177         tcp->mmap_cache_size = 0;
178 }
179
180 extern enum mmap_cache_rebuild_result
181 mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller)
182 {
183         enum mmap_cache_rebuild_result r = MMAP_CACHE_REBUILD_READY;
184         if ((tcp->mmap_cache_generation != mmap_cache_generation)
185             && tcp->mmap_cache)
186                 mmap_cache_delete(tcp, caller);
187
188         if (!tcp->mmap_cache) {
189                 r = MMAP_CACHE_REBUILD_RENEWED;
190                 build_mmap_cache(tcp);
191         }
192
193         if (!(tcp->mmap_cache && tcp->mmap_cache_size))
194                 r = MMAP_CACHE_REBUILD_NOCACHE;
195
196         return r;
197 }
198
199 void
200 mmap_cache_invalidate(struct tcb *tcp)
201 {
202 #if SUPPORTED_PERSONALITIES > 1
203         if (tcp->currpers != DEFAULT_PERSONALITY) {
204                 /* disable stack trace */
205                 return;
206         }
207 #endif
208         mmap_cache_generation++;
209         debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
210                        tcp->mmap_cache_generation,
211                        mmap_cache_generation,
212                        tcp, tcp->mmap_cache);
213 }
214
215 struct mmap_cache_t *
216 mmap_cache_search(struct tcb *tcp, unsigned long ip)
217 {
218         int lower = 0;
219         int upper = (int) tcp->mmap_cache_size - 1;
220
221         while (lower <= upper) {
222                 struct mmap_cache_t *cur_mmap_cache;
223                 int mid = (upper + lower) / 2;
224
225                 cur_mmap_cache = &tcp->mmap_cache[mid];
226
227                 if (ip >= cur_mmap_cache->start_addr &&
228                     ip < cur_mmap_cache->end_addr)
229                         return cur_mmap_cache;
230                 else if (ip < cur_mmap_cache->start_addr)
231                         upper = mid - 1;
232                 else
233                         lower = mid + 1;
234         }
235         return NULL;
236 }