1 /*
2  * Copyright (c) 2015-2018, 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_image;
51 
52 	errcode = _fstat(fd, stat);
53 
54 	_close(fd);
55 
56 	if (errcode)
57 		return -pte_bad_image;
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_image;
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 = CreateFile(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 = CreateFile(filename, GENERIC_READ, FILE_SHARE_WRITE, NULL,
269 				OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
270 		if (fh == INVALID_HANDLE_VALUE) {
271 			errcode = -pte_bad_image;
272 			goto out_unlock;
273 		}
274 	}
275 
276 	fd = _open_osfhandle((intptr_t) fh, _O_RDONLY);
277 	if (fd == -1) {
278 		errcode = -pte_bad_image;
279 		goto out_fh;
280 	}
281 
282 	errcode = check_file_status(section, fd);
283 	if (errcode < 0) {
284 		errcode = -pte_bad_image;
285 		goto out_fd;
286 	}
287 
288 	/* We leave the file open on success.  It will be closed when the
289 	 * section is unmapped.
290 	 */
291 	errcode = pt_sec_windows_map(section, fd);
292 	if (!errcode)
293 		return pt_sec_windows_map_success(section);
294 
295 	/* Fall back to file based sections - report the original error
296 	 * if we fail to convert the file descriptor.
297 	 */
298 	file = _fdopen(fd, "rb");
299 	if (!file) {
300 		errcode = -pte_bad_image;
301 		goto out_fd;
302 	}
303 
304 	/* We need to keep the file open on success.  It will be closed when
305 	 * the section is unmapped.
306 	 */
307 	errcode = pt_sec_file_map(section, file);
308 	if (!errcode)
309 		return pt_sec_windows_map_success(section);
310 
311 	fclose(file);
312 	goto out_unlock;
313 
314 out_fd:
315 	_close(fd);
316 	return errcode;
317 
318 out_fh:
319 	CloseHandle(fh);
320 
321 out_unlock:
322 	(void) pt_section_unlock(section);
323 	return errcode;
324 }
325 
326 int pt_sec_windows_unmap(struct pt_section *section)
327 {
328 	struct pt_sec_windows_mapping *mapping;
329 
330 	if (!section)
331 		return -pte_internal;
332 
333 	mapping = section->mapping;
334 	if (!mapping || !section->unmap || !section->read || !section->memsize)
335 		return -pte_internal;
336 
337 	section->mapping = NULL;
338 	section->unmap = NULL;
339 	section->read = NULL;
340 	section->memsize = NULL;
341 
342 	UnmapViewOfFile(mapping->begin);
343 	CloseHandle(mapping->mh);
344 	_close(mapping->fd);
345 	free(mapping);
346 
347 	return 0;
348 }
349 
350 int pt_sec_windows_read(const struct pt_section *section, uint8_t *buffer,
351 		      uint16_t size, uint64_t offset)
352 {
353 	struct pt_sec_windows_mapping *mapping;
354 	const uint8_t *begin;
355 
356 	if (!buffer || !section)
357 		return -pte_internal;
358 
359 	mapping = section->mapping;
360 	if (!mapping)
361 		return -pte_internal;
362 
363 	/* We already checked in pt_section_read() that the requested memory
364 	 * lies within the section's boundaries.
365 	 *
366 	 * And we checked that the entire section was mapped.  There's no need
367 	 * to check for overflows, again.
368 	 */
369 	begin = mapping->begin + offset;
370 
371 	memcpy(buffer, begin, size);
372 	return (int) size;
373 }
374 
375 
376 int pt_sec_windows_memsize(const struct pt_section *section, uint64_t *size)
377 {
378 	struct pt_sec_windows_mapping *mapping;
379 	const uint8_t *begin, *end;
380 
381 	if (!section || !size)
382 		return -pte_internal;
383 
384 	mapping = section->mapping;
385 	if (!mapping)
386 		return -pte_internal;
387 
388 	begin = mapping->base;
389 	end =  mapping->end;
390 
391 	if (!begin || !end || end < begin)
392 		return -pte_internal;
393 
394 	*size = (uint64_t) (end - begin);
395 
396 	return 0;
397 }
398