1 /*
2 * Copyright (c) 2013-2019, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * * Neither the name of Intel Corporation nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "pt_section.h"
30 #include "pt_section_posix.h"
31 #include "pt_section_file.h"
32
33 #include "intel-pt.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <limits.h>
39 #include <sys/types.h>
40 #include <sys/mman.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43
44
pt_section_mk_status(void ** pstatus,uint64_t * psize,const char * filename)45 int pt_section_mk_status(void **pstatus, uint64_t *psize, const char *filename)
46 {
47 struct pt_sec_posix_status *status;
48 struct stat buffer;
49 int errcode;
50
51 if (!pstatus || !psize)
52 return -pte_internal;
53
54 errcode = stat(filename, &buffer);
55 if (errcode < 0)
56 return -pte_bad_file;
57
58 if (buffer.st_size < 0)
59 return -pte_bad_image;
60
61 status = malloc(sizeof(*status));
62 if (!status)
63 return -pte_nomem;
64
65 status->stat = buffer;
66
67 *pstatus = status;
68 *psize = (uint64_t) buffer.st_size;
69
70 return 0;
71 }
72
check_file_status(struct pt_section * section,int fd)73 static int check_file_status(struct pt_section *section, int fd)
74 {
75 struct pt_sec_posix_status *status;
76 struct stat stat;
77 int errcode;
78
79 if (!section)
80 return -pte_internal;
81
82 errcode = fstat(fd, &stat);
83 if (errcode)
84 return -pte_bad_file;
85
86 status = section->status;
87 if (!status)
88 return -pte_internal;
89
90 if (stat.st_size != status->stat.st_size)
91 return -pte_bad_image;
92
93 if (stat.st_mtime != status->stat.st_mtime)
94 return -pte_bad_image;
95
96 return 0;
97 }
98
pt_sec_posix_map(struct pt_section * section,int fd)99 int pt_sec_posix_map(struct pt_section *section, int fd)
100 {
101 struct pt_sec_posix_mapping *mapping;
102 uint64_t offset, size, adjustment;
103 uint8_t *base;
104 long page_size;
105 int errcode;
106
107 if (!section)
108 return -pte_internal;
109
110 offset = section->offset;
111 size = section->size;
112
113 page_size = sysconf(_SC_PAGESIZE);
114 if (page_size < 0)
115 return -pte_bad_config;
116
117 adjustment = offset % (uint64_t) page_size;
118
119 offset -= adjustment;
120 size += adjustment;
121
122 /* The section is supposed to fit into the file so we shouldn't
123 * see any overflows, here.
124 */
125 if (size < section->size)
126 return -pte_internal;
127
128 if (SIZE_MAX < size)
129 return -pte_nomem;
130
131 if (INT_MAX < offset)
132 return -pte_nomem;
133
134 base = mmap(NULL, (size_t) size, PROT_READ, MAP_SHARED, fd,
135 (off_t) offset);
136 if (base == MAP_FAILED)
137 return -pte_nomem;
138
139 mapping = malloc(sizeof(*mapping));
140 if (!mapping) {
141 errcode = -pte_nomem;
142 goto out_map;
143 }
144
145 mapping->base = base;
146 mapping->size = size;
147 mapping->begin = base + adjustment;
148 mapping->end = base + size;
149
150 section->mapping = mapping;
151 section->unmap = pt_sec_posix_unmap;
152 section->read = pt_sec_posix_read;
153 section->memsize = pt_sec_posix_memsize;
154
155 return 0;
156
157 out_map:
158 munmap(base, (size_t) size);
159 return errcode;
160 }
161
pt_sec_posix_map_success(struct pt_section * section)162 static int pt_sec_posix_map_success(struct pt_section *section)
163 {
164 uint16_t mcount;
165 int errcode, status;
166
167 if (!section)
168 return -pte_internal;
169
170 mcount = section->mcount + 1;
171 if (!mcount) {
172 (void) pt_section_unlock(section);
173 return -pte_overflow;
174 }
175
176 section->mcount = mcount;
177
178 errcode = pt_section_unlock(section);
179 if (errcode < 0)
180 return errcode;
181
182 status = pt_section_on_map(section);
183 if (status < 0) {
184 /* We had to release the section lock for pt_section_on_map() so
185 * @section may have meanwhile been mapped by other threads.
186 *
187 * We still want to return the error so we release our mapping.
188 * Our caller does not yet know whether pt_section_map()
189 * succeeded.
190 */
191 (void) pt_section_unmap(section);
192 return status;
193 }
194
195 return 0;
196 }
197
pt_section_map(struct pt_section * section)198 int pt_section_map(struct pt_section *section)
199 {
200 const char *filename;
201 FILE *file;
202 int fd, errcode;
203
204 if (!section)
205 return -pte_internal;
206
207 errcode = pt_section_lock(section);
208 if (errcode < 0)
209 return errcode;
210
211 if (section->mcount)
212 return pt_sec_posix_map_success(section);
213
214 if (section->mapping)
215 goto out_unlock;
216
217 filename = section->filename;
218 if (!filename)
219 goto out_unlock;
220
221 errcode = -pte_bad_file;
222 fd = open(filename, O_RDONLY);
223 if (fd == -1)
224 goto out_unlock;
225
226 errcode = check_file_status(section, fd);
227 if (errcode < 0)
228 goto out_fd;
229
230 /* We close the file on success. This does not unmap the section. */
231 errcode = pt_sec_posix_map(section, fd);
232 if (!errcode) {
233 close(fd);
234
235 return pt_sec_posix_map_success(section);
236 }
237
238 /* Fall back to file based sections - report the original error
239 * if we fail to convert the file descriptor.
240 */
241 file = fdopen(fd, "rb");
242 if (!file) {
243 errcode = -pte_bad_file;
244 goto out_fd;
245 }
246
247 /* We need to keep the file open on success. It will be closed when
248 * the section is unmapped.
249 */
250 errcode = pt_sec_file_map(section, file);
251 if (!errcode)
252 return pt_sec_posix_map_success(section);
253
254 fclose(file);
255 goto out_unlock;
256
257 out_fd:
258 close(fd);
259
260 out_unlock:
261 (void) pt_section_unlock(section);
262 return errcode;
263 }
264
pt_sec_posix_unmap(struct pt_section * section)265 int pt_sec_posix_unmap(struct pt_section *section)
266 {
267 struct pt_sec_posix_mapping *mapping;
268
269 if (!section)
270 return -pte_internal;
271
272 mapping = section->mapping;
273 if (!mapping || !section->unmap || !section->read || !section->memsize)
274 return -pte_internal;
275
276 section->mapping = NULL;
277 section->unmap = NULL;
278 section->read = NULL;
279 section->memsize = NULL;
280
281 munmap(mapping->base, (size_t) mapping->size);
282 free(mapping);
283
284 return 0;
285 }
286
pt_sec_posix_read(const struct pt_section * section,uint8_t * buffer,uint16_t size,uint64_t offset)287 int pt_sec_posix_read(const struct pt_section *section, uint8_t *buffer,
288 uint16_t size, uint64_t offset)
289 {
290 struct pt_sec_posix_mapping *mapping;
291 const uint8_t *begin;
292
293 if (!buffer || !section)
294 return -pte_internal;
295
296 mapping = section->mapping;
297 if (!mapping)
298 return -pte_internal;
299
300 /* We already checked in pt_section_read() that the requested memory
301 * lies within the section's boundaries.
302 *
303 * And we checked that the entire section was mapped. There's no need
304 * to check for overflows, again.
305 */
306 begin = mapping->begin + offset;
307
308 memcpy(buffer, begin, size);
309 return (int) size;
310 }
311
pt_sec_posix_memsize(const struct pt_section * section,uint64_t * size)312 int pt_sec_posix_memsize(const struct pt_section *section, uint64_t *size)
313 {
314 struct pt_sec_posix_mapping *mapping;
315 const uint8_t *begin, *end;
316
317 if (!section || !size)
318 return -pte_internal;
319
320 mapping = section->mapping;
321 if (!mapping)
322 return -pte_internal;
323
324 begin = mapping->base;
325 end = mapping->end;
326
327 if (!begin || !end || end < begin)
328 return -pte_internal;
329
330 *size = (uint64_t) (end - begin);
331
332 return 0;
333 }
334