malloc()はどのようにしてSIGSEGVを引き起こしますか?
質問
プログラムに奇妙なバグがあります。malloc()がSIGSEGVを引き起こしているように見えますが、これは私の理解では意味がありません。ダイナミックリストにはsimclistというライブラリを使用しています。
これは、後で参照される構造体です。
typedef struct {
int msgid;
int status;
void* udata;
list_t queue;
} msg_t;
コードは次のとおりです:
msg_t* msg = (msg_t*) malloc( sizeof( msg_t ) );
msg->msgid = msgid;
msg->status = MSG_STAT_NEW;
msg->udata = udata;
list_init( &msg->queue );
list_init
はプログラムが失敗する場所です。list_initのコードは次のとおりです。
/* list initialization */
int list_init(list_t *restrict l) {
if (l == NULL) return -1;
srandom((unsigned long)time(NULL));
l->numels = 0;
/* head/tail sentinels and mid pointer */
l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
l->head_sentinel->next = l->tail_sentinel;
l->tail_sentinel->prev = l->head_sentinel;
l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL;
l->head_sentinel->data = l->tail_sentinel->data = NULL;
/* iteration attributes */
l->iter_active = 0;
l->iter_pos = 0;
l->iter_curentry = NULL;
/* free-list attributes */
l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *));
l->spareelsnum = 0;
#ifdef SIMCLIST_WITH_THREADS
l->threadcount = 0;
#endif
list_attributes_setdefaults(l);
assert(list_repOk(l));
assert(list_attrOk(l));
return 0;
}
l-> spareels =(struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS *
は、スタックトレースに従ってSIGSEGVが発生する場所です。デバッグにはgdb / nemiverを使用していますが、この関数が最初に呼び出されたときは正常に動作しますが、2回目には常に失敗しますmalloc()でSIGSEGVを発生させる方法
これはスタックトレースです:
#0 ?? () at :0
#1 malloc () at :0
#2 list_init (l=0x104f290) at src/simclist.c:205
#3 msg_new (msg_switch=0x1050dc0, msgid=8, udata=0x0) at src/msg_switch.c:218
#4 exread (sockfd=8, conn_info=0x104e0e0) at src/zimr-proxy/main.c:504
#5 zfd_select (tv_sec=0) at src/zfildes.c:124
#6 main (argc=3, argv=0x7fffcabe44f8) at src/zimr-proxy/main.c:210
ヘルプまたは洞察力は非常に高く評価されています!
解決
malloc
は、たとえば、ヒープが破損した場合にセグメンテーション違反を起こす可能性があります。以前の割り当ての範囲を超えて何も書いていないことを確認してください。
他のヒント
コードの他の部分でメモリ違反が発生している可能性があります。 Linuxを使用している場合は、valgrindを必ず試してください。 valgrindに合格しない限り、自分のCプログラムを信頼することはありません。
編集:他の便利なツールは、電気柵です。 Glibcは、 MALLOC_CHECK_ 環境変数も提供して、メモリの問題のデバッグを支援します。これらの2つの方法は、valgrindほど実行速度には影響しません。
バッファオーバーフローによって、または malloc
によって割り当てられていないポインタで free
を呼び出して(または既に解放)。
mallocで使用される内部データ構造がこのように破損した場合、mallocは無効なデータを使用しており、クラッシュする可能性があります。
malloc()
(および realloc()
および calloc()
)からコアダンプをトリガーする無数の方法があります。これらは次のとおりです。
- バッファオーバーフロー:割り当てられたスペースの終わりを超えて書き込む(
malloc()
がそこに保持していた制御情報を踏みつぶす)。 - バッファアンダーフロー:割り当てられたスペースの開始前に書き込みます(
malloc()
がそこに保持していた制御情報を踏みつぶします)。 -
malloc()
によって割り当てられなかったメモリを解放します。 CとC ++が混在するプログラムでは、new
によってC ++に割り当てられたメモリの解放が含まれます。 -
malloc()
によって割り当てられたメモリブロックの途中を指すポインターの解放-これは前のケースの特殊なケースです。 - すでに解放されているポインターの解放-悪名高い「二重解放」。
malloc()
の診断バージョンを使用するか、システムの標準バージョンで診断を有効にすると、これらの問題の特定に役立ちます。たとえば、小さなアンダーフローとオーバーフローを検出できる場合があります(要求したスペースの周囲にバッファゾーンを提供するために追加のスペースを割り当てるため)。または、割り当てられたスペースの途中のポインター-割り当てられたスペースとは別に情報を保存するためです。コストは、デバッグバージョンのスペースが増えることです。本当に優れたアロケーターは、スタックトレースと行番号を記録して、コード内で割り当てが発生した場所、または最初の解放が発生した場所を通知できます。
このコードを単独でデバッグして、問題が実際にセグメンテーション違反が生成された場所にあるかどうかを確認する必要があります。 (そうではないと思う)。
これは次を意味します:
#1:-O0を使用してコードをコンパイルし、gdbが正しい行番号情報を取得するようにします。
#2:コードのこの部分を呼び出す単体テストを作成します。
私の推測では、このコードは個別に使用しても正しく機能するでしょう。バグの原因がわかるまで、同じ方法で他のモジュールをテストできます。
Valgrindを使用することも、他の人が示唆しているように、非常に良いアイデアです。
コードに問題があります。 mallocがNULLを返す場合、このケースはコード内で正しく処理されません。メモリが実際に割り当てられていないときに割り当てられたと仮定するだけです。これにより、メモリが破損する可能性があります。