正在发生的事情是我正在读取加密数据包,并且遇到一个损坏的数据包,它会返回一个非常大的随机数。
size_t nLengthRemaining = packet.nLength - (packet.m_pSource->GetPosition() - packet.nDataOffset);
seckey.SecretValues.m_data.resize(nLengthRemaining);
在此代码中,m_data是std::vector<unsigned char>
。 nLengthRemaining由于数据包损坏而过大,因此抛出调整大小函数。问题不在于调整大小(我们处理异常),但调整大小已经损坏了内存,这会导致更多异常。
我想要做的是在调用resize之前知道长度是否太长,然后只有调用resize才可以。我已经尝试在调用resize之前输入此代码:
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 :: vector max_size成员函数来测试nLengthRemaining是否更大。但这肯定不可靠,因为nLengthRemaining仍然小于nMaxSize,但显然仍然足以导致调整大小出现问题(nMaxSize为4xxxxxxxxx且nLengthRemaining为3xxxxxxxxx)。
另外,我还没有确定调整大小的异常。它不是std :: length_error,它不是std :: bad_alloc。它真正抛出的例外对我来说并不重要,但我很想知道。
顺便说一句,您知道,此代码在正常情况下可以正常工作。这种损坏数据包的情况是它疯狂的唯一地方。请帮忙!感谢。
更新:
@迈克尔。现在,如果数据包大于5 MB,我将忽略该数据包。我将与其他团队成员讨论可能验证数据包的问题(它可能已存在,我只是不知道它)。我开始认为它确实是我们的STL版本中的一个错误,它抛出的异常甚至不是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开发机器上它具有与加密项目相同的行为,它会造成各种各样的破坏。当我在我的Visual Studio 2008机器上构建并运行它时,resize将抛出一个std :: bad_alloc异常,并且矢量不会被破坏,就像我们预期的那样!一些EA体育NCAA足球的时间嘿嘿!
答案 0 :(得分:5)
我认为vector::max_size()
几乎总是一个'硬编码'的东西 - 它与系统/库准备动态分配的内存量无关。您的问题似乎是矢量实现中的一个错误,当分配失败时会破坏事物。
'Bug'可能过于强烈。 vector::resize()
是根据vector::insert()
定义的,标准是关于vector::insert()
:
如果除了T的复制构造函数或赋值运算符之外抛出异常,则没有效果
所以似乎有时候允许resize()
操作来破坏一个向量,但是如果操作是异常安全的话它仍然会很好(我认为它不会出错期望图书馆这样做,但也许它比我想象的更难。)
您似乎有几个合理的选择:
vector::max_size()
设置nMaxSize
与您自己合理的最大值进行核对,而是执行上面的操作,而不是使用该阈值。编辑:
我看到你正在使用VC6 - vector::resize()
中肯定存在一个可能与你的问题有关的错误,虽然看着补丁我老实说也看不出来(实际上这是一个错误) vector::insert()
,但如上所述,resize()
调用insert()
)。我想有必要访问Dinkumwares' page for bug fixes to VC6并应用修复程序。
问题可能还与该页面上的<xmemory>
补丁有关 - 目前还不清楚那里讨论的错误是什么,但vector::insert()
确实调用_Destroy()
和{{1确定名称vector<>
,以便您可能遇到该问题。一件好事 - 您不必担心管理标题的更改,因为Microsoft从未再次触及它们。只需确保修补程序进入版本控制并记录下来。
请注意,“有效STL”中的Scott Meyers建议使用SGI's或STLPort's库来获得比VC6更好的STL支持。我没有这样做,所以我不确定这些库是如何工作的(但我也没有将VC6与STL一起使用)。当然,如果您可以选择转移到较新版本的VC,请务必执行此操作。
再一次编辑:
感谢您的测试计划......
VC6的默认分配器_Ty
实现(在_Allocate()
中)使用signed int来指定要分配的元素数,如果传入的大小是负数(显然就是你的那个)重做 - 当然在你的测试程序中)<xmemory>
函数强制所请求的分配大小为零并继续。请注意,零大小的分配请求几乎总是成功(不是_Allocate()
检查失败),因此vector
函数会愉快地尝试将其内容移动到新块中,这不是'至少可以说是相当大的。所以堆被破坏了,它可能会打到一个无效的内存页面,无论如何 - 你的程序被冲洗了。
所以底线是不要求VC6一次分配超过vector::resize()
个对象。在大多数情况下(VC6或其他情况)可能不是一个好主意。
此外,您应该记住,当分配失败而不是抛出INT_MAX
时,VC6使用从new
返回0的预标准惯用语。
答案 1 :(得分:5)
我强烈建议您在调用库函数之前检查数据是否有错误!
在数据包上使用某种哈希码或校验和算法。 您不能依赖图书馆来帮助您,因为它无法做到: 可能是你给它一个损坏但仍然有效(从库的角度来看)大小是真正的大,所以它分配例如768MB的RAM。如果系统中有足够的可用内存,但如果运行的其他程序在1024MB计算机上占用过多内存,则可能会失败。
如上所述:先检查一下!
答案 2 :(得分:4)
当你说“调整大小已损坏内存”时,我不知道你的意思。你是如何确定的?
FWIW,我不同意Michael's answer。如果std::vector<>::resize()
引发向量扩展,我会看到两种可能性:
使用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实现中的一个错误(不太可能,如果你问我,除非你使用一个非常旧的)或你在其他地方做错了。
编辑现在你说你正在使用VS6 ......
你应该早点说出来。 VC已经在十多年前被释放了,因为他们没有在会议上出现太长时间,因此他们已经失去了他们在std委员会的投票权。他们发布的std lib实现来自Dinkumware(很好),但是由于法律问题,它是VC5的一个(非常糟糕),它有很多越来越小的bug,甚至没有成员模板的支持,即使VC6编译器支持它。老实说,你对这么老的产品有什么期望?
如果你不能切换到一个不错的VC版本(我建议至少VC7.1又称VS.NET 2003,因为这是实现标准一致性的重大飞跃),至少看看{{3}仍然出售他们优秀图书馆的VC6t版本。 (实际上,我会感到惊讶,但他们曾经有过一个你永远都不知道......)
至于例外:在早期的VC版本中(这包括VC6并且不包括VC8又名VS.NET 2005,但我不确定VC7.1),默认情况下,访问冲突可能被{{1 }}。因此,如果这样的catch块捕获了某些东西,你就不会知道这是否是一个C ++异常。我的建议是只使用catch(...)
与catch(...)
一起使用才能让该异常传递。如果你这样做,你会在AV上遇到真正的崩溃,并能够在调试器中对它们进行堆栈跟踪。如果你不这样做,AV将被吞下,然后你就会被一个在你甚至不知道的情况下疯狂的应用程序所困扰。但做任何事情,除了用AV'ed应用程序中止是没有意义的。 AV是未定义行为的一个结果,之后,所有投注均已关闭。