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 (), ®ionAddress, ®ionSize, 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
11151250static 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
11761311SharedUtil::CCriticalSection::CCriticalSection ()
11771312{
@@ -1232,7 +1367,7 @@ void SharedUtil::RandomizeRandomSeed()
12321367 srand (rand () + GetTickCount32 ());
12331368}
12341369
1235- #ifdef WIN32
1370+ #ifdef SHAREDUTIL_PLATFORM_WINDOWS
12361371static LONG SafeNtQueryInformationThread (HANDLE ThreadHandle, INT ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength,
12371372 PULONG ReturnLength)
12381373{
@@ -1358,7 +1493,7 @@ DWORD SharedUtil::GetMainThreadId()
13581493//
13591494bool 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
13771512int 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
15211657std::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