2011-01-18 3 views
1

私はLinuxからMac OS X(ヒョウ)にアプリケーションを移植しようとしていますが、実行するとmalloc: *** error for object 0x100160 : double freeというエラーメッセージが表示されます。malloc:printfとNULL wchar_tでエラーダブルフリー*

私は以下のコードでこの問題を再現しました:もしので、それは奇妙だ

a=a, b=(null) 
test (5337) malloc: *** error for object 0x100160 : double free 
*** set a breakpoint in malloc_error_break to debug 

gccでコンパイルさ
//main.cpp 
#include <stdio.h> 
#include <wchar.h> 

int main(int argc, char*argv[]) 
{ 
    wchar_t *b=NULL; 
    printf("a=%ls, b=%ls \n", L"a", b); 
} 

gcc main.cpp -o test 

実行の出力私はこの行を使用します:printf("a=%ls, b=%ls", b, b)、エラーは表示されません。 さらに、wprintf(L"a=%ls, b=%ls", a, b)は使用できません。 Fedora 13では、このプログラムはエラーを出力しません。

printfのバグですか? このエラーを削除するにはどうすればよいですか?

+3

あなたは、なぜそれがデータを指すことを一般的に期待している関数にNULLポインタを渡すと、問題に遭遇するのか疑問に思っていますか? – Cascabel

+0

CRTライブラリのバグを除いて、この動作の説明はありません。 –

+3

@Al:printfがヌルポインタを正常に処理するという保証があることを示唆していますか?私はあなたが探している説明は "未定義の動作は未定義です"と思う。 – Cascabel

答えて

5

:あなたのような何かにあなたのコードを変更する必要があります。

厳密に言えば、図書館は、好きなことを行い、ヒープの破損も含めてこの状況を処理する権利を持っていますが、コードでは、ゴミのないものprintfの実装の場合のように、文字列を賢明にNULL扱うことです。これを行うコードにバグがあります。これらのことが起こります。

試みは以前の非NULLワイドchar型のポインタが印刷された後、vprintf_lは、次の文字列引数、またはときvprintf_l終了にアップコックますNULLワイドchar型のポインタを印刷するように作られている場合。 (あまりにも、私が思う、この現象が発生してもらうために他の方法があるかもしれません - 私はチェックしませんでした。)

問題のコードはここにある:

case 's': 
     if (flags & LONGINT) { 
      wchar_t *wcp; 

      if (convbuf != NULL) 
       free(convbuf); 
      if ((wcp = GETARG(wchar_t *)) == NULL) 
       cp = "(null)"; 
      else { 
       convbuf = __wcsconv(wcp, prec, loc); 
       if (convbuf == NULL) { 
        fp->_flags |= __SERR; 
        goto error; 
       } 
       cp = convbuf; 
      } 
     } else if ((cp = GETARG(char *)) == NULL) 

GETARG(wchar_t *)もし戻っNULLconvbufが指すう古い(今や解放された)バッファ。次に、関数の終了時、二重の自由があります:

if (convbuf != NULL) 
    free(convbuf); 

同じことが当てはまるでしょうがなかったが、別の文字列引数は、このケースでは二重解放が上記case 's'コードで起こるかかわら:

printf("a=%ls, b=%ls c=%ls\n", L"a", b, L"c"); 

解決策は、それが解放された後にconvbufNULLに設定することです。

printfコードはここにある:解体から判断

http://www.opensource.apple.com/source/Libc/Libc-594.1.4/stdio/vfprintf-fbsd.c

、それはSnow Leopardのデフォルトの実行時に使用されるコードです。

+0

素晴らしい答え。もう1つの解決策は、convbufチェックをelse内に移動することです。再利用する場合を除いて解放する必要はありません。 – tomlogic

+0

ありがとう!それは私をたくさん助けました! – Vincent

1

Ubuntuでは、a=a, b=(null)を印刷しますが、一般的にはNULL文字列を印刷することはお勧めできません。

-1

printfにNULLポインタを渡すことは良い習慣ではありませんが、printfを含む関数がメモリヒープを破壊して無効な入力に反応した場合、一般的な観点からは受け入れられません。したがって、NULLを渡すべきではありませんが、私はこの動作をライブラリのバグと見なします。関数が無効な引数を取得した場合、関数は多くの悲鳴を上げることができますが、メモリヒープを破壊するはずはありません。

もちろん、パラメータが有効かどうかを知ることはできませんが、ここではNULL定数があり、逆参照できないことがわかりやすいです。

+0

'b =(null)'を最初に印刷してから炎を打つのは丁寧ではありません... –

+0

'printf'が無効な入力にうまく反応すれば良いかもしれませんが、C標準はそうする必要はありません。 –

+0

@Adam:そうでないはずのNULL引数を取ったときに、メモリスタックを壊すことは100%正当であるとCの標準で宣言していますか?私はC標準がフリーを2回呼び出すことを拒否していると思うので、printfはここで違反しています。たぶん私はそれを間違って取るかもしれません。この奇妙な行動は合法ですが、私はそれを避ける事故と呼んでいます。 –

6

NULLポインタを文字列として出力することはできません。これは未定義の動作です。 C99標準から、§7.19.6.1/ 8

変換指定子とその意味は次のとおりです。
...
       なしl長さ修飾子は、引数者は、存在しない場合文字型の配列の最初の要素へのポインタです。 ...

長さ修飾子がある場合、引数はwchar_t型の配列の最初の 要素へのポインタになります。

NULLポインタは明示的に許可されていないため、使用できません。これは、単にprintfを扱うvprintf_lのバグ、(そしておそらくそのすべての友人)である

printf("a=%ls, b=%ls \n", L"a", b ? b : L"(null)"); 
+0

printfはNULLを拒否できます。しかし、printfがメモリヒープを破壊することが許されている場所を教えてください。 –

+4

@Al Keppの定義されていない動作は何もすることができません。 「動作しているように見える」、クラッシュ、メモリの破損、プリンタの起動を含む他の機能、例えば。 strlen、strcpyは、通常、定義されていない方法で使用するとsegfaultを引き起こします。 printfが壊れてメモリが壊れてしまう – nos

+2

コードが未定義の動作を引き起こすと、すべての賭けはオフになります。無効なコードがすべての異なるプラットフォームで発生する可能性のあるエフェクトをすべてリストすることはありません。スタックやヒープを壊すことは、実際にしか起こりません。 –

関連する問題