2009-10-23 171 views
6

發生了什麼是我正在讀取加密數據包,並且我遇到了一個損壞的數據包,它給出了一個長度非常大的隨機數。vector.resize函數在尺寸太大時破壞內存

size_t nLengthRemaining = packet.nLength - (packet.m_pSource->GetPosition() - packet.nDataOffset); 

seckey.SecretValues.m_data.resize(nLengthRemaining); 

在此代碼中,m_data是std::vector<unsigned char>。由於數據包損壞,nLengthRemaining過大,因此調整大小功能。問題不是調整大小拋出(我們處理異常),但調整大小已經損壞了內存,這導致更多的例外。

我想要做的是知道在調用調整大小之前長度是否過大,如果沒有問題,只調用調整大小。我試圖把這個代碼的調用之前調整:

std::vector<unsigned char>::size_type nMaxSize = seckey.SecretValues.m_data.max_size(); 
if(seckey.SecretValues.m_data.size() + nLengthRemaining >= nMaxSize) { 
    throw IHPGP::PgpException("corrupted packet: length too big."); 
} 
seckey.SecretValues.m_data.resize(nLengthRemaining); 

該代碼使用的std ::向量MAX_SIZE成員函數測試是否nLengthRemaining較大。儘管如此,nLengthRemaining仍然小於nMaxSize,但顯然仍然足以導致調整大小以產生問題(nMaxSize爲4xxxxxxxxx,nLengthRemaining爲3xxxxxxxxx),但這一定不可靠。

此外,我還沒有確定調整大小拋出什麼異常。它不是一個std :: length_error,它不是一個std :: bad_alloc。它拋出的異常對我來說並不重要,但我很想知道。

順便說一句,只是你知道,這段代碼在正常情況下確實能正常工作。這種數據包被破壞的情況是唯一一個瘋狂的地方。請幫忙!謝謝。

更新:

@Michael。現在,如果數據包大於5 MB,我會忽略它。我將與其他團隊成員討論可能驗證數據包(它可能已經在那裏,我只是不知道它)。我開始認爲它確實是我們STL版本中的一個bug,它拋出的異常甚至不是std :: exception,這讓我感到驚奇。我會試着從我的主管那裏瞭解我們正在運行的STL版本(我將如何檢查?)。

其他更新: 我只是證明它是我在Visual Studio 6開發機器上使用的STL版本中的一個錯誤。我寫了這個示例應用程序:

// VectorMaxSize.cpp:定義控制檯應用程序的入口點。 //

#include "stdafx.h" 
#include <vector> 
#include <iostream> 
#include <math.h> 
#include <typeinfo> 

typedef std::vector<unsigned char> vector_unsigned_char; 

void fill(vector_unsigned_char& v) { 
    for (int i=0; i<100; i++) v.push_back(i); 
} 


void oput(vector_unsigned_char& v) { 
    std::cout << "size: " << v.size() << std::endl; 
    std::cout << "capacity: " << v.capacity() << std::endl; 
    std::cout << "max_size: " << v.max_size() << std::endl << std::endl; 
} 

void main(int argc, char* argv[]) { 
    { 
     vector_unsigned_char v; 

     fill(v); 

     try{ 
      v.resize(static_cast<size_t>(3555555555)); 
     }catch(std::bad_alloc&) { 
      std::cout << "caught bad alloc exception" << std::endl; 
     }catch(const std::exception& x) { 
      std::cerr << typeid(x).name() << std::endl; 
     }catch(...) { 
      std::cerr << "unknown exception" << std::endl; 
     } 

     oput(v);  
     v.reserve(500); 
     oput(v); 
     v.resize(500); 
     oput(v); 
    } 

    std::cout << "done" << std::endl; 
} 

在我VS6 dev的機器它具有相同的行爲具有加密項目,它會導致各種混亂的。當我在Visual Studio 2008機器上構建並運行它時,調整大小會拋出std :: bad_alloc異常,並且矢量不會被破壞,就像我們預期的那樣!時間爲一些EA體育NCAA橄欖球呵呵!

+0

你在哪個平臺上? – sbi 2009-10-23 20:51:23

+0

我很好奇你使用的是什麼編譯器/ stl版本。如果分配失敗,我有權訪問的實現不會損壞矢量對象。 – jmucchiello 2009-10-23 21:34:57

+0

