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