1 /* 2 * Copyright (c) 2013-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_posix.h" 31 #include "pt_section_file.h" 32 33 #include "intel-pt.h" 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <limits.h> 39 #include <sys/types.h> 40 #include <sys/mman.h> 41 #include <fcntl.h> 42 #include <unistd.h> 43 44 45 int pt_section_mk_status(void **pstatus, uint64_t *psize, const char *filename) 46 { 47 struct pt_sec_posix_status *status; 48 struct stat buffer; 49 int errcode; 50 51 if (!pstatus || !psize) 52 return -pte_internal; 53 54 errcode = stat(filename, &buffer); 55 if (errcode < 0) 56 return -pte_bad_file; 57 58 if (buffer.st_size < 0) 59 return -pte_bad_image; 60 61 status = malloc(sizeof(*status)); 62 if (!status) 63 return -pte_nomem; 64 65 status->stat = buffer; 66 67 *pstatus = status; 68 *psize = (uint64_t) buffer.st_size; 69 70 return 0; 71 } 72 73 static int check_file_status(struct pt_section *section, int fd) 74 { 75 struct pt_sec_posix_status *status; 76 struct stat stat; 77 int errcode; 78 79 if (!section) 80 return -pte_internal; 81 82 errcode = fstat(fd, &stat); 83 if (errcode) 84 return -pte_bad_file; 85 86 status = section->status; 87 if (!status) 88 return -pte_internal; 89 90 if (stat.st_size != status->stat.st_size) 91 return -pte_bad_image; 92 93 if (stat.st_mtime != status->stat.st_mtime) 94 return -pte_bad_image; 95 96 return 0; 97 } 98 99 int pt_sec_posix_map(struct pt_section *section, int fd) 100 { 101 struct pt_sec_posix_mapping *mapping; 102 uint64_t offset, size, adjustment; 103 uint8_t *base; 104 long page_size; 105 int errcode; 106 107 if (!section) 108 return -pte_internal; 109 110 offset = section->offset; 111 size = section->size; 112 113 page_size = sysconf(_SC_PAGESIZE); 114 if (page_size < 0) 115 return -pte_bad_config; 116 117 adjustment = offset % (uint64_t) page_size; 118 119 offset -= adjustment; 120 size += adjustment; 121 122 /* The section is supposed to fit into the file so we shouldn't 123 * see any overflows, here. 124 */ 125 if (size < section->size) 126 return -pte_internal; 127 128 if (SIZE_MAX < size) 129 return -pte_nomem; 130 131 if (INT_MAX < offset) 132 return -pte_nomem; 133 134 base = mmap(NULL, (size_t) size, PROT_READ, MAP_SHARED, fd, 135 (off_t) offset); 136 if (base == MAP_FAILED) 137 return -pte_nomem; 138 139 mapping = malloc(sizeof(*mapping)); 140 if (!mapping) { 141 errcode = -pte_nomem; 142 goto out_map; 143 } 144 145 mapping->base = base; 146 mapping->size = size; 147 mapping->begin = base + adjustment; 148 mapping->end = base + size; 149 150 section->mapping = mapping; 151 section->unmap = pt_sec_posix_unmap; 152 section->read = pt_sec_posix_read; 153 section->memsize = pt_sec_posix_memsize; 154 155 return 0; 156 157 out_map: 158 munmap(base, (size_t) size); 159 return errcode; 160 } 161 162 static int pt_sec_posix_map_success(struct pt_section *section) 163 { 164 uint16_t mcount; 165 int errcode, status; 166 167 if (!section) 168 return -pte_internal; 169 170 mcount = section->mcount + 1; 171 if (!mcount) { 172 (void) pt_section_unlock(section); 173 return -pte_overflow; 174 } 175 176 section->mcount = mcount; 177 178 errcode = pt_section_unlock(section); 179 if (errcode < 0) 180 return errcode; 181 182 status = pt_section_on_map(section); 183 if (status < 0) { 184 /* We had to release the section lock for pt_section_on_map() so 185 * @section may have meanwhile been mapped by other threads. 186 * 187 * We still want to return the error so we release our mapping. 188 * Our caller does not yet know whether pt_section_map() 189 * succeeded. 190 */ 191 (void) pt_section_unmap(section); 192 return status; 193 } 194 195 return 0; 196 } 197 198 int pt_section_map(struct pt_section *section) 199 { 200 const char *filename; 201 FILE *file; 202 int fd, errcode; 203 204 if (!section) 205 return -pte_internal; 206 207 errcode = pt_section_lock(section); 208 if (errcode < 0) 209 return errcode; 210 211 if (section->mcount) 212 return pt_sec_posix_map_success(section); 213 214 if (section->mapping) 215 goto out_unlock; 216 217 filename = section->filename; 218 if (!filename) 219 goto out_unlock; 220 221 errcode = -pte_bad_file; 222 fd = open(filename, O_RDONLY); 223 if (fd == -1) 224 goto out_unlock; 225 226 errcode = check_file_status(section, fd); 227 if (errcode < 0) 228 goto out_fd; 229 230 /* We close the file on success. This does not unmap the section. */ 231 errcode = pt_sec_posix_map(section, fd); 232 if (!errcode) { 233 close(fd); 234 235 return pt_sec_posix_map_success(section); 236 } 237 238 /* Fall back to file based sections - report the original error 239 * if we fail to convert the file descriptor. 240 */ 241 file = fdopen(fd, "rb"); 242 if (!file) { 243 errcode = -pte_bad_file; 244 goto out_fd; 245 } 246 247 /* We need to keep the file open on success. It will be closed when 248 * the section is unmapped. 249 */ 250 errcode = pt_sec_file_map(section, file); 251 if (!errcode) 252 return pt_sec_posix_map_success(section); 253 254 fclose(file); 255 goto out_unlock; 256 257 out_fd: 258 close(fd); 259 260 out_unlock: 261 (void) pt_section_unlock(section); 262 return errcode; 263 } 264 265 int pt_sec_posix_unmap(struct pt_section *section) 266 { 267 struct pt_sec_posix_mapping *mapping; 268 269 if (!section) 270 return -pte_internal; 271 272 mapping = section->mapping; 273 if (!mapping || !section->unmap || !section->read || !section->memsize) 274 return -pte_internal; 275 276 section->mapping = NULL; 277 section->unmap = NULL; 278 section->read = NULL; 279 section->memsize = NULL; 280 281 munmap(mapping->base, (size_t) mapping->size); 282 free(mapping); 283 284 return 0; 285 } 286 287 int pt_sec_posix_read(const struct pt_section *section, uint8_t *buffer, 288 uint16_t size, uint64_t offset) 289 { 290 struct pt_sec_posix_mapping *mapping; 291 const uint8_t *begin; 292 293 if (!buffer || !section) 294 return -pte_internal; 295 296 mapping = section->mapping; 297 if (!mapping) 298 return -pte_internal; 299 300 /* We already checked in pt_section_read() that the requested memory 301 * lies within the section's boundaries. 302 * 303 * And we checked that the entire section was mapped. There's no need 304 * to check for overflows, again. 305 */ 306 begin = mapping->begin + offset; 307 308 memcpy(buffer, begin, size); 309 return (int) size; 310 } 311 312 int pt_sec_posix_memsize(const struct pt_section *section, uint64_t *size) 313 { 314 struct pt_sec_posix_mapping *mapping; 315 const uint8_t *begin, *end; 316 317 if (!section || !size) 318 return -pte_internal; 319 320 mapping = section->mapping; 321 if (!mapping) 322 return -pte_internal; 323 324 begin = mapping->base; 325 end = mapping->end; 326 327 if (!begin || !end || end < begin) 328 return -pte_internal; 329 330 *size = (uint64_t) (end - begin); 331 332 return 0; 333 } 334