@cchampion:「在我的VS6開發機器上......」如果你早些時候說過,我們不會浪費這麼多時間。這是超過10年的技術!當然,它是越野車。看到我的答案。 – sbi 2009-10-25 12:15:09

回答

5

我認爲vector::max_size()幾乎總是一個'硬編碼'的東西 - 它獨立於系統/庫準備動態分配多少內存。你的問題似乎是向量實現中的一個錯誤,當分配失敗時會破壞事物。

'Bug'可能太強大了。vector::resize()是在vector::insert()來定義和標準說,這大約vector::insert()

如果異常不是由T的拷貝構造函數或賦值操作符拋出的沒有影響

如此看來就像可能有些時候允許resize()操作破壞一個向量一樣,但是如果操作是異常安全的,那麼它仍然會很好(我認爲這對於期望庫來說是不會出錯的,但也許可能這比我想象的更難)。

你似乎有一對夫婦合理的選擇:(?您正在使用什麼編譯器/庫版本)

  • 修改或更新的庫,沒有腐敗的bug
  • ,而不是檢查對vector::max_size()設置nMaxSize爲您自己的合理最大值,並執行上面的操作,但使用該閾值。

編輯:

我看到你正在使用VC6 - 肯定有一個在vector::resize()一個錯誤,可能有一些與你的問題,雖然看着補丁老實說,我不看看它是如何的(實際上它是vector::insert()中的一個錯誤,但如前所述,resize()調用insert())。我想這將是值得訪問Dinkumwares' page for bug fixes to VC6並應用修復程序。

的問題還可能有一些做的<xmemory>補丁頁面上 - 這是不清楚的錯誤是什麼,在那裏討論,但vector::insert()不會調用_Destroy()vector<>確實定義名稱_Ty,所以你可能會運行到這個問題。一件好事 - 你不必擔心管理標題的變化,因爲微軟永遠不會再觸摸它們。只要確保這些修補程序能夠將它變成版本控制並獲得記錄。

請注意,Scott Meyers在「Effective STL」中建議使用SGI'sSTLPort's庫來獲得比VC6更好的STL支持。我沒有這樣做,所以我不確定這些庫的工作情況如何(但我還沒有非常多地使用VC6與STL)。當然,如果您可以選擇轉向更新版本的VC,那麼一定要這樣做。


還有一個編輯:

感謝您的測試程序...

VC6的_Allocate()執行默認分配器(在<xmemory>)使用符號整數指定的元素數量分配,並且如果傳入的大小是負數(顯然這就是您正在做的 - 當然在您的測試程序中),_Allocate()函數強制請求的分配大小爲零並繼續。請注意,零大小的分配請求幾乎總是會成功(不是vector無論如何都會檢查是否失敗),因此vector::resize()函數會愉快地嘗試將其內容移動到新塊中,該塊不夠大,不足以說至少。所以堆被損壞了,它可能會打到一個無效的內存頁面,而且不管 - 你的程序被洗劫一空。

因此,底線是永遠不要求VC6一次性分配多於INT_MAX的對象。在大多數情況下(VC6或其他)可能不是一個好主意。

另外,您應該記住,當分配失敗而不是投擲bad_alloc時,VC6使用從new返回0的預標準成語。

+0

「如果除T的拷貝構造函數或賦值運算符之外拋出異常,則不會產生」IRTA「效果,就好像不調用」resize()「。我相當確信,矢量上的任何操作都不會破壞內存。 – sbi 2009-10-23 20:50:32

+0

insert()操作可能會導致複製/分配操作(將矢量內容複製到新分配時) - 允許這些操作產生「效果」。它不應該像破壞堆一樣做任何事情,但不清楚OP是怎麼回事。在這些條件下的異常允許導致變化的矢量(可能並非矢量中的所有元素都達到新的矢量)。他的代碼可能會發現該矢量不再有意義。無論哪種方式都不是很好的行爲,我同意STL實現可以更好地處理這種情況。 – 2009-10-23 21:04:14

+0

