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