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 (), ®ionAddress, ®ionSize, 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
11201255static 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
11811316SharedUtil::CCriticalSection::CCriticalSection ()
11821317{
@@ -1237,7 +1372,7 @@ void SharedUtil::RandomizeRandomSeed()
12371372 srand (rand () + GetTickCount32 ());
12381373}
12391374
1240- #ifdef WIN32
1375+ #ifdef SHAREDUTIL_PLATFORM_WINDOWS
12411376static LONG SafeNtQueryInformationThread (HANDLE ThreadHandle, INT ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength,
12421377 PULONG ReturnLength)
12431378{
@@ -1363,7 +1498,7 @@ DWORD SharedUtil::GetMainThreadId()
13631498//
13641499bool 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
13821517int 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