1 /* 2 * Copyright (c) 2013-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_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 errcode; 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 = 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_image; 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 int errcode; 105 106 if (!section) 107 return -pte_internal; 108 109 offset = section->offset; 110 size = section->size; 111 112 adjustment = offset % sysconf(_SC_PAGESIZE); 113 114 offset -= adjustment; 115 size += adjustment; 116 117 /* The section is supposed to fit into the file so we shouldn't 118 * see any overflows, here. 119 */ 120 if (size < section->size) 121 return -pte_internal; 122 123 if (SIZE_MAX < size) 124 return -pte_nomem; 125 126 if (INT_MAX < offset) 127 return -pte_nomem; 128 129 base = mmap(NULL, (size_t) size, PROT_READ, MAP_SHARED, fd, 130 (off_t) offset); 131 if (base == MAP_FAILED) 132 return -pte_nomem; 133 134 mapping = malloc(sizeof(*mapping)); 135 if (!mapping) { 136 errcode = -pte_nomem; 137 goto out_map; 138 } 139 140 mapping->base = base; 141 mapping->size = size; 142 mapping->begin = base + adjustment; 143 mapping->end = base + size; 144 145 section->mapping = mapping; 146 section->unmap = pt_sec_posix_unmap; 147 section->read = pt_sec_posix_read; 148 section->memsize = pt_sec_posix_memsize; 149 150 return 0; 151 152 out_map: 153 munmap(base, (size_t) size); 154 return errcode; 155 } 156 157 static int pt_sec_posix_map_success(struct pt_section *section) 158 { 159 uint16_t mcount; 160 int errcode, status; 161 162 if (!section) 163 return -pte_internal; 164 165 mcount = section->mcount + 1; 166 if (!mcount) { 167 (void) pt_section_unlock(section); 168 return -pte_overflow; 169 } 170 171 section->mcount = mcount; 172 173 errcode = pt_section_unlock(section); 174 if (errcode < 0) 175 return errcode; 176 177 status = pt_section_on_map(section); 178 if (status < 0) { 179 /* We had to release the section lock for pt_section_on_map() so 180 * @section may have meanwhile been mapped by other threads. 181 * 182 * We still want to return the error so we release our mapping. 183 * Our caller does not yet know whether pt_section_map() 184 * succeeded. 185 */ 186 (void) pt_section_unmap(section); 187 return status; 188 } 189 190 return 0; 191 } 192 193 int pt_section_map(struct pt_section *section) 194 { 195 const char *filename; 196 FILE *file; 197 int fd, errcode; 198 199 if (!section) 200 return -pte_internal; 201 202 errcode = pt_section_lock(section); 203 if (errcode < 0) 204 return errcode; 205 206 if (section->mcount) 207 return pt_sec_posix_map_success(section); 208 209 if (section->mapping) 210 goto out_unlock; 211 212 filename = section->filename; 213 if (!filename) 214 goto out_unlock; 215 216 errcode = -pte_bad_image; 217 fd = open(filename, O_RDONLY); 218 if (fd == -1) 219 goto out_unlock; 220 221 errcode = check_file_status(section, fd); 222 if (errcode < 0) 223 goto out_fd; 224 225 /* We close the file on success. This does not unmap the section. */ 226 errcode = pt_sec_posix_map(section, fd); 227 if (!errcode) { 228 close(fd); 229 230 return pt_sec_posix_map_success(section); 231 } 232 233 /* Fall back to file based sections - report the original error 234 * if we fail to convert the file descriptor. 235 */ 236 file = fdopen(fd, "rb"); 237 if (!file) 238 goto out_fd; 239 240 /* We need to keep the file open on success. It will be closed when 241 * the section is unmapped. 242 */ 243 errcode = pt_sec_file_map(section, file); 244 if (!errcode) 245 return pt_sec_posix_map_success(section); 246 247 fclose(file); 248 goto out_unlock; 249 250 out_fd: 251 close(fd); 252 253 out_unlock: 254 (void) pt_section_unlock(section); 255 return errcode; 256 } 257 258 int pt_sec_posix_unmap(struct pt_section *section) 259 { 260 struct pt_sec_posix_mapping *mapping; 261 262 if (!section) 263 return -pte_internal; 264 265 mapping = section->mapping; 266 if (!mapping || !section->unmap || !section->read || !section->memsize) 267 return -pte_internal; 268 269 section->mapping = NULL; 270 section->unmap = NULL; 271 section->read = NULL; 272 section->memsize = NULL; 273 274 munmap(mapping->base, (size_t) mapping->size); 275 free(mapping); 276 277 return 0; 278 } 279 280 int pt_sec_posix_read(const struct pt_section *section, uint8_t *buffer, 281 uint16_t size, uint64_t offset) 282 { 283 struct pt_sec_posix_mapping *mapping; 284 const uint8_t *begin; 285 286 if (!buffer || !section) 287 return -pte_internal; 288 289 mapping = section->mapping; 290 if (!mapping) 291 return -pte_internal; 292 293 /* We already checked in pt_section_read() that the requested memory 294 * lies within the section's boundaries. 295 * 296 * And we checked that the entire section was mapped. There's no need 297 * to check for overflows, again. 298 */ 299 begin = mapping->begin + offset; 300 301 memcpy(buffer, begin, size); 302 return (int) size; 303 } 304 305 int pt_sec_posix_memsize(const struct pt_section *section, uint64_t *size) 306 { 307 struct pt_sec_posix_mapping *mapping; 308 const uint8_t *begin, *end; 309 310 if (!section || !size) 311 return -pte_internal; 312 313 mapping = section->mapping; 314 if (!mapping) 315 return -pte_internal; 316 317 begin = mapping->base; 318 end = mapping->end; 319 320 if (!begin || !end || end < begin) 321 return -pte_internal; 322 323 *size = (uint64_t) (end - begin); 324 325 return 0; 326 } 327