Skip to content

Commit 65de0f2

Browse files
committed
Add SharedUtil::IsReadablePointer and touch up headers
This helper function prefers compactness over readability, and never has to change again anyways because it's perfect by design, portable, and safe.
1 parent e885340 commit 65de0f2

File tree

2 files changed

+150
-12
lines changed

2 files changed

+150
-12
lines changed

Shared/sdk/SharedUtil.Misc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ namespace SharedUtil
110110
// Returns true if current process is GTA (i.e not MTA process)
111111
bool IsGTAProcess();
112112

113+
// Returns true if the pointer points to committed, readable memory
114+
bool IsReadablePointer(const void* ptr, size_t size);
115+
113116
//
114117
// Run ShellExecute with these parameters after exit
115118
//

Shared/sdk/SharedUtil.Misc.hpp

Lines changed: 147 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,35 @@
1111

1212
#include "SharedUtil.Misc.h"
1313
#include "SharedUtil.Time.h"
14+
#include <cstdint>
15+
#include <limits>
1416
#include <map>
17+
18+
#if defined(_WIN32) || defined(WIN32)
19+
#define SHAREDUTIL_PLATFORM_WINDOWS 1
20+
#endif
21+
22+
#if defined(__linux__) || defined(LINUX_x86) || defined(LINUX_x64) || defined(LINUX_arm) || defined(LINUX_arm64)
23+
#include <fstream>
24+
#include <string>
25+
#include <sstream>
26+
#include <cerrno>
27+
#include <cstdlib>
28+
#endif
29+
30+
#if defined(__APPLE__) || defined(APPLE_x64) || defined(APPLE_arm64)
31+
#include <mach/mach.h>
32+
#include <mach/mach_vm.h>
33+
#endif
34+
1535
#include "UTF8.h"
1636
#include "UTF8Detect.hpp"
1737
#include "CDuplicateLineFilter.h"
1838
#include "version.h"
1939

20-
#ifdef WIN32
21-
#include <ctime>
40+
#if defined(SHAREDUTIL_PLATFORM_WINDOWS)
2241
#include <windows.h>
42+
#include <ctime>
2343
#include <direct.h>
2444
#include <shellapi.h>
2545
#include <TlHelp32.h>
@@ -172,6 +192,121 @@ bool SharedUtil::IsGTAProcess()
172192
return strLaunchPathFilename.EndsWithI("gta_sa.exe");
173193
}
174194

