Bug Summary

File:Source/Core/Common/Src/MemArena.cpp
Location:line 191, column 28
Description:Dereference of null pointer (loaded from field 'out_ptr_low')

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++)
13
Loop condition is true. Entering loop body
16
Loop condition is true. Entering loop body
19
Loop condition is true. Entering loop body
23
Loop condition is false. Execution continues on line 184
177 {
178 if (views[i].out_ptr_low)
14
Taking false branch
17
Taking false branch
20
Assuming pointer value is null
21
Taking false branch
179 *views[i].out_ptr_low = 0;
180 if (views[i].out_ptr)
15
Taking true branch
18
Taking true branch
22
Taking false branch
181 *views[i].out_ptr = 0;
182 }
183
184 int i;
185 for (i = 0; i < num_views; i++)
24
Loop condition is true. Entering loop body
26
Loop condition is true. Entering loop body
28
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) {
25
Taking true branch
27
Taking true branch
29
Taking false branch
189 position = last_position;
190 } else {
191 *(views[i].out_ptr_low) = (u8*)arena->CreateView(position, views[i].size);
30
Dereference of null pointer (loaded from field 'out_ptr_low')
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
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 true. Entering loop body
7
Assuming 'i' is < 'num_views'
8
Loop condition is true. Entering loop body
10
Assuming 'i' is >= 'num_views'
11
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
6
Taking false branch
9
Taking true 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))
12
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}