windows IsBadReadPtr 的最有效替代品?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/496034/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 11:54:43  来源:igfitidea点击:

Most efficient replacement for IsBadReadPtr?

c++windowsvisual-c++memory

提问by jeffm

I have some Visual C++ code that receives a pointer to a buffer with data that needs to be processed by my code and the length of that buffer. Due to a bug outside my control, sometimes this pointer comes into my code uninitialized or otherwise unsuitable for reading (i.e. it causes a crash when I try to access the data in the buffer.)

我有一些 Visual C++ 代码,它接收一个指向缓冲区的指针,该缓冲区包含需要由我的代码处理的数据和该缓冲区的长度。由于我无法控制的错误,有时这个指针会在未初始化或不适合读取的情况下进入我的代码(即,当我尝试访问缓冲区中的数据时,它会导致崩溃。)

So, I need to verify this pointer before I use it. I don't want to use IsBadReadPtr or IsBadWritePtr because everyone agrees that they're buggy. (Google them for examples.) They're also not thread-safe -- that's probably not a concern in this case, though a thread-safe solution would be nice.

所以,我需要在使用它之前验证这个指针。我不想使用 IsBadReadPtr 或 IsBadWritePtr,因为每个人都认为它们有问题。(谷歌他们的例子。)他们也不是线程安全的——在这种情况下这可能不是问题,尽管线程安全的解决方案会很好。

I've seen suggestions on the net of accomplishing this by using VirtualQuery, or by just doing a memcpy inside an exception handler. However, the code where this check needs to be done is time sensitive so I need the most efficient check possible that is also 100% effective. Any ideas would be appreciated.

我在网上看到了通过使用 VirtualQuery 或仅在异常处理程序中执行 memcpy 来完成此操作的建议。但是,需要执行此检查的代码对时间很敏感,因此我需要尽可能高效且 100% 有效的检查。任何想法,将不胜感激。

Just to be clear: I know that the best practice would be to just read the bad pointer, let it cause an exception, then trace that back to the source and fix the actual problem. However, in this case the bad pointers are coming from Microsoft code that I don't have control over so I have to verify them.

澄清一下:我知道最好的做法是只读取坏指针,让它引发异常,然后追溯到源头并解决实际问题。但是,在这种情况下,坏指针来自我无法控制的 Microsoft 代码,因此我必须验证它们。

Note also that I don't care if the data pointed at is valid. My code is looking for specific data patterns and will ignore the data if it doesn't find them. I'm just trying to prevent the crash that occurs when running memcpy on this data, and handling the exception at the point memcpy is attempted would require changing a dozen places in legacy code (but if I had something like IsBadReadPtr to call I would only have to change code in one place).

另请注意,我不在乎指向的数据是否有效。我的代码正在寻找特定的数据模式,如果没有找到它们,将忽略这些数据。我只是想防止在对这些数据运行 memcpy 时发生崩溃,并且在尝试 memcpy 时处理异常需要更改遗留代码中的十几个地方(但如果我有类似 IsBadReadPtr 的东西要调用,我只会必须在一处更改代码)。

回答by constm

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

回答by ChrisW

a thread-safe solution would be nice

线程安全的解决方案会很好

I'm guessing it's only IsBadWritePtr that isn't thread-safe.

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

just doing a memcpy inside an exception handler

只是在异常处理程序中做一个 memcpy

This is effectively what IsBadReadPtr is doing ... and if you did it in your code, then your code would have the same bug as the IsBadReadPtr implementation: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx

这实际上是 IsBadReadPtr 正在做的事情……如果您在代码中这样做,那么您的代码将与 IsBadReadPtr 实现具有相同的错误:http: //blogs.msdn.com/oldnewthing/archive/2006/09/ 27/773741.aspx

--Edit:--

- 编辑: -

The only problem with IsBadReadPtr that I've read about is that the bad pointer might be pointing to (and so you might accidentally touch) a stack's guard page. Perhaps you could avoid this problem (and therefore use IsBadReadPtr safely), by:

我读过的 IsBadReadPtr 的唯一问题是坏指针可能指向(因此您可能会不小心触及)堆栈的保护页。也许您可以通过以下方式避免此问题(因此可以安全地使用 IsBadReadPtr):

  • Know what threads are running in your process
  • Know where the threads' stacks are, and how big they are
  • Walk down each stack, delberately touching each page of the stack at least once, before you begin to call isBadReadPtr
  • 了解您的进程中正在运行哪些线程
  • 知道线程的堆栈在哪里,以及它们有多大
  • 在开始调用 isBadReadPtr 之前,走下每个堆栈,故意触摸堆栈的每一页至少一次

Also, the some of the comments associated with the URL above also suggest using VirtualQuery.

此外,与上述 URL 相关的一些评论也建议使用 VirtualQuery。

回答by jalf

The reason these functions are bad to use is that the problem can't be solved reliably.

这些函数不好用的原因是问题不能可靠解决。

What if the function you're calling returns a pointer to memory that is allocated, so it looksvalid, but it's pointing to other, unrelated data, and will corrupt your application if you use it.

