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