1 /*
2  * Copyright (c) 2015-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_windows.h"
31 #include "pt_section_file.h"
32 
33 #include "intel-pt.h"
34 
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <io.h>
39 
40 
41 static int pt_sec_windows_fstat(const char *filename, struct _stat *stat)
42 {
43 	int fd, errcode;
44 
45 	if (!filename || !stat)
46 		return -pte_internal;
47 
48 	fd = _open(filename, _O_RDONLY);
49 	if (fd == -1)
50 		return -pte_bad_file;
51 
52 	errcode = _fstat(fd, stat);
53 
54 	_close(fd);
55 
56 	if (errcode)
57 		return -pte_bad_file;
58 
59 	return 0;
60 }
61 
62 int pt_section_mk_status(void **pstatus, uint64_t *psize, const char *filename)
63 {
64 	struct pt_sec_windows_status *status;
65 	struct _stat stat;
66 	int errcode;
67 
68 	if (!pstatus || !psize)
69 		return -pte_internal;
70 
71 	errcode = pt_sec_windows_fstat(filename, &stat);
72 	if (errcode < 0)
73 		return errcode;
74 
75 	if (stat.st_size < 0)
76 		return -pte_bad_image;
77 
78 	status = malloc(sizeof(*status));
79 	if (!status)
80 		return -pte_nomem;
81 
82 	status->stat = stat;
83 
84 	*pstatus = status;
85 	*psize = stat.st_size;
86 
87 	return 0;
88 }
89 
90 static int check_file_status(struct pt_section *section, int fd)
91 {
92 	struct pt_sec_windows_status *status;
93 	struct _stat stat;
94 	int errcode;
95 
96 	if (!section)
97 		return -pte_internal;
98 
99 	errcode = _fstat(fd, &stat);
100 	if (errcode)
101 		return -pte_bad_file;
102 
103 	status = section->status;
104 	if (!status)
105 		return -pte_internal;
106 
107 	if (stat.st_size != status->stat.st_size)
108 		return -pte_bad_image;
109 
110 	if (stat.st_mtime != status->stat.st_mtime)
111 		return -pte_bad_image;
112 
113 	return 0;
114 }
115 
116 static DWORD granularity(void)
117 {
118 	struct _SYSTEM_INFO sysinfo;
119 
120 	GetSystemInfo(&sysinfo);
121 
122 	return sysinfo.dwAllocationGranularity;
123 }
124 
125 int pt_sec_windows_map(struct pt_section *section, int fd)
126 {
127 	struct pt_sec_windows_mapping *mapping;
128 	uint64_t offset, size, adjustment;
129 	HANDLE fh, mh;
130 	DWORD dsize;
131 	uint8_t *base;
132 	int errcode;
133 
134 	if (!section)
135 		return -pte_internal;
136 
137 	offset = section->offset;
138 	size = section->size;
139 
140 	adjustment = offset % granularity();
141 
142 	offset -= adjustment;
143 	size += adjustment;
144 
145 	/* The section is supposed to fit into the file so we shouldn't
146 	 * see any overflows, here.
147 	 */
148 	if (size < section->size)
149 		return -pte_internal;
150 
151 	dsize = (DWORD) size;
152 	if ((uint64_t) dsize != size)
153 		return -pte_internal;
154 
155 	fh = (HANDLE) _get_osfhandle(fd);
156 
157 	mh = CreateFileMapping(fh, NULL, PAGE_READONLY, 0, 0, NULL);
158 	if (!mh)
159 		return -pte_bad_image;
160 
161 	base = MapViewOfFile(mh, FILE_MAP_READ, (DWORD) (offset >> 32),
162 			     (DWORD) (uint32_t) offset, dsize);
163 	if (!base) {
164 		errcode = -pte_bad_image;
165 		goto out_mh;
166 	}
167 
168 	mapping = malloc(sizeof(*mapping));
169 	if (!mapping) {
170 		errcode = -pte_nomem;
171 		goto out_map;
172 	}
173 
174 	mapping->fd = fd;
175 	mapping->mh = mh;
176 	mapping->base = base;
177 	mapping->begin = base + adjustment;
178 	mapping->end = base + size;
179 
180 	section->mapping = mapping;
181 	section->unmap = pt_sec_windows_unmap;
182 	section->read = pt_sec_windows_read;
183 	section->memsize = pt_sec_windows_memsize;
184 
185 	return 0;
186 
187 out_map:
188 	UnmapViewOfFile(base);
189 
190 out_mh:
191 	CloseHandle(mh);
192 	return errcode;
193 }
194 
195 static int pt_sec_windows_map_success(struct pt_section *section)
196 {
197 	uint16_t mcount;
198 	int errcode, status;
199 
200 	if (!section)
201 		return -pte_internal;
202 
203 	mcount = section->mcount + 1;
204 	if (!mcount) {
205 		(void) pt_section_unlock(section);
206 		return -pte_overflow;
207 	}
208 
209 	section->mcount = mcount;
210 
211 	errcode = pt_section_unlock(section);
212 	if (errcode < 0)
213 		return errcode;
214 
215 	status = pt_section_on_map(section);
216 	if (status < 0) {
217 		/* We had to release the section lock for pt_section_on_map() so
218 		 * @section may have meanwhile been mapped by other threads.
219 		 *
220 		 * We still want to return the error so we release our mapping.
221 		 * Our caller does not yet know whether pt_section_map()
222 		 * succeeded.
223 		 */
224 		(void) pt_section_unmap(section);
225 		return status;
226 	}
227 
228 	return 0;
229 }
230 
231 int pt_section_map(struct pt_section *section)
232 {
233 	const char *filename;
234 	HANDLE fh;
235 	FILE *file;
236 	int fd, errcode;
237 
238 	if (!section)
239 		return -pte_internal;
240 
241 	errcode = pt_section_lock(section);
242 	if (errcode < 0)
243 		return errcode;
244 
245 	if (section->mcount)
246 		return pt_sec_windows_map_success(section);
247 
248 	if (section->mapping) {
249 		errcode = -pte_internal;
250 		goto out_unlock;
251 	}
252 
253 	filename = section->filename;
254 	if (!filename) {
255 		errcode = -pte_internal;
256 		goto out_unlock;
257 	}
258 
259 	fh = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
260 			 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
261 	if (fh == INVALID_HANDLE_VALUE) {
262 		/* We failed to open the file read-only.  Let's try to open it
263 		 * read-write; maybe our user has the file open for writing.
264 		 *
265 		 * We will detect changes to the file via fstat().
266 		 */
267 
268 		fh = CreateFileA(filename, GENERIC_READ, FILE_SHARE_WRITE,
269 				 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
270 				 NULL);
271 		if (fh == INVALID_HANDLE_VALUE) {
272 			errcode = -pte_bad_file;
273 			goto out_unlock;
274 		}
275 	}
276 
277 	fd = _open_osfhandle((intptr_t) fh, _O_RDONLY);
278 	if (fd == -1) {
279 		errcode = -pte_bad_file;
280 		goto out_fh;
281 	}
282 
283 	errcode = check_file_status(section, fd);
284 	if (errcode < 0)
285 		goto out_fd;
286 
287 	/* We leave the file open on success.  It will be closed when the
288 	 * section is unmapped.
289 	 */
290 	errcode = pt_sec_windows_map(section, fd);
291 	if (!errcode)
292 		return pt_sec_windows_map_success(section);
293 
294 	/* Fall back to file based sections - report the original error
295 	 * if we fail to convert the file descriptor.
296 	 */
297 	file = _fdopen(fd, "rb");
298 	if (!file) {
299 		errcode = -pte_bad_file;
300 		goto out_fd;
301 	}
302 
303 	/* We need to keep the file open on success.  It will be closed when
304 	 * the section is unmapped.
305 	 */
306 	errcode = pt_sec_file_map(section, file);
307 	if (!errcode)
308 		return pt_sec_windows_map_success(section);
309 
310 	fclose(file);
311 	goto out_unlock;
312 
313 out_fd:
314 	_close(fd);
315 	return errcode;
316 
317 out_fh:
318 	CloseHandle(fh);
319 
320 out_unlock:
321 	(void) pt_section_unlock(section);
322 	return errcode;
323 }
324 
325 int pt_sec_windows_unmap(struct pt_section *section)
326 {
327 	struct pt_sec_windows_mapping *mapping;
328 
329 	if (!section)
330 		return -pte_internal;
331 
332 	mapping = section->mapping;
333 	if (!mapping || !section->unmap || !section->read || !section->memsize)
334 		return -pte_internal;
335 
336 	section->mapping = NULL;
337 	section->unmap = NULL;
338 	section->read = NULL;
339 	section->memsize = NULL;
340 
341 	UnmapViewOfFile(mapping->begin);
342 	CloseHandle(mapping->mh);
343 	_close(mapping->fd);
344 	free(mapping);
345 
346 	return 0;
347 }
348 
349 int pt_sec_windows_read(const struct pt_section *section, uint8_t *buffer,
350 		      uint16_t size, uint64_t offset)
351 {
352 	struct pt_sec_windows_mapping *mapping;
353 	const uint8_t *begin;
354 
355 	if (!buffer || !section)
356 		return -pte_internal;
357 
358 	mapping = section->mapping;
359 	if (!mapping)
360 		return -pte_internal;
361 
362 	/* We already checked in pt_section_read() that the requested memory
363 	 * lies within the section's boundaries.
364 	 *
365 	 * And we checked that the entire section was mapped.  There's no need
366 	 * to check for overflows, again.
367 	 */
368 	begin = mapping->begin + offset;
369 
370 	memcpy(buffer, begin, size);
371 	return (int) size;
372 }
373 
374 
375 int pt_sec_windows_memsize(const struct pt_section *section, uint64_t *size)
376 {
377 	struct pt_sec_windows_mapping *mapping;
378 	const uint8_t *begin, *end;
379 
380 	if (!section || !size)
381 		return -pte_internal;
382 
383 	mapping = section->mapping;
384 	if (!mapping)
385 		return -pte_internal;
386 
387 	begin = mapping->base;
388 	end =  mapping->end;
389 
390 	if (!begin || !end || end < begin)
391 		return -pte_internal;
392 
393 	*size = (uint64_t) (end - begin);
394 
395 	return 0;
396 }
397