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 
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 
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 
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 
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 
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 
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 
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 
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