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
45int 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
73static 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
99int 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
157out_map:
158	munmap(base, (size_t) size);
159	return errcode;
160}
161
162static 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
198int 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
257out_fd:
258	close(fd);
259
260out_unlock:
261	(void) pt_section_unlock(section);
262	return errcode;
263}
264
265int 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
287int 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
312int 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