2009-01-30 351 views
6

我有一些Visual C++代码接收指向缓冲区的指针,需要由我的代码和该缓冲区的长度来处理数据。由于我的控制之外的错误,有时这个指针进入我的代码未初始化或不适合读取(即当我尝试访问缓冲区中的数据时,它会导致崩溃)。IsBadReadPtr最有效的替代品?

所以,我需要验证这一点指针在我使用它之前。我不想使用IsBadReadPtr或IsBadWritePtr,因为每个人都同意他们是越野车。 (谷歌他们的例子。)他们也不是线程安全的 - 这可能不是一个担心在这种情况下,虽然线程安全的解决方案会很好。

我已经看到了通过使用VirtualQuery完成此操作的网络建议,或者只是在异常处理程序中执行memcpy。但是,需要完成此项检查的代码是时间敏感的,因此我需要进行最有效的检查,这也是100%有效的。任何想法,将不胜感激。

只是要清楚:我知道最好的做法是只读错误的指针,让它引发异常,然后追溯到源代码并修复实际问题。但是,在这种情况下,坏指针来自我无法控制的Microsoft代码,所以我必须验证它们。

请注意,我不关心指出的数据是否有效。我的代码正在寻找特定的数据模式,如果没有找到它们,它们会忽略数据。我只是试图防止在这个数据上运行memcpy时发生的崩溃,并且在memcpy点试图处理异常将需要更改我的代码中的十几个地方(但是如果我有像IsBadReadPtr这样的东西来调用,我只会必须在一个地方更改代码)。

+0

你可以发布一个调用堆栈(一直到main或WinMain)吗? – MSN 2009-01-30 16:25:56

+1

我在回答中添加了一段:如果您之前触摸了所有堆栈页面,也许您可​​以安全地使用IsBadReadPtr。 – ChrisW 2009-01-30 17:41:08

+0

我不知道,使用VirtualQuery的示例代码有什么问题。一个普遍的问题是,即使VirtualQuery认为内存是好的,另一个线程可能在测试之后但在尝试读取之前释放内存:因此,在给定多线程的情况下,事先对其进行测试并不是... – ChrisW 2009-01-30 20:40:48

回答

9

线程安全的解决方案将是不错

我猜这只是IsBadWritePtr不是线程安全的。

只是做了的memcpy异常处理程序

这是什么有效的IsBadReadPtr里面做......如果你在你的代码做了,那么你的代码将具有相同的错误作为IsBadReadPtr执行:http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx

- 编辑: -

与IsBadReadPtr唯一的问题是我读过的是,坏指针可能b e指向(也可能意外碰到)堆栈的守护页。也许你能避免这个问题(因此使用IsBadReadPtr安全),通过:

  • 知道什么的线程在运行过程中
  • 知道那里的线程的堆栈是,他们有多大
  • 城下来每堆,delberately触及堆栈的每一个页面至少一次,你就开始打电话isBadReadPtr

而且之前的一些与该URL关联的评论上述还建议您使用VirtualQuery来。

-1

恐怕你运气不好 - 没有办法可靠地检查指针的有效性。微软的代码给你什么坏指针?

+0

这是Winsock 2分层服务提供商(LSP)示例代码。 – jeffm 2009-01-30 16:18:23

+0

如果您正在研究LSP,请准备好多天拉动头发。并从微软获得一份很好的开发支持合同。 – 2009-01-30 16:44:22

6

这些功能使用不好的原因是问题无法可靠解决。

如果你正在调用的函数返回一个指向已分配内存的指针,那么它看起来有效,但它指向其他不相关的数据,如果使用它,将会损坏你的应用程序。

最有可能的是,您调用的函数实际上行为正确,并且您正在滥用它。 (不保证,但这是经常是的情况。)

这是哪个功能?

1

你为什么不能调用API

AfxIsValidAddress((P)的sizeof(型),FALSE));

-1

如果您使用VC++那么我建议使用Microsoft特定关键字__try __except 并赶上HW异常

0

检查存储器的有效期内任何执行受,使IsBadReadPtr失败同样的制约性。你可以发布一个示例调用堆栈来检查从Windows传递给你的指针内存的有效性吗?这可能会帮助其他人(包括我)诊断为什么您需要首先执行此操作。

5
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; 
} 
0

我能想到的最快的解决方法是使用VirtualQuery,看看是否有可读上,在给定的地址咨询虚拟内存管理器,并缓存结果(但是任何缓存会降低精度的支票)。

实施例(无缓存):

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); 
} 
0

如果变量未初始化您正在大清洗。迟早它会成为你不想玩的东西的地址(就像你自己的堆栈)。

如果你认为你需要这个,并且(uintptr_t)var < 65536是不够的(Windows不允许分配底部的64k),但没有真正的解决方案。 VirtualQuery等似乎“工作”,但迟早会烧你。

0

如果必须诉诸数据检查模式,这里有几个技巧:

  • 如果您提及使用IsBadReadPtr,你可能正在开发用于Windows x86或x64。

  • 您可能可以检查指针的范围。指向对象的指针将被字对齐。在32位窗口中,用户空间指针的范围是0x00401000-0x7FFFFFFF,或者对于支持大地址的应用程序0x00401000-0xBFFFFFFF。高位2GB/1GB保留给内核空间指针。

  • 该对象本身将存在于不可执行的读/写内存中。它可能生活在堆中,或者它可能是一个全局变量。如果它是一个全局变量,则可以验证它是否存在于正确的模块中。

  • 如果您的对象有一个VTable,并且您没有使用其他类,请将其VTable指针与来自已知良好对象的另一个VTable指针进行比较。

  • 范围检查变量以查看它们是否可能有效。例如,bools只能是1或0,所以如果你看到一个值为242,那显然是错误的。指针也可以进行范围检查并检查是否对齐。

  • 如果内部包含对象,请检查它们的VTable和数据。

  • 如果存在指向其他对象的指针,则可以检查该对象是否存在于读/写且不可执行的内存中,检查该VTable(如果适用)以及范围检查数据。

如果没有与已知的虚函数表地址的好对象,你可以使用这些规则来检查是否有虚函数表是有效的:

  • 当对象居住在读/写内存, VTable指针是对象的一部分,VTable本身将存在于只读且不可执行的内存中,并且将与字边界对齐。它也将属于该模块。
  • VTable的条目是指向代码的指针,它将是只读和可执行的,并且不可写。代码地址没有对齐限制。代码将属于该模块。