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
pt_sec_windows_fstat(const char * filename,struct _stat * stat)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
pt_section_mk_status(void ** pstatus,uint64_t * psize,const char * filename)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
check_file_status(struct pt_section * section,int fd)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
granularity(void)116 static DWORD granularity(void)
117 {
118 struct _SYSTEM_INFO sysinfo;
119
120 GetSystemInfo(&sysinfo);
121
122 return sysinfo.dwAllocationGranularity;
123 }
124
pt_sec_windows_map(struct pt_section * section,int fd)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
pt_sec_windows_map_success(struct pt_section * section)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
pt_section_map(struct pt_section * section)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
pt_sec_windows_unmap(struct pt_section * section)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
pt_sec_windows_read(const struct pt_section * section,uint8_t * buffer,uint16_t size,uint64_t offset)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
pt_sec_windows_memsize(const struct pt_section * section,uint64_t * size)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