Bug Summary

File:Source/Core/Common/Src/MemArena.cpp
Location:line 196, 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
24#if defined(__APPLE__)
25static const char* ram_temp_file = "/tmp/gc_mem.tmp";
26#elif !defined(_WIN32) // non OSX unixes
27static const char* ram_temp_file = "/dev/shm/gc_mem.tmp";
28#endif
29#ifdef ANDROID
30#define ASHMEM_DEVICE "/dev/ashmem"
31
32int 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
53void 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
75void MemArena::ReleaseSpace()
76{
77#ifdef _WIN32
78 CloseHandle(hMemoryMapping);
79 hMemoryMapping = 0;
80#else
81 close(fd);
82#endif
83}
84
85
86void *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
110void 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
120u8* 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
169static 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++)
7
Loop condition is true. Entering loop body
11
Loop condition is false. Execution continues on line 184
177 {
178 if (views[i].out_ptr_low)
8
Taking false branch
179 *views[i].out_ptr_low = 0;
180 if (views[i].out_ptr)
9
Assuming pointer value is null
10
Taking false branch
181 *views[i].out_ptr = 0;
182 }
183
184 int i;
185 for (i = 0; i < num_views; i++)
12
Loop condition is true. Entering loop body
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) {
13
Taking true branch
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(
14
Dereference of null pointer (loaded from field 'out_ptr')
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
215bail:
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
241u8 *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++)
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 253
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)
3
Taking false branch
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))
6
Calling 'Memory_TryBase'
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
301void 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}