lisz-works

プログラミングと興味を貴方に

Linux C言語でSegmentation fault。コアダンプを調べる方法

【スポンサーリンク】

プログラムのソースコード

Linux用にC言語のプログラムを作っていたろころ、実行したら

Segmentation fault (コアダンプ)

が出力されていた。

やばい雰囲気のする文言……

といことで、今回はSegmentation faultをコアダンプを使って調査する方法と、ぼくが作ったソースでSegmentation faultが発生した原因を説明していきます!

Segmentation faultとは?

ググった感じの代表的な原因が

  1. メモリアクセス違反
  2. 無限or深すぎる再帰処理

というのが原因とのこと。

一言でまとめると「よくないやーつ」ですね。

原因を調査するには、「コアダンプ」というものを使うのが定石ようです。

今回のソースコード

今回発生したのが、下記のようなコードです。
一部省略していますが

新しく作ったファイルを、前のファイルに上書きする

ということを、コマンドを作成して、system()を使って行っています。
パス名は伏せていますが、実際に使ったものを同じ長さになっています。

#include <stdio.h>
#include <string.h>

#define FNAME      "/home/work/coredump/aaa/xxxxxxxxxxxxxxxxxxxxxxxx/data/zzzzzzzz/testfilexxxxx.txt"
#define FNAME_NEW  "/home/work/coredump/aaa/xxxxxxxxxxxxxxxxxxxxxxxx/data/zzzzzzzz/testfilexxxxx_new.txt"

int main(void)
{
    char cmd[128];
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "mv -f %s %s", FNAME_NEW, FNAME);
    system(cmd);
    return 0;
}

コレをコンパイルして実行すると……

# ./a.out 
Segmentation fault (コアダンプ)

でちゃいました。

コアダンプで調べる

それではコアダンプを使って調べていきましょう。

コンソールでコアダンプを使って調査するためには、下記のような手順を踏めばようです。

  1. コアダンプのサイズ設定を行う
  2. gccの-gオプションでコンパイル(デバッグ情報付与)
  3. 実行→Coreファイルが生成される
  4. gdbでCoreファイルを読み込む
  5. 原因箇所を調査する

コアダンプのサイズ設定

まずはこのコマンドを実行します。

[root@localhost coredump]# ulimit -c
0
[root@localhost coredump]# ulimit -c unlimited
[root@localhost coredump]# ulimit -c
unlimited

厳密には、「ulimit -c」は、現在の値を調べているだけらしいので、下のコマンド「ulimit -c unlimited」だけでよさそうです。

「ulimit」は、システムリソースの上限を設定するコマンドらしい。

「-cオプション」は、コアダンプのサイズを設定。
「unlimited」、つまり上限なしにしています。

コンパイルでデバッグ情報を取得

次にコンパイルします。

いつものコンパイルに加えて「-g」をオプションにつけます。

[root@localhost coredump]# gcc -g main.c

これによって、デバッグ情報を付加することができます。

コンパイルが成功したら実行しましょう。

[root@localhost coredump]# ./a.out 
Segmentation fault (コアダンプ)

Segmentation faultが出ましたね。

ここでファイル一覧を表示してみると……

[root@localhost coredump]# ls -l
合計 168
-rwxr-xr-x. 1 root root   9656  49 17:08 a.out
drwxr-xr-x. 3 root root     38  49 17:04 aaa
-rw-------. 1 root root 245760  49 17:08 core.3618
-rwxrwxrwx. 1 root root    404  49 17:05 main.c

「core.XXXXX」というファイルができていますね。
これがコアダンプのファイルです。

gdbでコアダンプファイルを開く

gbdとはコンソールで扱うデバッガです。

このgdbを使って、コアダンプファイルを開くことで、Segmentation faultが発生した際のデバッグ情報を見ることができます。

[root@localhost coredump]# gdb a.out core.3618 
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-110.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/work/coredump/a.out...done.
[New LWP 3618]
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000000000400602 in main () at main.c:15
15    }
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.x86_64
(gdb) 

「main.cの15行目」と言っていますね。

frameとやらを見ると方法が書いてあったので念のため見てみましょう。
frameはデータの位置的なものみたい(詳しくは理解していない)。

(gdb) bt
#0  0x0000000000400602 in main () at main.c:15
(gdb) frame 0
#0  0x0000000000400602 in main () at main.c:15
15  }

今回の場合、最初の文面と特に変わりませんでした。

「q→Enter」で、gdbから抜けることができます。

コアダンプの内容を調査

コアダンプの調査手順を踏んで、内容を見てみたものの……

mainの15行目……

  1 #include <stdio.h>
  2 #include <string.h>
  3 
  4 #define FNAME      "/home/work/coredump/aaa/xxxxxxxxxxxxxxxxxxxxx    xxx/data/zzzzzzzz/testfilexxxxx.txt"
  5 
  6 #define FNAME_NEW  "/home/work/coredump/aaa/xxxxxxxxxxxxxxxxxxxxx    xxx/data/zzzzzzzz/testfilexxxxx_new.txt"
  7 
  8 int main(void)
  9 {
 10     char cmd[128];
 11     memset(cmd, 0, sizeof(cmd));
 12     sprintf(cmd, "mv -f %s %s", FNAME_NEW, FNAME);
 13     system(cmd);
 14     return 0;
 15 }   // ←ここ

「}」です。

「} (閉じカッコ)」の行がNGだよ!

と言ってくる……

謎で仕方なかったが、なんやかんや見てみました……

すると原因は

用意したバッファを超えてアクセスしたこと

でした。

char cmd[128];

に対して、sprintf()作り出そうとしているコマンドは

mv -f /home/work/coredump/aaa/xxxxxxxxxxxxxxxxxxxxxxxx/data/zzzzzzzz/testfilexxxxx_new.txt /home/work/coredump/aaa/xxxxxxxxxxxxxxxxxxxxxxxx/data/zzzzzzzz/testfilexxxxx.txt

と、計171Byte。

完全にオーバーですね……

戦犯 sprintfの猛威を喰らいました……(というか完全に見落とし凡ミス)

凡凡凡ミスなわけですが、全然気づかず頭を抱えていました……つらい……

バッファのサイズを

char cmd[256];

など、増やすことで解決しましたとさ。

参考

コチラを参考にしました。ありがとうございました!

minus9d.hatenablog.com

rabbitfoot141.hatenablog.com

itdoc.hitachi.co.jp

www.ysr.net.it-chiba.ac.jp

あとがき

LinuxのC言語で、Segmentation faultが発生したので、コアダンプを使って解決してみたお話でした!

結果的には凡ミスでしたが、基本的には原因が

  1. メモリアクセス違反
  2. 無限or深すぎる再帰処理

ということなので、ちょっと見つけにくいと思われる類のものです。

今回の例と同じような原因かはわかりませんが、Segmentation faultが出たらコアダンプを使って調査してみるといいと思います!