IsBadReadPtr的最有效替代品? 我有一些VisualC++代码,它接收一个缓冲区的指针,这些数据需要用我的代码和缓冲区的长度来处理。由于我无法控制的错误,有时此指针未初始化或不适合读取而进入我的代码(即,当我尝试访问缓冲区中的数据时,它会导致崩溃)
所以,我需要在使用它之前验证这个指针。我不想使用IsBadReadPtr或IsBadWritePtr,因为每个人都认为它们有缺陷。(以谷歌为例)它们也不是线程安全的——在本例中,这可能不是一个问题,尽管线程安全的解决方案会很好 我在网上看到过一些建议,可以通过使用VirtualQuery或在异常处理程序中执行memcpy来实现这一点。但是,需要执行此检查的代码是时间敏感的,因此我需要最有效的检查,也就是100%有效的检查。任何想法都将不胜感激 我只想说清楚:我知道最好的做法是读取坏指针,让它引发异常,然后将其追溯到源代码并修复实际问题。但是,在这种情况下,错误指针来自我无法控制的Microsoft代码,因此我必须验证它们IsBadReadPtr的最有效替代品? 我有一些VisualC++代码,它接收一个缓冲区的指针,这些数据需要用我的代码和缓冲区的长度来处理。由于我无法控制的错误,有时此指针未初始化或不适合读取而进入我的代码(即,当我尝试访问缓冲区中的数据时,它会导致崩溃),c++,windows,visual-c++,memory,C++,Windows,Visual C++,Memory,所以,我需要在使用它之前验证这个指针。我不想使用IsBadReadPtr或IsBadWritePtr,因为每个人都认为它们有缺陷。(以谷歌为例)它们也不是线程安全的——在本例中,这可能不是一个问题,尽管线程安全的解决方案会很好 我在网上看到过一些建议,可以通过使用VirtualQuery或在异常处理程序中执行memcpy来实现这一点。但是,需要执行此检查的代码是时间敏感的,因此我需要最有效的检查,也就是100%有效的检查。任何想法都将不胜感激 我只想说清楚:我知道最好的做法是读取坏指针,让它引发
还要注意的是,我并不关心所指向的数据是否有效。我的代码正在寻找特定的数据模式,如果找不到,就会忽略这些数据。我只是试图防止在这个数据上运行memcpy时发生崩溃,在尝试memcpy时处理异常需要在遗留代码中更改十几个位置(但是如果我调用了类似IsBadReadPtr的东西,我只需要在一个位置更改代码).恐怕您运气不好-无法可靠地检查指针的有效性。什么Microsoft代码给了你错误的指针 线程安全的解决方案会很好 我猜只有IsBadWritePtr不是线程安全的 只是在异常处理程序中执行memcpy 这就是IsBadReadPtr正在做的事情。。。如果您在代码中这样做,那么您的代码将与IsBadReadPtr实现有相同的bug: --编辑:- 我读过的关于IsBadReadPtr的唯一问题是,错误的指针可能指向(因此您可能会意外地触摸)堆栈的保护页。也许您可以通过以下方式避免此问题(并因此安全地使用IsBadReadPtr):
- 了解进程中正在运行的线程
- 知道线程的堆栈在哪里,以及它们有多大
- 在开始调用isBadReadPtr之前,遍历每个堆栈,深入地触摸堆栈的每个页面至少一次
此外,与上述URL相关的一些注释也建议使用VirtualQuery。这些函数不好用的原因是无法可靠地解决问题 如果您正在调用的函数返回一个指向已分配内存的指针,因此它看起来有效,但它指向其他不相关的数据,并且如果您使用它将损坏您的应用程序,该怎么办 最有可能的是,您正在调用的函数实际上运行正常,并且您正在误用它。(不能保证,但通常情况就是这样。)
它是哪个函数?为什么不能调用api
AfxIsValidAddress((p),sizeof(type),FALSE)) 如果您使用的是VC++,那么我建议您使用microsoft特定的关键字\uuuu try\uu
要捕获硬件异常任何检查内存有效性的实现都要遵守使IsBadReadPtr失败的相同条件。您能否发布一个示例callstack,说明您希望在何处检查从Windows传递给您的指针的内存有效性?这可能会帮助其他人(包括我)首先诊断您为什么需要这样做。我能想到的最快解决方案是咨询虚拟内存管理器,查看给定地址是否有可读页面,并缓存结果(但是任何缓存都会降低检查的准确性)
bool IsBadReadPtr(void* p)
{
MEMORY_BASIC_INFORMATION mbi = {0};
if (::VirtualQuery(p, &mbi, sizeof(mbi)))
{
DWORD mask = (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY);
bool b = !(mbi.Protect & mask);
// check the page is not a guard page
if (mbi.Protect & (PAGE_GUARD|PAGE_NOACCESS)) b = true;
return b;
}
return true;
}
示例(不带缓存):
如果变量未初始化,则使用软管。迟早它会成为你不想玩的东西的地址(比如你自己的堆栈)
如果您认为您需要这个,并且(uintpttr_t)var<65536还不够(Windows不允许分配底部64k),那么就没有真正的解决方案。VirtualQuery等看起来“有效”,但迟早会烧死你。如果你不得不求助于检查数据中的模式,以下是一些提示:
- 如果您提到使用IsBadReadPtr,您可能是在为Windows x86或x64开发
- 您可以对指针进行范围检查。指向对象的指针将与单词对齐。在32位窗口中,用户空间指针的范围为0x00401000-0x7FFFFFFF,对于大型地址感知应用程序,则改为0x00401000-0xBFFFFFFF(对于64位窗口上的32位程序,编辑:0x00401000-0xFFFF0000)。上面的2GB/1GB是为内核空间指针保留的
- 对象本身将位于不可执行的读/写内存中。它可能存在于堆中,也可能是一个全局变量。如果它是一个全局变量,您可以验证它是否位于正确的模块中
- 如果您的对象有一个VTable,并且您没有使用其他类,请将其VTable指针与已知良好对象的另一个VTable指针进行比较
- 范围检查变量,看它们是否可能有效。例如,布尔值只能是1或0,因此如果您看到一个值为242,那么这显然是错误的。指针也可以进行范围检查和对齐检查
- 如果其中包含对象,请检查它们的vTable和数据
- 如果有指向其他对象的指针,可以检查该对象是否存在于内存中
BOOL CanRead(LPVOID p) { MEMORY_BASIC_INFORMATION mbi; mbi.Protect = 0; ::VirtualQuery(((LPCSTR)p) + len - 1, &mbi, sizeof(mbi)); return ((mbi.Protect & 0xE6) != 0 && (mbi.Protect & PAGE_GUARD) == 0); }
// Check memory address access const DWORD dwForbiddenArea = PAGE_GUARD | PAGE_NOACCESS; const DWORD dwReadRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; const DWORD dwWriteRights = PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; template<DWORD dwAccessRights> bool CheckAccess(void* pAddress, size_t nSize) { if (!pAddress || !nSize) { return false; } MEMORY_BASIC_INFORMATION sMBI; bool bRet = false; UINT_PTR pCurrentAddress = UINT_PTR(pAddress); UINT_PTR pEndAdress = pCurrentAddress + (nSize - 1); do { ZeroMemory(&sMBI, sizeof(sMBI)); VirtualQuery(LPCVOID(pCurrentAddress), &sMBI, sizeof(sMBI)); bRet = (sMBI.State & MEM_COMMIT) // memory allocated and && !(sMBI.Protect & dwForbiddenArea) // access to page allowed and && (sMBI.Protect & dwAccessRights); // the required rights pCurrentAddress = (UINT_PTR(sMBI.BaseAddress) + sMBI.RegionSize); } while (bRet && pCurrentAddress <= pEndAdress); return bRet; } #define IsBadWritePtr(p,n) (!CheckAccess<dwWriteRights>(p,n)) #define IsBadReadPtr(p,n) (!CheckAccess<dwReadRights>(p,n)) #define IsBadStringPtrW(p,n) (!CheckAccess<dwReadRights>(p,n*2))
__try { memcpy(dest, src, size); }__except(1){}