2009-10-23 1 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 слишком велика из-за поврежденного пакета данных, поэтому функция изменения размера выбрасывает. Проблема заключается не в том, что resize throws (мы обрабатываем исключения), но этот размер имеет поврежденную память уже, и это приводит к большим исключениям позже.

Что я хочу сделать, это знать, если длина слишком велика до того, как я выберу изменение размера, тогда вызывается только изменение размера, если это нормально. Я попытался поставить этот код перед вызовом, чтобы изменить размер:

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

Этот код использует зЬй :: функции члена вектор max_size, чтобы проверить, если nLengthRemaining больше. Это не должно быть надежным, потому что nLengthRemaining все еще меньше nMaxSize, но, по-видимому, все еще достаточно большой, чтобы вызвать изменение размера (nMaxSize было 4xxxxxxxxx, а nLengthRemaining - 3xxxxxxxxx).

Кроме того, я не определяю, что выбрасывает изменение размера. Это не std :: length_error, и это не std :: bad_alloc. Какое исключение это бросание действительно не слишком важно для меня, но мне любопытно узнать.

Кстати, именно так вы знаете, этот код действительно работает правильно в обычных случаях. Этот случай поврежденного пакета данных является единственным местом, где он сходит с ума. Пожалуйста помоги! Благодарю.

ОБНОВЛЕНИЕ:

@Michael. Пока я просто проигнорирую пакет, если он больше 5 МБ. Я буду обсуждать с другими членами команды вопрос о возможности проверки пакетов (возможно, он уже существует, и я просто этого не знаю). Я начинаю думать, что это действительно ошибка в нашей версии STL, исключение, которое она бросает, - это даже не исключение std :: exception, которое меня удивило. Я попытаюсь выяснить у моего руководителя, какую версию STL мы запускаем (как бы проверить?).

ДРУГОЕ ОБНОВЛЕНИЕ: Я просто докажу, что это ошибка в версии STL, которую я использую на моей машине разработки Visual Studio 6. Я написал это примерное приложение:

// 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 Sport NCAA футбол хе-хе!

+0

На какой платформе вы работаете? – sbi

+0

Мне любопытно, какую версию компилятора/stl вы используете. Реализации, к которым у меня есть доступ, не приведут к повреждению векторного объекта, если сбой распределения. – jmucchiello

+0

@cchampion: «На моей машине VS6 dev ...» Если бы вы сказали это раньше, мы бы не потратили столько времени на это. Это более чем 10-летняя технология! Конечно, это багги. См. Мой ответ. – sbi

ответ

5

Я думаю, что vector::max_size() - это почти всегда «жестко закодированная» вещь - она ​​не зависит от того, сколько памяти система/библиотека готова динамически распределять. Ваша проблема, похоже, является ошибкой в ​​векторной реализации, которая искажает ситуацию при сбое распределения.

«Ошибка» может быть слишком сильным словом.vector::resize() определяется в терминах vector::insert() и стандарт говорит это о vector::insert():

Если исключение иначе, чем конструктор копирования или оператор присваивания Т нет никаких эффектов

Так что кажется например, могут быть случаи, когда операция resize() разрешена для коррумпирования вектора, но все равно было бы неплохо, если бы операция была безопасной для исключений (и я думаю, что это не было бы ошибкой ожидать от библиотеки этого, но, возможно, это сложнее, чем я себе представляю).

Вы, кажется, есть несколько разумных вариантов: (? Какой компилятор/версия библиотеки вы используете)

  • изменения или обновление библиотеки, который не имеет ошибку коррупции
  • вместо проверки против vector::max_size() установите nMaxSize на свой собственный разумный максимум и сделайте то, что у вас есть, но вместо этого используйте этот порог.

Edit:

Я вижу, что вы используете VC6 - есть определенно ошибка в vector::resize(), которые могли бы иметь что-то делать с вашей проблемой, хотя, глядя на патче я честно не см., как (на самом деле это ошибка в vector::insert(), но, как упоминалось, resize() звонки insert()). Я бы предположил, что стоит посетить Dinkumwares' page for bug fixes to VC6 и применить исправления.

Проблема может также иметь что-то делать с <xmemory> заплаты на этой странице - это неясно, что ошибка в том, что там обсуждается, но vector::insert() делает называть _Destroy() и vector<> делает определить имя _Ty, чтобы вы могли быть запущены в эту проблему , Одна хорошая вещь - вам не придется беспокоиться об управлении изменениями в заголовках, так как Microsoft никогда не прикасается к ним снова. Просто убедитесь, что исправления превращаются в контроль версий и документируются.

Обратите внимание, что Скотт Мейерс в «Эффективном STL» предлагает использовать SGI's или STLPort's библиотеку, чтобы получить лучшую поддержку STL, чем поставляется с VC6. Я этого не сделал, поэтому я не уверен, насколько хорошо работают эти библиотеки (но я тоже не использовал VC6 с STL). Конечно, если у вас есть возможность перейти на более новую версию VC, непременно сделайте это.


Еще одно редактирования:

Спасибо за программу испытаний ...

_Allocate() реализации vc6 для распределителя по умолчанию (в <xmemory>) использует подписанную Int, чтобы указать количество элементов для распределения , и если размер переданного значения отрицательный (что, по-видимому, является тем, что вы делаете - конечно, в тестовой программе), то функция _Allocate() заставляет запрошенный размер выделения равняться нулю и продолжается. Обратите внимание, что запрос на размещение нулевого размера почти всегда будет успешным (не то, что vector все равно проверяет отказ), поэтому функция vector::resize() весело пытается переместить свое содержимое в новый блок, что недостаточно велико, чтобы сказать наименее , Таким образом, куча повреждена, скорее всего, она ударит по недопустимой странице памяти, и независимо от того, ваша программа запущена.