195+
bool SharedUtil::IsReadablePointer(const void* ptr, size_t size)
196+
{
197+
// Guard against null or overflow before touching platform APIs
198+
if (!ptr || size == 0) return false;
199+
200+
const uintptr_t start = reinterpret_cast<uintptr_t>(ptr);
201+
constexpr uintptr_t maxAddress = std::numeric_limits<uintptr_t>::max();
202+
if (size > maxAddress - start) return false;
203+
204+
const uintptr_t end = start + size;
205+
206+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
207+
constexpr DWORD readableMask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
208+
for (uintptr_t current = start; current < end;)
209+
{
210+
MEMORY_BASIC_INFORMATION mbi{};
211+
if (VirtualQuery(reinterpret_cast<LPCVOID>(current), &mbi, sizeof(mbi)) == 0) return false;
212+
213+
const uintptr_t regionStart = reinterpret_cast<uintptr_t>(mbi.BaseAddress);
214+
const uintptr_t regionSize = static_cast<uintptr_t>(mbi.RegionSize);
215+
const uintptr_t regionEnd = regionStart + regionSize;
216+
const DWORD protection = mbi.Protect;
217+
if (regionSize == 0 || current < regionStart || regionStart > maxAddress - regionSize || regionEnd <= current || mbi.State != MEM_COMMIT ||
218+
(protection & PAGE_GUARD) || (protection & readableMask) == 0)
219+
return false;
220+
221+
current = regionEnd;
222+
}
223+
224+
return true;
225+
#elif defined(LINUX_x86) || defined(LINUX_x64) || defined(LINUX_arm) || defined(LINUX_arm64)
226+
static_assert(sizeof(uintptr_t) <= sizeof(unsigned long long), "Unexpected uintptr_t size");
227+
228+
std::ifstream maps("/proc/self/maps");
229+
if (!maps.is_open())
230+
return false;
231+
232+
const auto parseAddress = [maxAddress](const std::string& token, uintptr_t& out) -> bool {
233+
errno = 0;
234+
char* endPtr = nullptr;
235+
unsigned long long value = std::strtoull(token.c_str(), &endPtr, 16);
236+
if (errno != 0 || endPtr == token.c_str() || *endPtr != '\0' || value > maxAddress)
237+
return false;
238+
out = static_cast<uintptr_t>(value);
239+
return true;
240+
};
241+
242+
uintptr_t coverage = start;
243+
bool coveringRange = false;
244+
for (std::string line; std::getline(maps, line);)
245+
{
246+
if (line.empty()) continue;
247+
std::istringstream iss(line);
248+
std::string range, perms;
249+
if (!(iss >> range >> perms)) continue;
250+
const size_t dashPos = range.find('-');
251+
if (dashPos == std::string::npos) continue;
252+
uintptr_t regionStart = 0;
253+
uintptr_t regionEnd = 0;
254+
if (!parseAddress(range.substr(0, dashPos), regionStart) || !parseAddress(range.substr(dashPos + 1), regionEnd)) continue;
255+
if (regionEnd <= regionStart || regionEnd <= coverage) continue;
256+
if (coveringRange)
257+
{
258+
if (regionStart > coverage) return false;
259+
}
260+
else if (regionStart > coverage || coverage >= regionEnd)
261+
{
262+
continue;
263+
}
264+
else coveringRange = true;
265+
266+
if (perms.empty() || perms[0] != 'r') return false;
267+
coverage = regionEnd;
268+
if (coverage >= end) return true;
269+
}
270+
271+
return false;
272+
#elif defined(APPLE_x64) || defined(APPLE_arm64)
273+
mach_vm_address_t queryAddress = static_cast<mach_vm_address_t>(start);
274+
const mach_vm_address_t targetEnd = static_cast<mach_vm_address_t>(end);
275+
constexpr mach_vm_address_t maxAddressMac = std::numeric_limits<mach_vm_address_t>::max();
276+
vm_region_basic_info_data_64_t info;
277+
mach_msg_type_number_t infoCount = VM_REGION_BASIC_INFO_COUNT_64;
278+
mach_port_t objectName = MACH_PORT_NULL;
279+
280+
while (queryAddress < targetEnd)
281+
{
282+
mach_vm_size_t regionSize = 0;
283+
infoCount = VM_REGION_BASIC_INFO_COUNT_64;
284+
mach_vm_address_t regionAddress = queryAddress;
285+
kern_return_t kr = mach_vm_region(mach_task_self(), &regionAddress, &regionSize, VM_REGION_BASIC_INFO_64,
286+
reinterpret_cast<vm_region_info_t>(&info), &infoCount, &objectName);
287+
if (objectName != MACH_PORT_NULL)
288+
{
289+
mach_port_deallocate(mach_task_self(), objectName);
290+
objectName = MACH_PORT_NULL;
291+
}
292+
293+
if (kr != KERN_SUCCESS || regionSize == 0 || queryAddress < regionAddress || (info.protection & VM_PROT_READ) == 0 ||
294+
regionAddress > maxAddressMac - static_cast<mach_vm_address_t>(regionSize))
295+
return false;
296+
297+
const mach_vm_address_t regionEnd = regionAddress + static_cast<mach_vm_address_t>(regionSize);
298+
if (regionEnd <= queryAddress)
299+
return false;
300+
301+
queryAddress = regionEnd;
302+
}
303+
304+
return true;
305+
#else
306+
return false;
307+
#endif
308+
}
309+
175310
//
176311
// Write a registry string value
177312
//
@@ -1072,7 +1207,7 @@ bool SharedUtil::ShellExecuteNonBlocking(const SString& strAction, const SString
10721207

10731208
#endif // MTA_CLIENT
10741209

1075-
#ifdef WIN32
1210+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
10761211
#define _WIN32_WINNT_WIN8 0x0602
10771212
///////////////////////////////////////////////////////////////////////////
10781213
//
@@ -1115,7 +1250,7 @@ bool SharedUtil::IsWindows8OrGreater()
11151250
{
11161251
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
11171252
}
1118-
#endif // WIN32
1253+
#endif // SHAREDUTIL_PLATFORM_WINDOWS
11191254

11201255
static uchar ToHexChar(uchar c)
11211256
{
@@ -1176,7 +1311,7 @@ SString SharedUtil::EscapeURLArgument(const SString& strArg)
11761311
//
11771312
// Cross platform critical section
11781313
//
1179-
#ifdef WIN32
1314+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
11801315

11811316
SharedUtil::CCriticalSection::CCriticalSection()
11821317
{
@@ -1237,7 +1372,7 @@ void SharedUtil::RandomizeRandomSeed()
12371372
srand(rand() + GetTickCount32());
12381373
}
12391374

1240-
#ifdef WIN32
1375+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
12411376
static LONG SafeNtQueryInformationThread(HANDLE ThreadHandle, INT ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength,
12421377
PULONG ReturnLength)
12431378
{
@@ -1363,7 +1498,7 @@ DWORD SharedUtil::GetMainThreadId()
13631498
//
13641499
bool SharedUtil::IsMainThread()
13651500
{
1366-
#ifdef WIN32
1501+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
13671502
DWORD mainThreadID = GetMainThreadId();
13681503
DWORD currentThreadID = GetCurrentThreadId();
13691504
return mainThreadID == currentThreadID;
@@ -1376,7 +1511,7 @@ bool SharedUtil::IsMainThread()
13761511
//
13771512
// Expiry stuff
13781513
//
1379-
#ifdef WIN32
1514+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
13801515
#include <time.h>
13811516

13821517
int SharedUtil::GetBuildAge()
@@ -1654,7 +1789,7 @@ SString SharedUtil::ConformResourcePath(const char* szRes, bool bConvertToUnixPa
16541789
char cPathSep;
16551790

16561791
// Handle which path sep char
1657-
#ifdef WIN32
1792+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
16581793
if (!bConvertToUnixPathSep)
16591794
{
16601795
cPathSep = '\\';
@@ -1837,7 +1972,7 @@ namespace SharedUtil
18371972
outList.push_back(iter->first);
18381973
}
18391974

1840-
#ifdef WIN32
1975+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
18411976
///////////////////////////////////////////////////////////////////////////
18421977
//
18431978
// GetCurrentProcessorNumberXP for the current thread, especially for Windows XP
@@ -1873,7 +2008,7 @@ namespace SharedUtil
18732008
///////////////////////////////////////////////////////////////////////////
18742009
DWORD _GetCurrentProcessorNumber()
18752010
{
1876-
#ifdef WIN32
2011+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
18772012
// Dynamically load GetCurrentProcessorNumber, as it does not exist on XP
18782013
using GetCurrentProcessorNumber_t = DWORD(WINAPI*)();
18792014

@@ -1936,7 +2071,7 @@ namespace SharedUtil
19362071
{
19372072
outUserTime = 0;
19382073
outKernelTime = 0;
1939-
#ifdef WIN32
2074+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
19402075
FILETIME CreationTime, ExitTime, KernelTime, UserTime;
19412076
if (GetThreadTimes(GetCurrentThread(), &CreationTime, &ExitTime, &KernelTime, &UserTime))
19422077
{

0 commit comments

Comments
 (0)