1/************************************************************************** 2 * 3 * Copyright 2021 Snap Inc. 4 * SPDX-License-Identifier: MIT 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sublicense, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 **************************************************************************/ 26 27/* 28 * Memory fd wrappers. 29 */ 30 31#include "detect_os.h" 32 33#if DETECT_OS_UNIX 34 35#include <string.h> 36#include <fcntl.h> 37#include <unistd.h> 38#include <sys/mman.h> 39 40#include "anon_file.h" 41#include "mesa-sha1.h" 42#include "u_math.h" 43#include "os_memory.h" 44 45/* (Re)define UUID_SIZE to avoid including vulkan.h (or p_defines.h) here. */ 46#define UUID_SIZE 16 47 48struct memory_header { 49 size_t size; 50 size_t offset; 51 uint8_t uuid[UUID_SIZE]; 52}; 53 54static void 55get_driver_id_sha1_hash(uint8_t sha1[SHA1_DIGEST_LENGTH], const char *driver_id) { 56 struct mesa_sha1 sha1_ctx; 57 _mesa_sha1_init(&sha1_ctx); 58 59 _mesa_sha1_update(&sha1_ctx, driver_id, strlen(driver_id)); 60 61 _mesa_sha1_final(&sha1_ctx, sha1); 62} 63 64/** 65 * Imports memory from a file descriptor 66 */ 67bool 68os_import_memory_fd(int fd, void **ptr, uint64_t *size, char const *driver_id) 69{ 70 void *mapped_ptr; 71 struct memory_header header; 72 73 lseek(fd, 0, SEEK_SET); 74 int bytes_read = read(fd, &header, sizeof(header)); 75 if(bytes_read != sizeof(header)) 76 return false; 77 78 // Check the uuid we put after the sizes in order to verify that the fd 79 // is a memfd that we created and not some random fd. 80 uint8_t sha1[SHA1_DIGEST_LENGTH]; 81 get_driver_id_sha1_hash(sha1, driver_id); 82 83 assert(SHA1_DIGEST_LENGTH >= UUID_SIZE); 84 if (memcmp(header.uuid, sha1, UUID_SIZE)) { 85 return false; 86 } 87 88 mapped_ptr = mmap(NULL, header.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 89 if (mapped_ptr == MAP_FAILED) { 90 return false; 91 } 92 *ptr = (void*)((uintptr_t)mapped_ptr + header.offset); 93 // the offset does not count as part of the size 94 *size = header.size - header.offset; 95 return true; 96} 97 98/** 99 * Return memory on given byte alignment 100 */ 101void * 102os_malloc_aligned_fd(size_t size, size_t alignment, int *fd, char const *fd_name, char const *driver_id) 103{ 104 void *ptr, *buf; 105 int mem_fd; 106 size_t alloc_size, offset; 107 108 *fd = -1; 109 110 /* 111 * Calculate 112 * 113 * alloc_size = size + alignment + sizeof(struct memory_header) + sizeof(size_t) 114 * 115 * while checking for overflow. 116 */ 117 const size_t header_size = sizeof(struct memory_header) + sizeof(size_t); 118 if (add_overflow_size_t(size, alignment, &alloc_size) || 119 add_overflow_size_t(alloc_size, header_size, &alloc_size)) 120 return NULL; 121 122 mem_fd = os_create_anonymous_file(alloc_size, fd_name); 123 124 if(mem_fd < 0) 125 return NULL; 126 127#if defined(HAVE_MEMFD_CREATE) || defined(ANDROID) 128 // Seal fd, so no one can grow or shrink the memory. 129 if (fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL) != 0) 130 goto fail; 131#endif 132 133 ptr = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0); 134 if (ptr == MAP_FAILED) 135 goto fail; 136 137 // Save the size and offset at the start, so we have all we need to unmap the memory 138 // and we are able to find the start of the actual data-section. Also save the 139 // offset directly before the data-section, so we can find the start of the mapped memory. 140 // | size | offset | ... padding ... | offset | ... data ... | 141 // ^ ^ ^ 142 // 0 offset size 143 buf = (char *)(((uintptr_t)ptr + header_size + alignment - 1) & ~((uintptr_t)(alignment - 1))); 144 offset = (size_t)((uintptr_t)buf - (uintptr_t)ptr); 145 struct memory_header* header = (struct memory_header*)ptr; 146 header->size = alloc_size; 147 header->offset = offset; 148 ((size_t*)buf)[-1] = offset; 149 150 // Add the hash of the driver_id as a uuid to the header in order to identify the memory 151 // when importing. 152 uint8_t sha1[SHA1_DIGEST_LENGTH]; 153 get_driver_id_sha1_hash(sha1, driver_id); 154 155 assert(SHA1_DIGEST_LENGTH >= UUID_SIZE); 156 memcpy(header->uuid, sha1, UUID_SIZE); 157 158 *fd = mem_fd; 159 return buf; 160 161fail: 162 close(mem_fd); 163 return NULL; 164} 165 166/** 167 * Free memory returned by os_malloc_aligned_fd(). 168 */ 169void 170os_free_fd(void *ptr) 171{ 172 if (ptr) { 173 size_t offset = ((size_t*)ptr)[-1]; 174 struct memory_header* header = (struct memory_header*)((uintptr_t)ptr - offset); 175 // check if the offset at the beginning of the memory 176 // is the same as the one we saved directly before the data. 177 assert(offset == header->offset); 178 munmap(header, header->size); 179 } 180} 181 182#endif 183