web-dev-qa-db-ja.com

「破損したサイズとprev_size」glibcエラーの理解

FDK-AACへのJNAブリッジを実装しました。ソースコードは here にあります。

コードをベンチマークでマークすると、同じ入力で何百回も実行が成功し、場合によってはプロセス全体を強制終了するCレベルのクラッシュが発生し、コアダンプが生成されます。

コアダンプを見ると、次のようになっています。

#1  0x00007f3e92e00f5d in __GI_abort () at abort.c:90
#2  0x00007f3e92e4928d in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7f3e92f70528 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:181
#3  0x00007f3e92e5064a in malloc_printerr (action=<optimized out>, str=0x7f3e92f6cdee "corrupted size vs. prev_size", ptr=<optimized out>, ar_ptr=<optimized out>) at malloc.c:5426
#4  0x00007f3e92e5304a in _int_free (av=0x7f3de0000020, p=<optimized out>, have_lock=0) at malloc.c:4337
#5  0x00007f3e92e5744e in __GI___libc_free (mem=<optimized out>) at malloc.c:3145
#6  0x00007f3e113921e9 in FDKfree (ptr=0x7f3de009df60) at libSYS/src/genericStds.cpp:233
#7  0x00007f3e1130d7d3 in Free_AacEncoder (p=0x7f3de0115740) at libAACenc/src/aacenc_lib.cpp:407
#8  0x00007f3e1130fbb3 in aacEncClose (phAacEncoder=0x7f3de0115740) at libAACenc/src/aacenc_lib.cpp:1395

繰り返しベンチマークを十分に実行すると、このバック/スタックトレースエラーは再現可能ですが、そのようなエラーの原因は何かを理解するのに苦労していますか?ポインター0x7f3de009df60に割り当てられたメモリもCPP/Cコード内で割り当てられ、割り当てられた同じインスタンスが解放されることを保証できます。もちろん、ベンチマークはシングルスレッドです。

これらを読んだ後:

セキュリティチェック && 内部関数

私はまだ理解するのに苦労しています-上記のエラーが発生する原因となる実際の(悪用ではなく、むしろエラー)シナリオは何ですか?そして、なぜそれは非常にまれに起こるのですか?

現在の疑い

詳細なバックトレースを実行すると、次の入力が得られます。

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
        set = {__val = {4, 6378670679680, 645636045657660056, 90523359816, 139904561311072, 292199584, 139903730612120, 139903730611784, 139904561311088, 1460617926600, 47573685816, 4119199860131166208, 
            139904593745464, 139904553224483, 139904561311136, 288245657}}
        pid = <optimized out>
        tid = <optimized out>
#1  0x00007f3e92e00f5d in __GI_abort () at abort.c:90
        save_stage = 2
        act = {__sigaction_handler = {sa_handler = 0x7f3de026db10, sa_sigaction = 0x7f3de026db10}, sa_mask = {__val = {139903730540556, 19, 30064771092, 812522497172832284, 139903728706672, 1887866374039011357, 
              139900298780168, 3775732748407067896, 763430436865, 35180077121538, 4119199860131166208, 139904561311552, 139904553065676, 1, 139904561311584, 139904561312192}}, sa_flags = 4096, 
          sa_restorer = 0x14}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007f3e92e4928d in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7f3e92f70528 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:181
        ap = {{gp_offset = 40, fp_offset = 32574, overflow_arg_area = 0x7f3e11adf1d0, reg_save_area = 0x7f3e11adf160}}
        fd = <optimized out>
        list = <optimized out>
        nlist = <optimized out>
        cp = <optimized out>
        written = <optimized out>
#3  0x00007f3e92e5064a in malloc_printerr (action=<optimized out>, str=0x7f3e92f6cdee "corrupted size vs. prev_size", ptr=<optimized out>, ar_ptr=<optimized out>) at malloc.c:5426
        buf = "00007f3de009e9f0"
        cp = <optimized out>
        ar_ptr = <optimized out>
        ptr = <optimized out>
        str = 0x7f3e92f6cdee "corrupted size vs. prev_size"
        action = <optimized out>
#4  0x00007f3e92e5304a in _int_free (av=0x7f3de0000020, p=<optimized out>, have_lock=0) at malloc.c:4337
        size = 2720
        fb = <optimized out>
        nextchunk = 0x7f3de009e9f0
        nextsize = 736
        nextinuse = <optimized out>
        prevsize = <optimized out>
        bck = <optimized out>
        fwd = <optimized out>
        errstr = 0x0
        locked = <optimized out>
#5  0x00007f3e92e5744e in __GI___libc_free (mem=<optimized out>) at malloc.c:3145
        ar_ptr = <optimized out>
        p = <optimized out>
        hook = <optimized out>
#6  0x00007f3e113921e9 in FDKfree (ptr=0x7f3de009df60) at libSYS/src/genericStds.cpp:233
No locals.
#7  0x00007f3e1130d7d3 in Free_AacEncoder (p=0x7f3de0115740) at libAACenc/src/aacenc_lib.cpp:407
No locals.
#8  0x00007f3e1130fbb3 in aacEncClose (phAacEncoder=0x7f3de0115740) at libAACenc/src/aacenc_lib.cpp:1395
        hAacEncoder = 0x7f3de009df60
        err = AACENC_OK
  • フレーム#6では、質問のポインターが0x7f3de009df60であることがわかります。
  • フレーム#4では、サイズが2720であることがわかります。これは実際に解放される構造の予想サイズです。
  • ただし、nextchunkのアドレスは0x7f3de009e9f0であり、現在リリースされているポインターの2704バイト後です。
  • エラーが再現される場合、これが常に当てはまることを確認できます。
  • これは私が直面しているエラーの強力な兆候でしょうか?
13
Sheinbergon

OK、だからこの問題を克服することができました。

まず第一に、「破損したサイズとprev_size」の実際的な原因は非常に単純です-隣接する後続のチャンクのメモリチャンク制御構造フィールドは、コードによる境界外アクセスのために上書きされています。ポインタxpバイトを割り当てたが、同じポインタに関してxを超えて書き込みを終えると、現在のメモリ割り当て(チャンク)サイズが次のチャンク制御構造で見つかったものと同じです(上書きされたため)。

このメモリリークの原因に関しては、Java/JNA層で行われた構造マッピングは、dll/soがコンパイルされたものとは異なる#pragma関連のパディング/アライメントを暗示していました。これにより、割り当てられた構造の境界を超えてデータが書き込まれました。その調整を無効にすると、問題はなくなりました。 (1回もクラッシュせずに何千回も実行されます!)。

19
Sheinbergon