| File: | Source/Core/Common/Src/MemArena.cpp |
| Location: | line 196, column 21 |
| Description: | Dereference of null pointer (loaded from field 'out_ptr') |
| 1 | // Copyright 2013 Dolphin Emulator Project | |||
| 2 | // Licensed under GPLv2 | |||
| 3 | // Refer to the license.txt file included. | |||
| 4 | ||||
| 5 | ||||
| 6 | #include "Common.h" | |||
| 7 | #include "MemoryUtil.h" | |||
| 8 | #include "MemArena.h" | |||
| 9 | ||||
| 10 | #ifdef _WIN32 | |||
| 11 | #include <windows.h> | |||
| 12 | #else | |||
| 13 | #include <sys/stat.h> | |||
| 14 | #include <fcntl.h> | |||
| 15 | #include <unistd.h> | |||
| 16 | #include <cerrno> | |||
| 17 | #include <cstring> | |||
| 18 | #ifdef ANDROID | |||
| 19 | #include <sys/ioctl.h> | |||
| 20 | #include <linux1/ashmem.h> | |||
| 21 | #endif | |||
| 22 | #endif | |||
| 23 | ||||
| 24 | #if defined(__APPLE__) | |||
| 25 | static const char* ram_temp_file = "/tmp/gc_mem.tmp"; | |||
| 26 | #elif !defined(_WIN32) // non OSX unixes | |||
| 27 | static const char* ram_temp_file = "/dev/shm/gc_mem.tmp"; | |||
| 28 | #endif | |||
| 29 | #ifdef ANDROID | |||
| 30 | #define ASHMEM_DEVICE "/dev/ashmem" | |||
| 31 | ||||
| 32 | int AshmemCreateFileMapping(const char *name, size_t size) | |||
| 33 | { | |||
| 34 | int fd, ret; | |||
| 35 | fd = open(ASHMEM_DEVICE, O_RDWR02); | |||
| 36 | if (fd < 0) | |||
| 37 | return fd; | |||
| 38 | ||||
| 39 | // We don't really care if we can't set the name, it is optional | |||
| 40 | ret = ioctl(fd, ASHMEM_SET_NAME, name); | |||
| 41 | ||||
| 42 | ret = ioctl(fd, ASHMEM_SET_SIZE, size); | |||
| 43 | if (ret < 0) | |||
| 44 | { | |||
| 45 | close(fd); | |||
| 46 | NOTICE_LOG(MEMMAP, "Ashmem returned error: 0x%08x", ret)do { { if (LogTypes::LNOTICE <= 3) GenericLog(LogTypes::LNOTICE , LogTypes::MEMMAP, "/home/anal/dolphin-emu/Source/Core/Common/Src/MemArena.cpp" , 46, "Ashmem returned error: 0x%08x", ret); } } while (0); | |||
| 47 | return ret; | |||
| 48 | } | |||
| 49 | return fd; | |||
| 50 | } | |||
| 51 | #endif | |||
| 52 | ||||
| 53 | void MemArena::GrabLowMemSpace(size_t size) | |||
| 54 | { | |||
| 55 | #ifdef _WIN32 | |||
| 56 | hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL__null, PAGE_READWRITE, 0, (DWORD)(size), NULL__null); | |||
| 57 | #elif defined(ANDROID) | |||
| 58 | fd = AshmemCreateFileMapping("Dolphin-emu", size); | |||
| 59 | if (fd < 0) | |||
| 60 | { | |||
| 61 | NOTICE_LOG(MEMMAP, "Ashmem allocation failed")do { { if (LogTypes::LNOTICE <= 3) GenericLog(LogTypes::LNOTICE , LogTypes::MEMMAP, "/home/anal/dolphin-emu/Source/Core/Common/Src/MemArena.cpp" , 61, "Ashmem allocation failed"); } } while (0); | |||
| 62 | return; | |||
| 63 | } | |||
| 64 | #else | |||
| 65 | mode_t mode = S_IRUSR0400 | S_IWUSR0200 | S_IRGRP(0400 >> 3) | S_IROTH((0400 >> 3) >> 3); | |||
| 66 | fd = open(ram_temp_file, O_RDWR02 | O_CREAT0100, mode); | |||
| 67 | unlink(ram_temp_file); | |||
| 68 | if (ftruncate(fd, size) < 0) | |||
| 69 | ERROR_LOG(MEMMAP, "Failed to allocate low memory space")do { { if (LogTypes::LERROR <= 3) GenericLog(LogTypes::LERROR , LogTypes::MEMMAP, "/home/anal/dolphin-emu/Source/Core/Common/Src/MemArena.cpp" , 69, "Failed to allocate low memory space"); } } while (0); | |||
| 70 | return; | |||
| 71 | #endif | |||
| 72 | } | |||
| 73 | ||||
| 74 | ||||
| 75 | void MemArena::ReleaseSpace() | |||
| 76 | { | |||
| 77 | #ifdef _WIN32 | |||
| 78 | CloseHandle(hMemoryMapping); | |||
| 79 | hMemoryMapping = 0; | |||
| 80 | #else | |||
| 81 | close(fd); | |||
| 82 | #endif | |||
| 83 | } | |||
| 84 | ||||
| 85 | ||||
| 86 | void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| 87 | { | |||
| 88 | #ifdef _WIN32 | |||
| 89 | return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base); | |||
| 90 | #else | |||
| 91 | void *retval = mmap( | |||
| 92 | base, size, | |||
| 93 | PROT_READ0x1 | PROT_WRITE0x2, | |||
| 94 | MAP_SHARED0x01 | ((base == nullptr) ? 0 : MAP_FIXED0x10), | |||
| 95 | fd, offset); | |||
| 96 | ||||
| 97 | if (retval == MAP_FAILED((void *) -1)) | |||
| 98 | { | |||
| 99 | NOTICE_LOG(MEMMAP, "mmap on %s failed", ram_temp_file)do { { if (LogTypes::LNOTICE <= 3) GenericLog(LogTypes::LNOTICE , LogTypes::MEMMAP, "/home/anal/dolphin-emu/Source/Core/Common/Src/MemArena.cpp" , 99, "mmap on %s failed", ram_temp_file); } } while (0); | |||
| 100 | return nullptr; | |||
| 101 | } | |||
| 102 | else | |||
| 103 | { | |||
| 104 | return retval; | |||
| 105 | } | |||
| 106 | #endif | |||
| 107 | } | |||
| 108 | ||||
| 109 | ||||
| 110 | void MemArena::ReleaseView(void* view, size_t size) | |||
| 111 | { | |||
| 112 | #ifdef _WIN32 | |||
| 113 | UnmapViewOfFile(view); | |||
| 114 | #else | |||
| 115 | munmap(view, size); | |||
| 116 | #endif | |||
| 117 | } | |||
| 118 | ||||
| 119 | ||||
| 120 | u8* MemArena::Find4GBBase() | |||
| 121 | { | |||
| 122 | #ifdef _M_X641 | |||
| 123 | #ifdef _WIN32 | |||
| 124 | // 64 bit | |||
| 125 | u8* base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE); | |||
| 126 | VirtualFree(base, 0, MEM_RELEASE); | |||
| 127 | return base; | |||
| 128 | #else | |||
| 129 | // Very precarious - mmap cannot return an error when trying to map already used pages. | |||
| 130 | // This makes the Windows approach above unusable on Linux, so we will simply pray... | |||
| 131 | return reinterpret_cast<u8*>(0x2300000000ULL); | |||
| 132 | #endif | |||
| 133 | ||||
| 134 | #else | |||
| 135 | // 32 bit | |||
| 136 | #ifdef _WIN32 | |||
| 137 | // The highest thing in any 1GB section of memory space is the locked cache. We only need to fit it. | |||
| 138 | u8* base = (u8*)VirtualAlloc(0, 0x31000000, MEM_RESERVE, PAGE_READWRITE); | |||
| 139 | if (base) { | |||
| 140 | VirtualFree(base, 0, MEM_RELEASE); | |||
| 141 | } | |||
| 142 | return base; | |||
| 143 | #else | |||
| 144 | #ifdef ANDROID | |||
| 145 | const u32 MemSize = 0x04000000; | |||
| 146 | #else | |||
| 147 | const u32 MemSize = 0x31000000; | |||
| 148 | #endif | |||
| 149 | void* base = mmap(0, MemSize, PROT_NONE0x0, MAP_ANON0x20 | MAP_PRIVATE0x02, -1, 0); | |||
| 150 | if (base == MAP_FAILED((void *) -1)) { | |||
| 151 | PanicAlert("Failed to map 1 GB of memory space: %s", strerror(errno))MsgAlert(false, WARNING, "Failed to map 1 GB of memory space: %s" , strerror((*__errno_location ()))); | |||
| 152 | return 0; | |||
| 153 | } | |||
| 154 | munmap(base, 0x31000000); | |||
| 155 | return static_cast<u8*>(base); | |||
| 156 | #endif | |||
| 157 | #endif | |||
| 158 | } | |||
| 159 | ||||
| 160 | ||||
| 161 | // yeah, this could also be done in like two bitwise ops... | |||
| 162 | #define SKIP(a_flags, b_flags)if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY )) continue; if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) continue; \ | |||
| 163 | if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) \ | |||
| 164 | continue; \ | |||
| 165 | if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) \ | |||
| 166 | continue; \ | |||
| 167 | ||||
| 168 | ||||
| 169 | static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) { | |||
| 170 | // OK, we know where to find free space. Now grab it! | |||
| 171 | // We just mimic the popular BAT setup. | |||
| 172 | u32 position = 0; | |||
| 173 | u32 last_position = 0; | |||
| 174 | ||||
| 175 | // Zero all the pointers to be sure. | |||
| 176 | for (int i = 0; i < num_views; i++) | |||
| 177 | { | |||
| 178 | if (views[i].out_ptr_low) | |||
| 179 | *views[i].out_ptr_low = 0; | |||
| 180 | if (views[i].out_ptr) | |||
| 181 | *views[i].out_ptr = 0; | |||
| 182 | } | |||
| 183 | ||||
| 184 | int i; | |||
| 185 | for (i = 0; i < num_views; i++) | |||
| 186 | { | |||
| 187 | SKIP(flags, views[i].flags)if (!(flags & MV_WII_ONLY) && (views[i].flags & MV_WII_ONLY)) continue; if (!(flags & MV_FAKE_VMEM) && (views[i].flags & MV_FAKE_VMEM)) continue;; | |||
| 188 | if (views[i].flags & MV_MIRROR_PREVIOUS) { | |||
| 189 | position = last_position; | |||
| 190 | } else { | |||
| 191 | *(views[i].out_ptr_low) = (u8*)arena->CreateView(position, views[i].size); | |||
| 192 | if (!*views[i].out_ptr_low) | |||
| 193 | goto bail; | |||
| 194 | } | |||
| 195 | #ifdef _M_X641 | |||
| 196 | *views[i].out_ptr = (u8*)arena->CreateView( | |||
| ||||
| 197 | position, views[i].size, base + views[i].virtual_address); | |||
| 198 | #else | |||
| 199 | if (views[i].flags & MV_MIRROR_PREVIOUS) { | |||
| 200 | // No need to create multiple identical views. | |||
| 201 | *views[i].out_ptr = *views[i - 1].out_ptr; | |||
| 202 | } else { | |||
| 203 | *views[i].out_ptr = (u8*)arena->CreateView( | |||
| 204 | position, views[i].size, base + (views[i].virtual_address & 0x3FFFFFFF)); | |||
| 205 | if (!*views[i].out_ptr) | |||
| 206 | goto bail; | |||
| 207 | } | |||
| 208 | #endif | |||
| 209 | last_position = position; | |||
| 210 | position += views[i].size; | |||
| 211 | } | |||
| 212 | ||||
| 213 | return true; | |||
| 214 | ||||
| 215 | bail: | |||
| 216 | // Argh! ERROR! Free what we grabbed so far so we can try again. | |||
| 217 | for (int j = 0; j <= i; j++) | |||
| 218 | { | |||
| 219 | SKIP(flags, views[i].flags)if (!(flags & MV_WII_ONLY) && (views[i].flags & MV_WII_ONLY)) continue; if (!(flags & MV_FAKE_VMEM) && (views[i].flags & MV_FAKE_VMEM)) continue;; | |||
| 220 | if (views[j].out_ptr_low && *views[j].out_ptr_low) | |||
| 221 | { | |||
| 222 | arena->ReleaseView(*views[j].out_ptr_low, views[j].size); | |||
| 223 | *views[j].out_ptr_low = NULL__null; | |||
| 224 | } | |||
| 225 | if (*views[j].out_ptr) | |||
| 226 | { | |||
| 227 | #ifdef _M_X641 | |||
| 228 | arena->ReleaseView(*views[j].out_ptr, views[j].size); | |||
| 229 | #else | |||
| 230 | if (!(views[j].flags & MV_MIRROR_PREVIOUS)) | |||
| 231 | { | |||
| 232 | arena->ReleaseView(*views[j].out_ptr, views[j].size); | |||
| 233 | } | |||
| 234 | #endif | |||
| 235 | *views[j].out_ptr = NULL__null; | |||
| 236 | } | |||
| 237 | } | |||
| 238 | return false; | |||
| 239 | } | |||
| 240 | ||||
| 241 | u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena) | |||
| 242 | { | |||
| 243 | u32 total_mem = 0; | |||
| 244 | int base_attempts = 0; | |||
| 245 | ||||
| 246 | for (int i = 0; i < num_views; i++) | |||
| ||||
| 247 | { | |||
| 248 | SKIP(flags, views[i].flags)if (!(flags & MV_WII_ONLY) && (views[i].flags & MV_WII_ONLY)) continue; if (!(flags & MV_FAKE_VMEM) && (views[i].flags & MV_FAKE_VMEM)) continue;; | |||
| 249 | if ((views[i].flags & MV_MIRROR_PREVIOUS) == 0) | |||
| 250 | total_mem += views[i].size; | |||
| 251 | } | |||
| 252 | // Grab some pagefile backed memory out of the void ... | |||
| 253 | arena->GrabLowMemSpace(total_mem); | |||
| 254 | ||||
| 255 | // Now, create views in high memory where there's plenty of space. | |||
| 256 | #ifdef _M_X641 | |||
| 257 | u8 *base = MemArena::Find4GBBase(); | |||
| 258 | // This really shouldn't fail - in 64-bit, there will always be enough | |||
| 259 | // address space. | |||
| 260 | if (!Memory_TryBase(base, views, num_views, flags, arena)) | |||
| 261 | { | |||
| 262 | PanicAlert("MemoryMap_Setup: Failed finding a memory base.")MsgAlert(false, WARNING, "MemoryMap_Setup: Failed finding a memory base." ); | |||
| 263 | exit(0); | |||
| 264 | return 0; | |||
| 265 | } | |||
| 266 | #else | |||
| 267 | #ifdef _WIN32 | |||
| 268 | // Try a whole range of possible bases. Return once we got a valid one. | |||
| 269 | u32 max_base_addr = 0x7FFF0000 - 0x31000000; | |||
| 270 | u8 *base = NULL__null; | |||
| 271 | ||||
| 272 | for (u32 base_addr = 0x40000; base_addr < max_base_addr; base_addr += 0x40000) | |||
| 273 | { | |||
| 274 | base_attempts++; | |||
| 275 | base = (u8 *)base_addr; | |||
| 276 | if (Memory_TryBase(base, views, num_views, flags, arena)) | |||
| 277 | { | |||
| 278 | INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO , LogTypes::MEMMAP, "/home/anal/dolphin-emu/Source/Core/Common/Src/MemArena.cpp" , 278, "Found valid memory base at %p after %i tries.", base, base_attempts); } } while (0); | |||
| 279 | base_attempts = 0; | |||
| 280 | break; | |||
| 281 | } | |||
| 282 | ||||
| 283 | } | |||
| 284 | #else | |||
| 285 | // Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors. | |||
| 286 | u8 *base = MemArena::Find4GBBase(); | |||
| 287 | if (!Memory_TryBase(base, views, num_views, flags, arena)) | |||
| 288 | { | |||
| 289 | PanicAlert("MemoryMap_Setup: Failed finding a memory base.")MsgAlert(false, WARNING, "MemoryMap_Setup: Failed finding a memory base." ); | |||
| 290 | exit(0); | |||
| 291 | return 0; | |||
| 292 | } | |||
| 293 | #endif | |||
| 294 | ||||
| 295 | #endif | |||
| 296 | if (base_attempts) | |||
| 297 | PanicAlert("No possible memory base pointer found!")MsgAlert(false, WARNING, "No possible memory base pointer found!" ); | |||
| 298 | return base; | |||
| 299 | } | |||
| 300 | ||||
| 301 | void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena) | |||
| 302 | { | |||
| 303 | for (int i = 0; i < num_views; i++) | |||
| 304 | { | |||
| 305 | SKIP(flags, views[i].flags)if (!(flags & MV_WII_ONLY) && (views[i].flags & MV_WII_ONLY)) continue; if (!(flags & MV_FAKE_VMEM) && (views[i].flags & MV_FAKE_VMEM)) continue;; | |||
| 306 | if (views[i].out_ptr_low && *views[i].out_ptr_low) | |||
| 307 | arena->ReleaseView(*views[i].out_ptr_low, views[i].size); | |||
| 308 | if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low)) | |||
| 309 | arena->ReleaseView(*views[i].out_ptr, views[i].size); | |||
| 310 | *views[i].out_ptr = NULL__null; | |||
| 311 | if (views[i].out_ptr_low) | |||
| 312 | *views[i].out_ptr_low = NULL__null; | |||
| 313 | } | |||
| 314 | } |