Таким образом, нижняя строка не требует от VC6 выделить более INT_MAX объектов за один раз. Вероятно, это не очень хорошая идея в большинстве случаев (VC6 или иначе).

Кроме того, вы должны иметь в виду, что VC6 использует предварительную стандартную идиому возврата 0 от new, когда распределение прерывается, а не бросает bad_alloc.

+0

«Если исключение выбрано иначе, чем конструктор копирования или оператор присваивания T, нет никаких эффектов« IRTA », так как« resize() 'не был вызван». И я уверен, что никакая операция на векторе не должна испортить память. – sbi

+0

Операция insert() может привести к операциям копирования/присваивания (когда содержимое вектора будет скопировано в новое распределение) - им разрешено «иметь эффект». Он не должен делать ничего плохого, как, например, развращать кучу, но неясно, так ли это то, что происходит с OP. Исключение в этих условиях позволяет привести к изменению вектора (возможно, не все элементы вектора попадают в новый). Его код может обнаружить, что вектор больше не имеет смысла. В любом случае это не большое поведение, и я согласен с тем, что реализация STL может лучше справляться с ситуацией. –

+0

Вы правы, хотя копирование/назначение элементов в 'vector ' не должно приводить к каким-либо исключениям - мне кажется, что я указываю на ошибочную реализацию STL, которая не справляется с ситуацией вне памяти , Мне было бы интересно узнать о используемой платформе/компиляторе/библиотеке. –

5

Я бы настоятельно советовал вам проверять ваши данные на наличие сбоев перед вызовом функций библиотеки, возможно, с неправильными аргументами!

Использовать какой-либо хэш-код или алгоритм проверки суммы на ваших пакетах. Вы не можете полагаться на библиотеку, чтобы помочь вам, поскольку она не в состоянии: Возможно, вы даете ей поврежденный, но все еще действительный (с точки зрения библиотеки) размер, который является большим, поэтому он выделяет, например 768 МБ ОЗУ. Это может работать, если в системе достаточно свободной памяти, но может не работать, если есть другие запущенные программы, которые потребляют слишком много памяти на вашем компьютере с разрешением 1024 МБ.

Как указано выше: сначала проверьте!

+0

Согласен. Я думаю, что корень вашей проблемы заключается в том, что вы полагаетесь на свой алгоритм шифрования, чтобы рассказать вам, какой размер он «притворяется». Вы действительно нуждаетесь в принудительной блокировке с дополнением (например, MD5) или имеете другой внеполосный способ предоставления информации о размере. –

+0

Согласен, нам нужен какой-то метод проверки пакетов. Я упомянул об этом другим программистам в этом проекте в понедельник.На данный момент я просто пропущу пакет, если он больше 5 МБ. – cchampion

4

Я понятия не имею, что вы имеете в виду, когда говорите «изменение размера имеет поврежденную память». Как вы это определяете?

FWIW, я не согласен с Michael's answer. Если std::vector<>::resize() бросает на вектор расширения, я вижу две возможности:

  1. Либо один из конструкторов, используемых для заполнения нового пространства (или скопировать элементы) бросала или
  2. аллокатор выращены вектор сделал
  3. или вектор, определенный заранее, чтобы запрошенный размер был слишком большим и выбрасывал.

С 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 ...

Вы должны были сказать об этом раньше. VC6 был выпущен более десяти лет назад, после того как MS потеряла свой голос в комитете std, потому что они слишком долго не появлялись на встречах.Реализация std lib, которую они отправляли, была от Dinkumware (хорошая), но из-за юридических проблем она была для VC5 (очень плохо), у которой было много мелких и больших ошибок, и даже не поддерживали шаблоны участников, хотя компилятор VC6 поддерживал его. Честно говоря, что вы ожидаете от такого старого продукта?

Если вы не можете переключиться на достойную версию VC (я бы порекомендовал хотя бы VC7.1 aka VS.NET 2003, так как это сделал основной шаг к стандартному соответствию), по крайней мере, посмотрите, Dinkumware все еще продает версия VC6t их отличной библиотеки. (На самом деле, я был бы удивлен, но у них был один, и вы никогда не знаете ...)

Что касается исключений: В более ранней версии VC (это включает VC6 и не включает VC8 aka VS.NET 2005 , Я не уверен в VC7.1, хотя) по умолчанию Нарушения доступа могут быть пойманы на catch(...). Поэтому, если такой блок catch поймал что-то, вы бы не знали, было ли это даже исключением C++. Мой совет должен был использовать только catch(...) в сочетании с throw;, чтобы это исключение прошло. Если вы это сделаете, вы получите реальный крах в AV и сможете стекировать их в отладчике. Если вы этого не сделаете, AV будет проглочен, и тогда вы застрянете с приложением, которое исчезло без вашего ведома. Но делать что-либо, кроме прерывания с помощью AV-приложения, не имеет смысла. AV является одним из результатов неопределенного поведения, после чего все ставки отключены.

+0

Причина, по которой я говорю, что это испорченная память, состоит в том, что простые заявления журнала начинают бросать исключения, а также целая куча связанных с памятью утверждений продолжают появляться. После функции изменения размера вся программа теряет сознание. Это не происходит, если вы пройдете изменение размера разумной длины. Этого не происходит, если я пропущу этот пакет. Я попробую этот код, чтобы определить тип исключения, я не знал о typeid! Благодарю. – cchampion

+0

Если вы не знали: вы должны '#include ' для этого. – sbi

+0

Верьте или нет, const std :: exception & handler DID NOT его не улавливает. Я не знаю, что это за исключением. Теперь я действительно начинаю верить, что версия STL, которую мы используем, имеет в ней ошибку. Я обсужу это с программистом, который ведет в понедельник. – cchampion