101e04c3fSmrg/* 201e04c3fSmrg * Copyright © 2015-2018 Intel Corporation 301e04c3fSmrg * 401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 501e04c3fSmrg * copy of this software and associated documentation files (the "Software"), 601e04c3fSmrg * to deal in the Software without restriction, including without limitation 701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the 901e04c3fSmrg * Software is furnished to do so, subject to the following conditions: 1001e04c3fSmrg * 1101e04c3fSmrg * The above copyright notice and this permission notice (including the next 1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the 1301e04c3fSmrg * Software. 1401e04c3fSmrg * 1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 2101e04c3fSmrg * IN THE SOFTWARE. 2201e04c3fSmrg */ 2301e04c3fSmrg 2401e04c3fSmrg#undef _FILE_OFFSET_BITS /* prevent #define open open64 */ 2501e04c3fSmrg 2601e04c3fSmrg#include <string.h> 2701e04c3fSmrg#include <stdlib.h> 2801e04c3fSmrg#include <stdio.h> 2901e04c3fSmrg#include <stdint.h> 3001e04c3fSmrg#include <stdarg.h> 3101e04c3fSmrg#include <fcntl.h> 3201e04c3fSmrg#include <unistd.h> 3301e04c3fSmrg#include <sys/ioctl.h> 3401e04c3fSmrg#include <sys/stat.h> 3501e04c3fSmrg#include <sys/mman.h> 3601e04c3fSmrg#include <sys/sysmacros.h> 3701e04c3fSmrg#include <dlfcn.h> 3801e04c3fSmrg#include <pthread.h> 399f464c52Smaya#include "drm-uapi/i915_drm.h" 4001e04c3fSmrg 4101e04c3fSmrg#include "util/hash_table.h" 429f464c52Smaya#include "util/u_math.h" 4301e04c3fSmrg 447ec681f3Smrg#define MESA_LOG_TAG "INTEL-SANITIZE-GPU" 457ec681f3Smrg#include "util/log.h" 467ec681f3Smrg#include "common/intel_clflush.h" 4701e04c3fSmrg 4801e04c3fSmrgstatic int (*libc_open)(const char *pathname, int flags, mode_t mode); 4901e04c3fSmrgstatic int (*libc_close)(int fd); 5001e04c3fSmrgstatic int (*libc_ioctl)(int fd, unsigned long request, void *argp); 5101e04c3fSmrgstatic int (*libc_fcntl)(int fd, int cmd, int param); 5201e04c3fSmrg 5301e04c3fSmrg#define DRM_MAJOR 226 5401e04c3fSmrg 5501e04c3fSmrg/* TODO: we want to make sure that the padding forces 5601e04c3fSmrg * the BO to take another page on the (PP)GTT; 4KB 5701e04c3fSmrg * may or may not be the page size for the BO. Indeed, 5801e04c3fSmrg * depending on GPU, kernel version and GEM size, the 5901e04c3fSmrg * page size can be one of 4KB, 64KB or 2M. 6001e04c3fSmrg */ 6101e04c3fSmrg#define PADDING_SIZE 4096 6201e04c3fSmrg 6301e04c3fSmrgstruct refcnt_hash_table { 6401e04c3fSmrg struct hash_table *t; 6501e04c3fSmrg int refcnt; 6601e04c3fSmrg}; 6701e04c3fSmrg 6801e04c3fSmrgpthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 6901e04c3fSmrg#define MUTEX_LOCK() do { \ 7001e04c3fSmrg if (unlikely(pthread_mutex_lock(&mutex))) { \ 717ec681f3Smrg mesa_loge("mutex_lock failed"); \ 7201e04c3fSmrg abort(); \ 7301e04c3fSmrg } \ 7401e04c3fSmrg} while (0) 7501e04c3fSmrg#define MUTEX_UNLOCK() do { \ 7601e04c3fSmrg if (unlikely(pthread_mutex_unlock(&mutex))) { \ 777ec681f3Smrg mesa_loge("mutex_unlock failed"); \ 7801e04c3fSmrg abort(); \ 7901e04c3fSmrg } \ 8001e04c3fSmrg} while (0) 8101e04c3fSmrg 8201e04c3fSmrgstatic struct hash_table *fds_to_bo_sizes = NULL; 8301e04c3fSmrg 8401e04c3fSmrgstatic inline struct hash_table* 8501e04c3fSmrgbo_size_table(int fd) 8601e04c3fSmrg{ 8701e04c3fSmrg struct hash_entry *e = _mesa_hash_table_search(fds_to_bo_sizes, 8801e04c3fSmrg (void*)(uintptr_t)fd); 8901e04c3fSmrg return e ? ((struct refcnt_hash_table*)e->data)->t : NULL; 9001e04c3fSmrg} 9101e04c3fSmrg 9201e04c3fSmrgstatic inline uint64_t 9301e04c3fSmrgbo_size(int fd, uint32_t handle) 9401e04c3fSmrg{ 9501e04c3fSmrg struct hash_table *t = bo_size_table(fd); 9601e04c3fSmrg if (!t) 9701e04c3fSmrg return UINT64_MAX; 9801e04c3fSmrg struct hash_entry *e = _mesa_hash_table_search(t, (void*)(uintptr_t)handle); 997ec681f3Smrg return e ? (uint64_t)(uintptr_t)e->data : UINT64_MAX; 10001e04c3fSmrg} 10101e04c3fSmrg 10201e04c3fSmrgstatic inline bool 10301e04c3fSmrgis_drm_fd(int fd) 10401e04c3fSmrg{ 10501e04c3fSmrg return !!bo_size_table(fd); 10601e04c3fSmrg} 10701e04c3fSmrg 10801e04c3fSmrgstatic inline void 10901e04c3fSmrgadd_drm_fd(int fd) 11001e04c3fSmrg{ 11101e04c3fSmrg struct refcnt_hash_table *r = malloc(sizeof(*r)); 11201e04c3fSmrg r->refcnt = 1; 1139f464c52Smaya r->t = _mesa_pointer_hash_table_create(NULL); 11401e04c3fSmrg _mesa_hash_table_insert(fds_to_bo_sizes, (void*)(uintptr_t)fd, 11501e04c3fSmrg (void*)(uintptr_t)r); 11601e04c3fSmrg} 11701e04c3fSmrg 11801e04c3fSmrgstatic inline void 11901e04c3fSmrgdup_drm_fd(int old_fd, int new_fd) 12001e04c3fSmrg{ 12101e04c3fSmrg struct hash_entry *e = _mesa_hash_table_search(fds_to_bo_sizes, 12201e04c3fSmrg (void*)(uintptr_t)old_fd); 12301e04c3fSmrg struct refcnt_hash_table *r = e->data; 12401e04c3fSmrg r->refcnt++; 12501e04c3fSmrg _mesa_hash_table_insert(fds_to_bo_sizes, (void*)(uintptr_t)new_fd, 12601e04c3fSmrg (void*)(uintptr_t)r); 12701e04c3fSmrg} 12801e04c3fSmrg 12901e04c3fSmrgstatic inline void 13001e04c3fSmrgdel_drm_fd(int fd) 13101e04c3fSmrg{ 13201e04c3fSmrg struct hash_entry *e = _mesa_hash_table_search(fds_to_bo_sizes, 13301e04c3fSmrg (void*)(uintptr_t)fd); 13401e04c3fSmrg struct refcnt_hash_table *r = e->data; 13501e04c3fSmrg if (!--r->refcnt) { 13601e04c3fSmrg _mesa_hash_table_remove(fds_to_bo_sizes, e); 13701e04c3fSmrg _mesa_hash_table_destroy(r->t, NULL); 13801e04c3fSmrg free(r); 13901e04c3fSmrg } 14001e04c3fSmrg} 14101e04c3fSmrg 14201e04c3fSmrg/* Our goal is not to have noise good enough for cryto, 14301e04c3fSmrg * but instead values that are unique-ish enough that 14401e04c3fSmrg * it is incredibly unlikely that a buffer overwrite 14501e04c3fSmrg * will produce the exact same values. 14601e04c3fSmrg */ 14701e04c3fSmrgstatic uint8_t 14801e04c3fSmrgnext_noise_value(uint8_t prev_noise) 14901e04c3fSmrg{ 15001e04c3fSmrg uint32_t v = prev_noise; 15101e04c3fSmrg return (v * 103u + 227u) & 0xFF; 15201e04c3fSmrg} 15301e04c3fSmrg 15401e04c3fSmrgstatic void 15501e04c3fSmrgfill_noise_buffer(uint8_t *dst, uint8_t start, uint32_t length) 15601e04c3fSmrg{ 15701e04c3fSmrg for(uint32_t i = 0; i < length; ++i) { 15801e04c3fSmrg dst[i] = start; 15901e04c3fSmrg start = next_noise_value(start); 16001e04c3fSmrg } 16101e04c3fSmrg} 16201e04c3fSmrg 16301e04c3fSmrgstatic bool 16401e04c3fSmrgpadding_is_good(int fd, uint32_t handle) 16501e04c3fSmrg{ 16601e04c3fSmrg struct drm_i915_gem_mmap mmap_arg = { 16701e04c3fSmrg .handle = handle, 1689f464c52Smaya .offset = align64(bo_size(fd, handle), 4096), 16901e04c3fSmrg .size = PADDING_SIZE, 17001e04c3fSmrg .flags = 0, 17101e04c3fSmrg }; 17201e04c3fSmrg 17301e04c3fSmrg /* Unknown bo, maybe prime or userptr. Ignore */ 17401e04c3fSmrg if (mmap_arg.offset == UINT64_MAX) 17501e04c3fSmrg return true; 17601e04c3fSmrg 17701e04c3fSmrg uint8_t *mapped; 17801e04c3fSmrg int ret; 17901e04c3fSmrg uint8_t expected_value; 18001e04c3fSmrg 18101e04c3fSmrg ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg); 18201e04c3fSmrg if (ret != 0) { 1837ec681f3Smrg mesa_logd("Unable to map buffer %d for pad checking.", handle); 18401e04c3fSmrg return false; 18501e04c3fSmrg } 18601e04c3fSmrg 18701e04c3fSmrg mapped = (uint8_t*) (uintptr_t) mmap_arg.addr_ptr; 18801e04c3fSmrg /* bah-humbug, we need to see the latest contents and 18901e04c3fSmrg * if the bo is not cache coherent we likely need to 19001e04c3fSmrg * invalidate the cache lines to get it. 19101e04c3fSmrg */ 1927ec681f3Smrg intel_invalidate_range(mapped, PADDING_SIZE); 19301e04c3fSmrg 19401e04c3fSmrg expected_value = handle & 0xFF; 19501e04c3fSmrg for (uint32_t i = 0; i < PADDING_SIZE; ++i) { 19601e04c3fSmrg if (expected_value != mapped[i]) { 19701e04c3fSmrg munmap(mapped, PADDING_SIZE); 19801e04c3fSmrg return false; 19901e04c3fSmrg } 20001e04c3fSmrg expected_value = next_noise_value(expected_value); 20101e04c3fSmrg } 20201e04c3fSmrg munmap(mapped, PADDING_SIZE); 20301e04c3fSmrg 20401e04c3fSmrg return true; 20501e04c3fSmrg} 20601e04c3fSmrg 20701e04c3fSmrgstatic int 20801e04c3fSmrgcreate_with_padding(int fd, struct drm_i915_gem_create *create) 20901e04c3fSmrg{ 2109f464c52Smaya uint64_t original_size = create->size; 2119f464c52Smaya 2129f464c52Smaya create->size = align64(original_size, 4096) + PADDING_SIZE; 21301e04c3fSmrg int ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, create); 2149f464c52Smaya create->size = original_size; 21501e04c3fSmrg 21601e04c3fSmrg if (ret != 0) 21701e04c3fSmrg return ret; 21801e04c3fSmrg 21901e04c3fSmrg uint8_t *noise_values; 22001e04c3fSmrg struct drm_i915_gem_mmap mmap_arg = { 22101e04c3fSmrg .handle = create->handle, 2229f464c52Smaya .offset = align64(create->size, 4096), 22301e04c3fSmrg .size = PADDING_SIZE, 22401e04c3fSmrg .flags = 0, 22501e04c3fSmrg }; 22601e04c3fSmrg 22701e04c3fSmrg ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg); 2289f464c52Smaya if (ret != 0) { 2297ec681f3Smrg mesa_logd("Unable to map buffer %d for pad creation.\n", create->handle); 23001e04c3fSmrg return 0; 2319f464c52Smaya } 23201e04c3fSmrg 23301e04c3fSmrg noise_values = (uint8_t*) (uintptr_t) mmap_arg.addr_ptr; 23401e04c3fSmrg fill_noise_buffer(noise_values, create->handle & 0xFF, 23501e04c3fSmrg PADDING_SIZE); 23601e04c3fSmrg munmap(noise_values, PADDING_SIZE); 23701e04c3fSmrg 23801e04c3fSmrg _mesa_hash_table_insert(bo_size_table(fd), (void*)(uintptr_t)create->handle, 23901e04c3fSmrg (void*)(uintptr_t)create->size); 24001e04c3fSmrg 24101e04c3fSmrg return 0; 24201e04c3fSmrg} 24301e04c3fSmrg 24401e04c3fSmrgstatic int 24501e04c3fSmrgexec_and_check_padding(int fd, unsigned long request, 24601e04c3fSmrg struct drm_i915_gem_execbuffer2 *exec) 24701e04c3fSmrg{ 24801e04c3fSmrg int ret = libc_ioctl(fd, request, exec); 24901e04c3fSmrg if (ret != 0) 25001e04c3fSmrg return ret; 25101e04c3fSmrg 25201e04c3fSmrg struct drm_i915_gem_exec_object2 *objects = 25301e04c3fSmrg (void*)(uintptr_t)exec->buffers_ptr; 25401e04c3fSmrg uint32_t batch_bo = exec->flags & I915_EXEC_BATCH_FIRST ? objects[0].handle : 25501e04c3fSmrg objects[exec->buffer_count - 1].handle; 25601e04c3fSmrg 25701e04c3fSmrg struct drm_i915_gem_wait wait = { 25801e04c3fSmrg .bo_handle = batch_bo, 25901e04c3fSmrg .timeout_ns = -1, 26001e04c3fSmrg }; 26101e04c3fSmrg ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_WAIT, &wait); 26201e04c3fSmrg if (ret != 0) 26301e04c3fSmrg return ret; 26401e04c3fSmrg 26501e04c3fSmrg bool detected_out_of_bounds_write = false; 26601e04c3fSmrg 26701e04c3fSmrg for (int i = 0; i < exec->buffer_count; i++) { 26801e04c3fSmrg uint32_t handle = objects[i].handle; 26901e04c3fSmrg 27001e04c3fSmrg if (!padding_is_good(fd, handle)) { 27101e04c3fSmrg detected_out_of_bounds_write = true; 2727ec681f3Smrg mesa_loge("Detected buffer out-of-bounds write in bo %d", handle); 27301e04c3fSmrg } 27401e04c3fSmrg } 27501e04c3fSmrg 27601e04c3fSmrg if (unlikely(detected_out_of_bounds_write)) { 27701e04c3fSmrg abort(); 27801e04c3fSmrg } 27901e04c3fSmrg 28001e04c3fSmrg return 0; 28101e04c3fSmrg} 28201e04c3fSmrg 28301e04c3fSmrgstatic int 28401e04c3fSmrggem_close(int fd, struct drm_gem_close *close) 28501e04c3fSmrg{ 28601e04c3fSmrg int ret = libc_ioctl(fd, DRM_IOCTL_GEM_CLOSE, close); 28701e04c3fSmrg if (ret != 0) 28801e04c3fSmrg return ret; 28901e04c3fSmrg 29001e04c3fSmrg struct hash_table *t = bo_size_table(fd); 29101e04c3fSmrg struct hash_entry *e = 29201e04c3fSmrg _mesa_hash_table_search(t, (void*)(uintptr_t)close->handle); 29301e04c3fSmrg 29401e04c3fSmrg if (e) 29501e04c3fSmrg _mesa_hash_table_remove(t, e); 29601e04c3fSmrg 29701e04c3fSmrg return 0; 29801e04c3fSmrg} 29901e04c3fSmrg 30001e04c3fSmrgstatic bool 30101e04c3fSmrgis_i915(int fd) { 30201e04c3fSmrg struct stat stat; 30301e04c3fSmrg if (fstat(fd, &stat)) 30401e04c3fSmrg return false; 30501e04c3fSmrg 30601e04c3fSmrg if (!S_ISCHR(stat.st_mode) || major(stat.st_rdev) != DRM_MAJOR) 30701e04c3fSmrg return false; 30801e04c3fSmrg 30901e04c3fSmrg char name[5] = ""; 31001e04c3fSmrg drm_version_t version = { 31101e04c3fSmrg .name = name, 31201e04c3fSmrg .name_len = sizeof(name) - 1, 31301e04c3fSmrg }; 31401e04c3fSmrg if (libc_ioctl(fd, DRM_IOCTL_VERSION, &version)) 31501e04c3fSmrg return false; 31601e04c3fSmrg 31701e04c3fSmrg return strcmp("i915", name) == 0; 31801e04c3fSmrg} 31901e04c3fSmrg 32001e04c3fSmrg__attribute__ ((visibility ("default"))) int 32101e04c3fSmrgopen(const char *path, int flags, ...) 32201e04c3fSmrg{ 32301e04c3fSmrg va_list args; 32401e04c3fSmrg mode_t mode; 32501e04c3fSmrg 32601e04c3fSmrg va_start(args, flags); 32701e04c3fSmrg mode = va_arg(args, int); 32801e04c3fSmrg va_end(args); 32901e04c3fSmrg 33001e04c3fSmrg int fd = libc_open(path, flags, mode); 33101e04c3fSmrg 33201e04c3fSmrg MUTEX_LOCK(); 33301e04c3fSmrg 33401e04c3fSmrg if (fd >= 0 && is_i915(fd)) 33501e04c3fSmrg add_drm_fd(fd); 33601e04c3fSmrg 33701e04c3fSmrg MUTEX_UNLOCK(); 33801e04c3fSmrg 33901e04c3fSmrg return fd; 34001e04c3fSmrg} 34101e04c3fSmrg 34201e04c3fSmrg__attribute__ ((visibility ("default"), alias ("open"))) int 34301e04c3fSmrgopen64(const char *path, int flags, ...); 34401e04c3fSmrg 34501e04c3fSmrg__attribute__ ((visibility ("default"))) int 34601e04c3fSmrgclose(int fd) 34701e04c3fSmrg{ 34801e04c3fSmrg MUTEX_LOCK(); 34901e04c3fSmrg 35001e04c3fSmrg if (is_drm_fd(fd)) 35101e04c3fSmrg del_drm_fd(fd); 35201e04c3fSmrg 35301e04c3fSmrg MUTEX_UNLOCK(); 35401e04c3fSmrg 35501e04c3fSmrg return libc_close(fd); 35601e04c3fSmrg} 35701e04c3fSmrg 35801e04c3fSmrg__attribute__ ((visibility ("default"))) int 35901e04c3fSmrgfcntl(int fd, int cmd, ...) 36001e04c3fSmrg{ 36101e04c3fSmrg va_list args; 36201e04c3fSmrg int param; 36301e04c3fSmrg 36401e04c3fSmrg va_start(args, cmd); 36501e04c3fSmrg param = va_arg(args, int); 36601e04c3fSmrg va_end(args); 36701e04c3fSmrg 36801e04c3fSmrg int res = libc_fcntl(fd, cmd, param); 36901e04c3fSmrg 37001e04c3fSmrg MUTEX_LOCK(); 37101e04c3fSmrg 37201e04c3fSmrg if (is_drm_fd(fd) && cmd == F_DUPFD_CLOEXEC) 37301e04c3fSmrg dup_drm_fd(fd, res); 37401e04c3fSmrg 37501e04c3fSmrg MUTEX_UNLOCK(); 37601e04c3fSmrg 37701e04c3fSmrg return res; 37801e04c3fSmrg} 37901e04c3fSmrg 38001e04c3fSmrg__attribute__ ((visibility ("default"))) int 38101e04c3fSmrgioctl(int fd, unsigned long request, ...) 38201e04c3fSmrg{ 38301e04c3fSmrg int res; 38401e04c3fSmrg va_list args; 38501e04c3fSmrg void *argp; 38601e04c3fSmrg 38701e04c3fSmrg MUTEX_LOCK(); 38801e04c3fSmrg 38901e04c3fSmrg va_start(args, request); 39001e04c3fSmrg argp = va_arg(args, void *); 39101e04c3fSmrg va_end(args); 39201e04c3fSmrg 39301e04c3fSmrg if (_IOC_TYPE(request) == DRM_IOCTL_BASE && !is_drm_fd(fd) && is_i915(fd)) { 3947ec681f3Smrg mesa_loge("missed drm fd %d", fd); 39501e04c3fSmrg add_drm_fd(fd); 39601e04c3fSmrg } 39701e04c3fSmrg 39801e04c3fSmrg if (is_drm_fd(fd)) { 39901e04c3fSmrg switch (request) { 40001e04c3fSmrg case DRM_IOCTL_GEM_CLOSE: 40101e04c3fSmrg res = gem_close(fd, (struct drm_gem_close*)argp); 40201e04c3fSmrg goto out; 40301e04c3fSmrg 40401e04c3fSmrg case DRM_IOCTL_I915_GEM_CREATE: 40501e04c3fSmrg res = create_with_padding(fd, (struct drm_i915_gem_create*)argp); 40601e04c3fSmrg goto out; 40701e04c3fSmrg 40801e04c3fSmrg case DRM_IOCTL_I915_GEM_EXECBUFFER2: 40901e04c3fSmrg case DRM_IOCTL_I915_GEM_EXECBUFFER2_WR: 41001e04c3fSmrg res = exec_and_check_padding(fd, request, 41101e04c3fSmrg (struct drm_i915_gem_execbuffer2*)argp); 41201e04c3fSmrg goto out; 41301e04c3fSmrg 41401e04c3fSmrg default: 41501e04c3fSmrg break; 41601e04c3fSmrg } 41701e04c3fSmrg } 41801e04c3fSmrg res = libc_ioctl(fd, request, argp); 41901e04c3fSmrg 42001e04c3fSmrg out: 42101e04c3fSmrg MUTEX_UNLOCK(); 42201e04c3fSmrg return res; 42301e04c3fSmrg} 42401e04c3fSmrg 42501e04c3fSmrgstatic void __attribute__ ((constructor)) 42601e04c3fSmrginit(void) 42701e04c3fSmrg{ 4289f464c52Smaya fds_to_bo_sizes = _mesa_pointer_hash_table_create(NULL); 42901e04c3fSmrg libc_open = dlsym(RTLD_NEXT, "open"); 43001e04c3fSmrg libc_close = dlsym(RTLD_NEXT, "close"); 43101e04c3fSmrg libc_fcntl = dlsym(RTLD_NEXT, "fcntl"); 43201e04c3fSmrg libc_ioctl = dlsym(RTLD_NEXT, "ioctl"); 43301e04c3fSmrg} 434