C++ 如何通过boost::iostream防止zip炸弹
我编写了如下代码:C++ 如何通过boost::iostream防止zip炸弹,c++,boost,compression,boost-iostreams,C++,Boost,Compression,Boost Iostreams,我编写了如下代码: std::vector<char> unzip(std::vector<char> const& compressed) { std::vector<char> decompressed; boost::iostreams::filtering_ostream os; os.push(boost::iostreams::gzip_decompressor()); os.push(boost::iostre
std::vector<char> unzip(std::vector<char> const& compressed)
{
std::vector<char> decompressed;
boost::iostreams::filtering_ostream os;
os.push(boost::iostreams::gzip_decompressor());
os.push(boost::iostreams::back_inserter(decompressed));
boost::iostreams::write(os, &compressed[0], compressed.size());
os.reset();
return decompressed;
}
std::vector解压(std::vector const&compressed)
{
std::向量解压缩;
boost::iostreams::过滤ostream操作系统;
push(boost::iostreams::gzip_decompressor());
push(boost::iostreams::back_inserter(解压缩));
boost::iostreams::write(操作系统,&compressed[0],compressed.size());
os.reset();
减压返回;
}
如果压缩的是一个压缩炸弹,会发生什么?我认为内存将耗尽,进程将崩溃
那么如何避免这种情况呢?如何在解压缩前检查原始数据大小?schorsch\u 76刚刚说我可以编写一个自定义的back\u插入器,所以我只编写了一个,它可以工作:
namespace boost {
namespace iostreams {
template<typename Container>
class limit_back_insert_device {
public:
typedef typename Container::value_type char_type;
typedef sink_tag category;
limit_back_insert_device(Container& cnt, size_t max_size)
: container(&cnt)
, max_size(max_size) {
check(0);
}
std::streamsize write(const char_type* s, std::streamsize n) {
check(n);
container->insert(container->end(), s, s + n);
return n;
}
private:
void check(size_t n) {
if (std::numeric_limits<size_t>::max() - n < container->size()) {
throw std::runtime_error("size_t overflow");
}
if ((container->size() + n) > max_size) {
throw std::runtime_error("container->size() > max_size");
}
}
protected:
Container * container;
size_t const max_size;
};
template<typename Container>
limit_back_insert_device<Container> limit_back_inserter(Container& cnt,
size_t max_size) {
return limit_back_insert_device<Container>(cnt, max_size);
}
}
}
namespace boost{
命名空间iostreams{
模板
类限制\u返回\u插入\u设备{
公众:
typedef typename容器::值\类型字符\类型;
typedef sink_标签类别;
限制返回插入设备(容器和cnt,最大尺寸)
:容器(&cnt)
,最大尺寸(最大尺寸){
检查(0);
}
std::streamsize写入(常量字符类型*s,std::streamsize n){
检查(n);
容器->插入(容器->结束(),s,s+n);
返回n;
}
私人:
无效检查(尺寸){
如果(std::numeric\u limits::max()-nsize()){
抛出std::运行时错误(“大小溢出”);
}
如果((容器->大小()+n)>最大大小){
抛出std::runtime_错误(“容器->大小()>最大大小”);
}
}
受保护的:
集装箱*集装箱;
尺寸常数最大尺寸;
};
模板
限制返回插入设备限制返回插入器(容器和cnt,
尺寸(最大尺寸){
返回限制返回插入设备(cnt,最大尺寸);
}
}
}
你会这样做,就像往常一样:解压时要注意
您可以使用容量固定/有限的缓冲区(如useboost::iostreams::array\u sink
),也可以使用最大大小的保护来包装复制操作
此外,在您的示例中,输入是内存缓冲区,因此使用设备比使用流进行输入更有意义。下面是一个简单的例子:
std::vector<char> unzip(size_t limit, std::vector<char> const& compressed) {
std::vector<char> decompressed;
boost::iostreams::filtering_istream is;
is.push(boost::iostreams::gzip_decompressor());
is.push(boost::iostreams::array_source(compressed.data(), compressed.size()));
while (is && (decompressed.size() < limit)) {
char buf[512];
is.read(buf, sizeof(buf));
decompressed.insert(decompressed.end(), buf, buf + is.gcount());
}
return decompressed;
}
投掷
当然,如果超出限制,您可以选择抛出:
std::vector<char> unzip(size_t limit, std::vector<char> const& compressed) {
std::vector<char> decompressed;
boost::iostreams::filtering_istream is;
is.push(boost::iostreams::gzip_decompressor());
is.push(boost::iostreams::array_source(compressed.data(), compressed.size()));
while (is) {
char buf[512];
is.read(buf, sizeof(buf)); // can't detect EOF before attempting read on some streams
if (decompressed.size() + is.gcount() >= limit)
throw std::runtime_error("unzip limit exceeded");
decompressed.insert(decompressed.end(), buf, buf + is.gcount());
}
return decompressed;
}
std::vector解压(大小限制、std::vector常量和压缩){
std::向量解压缩;
boost::iostreams::filtering\u istream是;
is.push(boost::iostreams::gzip_decompressor());
is.push(boost::iostreams::array_source(compressed.data(),compressed.size());
while(is){
char-buf[512];
is.read(buf,sizeof(buf));//在尝试读取某些流之前无法检测到EOF
if(解压缩的.size()+为.gcount()>=limit)
抛出std::运行时_错误(“超出解压缩限制”);
decompressed.insert(decompressed.end(),buf,buf+is.gcount());
}
减压返回;
}
我将在std::thread
中运行此程序,检查已解压缩的共享线程的大小,如果超出限制,则终止线程。请参阅编写自定义back_插入器,在其中可以添加终止标志,用于抛出异常以转到添加的try/catch处理程序。@schorsch_76我认为您是对的。否。这个想法太可怕了。它无缘无故地造成了许多复杂性和低效性。(如果您打算这样做,您可以启动一个具有适当资源限制的单独进程。这一点同时具有进程空间隔离的好处,因此它可以防止解压缩编解码器中可能存在的大量其他漏洞)@schorsch_76您无法真正终止线程。
max10k: 10240 bytes
max100k: 20480 bytes
std::vector<char> unzip(size_t limit, std::vector<char> const& compressed) {
std::vector<char> decompressed;
boost::iostreams::filtering_istream is;
is.push(boost::iostreams::gzip_decompressor());
is.push(boost::iostreams::array_source(compressed.data(), compressed.size()));
while (is) {
char buf[512];
is.read(buf, sizeof(buf)); // can't detect EOF before attempting read on some streams
if (decompressed.size() + is.gcount() >= limit)
throw std::runtime_error("unzip limit exceeded");
decompressed.insert(decompressed.end(), buf, buf + is.gcount());
}
return decompressed;
}