如果您调用的函数返回一个指向已分配内存的指针,它看起来有效,但它指向其他不相关的数据,并且如果您使用它会损坏您的应用程序,该怎么办。

Most likely, the function you're calling actually behaves correctly, and you are misusing it. (Not guaranteed, but that is oftenthe case.)

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

Which function is it?

是哪个功能?

回答by Vinay

Why can't you call the api

为什么不能调用api

AfxIsValidAddress((p), sizeof(type), FALSE));

AfxIsValidAddress((p), sizeof(type), FALSE));

回答by rustyx

The fastest solution I can think of is to consult the virtual memory manager using VirtualQueryto see if there is a readable page at the given address, and cache the results(however any caching will reduce the accuracy of the check).

我能想到的最快的解决方案是使用虚拟内存管理器VirtualQuery查看给定地址处是否有可读页面,并缓存结果(但是任何缓存都会降低检查的准确性)。

Example (without caching):

示例(无缓存):

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

回答by Joshua

If the variable is uninitialized you are hosed. Sooner or later it's going to be an address for something you don't want to play with (like your own stack).

如果变量未初始化,您将被灌输。迟早它会成为你不想玩的东西的地址(比如你自己的堆栈)。

If you think you need this, and (uintptr_t)var < 65536 does not suffice (Windows does not allow allocating the bottom 64k), there is no real solution. VirtualQuery, etc. appear to "work" but sooner or later will burn you.

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

回答by Dwedit

If you have to resort to checking patterns in data, here are a few tips:

如果您不得不求助于检查数据中的模式,这里有一些提示:

  • If you mention using IsBadReadPtr, you are probably developing for Windows x86 or x64.

  • You may be able to range check the pointer. Pointers to objects will be word aligned. In 32-bit windows, user-space pointers are in the range of 0x00401000-0x7FFFFFFF, or for large-address-aware applications, 0x00401000-0xBFFFFFFF instead. The upper 2GB/1GB is reserved for kernel-space pointers.

  • The object itself will live in Read/Write memory which is not executable. It may live in the heap, or it may be a global variable. If it is a global variable, you can validate that it lives in the correct module.

  • If your object has a VTable, and you are not using other classes, compare its VTable pointer with another VTable pointer from a known good object.

  • Range check the variables to see if they are possibly valid. For example, bools can only be 1 or 0, so if you see one with a value of 242, that's obviously wrong. Pointers can also be range checked and checked for alignment as well.

  • If there are objects contained within, check their VTables and data as well.

  • If there are pointers to other objects, you can check that the object lives in memory that is Read/Write and not executable, check the VTable if applicable, and range check the data as well.
  • 如果您提到使用 IsBadReadPtr,则您可能正在为 Windows x86 或 x64 进行开发。

  • 您也许可以对指针进行范围检查。指向对象的指针将按字对齐。在 32 位窗口中,用户空间指针在 0x00401000-0x7FFFFFFF 的范围内,或者对于大地址感知应用程序,则为 0x00401000-0xBFFFFFFF。上面的 2GB/1GB 是为内核空间指针保留的。

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

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

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

  • 如果其中包含对象,还要检查它们的 VTable 和数据。

  • 如果有指向其他对象的指针,您可以检查该对象是否位于可读/可写且不可执行的内存中,如果适用,请检查 VTable,并对数据进行范围检查。

If you do not have a good object with a known VTable address, you can use these rules to check if a VTable is valid:

如果您没有已知 VTable 地址的好对象,您可以使用这些规则来检查 VTable 是否有效:

  • While the object lives in Read/Write memory, and the VTable pointer is part of the object, the VTable itself will live in memory that is Read Only and not executable, and will be aligned to a word boundary. It will also belong to the module.
  • The entries of the VTable are pointers to code, which will be Read Only and Executable, and not writable. There is no alignment restrictions for code addresses. Code will belong to the module.
  • 虽然对象位于读/写内存中,并且 VTable 指针是对象的一部分,但 VTable 本身将位于只读且不可执行的内存中,并将与字边界对齐。它也将属于该模块。
  • VTable 的条目是指向代码的指针,它们将是只读和可执行的,不可写。代码地址没有对齐限制。代码将属于该模块。

回答by MSN

Any implementation of checking the validity of memory is subject to the same constriants that make IsBadReadPtr fail. Can you post an example callstack of where you want to check the validity of memory of a pointer passed to you from Windows? That might help other people (including me) diagnose why you need to do this in the first place.

任何检查内存有效性的实现都受到使 IsBadReadPtr 失败的相同约束。您能否发布一个示例调用堆栈,说明您要检查从 Windows 传递给您的指针的内存有效性的位置?这可能会帮助其他人(包括我)首先诊断出为什么需要这样做。

回答by Nemanja Trifunovic

I am afraid you are out of luck - there is no way to reliably check the validity of a pointer. What Microsoft code is giving you bad pointers?

恐怕您不走运 - 没有办法可靠地检查指针的有效性。哪些 Microsoft 代码给了您错误的指示?

回答by Ahmed Said

if you are using VC++ then I suggest to use microsoft specific keywords __try __except to and catch HW exceptions

如果您使用的是 VC++,那么我建议使用微软特定的关键字 __try __except 来捕获硬件异常