你說得對,雖然在vector {unsigned char>中複製/分配元素不應該導致任何異常 - 在我看來,這似乎指向了一個錯誤的STL實現,它不能很好地處理內存不足的情況。我會對正在使用的平臺/編譯器/庫的細節感興趣。 – 2009-10-23 21:07:47

5

我會強烈建議您在調用庫函數之前檢查數據是否有損壞,可能有錯誤的參數!

在你的數據包上使用某種散列碼或校驗和算法。 你不能依靠圖書館來幫助你,因爲它無法做到: 它可能是你給它一個損壞但仍然有效(從圖書館的角度來看)的大小,這是真正的大,所以它分配的例子768MB的RAM。如果系統中有足夠的可用內存,這可能會有效,但如果有其他程序在1024MB計算機上佔用過多內存,則可能會失敗。

所以如上所述:先檢查!

+0

我同意。我認爲你問題的根源在於你依靠你的加密算法來告訴你它「假裝」的大小。你真的需要使用填充來實現塊大小(比如MD5),或者提供另一種帶外提供大小信息的方式。 – 2009-10-23 20:15:17

+0

我同意,我們確實需要一些方法來驗證數據包。我會在週一向其他程序員提到這個項目。現在,如果數據包大於5 MB,我將跳過它。 – cchampion 2009-10-24 22:36:55

4

我不知道當你說「調整大小已損壞內存」時你的意思。你如何確定?

FWIW,我不同意Michael's answer。如果std::vector<>::resize()拋出矢量擴展,我看到兩種可能性:

  1. 無論是用來填補新的空間(或複製的元素)構造函數的一個投擲或
  2. 用來種植向量做
  3. 分配器
  4. 或預先確定的要求的大小過多並且拋出的矢量。

std::vector<unsigned char>我們可以放心地關閉#1,讓葉#2。如果您不使用任何特殊分配器,則應使用std::allocator,並且AFAIK將調用new來分配內存。而new會拋出std::bad_alloc。但是,你說你無法理解這一點,所以我不知道會發生什麼。

不管是什麼,它應該從std::exception導出,所以你可以這樣做,找出:

try { 
    my_vec.resize(static_cast<std::size_t>(-1)); 
} catch(const std::exception& x) { 
    std::cerr << typeid(x).name() << '\n'; 
} 

什麼的這個結果?

無論如何,無論如何,我相當確信它不應該損壞記憶。要麼是你的std lib實現中的一個bug(如果你問我,除非你使用的是舊版本),否則你在其他地方做了一些錯誤。


編輯現在你說你正在使用VS6 ...

你應該說這點。 VC6是在十多年前發佈的,因爲MS已經在標準委員會中失去了投票權,因爲他們在會議上沒有出現太久。他們發佈的std lib實現來自Dinkumware(好),但由於法律問題,它是VC5的一個(非常糟糕),它有很多更小更大的錯誤,甚至沒有支持成員模板,儘管VC6編譯器支持它。老實說,你對這樣的舊產品有什麼期望?

如果您不能切換到一個像樣的VC版(我建議至少VC7.1又名VS.NET 2003,因爲這是這使得對符合標準的重大飛躍的一個),至少看看Dinkumware還在賣一個VC6t版本的優秀庫。 (其實,我會很驚訝,但他們曾經有一個,你永遠不知道...)

至於例外情況:在早期的VC版本(這包括VC6和不包括VC8又名VS.NET 2005 ,但我不確定VC7.1,但默認情況下,訪問衝突可能被catch(...)發現。所以如果這樣一個catch塊捕獲了一些東西,你不知道這是否是一個C++異常。我的建議是隻使用catch(...)throw;配合使用,以便讓異常通過。如果你這樣做,你會在AV中發生真正的崩潰,並且能夠在調試器中對它們進行堆棧跟蹤。如果你不這樣做,那麼AV就會被吞噬,然後你就會被一個沒有你知道的瘋狂應用程序所困住。但是做任何事情,除了用AV'ed應用程序夭折都沒有意義。一個AV是未定義行爲的一個結果,在此之後,所有投注都關閉。

+0

我說它損壞內存的原因是因爲簡單的日誌語句開始拋出異常,並且一大堆內存相關的斷言不斷彈出。調整大小功能之後,整個程序就會失去它的想法。如果您通過合理的長度調整大小,則不會發生這種情況。如果我跳過那個數據包也不會發生。我將嘗試使用該代碼來確定異常的類型,但我不知道typeid!謝謝。 – cchampion 2009-10-24 03:08:35

+0

如果你不知道:你必須爲此#include 。 – sbi 2009-10-24 09:09:52

+0

信不信由你,const std :: exception&handler不知道它是什麼。我不知道這個例外是什麼。現在我真的開始相信我們正在使用的STL版本有一個錯誤。我將在週一與程序員領導討論這個問題。 – cchampion 2009-10-24 22:55:07