Bug Summary

File:/home/anal/dolphin-emu/Source/Core/Common/Src/MemArena.cpp
Location:line 196, 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#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++)
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 189
182 {
183 if (views[i].out_ptr_low)
14
Taking false branch
17
Taking false branch
20
Assuming pointer value is null
21
Taking false branch
184 *views[i].out_ptr_low = 0;
185 if (views[i].out_ptr)
15
Taking true branch
18
Taking true branch
22
Taking false branch
186 *views[i].out_ptr = 0;
187 }
188
189 int i;
190 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
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) {
25
Taking true branch
27
Taking true branch
29
Taking false branch
194 position = last_position;
195 } else {
196 *(views[i].out_ptr_low) = (u8*)arena->CreateView(position, views[i].size);
30
Dereference of null pointer (loaded from field 'out_ptr_low')
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
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 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 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
6
Taking false branch
9
Taking true 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))
12
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}