]> granicus.if.org Git - strace/blob - mmap_cache.c
mmap_cache: new subsystem derived from unwind.c
[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
45 /*
46  * caching of /proc/ID/maps for each process to speed up stack tracing
47  *
48  * The cache must be refreshed after syscalls that affect memory mappings,
49  * e.g. mmap, mprotect, munmap, execve.
50  */
51 static void
52 build_mmap_cache(struct tcb *tcp)
53 {
54         FILE *fp;
55         struct mmap_cache_t *cache_head = NULL;
56         /* start with a small dynamically-allocated array and then expand it */
57         size_t cur_array_size = 0;
58         char filename[sizeof("/proc/4294967296/maps")];
59         char buffer[PATH_MAX + 80];
60
61         xsprintf(filename, "/proc/%u/maps", tcp->pid);
62         fp = fopen_for_input(filename, "r");
63         if (!fp) {
64                 perror_msg("fopen: %s", filename);
65                 return;
66         }
67
68         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
69                 struct mmap_cache_t *entry;
70                 unsigned long start_addr, end_addr, mmap_offset;
71                 char exec_bit;
72                 char binary_path[sizeof(buffer)];
73
74                 if (sscanf(buffer, "%lx-%lx %*c%*c%c%*c %lx %*x:%*x %*d %[^\n]",
75                            &start_addr, &end_addr, &exec_bit,
76                            &mmap_offset, binary_path) != 5)
77                         continue;
78
79                 /* ignore mappings that have no PROT_EXEC bit set */
80                 if (exec_bit != 'x')
81                         continue;
82
83                 if (end_addr < start_addr) {
84                         error_msg("%s: unrecognized file format", filename);
85                         break;
86                 }
87
88                 /*
89                  * sanity check to make sure that we're storing
90                  * non-overlapping regions in ascending order
91                  */
92                 if (tcp->mmap_cache_size > 0) {
93                         entry = &cache_head[tcp->mmap_cache_size - 1];
94                         if (entry->start_addr == start_addr &&
95                             entry->end_addr == end_addr) {
96                                 /* duplicate entry, e.g. [vsyscall] */
97                                 continue;
98                         }
99                         if (start_addr <= entry->start_addr ||
100                             start_addr < entry->end_addr) {
101                                 debug_msg("%s: overlapping memory region: "
102                                           "\"%s\" [%08lx-%08lx] overlaps with "
103                                           "\"%s\" [%08lx-%08lx]",
104                                           filename, binary_path, start_addr,
105                                           end_addr, entry->binary_filename,
106                                           entry->start_addr, entry->end_addr);
107                                 continue;
108                         }
109                 }
110
111                 if (tcp->mmap_cache_size >= cur_array_size)
112                         cache_head = xgrowarray(cache_head, &cur_array_size,
113                                                 sizeof(*cache_head));
114
115                 entry = &cache_head[tcp->mmap_cache_size];
116                 entry->start_addr = start_addr;
117                 entry->end_addr = end_addr;
118                 entry->mmap_offset = mmap_offset;
119                 entry->binary_filename = xstrdup(binary_path);
120                 tcp->mmap_cache_size++;
121         }
122         fclose(fp);
123         tcp->mmap_cache = cache_head;
124         tcp->mmap_cache_generation = mmap_cache_generation;
125
126         debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
127                        tcp->mmap_cache_generation,
128                        mmap_cache_generation,
129                        tcp, tcp->mmap_cache);
130 }
131
132 /* deleting the cache */
133 extern void
134 mmap_cache_delete(struct tcb *tcp, const char *caller)
135 {
136         unsigned int i;
137
138         debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
139                        tcp->mmap_cache_generation,
140                        mmap_cache_generation,
141                        tcp, tcp->mmap_cache, caller);
142
143         for (i = 0; i < tcp->mmap_cache_size; i++) {
144                 free(tcp->mmap_cache[i].binary_filename);
145                 tcp->mmap_cache[i].binary_filename = NULL;
146         }
147         free(tcp->mmap_cache);
148         tcp->mmap_cache = NULL;
149         tcp->mmap_cache_size = 0;
150 }
151
152 extern enum mmap_cache_rebuild_result
153 mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller)
154 {
155         enum mmap_cache_rebuild_result r = MMAP_CACHE_REBUILD_READY;
156         if ((tcp->mmap_cache_generation != mmap_cache_generation)
157             && tcp->mmap_cache)
158                 mmap_cache_delete(tcp, caller);
159
160         if (!tcp->mmap_cache) {
161                 r = MMAP_CACHE_REBUILD_RENEWED;
162                 build_mmap_cache(tcp);
163         }
164
165         if (!(tcp->mmap_cache && tcp->mmap_cache_size))
166                 r = MMAP_CACHE_REBUILD_NOCACHE;
167
168         return r;
169 }
170
171 void
172 mmap_cache_invalidate(struct tcb *tcp)
173 {
174 #if SUPPORTED_PERSONALITIES > 1
175         if (tcp->currpers != DEFAULT_PERSONALITY) {
176                 /* disable stack trace */
177                 return;
178         }
179 #endif
180         mmap_cache_generation++;
181         debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
182                        tcp->mmap_cache_generation,
183                        mmap_cache_generation,
184                        tcp, tcp->mmap_cache);
185 }