Segmentation fault(strcpyによるバッファオーバーフロー)
コピー先のchar配列より長い文字列をstrcpyでコピーすると、確保した領域を超えて書き込みが起こります。隣接するメモリを破壊し、クラッシュにつながります。
エラーメッセージの読み方
Segmentation fault
(エラーメッセージなし)- これはプログラム自身の出力ではありません。プログラムは何も言わずに落ちています
Segmentation fault- シェルが検知した異常終了の種類 — OSが「不正なメモリアクセス」などを検出しプロセスを強制終了させました
見つけ方- このメッセージだけでは行番号すら分かりません。原因はコードを読んで特定するしかありません
このエラーが出る典型パターン
パターン1
1 #include <stdio.h> 2 #include <string.h> 3 4 int main(void) { 5 char buf[8]; 6 strcpy(buf, "AAAAAAAAAAAAAAAAAAAAAAAA"); ^ 7 printf("%s\n", buf); 8 return 0; 9 }
buf[8]は8バイトしか確保されていません。それより長い文字列をstrcpyでコピーすると、確保した領域を超えて書き込みが起こり、隣接するメモリ(戻り先のアドレスなど)を破壊してクラッシュします。
直し方: "AAAAAAAAAAAAAAAAAAAAAAAA" を "hi" にします。
パターン2
1 #include <stdio.h> 2 #include <string.h> 3 4 int main(void) { 5 char name[6]; 6 strcpy(name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); ^ 7 printf("%s\n", name); 8 return 0; 9 }
確保したバッファのサイズより長い文字列を渡すと、strcpyはコピー先の限界を無視してそのまま書き込み続けます。
直し方: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" を "Al" にします。
パターン3
1 #include <stdio.h> 2 #include <string.h> 3 4 int main(void) { 5 char code[4]; 6 strcpy(code, "1234567890123456789012"); ^ 7 printf("%s\n", code); 8 return 0; 9 }
小さいバッファほど、少し長い文字列を渡しただけでも重要な領域まで書き潰しやすくなります。安全に使うにはstrncpyや十分なサイズのバッファ確保が必要です。
直し方: "1234567890123456789012" を "AB" にします。
パターン4
1 #include <stdio.h> 2 #include <string.h> 3 4 int main(void) { 5 char label[5]; 6 strcpy(label, "ThisIsWayTooLong"); ^ 7 printf("%s\n", label); 8 return 0; 9 }
確保した領域より長い文字列をコピーすると、隣接するメモリまで書き潰してしまいクラッシュします。
直し方: "ThisIsWayTooLong" を "OK" にします。
パターン5
1 #include <stdio.h> 2 #include <string.h> 3 4 int main(void) { 5 char id[3]; 6 strcpy(id, "IDNumber12345"); ^ 7 printf("%s\n", id); 8 return 0; 9 }
バッファが小さいほど、少し長い文字列を渡しただけでも重要な領域まで書き潰しやすくなります。
直し方: "IDNumber12345" を "A1" にします。
よくある誤解
「多少長くてもコピーはできる」というのは大きな誤解です。strcpyはコピー先のサイズをまったく知らないため、渡された文字列の長さだけ書き込もうとします。書き込んだ先が戻り先のアドレスなど重要な情報を破壊すると、その場でクラッシュします。安全に使うにはstrncpyや十分なバッファサイズの確保が必要です。
実務での勘所
strcpyのバッファオーバーフローは、単なるクラッシュにとどまらない歴史があります。書き込み先を超えて隣接するメモリを上書きできるということは、関数の戻り先のアドレスなど、プログラムの実行を制御する情報まで書き換えられる可能性があるということです。悪用されると、想定していないコードを実行させられる脆弱性(スタックオーバーフロー攻撃)につながるため、strcpyやgets、sprintfのような境界チェックをしない関数群は、セキュリティの観点から現在は使用を避けるべきとされています。