Path: blob/21.2-virgl/src/intel/tools/intel_sanitize_gpu.c
4547 views
/*1* Copyright © 2015-2018 Intel Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*/2223#undef _FILE_OFFSET_BITS /* prevent #define open open64 */2425#include <string.h>26#include <stdlib.h>27#include <stdio.h>28#include <stdint.h>29#include <stdarg.h>30#include <fcntl.h>31#include <unistd.h>32#include <sys/ioctl.h>33#include <sys/stat.h>34#include <sys/mman.h>35#include <sys/sysmacros.h>36#include <dlfcn.h>37#include <pthread.h>38#include "drm-uapi/i915_drm.h"3940#include "util/hash_table.h"41#include "util/u_math.h"4243#define MESA_LOG_TAG "INTEL-SANITIZE-GPU"44#include "util/log.h"45#include "common/intel_clflush.h"4647static int (*libc_open)(const char *pathname, int flags, mode_t mode);48static int (*libc_close)(int fd);49static int (*libc_ioctl)(int fd, unsigned long request, void *argp);50static int (*libc_fcntl)(int fd, int cmd, int param);5152#define DRM_MAJOR 2265354/* TODO: we want to make sure that the padding forces55* the BO to take another page on the (PP)GTT; 4KB56* may or may not be the page size for the BO. Indeed,57* depending on GPU, kernel version and GEM size, the58* page size can be one of 4KB, 64KB or 2M.59*/60#define PADDING_SIZE 40966162struct refcnt_hash_table {63struct hash_table *t;64int refcnt;65};6667pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;68#define MUTEX_LOCK() do { \69if (unlikely(pthread_mutex_lock(&mutex))) { \70mesa_loge("mutex_lock failed"); \71abort(); \72} \73} while (0)74#define MUTEX_UNLOCK() do { \75if (unlikely(pthread_mutex_unlock(&mutex))) { \76mesa_loge("mutex_unlock failed"); \77abort(); \78} \79} while (0)8081static struct hash_table *fds_to_bo_sizes = NULL;8283static inline struct hash_table*84bo_size_table(int fd)85{86struct hash_entry *e = _mesa_hash_table_search(fds_to_bo_sizes,87(void*)(uintptr_t)fd);88return e ? ((struct refcnt_hash_table*)e->data)->t : NULL;89}9091static inline uint64_t92bo_size(int fd, uint32_t handle)93{94struct hash_table *t = bo_size_table(fd);95if (!t)96return UINT64_MAX;97struct hash_entry *e = _mesa_hash_table_search(t, (void*)(uintptr_t)handle);98return e ? (uint64_t)(uintptr_t)e->data : UINT64_MAX;99}100101static inline bool102is_drm_fd(int fd)103{104return !!bo_size_table(fd);105}106107static inline void108add_drm_fd(int fd)109{110struct refcnt_hash_table *r = malloc(sizeof(*r));111r->refcnt = 1;112r->t = _mesa_pointer_hash_table_create(NULL);113_mesa_hash_table_insert(fds_to_bo_sizes, (void*)(uintptr_t)fd,114(void*)(uintptr_t)r);115}116117static inline void118dup_drm_fd(int old_fd, int new_fd)119{120struct hash_entry *e = _mesa_hash_table_search(fds_to_bo_sizes,121(void*)(uintptr_t)old_fd);122struct refcnt_hash_table *r = e->data;123r->refcnt++;124_mesa_hash_table_insert(fds_to_bo_sizes, (void*)(uintptr_t)new_fd,125(void*)(uintptr_t)r);126}127128static inline void129del_drm_fd(int fd)130{131struct hash_entry *e = _mesa_hash_table_search(fds_to_bo_sizes,132(void*)(uintptr_t)fd);133struct refcnt_hash_table *r = e->data;134if (!--r->refcnt) {135_mesa_hash_table_remove(fds_to_bo_sizes, e);136_mesa_hash_table_destroy(r->t, NULL);137free(r);138}139}140141/* Our goal is not to have noise good enough for cryto,142* but instead values that are unique-ish enough that143* it is incredibly unlikely that a buffer overwrite144* will produce the exact same values.145*/146static uint8_t147next_noise_value(uint8_t prev_noise)148{149uint32_t v = prev_noise;150return (v * 103u + 227u) & 0xFF;151}152153static void154fill_noise_buffer(uint8_t *dst, uint8_t start, uint32_t length)155{156for(uint32_t i = 0; i < length; ++i) {157dst[i] = start;158start = next_noise_value(start);159}160}161162static bool163padding_is_good(int fd, uint32_t handle)164{165struct drm_i915_gem_mmap mmap_arg = {166.handle = handle,167.offset = align64(bo_size(fd, handle), 4096),168.size = PADDING_SIZE,169.flags = 0,170};171172/* Unknown bo, maybe prime or userptr. Ignore */173if (mmap_arg.offset == UINT64_MAX)174return true;175176uint8_t *mapped;177int ret;178uint8_t expected_value;179180ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg);181if (ret != 0) {182mesa_logd("Unable to map buffer %d for pad checking.", handle);183return false;184}185186mapped = (uint8_t*) (uintptr_t) mmap_arg.addr_ptr;187/* bah-humbug, we need to see the latest contents and188* if the bo is not cache coherent we likely need to189* invalidate the cache lines to get it.190*/191intel_invalidate_range(mapped, PADDING_SIZE);192193expected_value = handle & 0xFF;194for (uint32_t i = 0; i < PADDING_SIZE; ++i) {195if (expected_value != mapped[i]) {196munmap(mapped, PADDING_SIZE);197return false;198}199expected_value = next_noise_value(expected_value);200}201munmap(mapped, PADDING_SIZE);202203return true;204}205206static int207create_with_padding(int fd, struct drm_i915_gem_create *create)208{209uint64_t original_size = create->size;210211create->size = align64(original_size, 4096) + PADDING_SIZE;212int ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, create);213create->size = original_size;214215if (ret != 0)216return ret;217218uint8_t *noise_values;219struct drm_i915_gem_mmap mmap_arg = {220.handle = create->handle,221.offset = align64(create->size, 4096),222.size = PADDING_SIZE,223.flags = 0,224};225226ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg);227if (ret != 0) {228mesa_logd("Unable to map buffer %d for pad creation.\n", create->handle);229return 0;230}231232noise_values = (uint8_t*) (uintptr_t) mmap_arg.addr_ptr;233fill_noise_buffer(noise_values, create->handle & 0xFF,234PADDING_SIZE);235munmap(noise_values, PADDING_SIZE);236237_mesa_hash_table_insert(bo_size_table(fd), (void*)(uintptr_t)create->handle,238(void*)(uintptr_t)create->size);239240return 0;241}242243static int244exec_and_check_padding(int fd, unsigned long request,245struct drm_i915_gem_execbuffer2 *exec)246{247int ret = libc_ioctl(fd, request, exec);248if (ret != 0)249return ret;250251struct drm_i915_gem_exec_object2 *objects =252(void*)(uintptr_t)exec->buffers_ptr;253uint32_t batch_bo = exec->flags & I915_EXEC_BATCH_FIRST ? objects[0].handle :254objects[exec->buffer_count - 1].handle;255256struct drm_i915_gem_wait wait = {257.bo_handle = batch_bo,258.timeout_ns = -1,259};260ret = libc_ioctl(fd, DRM_IOCTL_I915_GEM_WAIT, &wait);261if (ret != 0)262return ret;263264bool detected_out_of_bounds_write = false;265266for (int i = 0; i < exec->buffer_count; i++) {267uint32_t handle = objects[i].handle;268269if (!padding_is_good(fd, handle)) {270detected_out_of_bounds_write = true;271mesa_loge("Detected buffer out-of-bounds write in bo %d", handle);272}273}274275if (unlikely(detected_out_of_bounds_write)) {276abort();277}278279return 0;280}281282static int283gem_close(int fd, struct drm_gem_close *close)284{285int ret = libc_ioctl(fd, DRM_IOCTL_GEM_CLOSE, close);286if (ret != 0)287return ret;288289struct hash_table *t = bo_size_table(fd);290struct hash_entry *e =291_mesa_hash_table_search(t, (void*)(uintptr_t)close->handle);292293if (e)294_mesa_hash_table_remove(t, e);295296return 0;297}298299static bool300is_i915(int fd) {301struct stat stat;302if (fstat(fd, &stat))303return false;304305if (!S_ISCHR(stat.st_mode) || major(stat.st_rdev) != DRM_MAJOR)306return false;307308char name[5] = "";309drm_version_t version = {310.name = name,311.name_len = sizeof(name) - 1,312};313if (libc_ioctl(fd, DRM_IOCTL_VERSION, &version))314return false;315316return strcmp("i915", name) == 0;317}318319__attribute__ ((visibility ("default"))) int320open(const char *path, int flags, ...)321{322va_list args;323mode_t mode;324325va_start(args, flags);326mode = va_arg(args, int);327va_end(args);328329int fd = libc_open(path, flags, mode);330331MUTEX_LOCK();332333if (fd >= 0 && is_i915(fd))334add_drm_fd(fd);335336MUTEX_UNLOCK();337338return fd;339}340341__attribute__ ((visibility ("default"), alias ("open"))) int342open64(const char *path, int flags, ...);343344__attribute__ ((visibility ("default"))) int345close(int fd)346{347MUTEX_LOCK();348349if (is_drm_fd(fd))350del_drm_fd(fd);351352MUTEX_UNLOCK();353354return libc_close(fd);355}356357__attribute__ ((visibility ("default"))) int358fcntl(int fd, int cmd, ...)359{360va_list args;361int param;362363va_start(args, cmd);364param = va_arg(args, int);365va_end(args);366367int res = libc_fcntl(fd, cmd, param);368369MUTEX_LOCK();370371if (is_drm_fd(fd) && cmd == F_DUPFD_CLOEXEC)372dup_drm_fd(fd, res);373374MUTEX_UNLOCK();375376return res;377}378379__attribute__ ((visibility ("default"))) int380ioctl(int fd, unsigned long request, ...)381{382int res;383va_list args;384void *argp;385386MUTEX_LOCK();387388va_start(args, request);389argp = va_arg(args, void *);390va_end(args);391392if (_IOC_TYPE(request) == DRM_IOCTL_BASE && !is_drm_fd(fd) && is_i915(fd)) {393mesa_loge("missed drm fd %d", fd);394add_drm_fd(fd);395}396397if (is_drm_fd(fd)) {398switch (request) {399case DRM_IOCTL_GEM_CLOSE:400res = gem_close(fd, (struct drm_gem_close*)argp);401goto out;402403case DRM_IOCTL_I915_GEM_CREATE:404res = create_with_padding(fd, (struct drm_i915_gem_create*)argp);405goto out;406407case DRM_IOCTL_I915_GEM_EXECBUFFER2:408case DRM_IOCTL_I915_GEM_EXECBUFFER2_WR:409res = exec_and_check_padding(fd, request,410(struct drm_i915_gem_execbuffer2*)argp);411goto out;412413default:414break;415}416}417res = libc_ioctl(fd, request, argp);418419out:420MUTEX_UNLOCK();421return res;422}423424static void __attribute__ ((constructor))425init(void)426{427fds_to_bo_sizes = _mesa_pointer_hash_table_create(NULL);428libc_open = dlsym(RTLD_NEXT, "open");429libc_close = dlsym(RTLD_NEXT, "close");430libc_fcntl = dlsym(RTLD_NEXT, "fcntl");431libc_ioctl = dlsym(RTLD_NEXT, "ioctl");432}433434435