]> granicus.if.org Git - strace/blob - mmap_cache.c
nlattr: add UID/GID netlink attribute decoders
[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 "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 }