0

不可解なクラッシュがあり、これまでのところ、一貫して再現することは不可能であることがわかりました。コードは Visual Studio 2008 でコンパイルされています。

(もちろん簡略化された) ソースコードは次のようになります。

class AbstractParentClass 
{
private:
    /* data members */
public:
    AbstractParentClass();
    /* 
       virtual functions ...
     */
}; 

class ChildClass : public AbstractParentClass
{
private:
    /* data members */
public:
    ChildClass();
    /* 
       overridden/implemented virtual functions ...
     */
};

void DifferentClass::func(const char ** strs)
{
     ChildClass child_class;
     int i = 0;
     [...]
}

クラッシュ ダンプからの逆アセンブルは次のようになります。

Library!DifferentClass::func:
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10  
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]

逆アセンブリに対して func() のソースをマッピングすると、次のようになります。

Library!DifferentClass::func:
void DifferentClass::func(const char ** strs)
{
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
     ChildClass child_class;
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10 
     int i = 0;
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]
}

正常に実行された場合 (別のマシン、同じマシンでも、クラッシュは確実に再現できません)、逆アセンブリの唯一の違いは call 命令です。これ:

00404e8b  call        ChildClass::ChildClass (40a3d0h)

好きではなく:

612cab2b  call        a06cff10

そのため、クラッシュ実行では、呼び出し命令のパラメーターとして機能する a06cff10 アドレスは、誰がどこから来たのかを知っているようで、特に何にもマップされていません。そのため、予想どおり、そのアドレスに (ChildClass の既定のコンストラクターに到達するために) アクセスしようとすると、アクセス違反が発生します。

EXCEPTION_RECORD:  0012f688 -- (.exr 0x12f688)
ExceptionAddress: a06cff10
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: a06cff10
Attempt to read from address a06cff10

クラッシュ ダンプでそのアドレスを調べようとすると、アドレスがプロセスの範囲外にあることが示されます。

更新: 以下の zvrba からの応答を読んでさらに詳しく調べたところ、問題のある呼び出しは、静的ライブラリ (DLL によって読み込まれる) 内の 12 ほどの関数呼び出しの最初の呼び出しであると思われます。関数オフセット。それらはすべて同じクラスの関数ではありません。すべてのクラス (呼び出し元と呼び出し先の両方) が同じ静的ライブラリに存在しますが、関数が影響を受ける 3 つまたは 4 つの異なるクラスがあります。クラッシュしたこの最初の呼び出しでは、命令は e8e053403f であり、その命令の 3F4053E0 オフセットちょうど 53E0 のオフセットでした。他のすべてのインスタンスには、同じオフセットの問題があります。命令のオフセットは 3F40XXXX ですが、XXXX だけである必要があります。余分な 3F400000 はもちろん、ネバー ネバー ランドに物を送り込んでいます。これまでのところ、逆アセンブリ内のどの関数アドレスが有効で、どれが有効かどうかに関するパターンを見つけていません。ライブラリ内の DifferentClass の 1 つのメンバー関数では、ChildClass へのすべての呼び出しが不適切であると見なされますが、別のメンバー関数 DifferentClass では、ChildClass への別の呼び出しが適切に表示されます。

誰かがこのようなものを見たことがありますか/その考えられる原因について何か考えがありますか?

4

3 に答える 3

2

別の DLL で子クラスのコンストラクターを実装した可能性がありますか? 私が推測しているのは、クラッシュの実行時に、DLL が優先アドレスとは別のアドレスにロードされていることです。これは、VS デバッガーのモジュール ウィンドウで確認できます。これにより、呼び出しターゲットが誤って計算されます (その特定の呼び出し命令は相対的です)。アセンブリのオフセット (E8 オペコードの 4 バイト後) も非常に奇妙で、有効なオフセットというよりも、修正されていない再配置のように見えます。そのDLLをどのようにロードしますか?

于 2010-12-30T07:27:48.553 に答える
2

ソースコードのほとんどが省略されているため、何が起こっているのかを理解するのは困難ですが、コメントと逆アセンブリから、ChildClass vtable のアドレスが破損しているように見えます。これにはいくつかの原因が考えられます。

  • @contactmattが述べたように、配列/バッファオーバーラン
  • 破壊されたオブジェクトを使用する
  • 単位化された変数を使用する
  • 変数/ポインターの誤ったキャスト/変換
  • バイトアラインメントをパック/チェックせずにバッファからオブジェクトにデータをロードする

まず、vtable アドレスを見つけてデバッガーをステップ実行し、vtable メモリがいつ上書きされるかを確認します。ご指摘の場所 (40a3d0h) より数バイト上にある可能性があります。

  call        ChildClass::ChildClass (40a3d0h)

次に、その時点で実行されている可能性のあるコードを探します。

警告: 原因はほとんど不明であるため、実際の修正を見つけるには、多くのコードを読んだり、ソース管理バージョンを調べたりして、考えられる危険を確認する必要があります。経験上、破損の原因となっている問題 (言及されているものの 1 つなど) は、アクセス違反のあるコード行の近くにさえない可能性があります。

于 2010-12-30T09:06:33.233 に答える
0

これが役立つわけではありませんが、私が働いている場所では、C プログラムで範囲外の配列にアクセスした結果、アクセス違反エラーが発生する問題を修正していました。私のマシンでは、たまにしか再現できませんでした。私たちにできる唯一の修正は、多くの「範囲外の配列」チェックを配置することでした。

于 2010-12-30T06:23:38.547 に答える