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