質問

起こっているのは暗号化パケットを読んでいるのに、破損したパケットに遭遇し、その長さに対して非常に大きな乱数が返されることです。

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を呼び出します。サイズ変更の呼び出しの前にこのコードを入れてみました:

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ではありません。それが実際にスローしている例外は私にとってあまり重要ではありませんが、知りたいです。

btw、ご存知のとおり、このコードは通常の場合は正しく機能します。破損したデータパケットのこのケースは、それがクレイジーになる唯一の場所です。助けてください!ありがとう。

更新:

@マイケル。今のところ、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マシンでビルドして実行すると、サイズ変更によってstd :: bad_alloc例外がスローされ、予想どおりベクトルが破損しません。 EA Sport NCAAフットボールの時間です!

役に立ちましたか?

解決

vector::max_size()は常に「ハードコーディングされた」ものだと思います-システム/ライブラリが動的に割り当てるために準備されているメモリの量とは無関係です。あなたの問題は、割り当てが失敗したときに物事を破壊するベクトル実装のバグのようです。

「バグ」は言葉が強すぎるかもしれません。 vector::resize()vector::insert()の観点から定義されており、標準ではresize()について次のように記述されています:

  

Tのコピーコンストラクタまたは代入演算子以外で例外がスローされた場合、影響はありません

したがって、nMaxSize演算がベクトルを破損することが許される場合もあるように見えますが、演算が例外に対して安全であれば、それでもいいと思います(そして、私はそれが期待を裏切らないだろうと思います)それを行うためのライブラリですが、おそらく想像以上に難しいかもしれません。

いくつかの合理的なオプションがあるようです:

  • 破損バグのないライブラリの変更または更新(使用しているコンパイラ/ライブラリのバージョンは何ですか?)
  • insert()をチェックする代わりに、<xmemory>を適切な最大値に設定し、上記の処理を行いますが、代わりにそのしきい値を使用します。

編集:

VC6を使用していることがわかります-_Destroy()には間違いなく問題に関係するバグがありますが、パッチを見ると正直なところわかりません(実際にはvector<>のバグです) >、ただし、前述のように、_Ty_Allocate())を呼び出します。 VC6のバグ修正については、 Dinkumwaresのページにアクセスして適用する価値があると思います修正。

問題は、そのページのvectorパッチにも関係している可能性があります。そこで議論されているバグは不明ですが、INT_MAXnewを呼び出し、bad_allocは名前<= >そのため、その問題に直面している可能性があります。良いことの1つは、ヘッダーの変更を管理することを心配する必要がないことです。Microsoftがヘッダーに再び触れることはないからです。パッチがバージョン管理に組み込まれ、文書化されることを確認してください。

スコット・マイヤーズは<!> quot;効果的なSTL <!> quot; SGI または STLPortのライブラリにより、VC6よりも優れたSTLサポートが得られます。私はそれを行っていないので、それらのライブラリがどれだけうまく機能するかはわかりません(しかし、STLでVC6をあまり使用していません)。もちろん、VCの新しいバージョンに移行するオプションがある場合は、必ず実行してください。


もう1つの編集:

テストプログラムに感謝...

VC6のデフォルトアロケーター(<=>内)の実装は、signed intを使用して、割り当てる要素の数を指定し、渡されたサイズが負の場合(明らかに、あなたがやっていることです)テストプログラムでは、<=>関数が要求された割り当てサイズを強制的にゼロにして続行します。サイズがゼロの割り当て要求は常に成功することに注意してください(<=>が失敗をチェックするわけではありません)。そのため、<=>関数は内容を新しいブロックに移動しようとします控えめに言っても。そのため、ヒープが破損し、無効なメモリページにヒットする可能性が高くなります。それにもかかわらず、プログラムはホースでつながれます。

したがって、最終的には、VC6に一度に<=>以上のオブジェクトを割り当てるように要求しないでください。おそらくほとんどの状況(VC6など)で素晴らしいアイデアではありません。

また、VC6は、割り当てが失敗した場合に<=>をスローするのではなく、<=>から0を返す先行標準のイディオムを使用することに注意してください。

他のヒント

間違った引数でライブラリ関数を呼び出す前に、データの破損を確認することを強くお勧めします!

パケットで何らかのハッシュコードを使用するか、チェックサムアルゴリズムを使用します。 ライブラリができないので、あなたはあなたを助けるためにライブラリに頼ることはできません: 破損しているが有効な(ライブラリの観点から)有効なサイズを指定して、たとえば768MBのRAMを割り当てることができます。これは、システムに十分な空きメモリがある場合は機能する可能性がありますが、1024MBマシンでメモリを消費しすぎる他のプログラムが実行されている場合は失敗する可能性があります。

上記のように、最初に確認してください!

<!> quot; resize has破損したメモリ<!> quot;と言ったときの意味がわかりません。それをどのように判断しますか?

FWIW、マイケルの回答に同意しません。 std::vector<>::resize()がベクトル展開でスローされる場合、2つの可能性があります:

  1. 新しいスペースを埋める(または要素をコピーする)ために使用されたコンストラクターのいずれかがスローされたか、
  2. ベクトルの成長に使用されたアロケーターは
  3. または要求されたサイズが大きすぎてスローすることを事前に決定したベクトル。

std::vector<unsigned char>を使用すると、#1を安全に削除できるため、#2が残ります。特別なアロケーターを使用しない場合は、std::allocatorを使用する必要があります。また、知る限りでは、newを呼び出してメモリを割り当てます。そして、std::bad_allocstd::exceptionをスローします。しかし、あなたはこれを捕まえられないと言うので、何が起こるかわかりません。

それが何であれ、catch(...)から派生する必要があるため、これを実行して次のことを確認できます。

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は10年以上前にリリースされました。MSが会議にあまり長く参加しなかったため、std委員会で投票権を失った後です。彼らが出荷したstd lib実装はDinkumware(良い)からのものでしたが、法的な問題によりVC5(非常に悪い)のものであり、多くの小さなバグと大きなバグがあり、メンバーテンプレートのサポートさえありませんでしたVC6コンパイラがサポートしていました。正直なところ、あなたはそのような古い製品に何を期待していますか?

適切なVCバージョンに切り替えることができない場合(少なくともVC7.1、つまりVS.NET 2003をお勧めします。これは、標準準拠への大きな飛躍をもたらしたものです)、少なくとも Dinkumware は、VC6tバージョンの優れたライブラリをまだ販売しています。 (実際、私は驚くでしょうが、以前は持っていて、あなたは決して知りません...)

例外について:以前のVCバージョン(これにはVC6が含まれ、VC8、つまりVS.NET 2005は含まれませんが、VC7.1についてはわかりません)が、デフォルトではアクセス違反はthrow;でキャッチされます。そのため、このようなcatchブロックが何かをキャッチした場合、これがC ++例外であるかどうかはわかりません。私のアドバイスは、<=>と一緒に<=>のみを使用して、その例外を渡すようにすることです。実行すると、AVで実際のクラッシュが発生し、デバッガでそれらをスタックトレースできます。そうしないと、AVが飲み込まれ、知らないうちに凶暴になってしまったアプリケーションで止まってしまいます。しかし、AVで処理されたアプリケーションで中止する以外は何も意味がありません。 AVは、未定義の動作の結果の1つであり、その後、すべてのベットはオフになります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top