Skip to content

Commit ebfed7f

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 e366e9c commit ebfed7f

File tree

2 files changed

+151
-12
lines changed

2 files changed

+151
-12
lines changed

Shared/sdk/SharedUtil.Misc.h

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

90+
// Returns true if the pointer points to committed, readable memory
91+
bool IsReadablePointer(const void* ptr, size_t size);
92+
9093
//
9194
// Run ShellExecute with these parameters after exit
9295
//

Shared/sdk/SharedUtil.Misc.hpp

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,34 @@
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"
19-
#ifdef WIN32
20-
#include <ctime>
39+
#if defined(SHAREDUTIL_PLATFORM_WINDOWS)
2140
#include <windows.h>
41+
#include <ctime>
2242
#include <direct.h>
2343
#include <shellapi.h>
2444
#include <TlHelp32.h>
@@ -167,6 +187,121 @@ bool SharedUtil::IsGTAProcess()
167187
return strLaunchPathFilename.EndsWithI("gta_sa.exe");
168188
}
169189

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

10681203
#endif // MTA_CLIENT
10691204

1070-
#ifdef WIN32
1205+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
10711206
#define _WIN32_WINNT_WIN8 0x0602
10721207
///////////////////////////////////////////////////////////////////////////
10731208
//
@@ -1110,7 +1245,7 @@ bool SharedUtil::IsWindows8OrGreater()
11101245
{
11111246
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
11121247
}
1113-
#endif // WIN32
1248+
#endif // SHAREDUTIL_PLATFORM_WINDOWS
11141249

11151250
static uchar ToHexChar(uchar c)
11161251
{
@@ -1171,7 +1306,7 @@ SString SharedUtil::EscapeURLArgument(const SString& strArg)
11711306
//
11721307
// Cross platform critical section
11731308
//
1174-
#ifdef WIN32
1309+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
11751310

11761311
SharedUtil::CCriticalSection::CCriticalSection()
11771312
{
@@ -1232,7 +1367,7 @@ void SharedUtil::RandomizeRandomSeed()
12321367
srand(rand() + GetTickCount32());
12331368
}
12341369

1235-
#ifdef WIN32
1370+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
12361371
static LONG SafeNtQueryInformationThread(HANDLE ThreadHandle, INT ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength,
12371372
PULONG ReturnLength)
12381373
{
@@ -1358,7 +1493,7 @@ DWORD SharedUtil::GetMainThreadId()
13581493
//
13591494
bool SharedUtil::IsMainThread()
13601495
{
1361-
#ifdef WIN32
1496+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
13621497
DWORD mainThreadID = GetMainThreadId();
13631498
DWORD currentThreadID = GetCurrentThreadId();
13641499
return mainThreadID == currentThreadID;
@@ -1371,7 +1506,7 @@ bool SharedUtil::IsMainThread()
13711506
//
13721507
// Expiry stuff
13731508
//
1374-
#ifdef WIN32
1509+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
13751510
#include <time.h>
13761511

13771512
int SharedUtil::GetBuildAge()
@@ -1517,6 +1652,7 @@ char* SharedUtil::Trim(char* szText)
15171652
return static_cast<char*>(memmove(szOriginal, szText, uiLen + 1));
15181653
}
15191654

1655+
15201656
// Convert a standard multibyte UTF-8 std::string into a UTF-16 std::wstring
15211657
std::wstring SharedUtil::MbUTF8ToUTF16(const SString& input)
15221658
{
@@ -1637,7 +1773,7 @@ SString SharedUtil::ConformResourcePath(const char* szRes, bool bConvertToUnixPa
16371773
char cPathSep;
16381774

16391775
// Handle which path sep char
1640-
#ifdef WIN32
1776+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
16411777
if (!bConvertToUnixPathSep)
16421778
{
16431779
cPathSep = '\\';
@@ -1820,7 +1956,7 @@ namespace SharedUtil
18201956
outList.push_back(iter->first);
18211957
}
18221958

1823-
#ifdef WIN32
1959+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
18241960
///////////////////////////////////////////////////////////////////////////
18251961
//
18261962
// GetCurrentProcessorNumberXP for the current thread, especially for Windows XP
@@ -1856,7 +1992,7 @@ namespace SharedUtil
18561992
///////////////////////////////////////////////////////////////////////////
18571993
DWORD _GetCurrentProcessorNumber()
18581994
{
1859-
#ifdef WIN32
1995+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
18601996
// Dynamically load GetCurrentProcessorNumber, as it does not exist on XP
18611997
using GetCurrentProcessorNumber_t = DWORD(WINAPI*)();
18621998

@@ -1919,7 +2055,7 @@ namespace SharedUtil
19192055
{
19202056
outUserTime = 0;
19212057
outKernelTime = 0;
1922-
#ifdef WIN32
2058+
#ifdef SHAREDUTIL_PLATFORM_WINDOWS
19232059
FILETIME CreationTime, ExitTime, KernelTime, UserTime;
19242060
if (GetThreadTimes(GetCurrentThread(), &CreationTime, &ExitTime, &KernelTime, &UserTime))
19252061
{

0 commit comments

Comments
 (0)