2009-01-30 102 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的條目是指向代碼的指針,它將是隻讀和可執行的,並且不可寫。代碼地址沒有對齊限制。代碼將屬於該模塊。