Bug Summary

File:/home/anal/dolphin-emu/Source/Core/Common/Src/MemArena.cpp
Location:line 201, column 21
Description:Dereference of null pointer (loaded from field 'out_ptr')

Annotated Source Code

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__)
26static const char* ram_temp_file = "/tmp/gc_mem.tmp";
27#elif !defined(_WIN32) // non OSX unixes
28static const char* ram_temp_file = "/dev/shm/gc_mem.tmp";
29#endif
30#ifdef ANDROID
31#define ASHMEM_DEVICE "/dev/ashmem"
32
33int 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
54void 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
76void MemArena::ReleaseSpace()
77{
78#ifdef _WIN32
79 CloseHandle(hMemoryMapping);
80 hMemoryMapping = 0;
81#else
82 close(fd);
83#endif
84}
85
86
87void *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
111void 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
121u8* 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
174static 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++)
7
Loop condition is true. Entering loop body
11
Loop condition is false. Execution continues on line 189
182 {
183 if (views[i].out_ptr_low)
8
Taking false branch
184 *views[i].out_ptr_low = 0;
185 if (views[i].out_ptr)
9
Assuming pointer value is null
10
Taking false branch
186 *views[i].out_ptr = 0;
187 }
188
189 int i;
190 for (i = 0; i < num_views; i++)
12
Loop condition is true. Entering loop body
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) {
13
Taking true branch
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(
14
Dereference of null pointer (loaded from field 'out_ptr')
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
220bail:
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
226u8 *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++)
1
Assuming 'i' is < 'num_views'
2
Loop condition is true. Entering loop body
4
Assuming 'i' is >= 'num_views'
5
Loop condition is false. Execution continues on line 238
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)
3
Taking false branch
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))
6
Calling 'Memory_TryBase'
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
286void 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}