# segmentation faultのお話

# segmentation faultとは

セグメンテーション違反(英語:segmentation fault)はソフトウェアの実行時に起きる特定のエラー条件である。segfault(セグフォールト)と略される場合がある。 セグメンテーション違反はアクセスが許可されていないメモリ上の位置、もしくは許可されていない方法(例えばリードオンリーの位置へ書き込みをしようとする、もしくはオペレーティングシステムの部分を上書きしようとする)でメモリ上の位置にアクセスしようとするときに起こる。

セグメンテーション違反 @ Wikipediaより引用 (opens new window)

今回出てくるsegmentation faultは, 存在しない位置へのアクセスにより生じたものである。

# Q1

  • Q. 以下のポインタ変数sはmain関数内で宣言した時点でallocateされているのか?
int main(void) {
  char* s;
  s = "hoge";
  puts(s);
}
  • A. されている。ポインタ変数はmain関数のローカル変数なので, スタックに積まれた段階で使用できる。

ただし, 以下のような代入はsegmentation faultが発生する。
なぜなら, *sは参照外しが行われており, allocateされていないデータに対しるアクセスが発生するためである。

int main(void) {
  char* s;
  *s = "hoge";
  puts(s);
}

# Q2

  • Q. 上の例ではsegmentation faultが発生するが, 下の例ではsegmentation faultが発生しなかった。なぜか?
#include <stdio.h>

void func(char* c) {
  c = "B";
}

int main(void) {
  char* c2;
  func(c2);
  printf("%s\n", c2);
}
#include <stdio.h>

void func(char* c) {
  c = "B";
}

int main(void) {
  char* c;
  c = "A";
  printf("%s\n", c);  

  char* c2; 
  func(c2);
  printf("%s\n", c2);
}
  • A. 偶然。具体的には, 下の例では偶然c2の参照先とスタック上のデータがアクセス許可されている値になったためである。その根拠として, gdbによるデバッグを行うと以下の結果が得られた。
gdb-peda$ s
[----------------------------------registers-----------------------------------]
RAX: 0x7ffff7faad60 --> 0x7fffffffe298 --> 0x7fffffffe579 ("SHELL=/bin/bash")
RBX: 0x0 
RCX: 0x7ffff7fa7718 --> 0x7ffff7fa9a40 --> 0x0 
RDX: 0x7fffffffe298 --> 0x7fffffffe579 ("SHELL=/bin/bash")
RSI: 0x7fffffffe288 --> 0x7fffffffe566 ("/root/work/2/test2")
RDI: 0x1 
RBP: 0x401190 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffe198 --> 0x0 
RIP: 0x40119e (<__libc_csu_init+14>:	push   r13)
R8 : 0x7ffff7fa9a50 --> 0x4 
R9 : 0x7ffff7fe4780 (<_dl_fini>:	push   rbp)
R10: 0x0 
R11: 0x27 ("'")
R12: 0x401040 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe280 --> 0x1 
R14: 0x7fffffffe298 --> 0x7fffffffe579 ("SHELL=/bin/bash")
R15: 0x403e10 --> 0x401120 (<frame_dummy>:	jmp    0x4010b0 <register_tm_clones>)
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x401192 <__libc_csu_init+2>:	lea    r15,[rip+0x2c77]        # 0x403e10
   0x401199 <__libc_csu_init+9>:	push   r14
   0x40119b <__libc_csu_init+11>:	mov    r14,rdx
=> 0x40119e <__libc_csu_init+14>:	push   r13
   0x4011a0 <__libc_csu_init+16>:	mov    r13,rsi
   0x4011a3 <__libc_csu_init+19>:	push   r12
   0x4011a5 <__libc_csu_init+21>:	mov    r12d,edi
   0x4011a8 <__libc_csu_init+24>:	push   rbp
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe198 --> 0x0 
0008| 0x7fffffffe1a0 --> 0x0 
0016| 0x7fffffffe1a8 --> 0x7ffff7e14b4a (<__libc_start_main+122>:	mov    rax,QWORD PTR [rip+0x19230f]        # 0x7ffff7fa6e60)
0024| 0x7fffffffe1b0 --> 0x0 
0032| 0x7fffffffe1b8 --> 0x7fffffffe288 --> 0x7fffffffe566 ("/root/work/2/test2")
0040| 0x7fffffffe1c0 --> 0x100040000 
0048| 0x7fffffffe1c8 --> 0x401151 (<main>:	push   rbp)
0056| 0x7fffffffe1d0 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000000000040119e in __libc_csu_init ()
gdb-peda$ x/64b 0x7fffffffe190
0x7fffffffe190:	0x90	0x11	0x40	0x00	0x00	0x00	0x00	0x00
0x7fffffffe198:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1a0:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1a8:	0x4a	0x4b	0xe1	0xf7	0xff	0x7f	0x00	0x00
0x7fffffffe1b0:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1b8:	0x88	0xe2	0xff	0xff	0xff	0x7f	0x00	0x00
0x7fffffffe1c0:	0x00	0x00	0x04	0x00	0x01	0x00	0x00	0x00
0x7fffffffe1c8:	0x51	0x11	0x40	0x00	0x00	0x00	0x00	0x00
gdb-peda$ s
[----------------------------------registers-----------------------------------]
RAX: 0x7ffff7faad60 --> 0x7fffffffe298 --> 0x7fffffffe579 ("SHELL=/bin/bash")
RBX: 0x0 
RCX: 0x7ffff7fa7718 --> 0x7ffff7fa9a40 --> 0x0 
RDX: 0x7fffffffe298 --> 0x7fffffffe579 ("SHELL=/bin/bash")
RSI: 0x7fffffffe288 --> 0x7fffffffe566 ("/root/work/2/test2")
RDI: 0x1 
RBP: 0x401190 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
RIP: 0x4011a0 (<__libc_csu_init+16>:	mov    r13,rsi)
R8 : 0x7ffff7fa9a50 --> 0x4 
R9 : 0x7ffff7fe4780 (<_dl_fini>:	push   rbp)
R10: 0x0 
R11: 0x27 ("'")
R12: 0x401040 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe280 --> 0x1 
R14: 0x7fffffffe298 --> 0x7fffffffe579 ("SHELL=/bin/bash")
R15: 0x403e10 --> 0x401120 (<frame_dummy>:	jmp    0x4010b0 <register_tm_clones>)
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x401199 <__libc_csu_init+9>:	push   r14
   0x40119b <__libc_csu_init+11>:	mov    r14,rdx
   0x40119e <__libc_csu_init+14>:	push   r13
=> 0x4011a0 <__libc_csu_init+16>:	mov    r13,rsi
   0x4011a3 <__libc_csu_init+19>:	push   r12
   0x4011a5 <__libc_csu_init+21>:	mov    r12d,edi
   0x4011a8 <__libc_csu_init+24>:	push   rbp
   0x4011a9 <__libc_csu_init+25>:	lea    rbp,[rip+0x2c68]        # 0x403e18
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
0008| 0x7fffffffe198 --> 0x0 
0016| 0x7fffffffe1a0 --> 0x0 
0024| 0x7fffffffe1a8 --> 0x7ffff7e14b4a (<__libc_start_main+122>:	mov    rax,QWORD PTR [rip+0x19230f]        # 0x7ffff7fa6e60)
0032| 0x7fffffffe1b0 --> 0x0 
0040| 0x7fffffffe1b8 --> 0x7fffffffe288 --> 0x7fffffffe566 ("/root/work/2/test2")
0048| 0x7fffffffe1c0 --> 0x100040000 
0056| 0x7fffffffe1c8 --> 0x401151 (<main>:	push   rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000004011a0 in __libc_csu_init ()
gdb-peda$ x/64b 0x7fffffffe190
0x7fffffffe190:	0x80	0xe2	0xff	0xff	0xff	0x7f	0x00	0x00
0x7fffffffe198:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1a0:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1a8:	0x4a	0x4b	0xe1	0xf7	0xff	0x7f	0x00	0x00
0x7fffffffe1b0:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1b8:	0x88	0xe2	0xff	0xff	0xff	0x7f	0x00	0x00
0x7fffffffe1c0:	0x00	0x00	0x04	0x00	0x01	0x00	0x00	0x00
0x7fffffffe1c8:	0x51	0x11	0x40	0x00	0x00	0x00	0x00	0x00
gdb-peda$ info registers
rax            0x7ffff7faad60      0x7ffff7faad60
rbx            0x0                 0x0
rcx            0x7ffff7fa7718      0x7ffff7fa7718
rdx            0x7fffffffe298      0x7fffffffe298
rsi            0x7fffffffe288      0x7fffffffe288
rdi            0x1                 0x1
rbp            0x401190            0x401190 <__libc_csu_init>
rsp            0x7fffffffe190      0x7fffffffe190
r8             0x7ffff7fa9a50      0x7ffff7fa9a50
r9             0x7ffff7fe4780      0x7ffff7fe4780
r10            0x0                 0x0
r11            0x27                0x27
r12            0x401040            0x401040
r13            0x7fffffffe280      0x7fffffffe280
r14            0x7fffffffe298      0x7fffffffe298
r15            0x403e10            0x403e10
rip            0x4011a0            0x4011a0 <__libc_csu_init+16>
eflags         0x206               [ PF IF ]
cs             0x33                0x33
ss             0x2b                0x2b
ds             0x0                 0x0
es             0x0                 0x0
fs             0x0                 0x0
gs             0x0                 0x0


以上より, r13をpushしたことにより, 0x7fffffffe190の値が書き換わっていることがわかる。
その上でmainを見ると以下のように, main+43でmov rax,QWORD PTR [rbp-0x10]を実行している。ここで, rbpは0x7fffffffe1a0なので, [rbp-10] \Leftrightarrow [0x7fffffffe190] \Leftrightarrow 0x7fffffff2e80である。そのため, raxにこの値が入り, printf()で参照されるため, segmentation faultは発生しないという訳である。

gdb-peda$ disas main
Dump of assembler code for function main:
   0x0000000000401151 <+0>:	push   rbp
   0x0000000000401152 <+1>:	mov    rbp,rsp
   0x0000000000401155 <+4>:	sub    rsp,0x10
   0x0000000000401159 <+8>:	lea    rax,[rip+0xea8]        # 0x402008
   0x0000000000401160 <+15>:	mov    QWORD PTR [rbp-0x8],rax
   0x0000000000401164 <+19>:	mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000401168 <+23>:	mov    rdi,rax
   0x000000000040116b <+26>:	call   0x401030 <puts@plt>
   0x0000000000401170 <+31>:	mov    rax,QWORD PTR [rbp-0x10]
   0x0000000000401174 <+35>:	mov    rdi,rax
   0x0000000000401177 <+38>:	call   0x401122 <func>
   0x000000000040117c <+43>:	mov    rax,QWORD PTR [rbp-0x10]
   0x0000000000401180 <+47>:	mov    rdi,rax
   0x0000000000401183 <+50>:	call   0x401030 <puts@plt>
   0x0000000000401188 <+55>:	mov    eax,0x0
   0x000000000040118d <+60>:	leave  
   0x000000000040118e <+61>:	ret    
End of assembler dump.
gdb-peda$ info registers 
The program has no registers now.
gdb-peda$ b main+43
Function "main+43" not defined.
gdb-peda$ b *0x401170
Breakpoint 1 at 0x401170
gdb-peda$ r
Starting program: /root/work/2/test2 
A
[----------------------------------registers-----------------------------------]
RAX: 0x2 
RBX: 0x0 
RCX: 0x7ffff7ed9904 (<__GI___libc_write+20>:	cmp    rax,0xfffffffffffff000)
RDX: 0x7ffff7faa580 --> 0x0 
RSI: 0x405260 --> 0xa41 ('A\n')
RDI: 0x0 
RBP: 0x7fffffffe1a0 --> 0x401190 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
RIP: 0x401170 (<main+31>:	mov    rax,QWORD PTR [rbp-0x10])
R8 : 0x2 
R9 : 0x7ffff7e92f90 (<__wcpcpy>:	lea    rax,[rdi-0x4])
R10: 0x410 
R11: 0x246 
R12: 0x401040 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe280 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x401164 <main+19>:	mov    rax,QWORD PTR [rbp-0x8]
   0x401168 <main+23>:	mov    rdi,rax
   0x40116b <main+26>:	call   0x401030 <puts@plt>
=> 0x401170 <main+31>:	mov    rax,QWORD PTR [rbp-0x10]
   0x401174 <main+35>:	mov    rdi,rax
   0x401177 <main+38>:	call   0x401122 <func>
   0x40117c <main+43>:	mov    rax,QWORD PTR [rbp-0x10]
   0x401180 <main+47>:	mov    rdi,rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
0008| 0x7fffffffe198 --> 0x402008 --> 0x3b031b0100000041 
0016| 0x7fffffffe1a0 --> 0x401190 (<__libc_csu_init>:	push   r15)
0024| 0x7fffffffe1a8 --> 0x7ffff7e14bbb (<__libc_start_main+235>:	mov    edi,eax)
0032| 0x7fffffffe1b0 --> 0x0 
0040| 0x7fffffffe1b8 --> 0x7fffffffe288 --> 0x7fffffffe566 ("/root/work/2/test2")
0048| 0x7fffffffe1c0 --> 0x100040000 
0056| 0x7fffffffe1c8 --> 0x401151 (<main>:	push   rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x0000000000401170 in main ()
gdb-peda$ info registers 
rax            0x2                 0x2
rbx            0x0                 0x0
rcx            0x7ffff7ed9904      0x7ffff7ed9904
rdx            0x7ffff7faa580      0x7ffff7faa580
rsi            0x405260            0x405260
rdi            0x0                 0x0
rbp            0x7fffffffe1a0      0x7fffffffe1a0
rsp            0x7fffffffe190      0x7fffffffe190
r8             0x2                 0x2
r9             0x7ffff7e92f90      0x7ffff7e92f90
r10            0x410               0x410
r11            0x246               0x246
r12            0x401040            0x401040
r13            0x7fffffffe280      0x7fffffffe280
r14            0x0                 0x0
r15            0x0                 0x0
rip            0x401170            0x401170 <main+31>
eflags         0x246               [ PF ZF IF ]
cs             0x33                0x33
ss             0x2b                0x2b
ds             0x0                 0x0
es             0x0                 0x0
fs             0x0                 0x0
gs             0x0                 0x0
gdb-peda$ x/64b 0x0x7fffffffe190
Invalid number "0x0x7fffffffe190".
gdb-peda$ x/64b 0x7fffffffe190
0x7fffffffe190:	0x80	0xe2	0xff	0xff	0xff	0x7f	0x00	0x00
0x7fffffffe198:	0x08	0x20	0x40	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1a0:	0x90	0x11	0x40	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1a8:	0xbb	0x4b	0xe1	0xf7	0xff	0x7f	0x00	0x00
0x7fffffffe1b0:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x7fffffffe1b8:	0x88	0xe2	0xff	0xff	0xff	0x7f	0x00	0x00
0x7fffffffe1c0:	0x00	0x00	0x04	0x00	0x01	0x00	0x00	0x00
0x7fffffffe1c8:	0x51	0x11	0x40	0x00	0x00	0x00	0x00	0x00
gdb-peda$ s
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe280 --> 0x1 
RBX: 0x0 
RCX: 0x7ffff7ed9904 (<__GI___libc_write+20>:	cmp    rax,0xfffffffffffff000)
RDX: 0x7ffff7faa580 --> 0x0 
RSI: 0x405260 --> 0xa41 ('A\n')
RDI: 0x0 
RBP: 0x7fffffffe1a0 --> 0x401190 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
RIP: 0x401174 (<main+35>:	mov    rdi,rax)
R8 : 0x2 
R9 : 0x7ffff7e92f90 (<__wcpcpy>:	lea    rax,[rdi-0x4])
R10: 0x410 
R11: 0x246 
R12: 0x401040 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe280 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x401168 <main+23>:	mov    rdi,rax
   0x40116b <main+26>:	call   0x401030 <puts@plt>
   0x401170 <main+31>:	mov    rax,QWORD PTR [rbp-0x10]
=> 0x401174 <main+35>:	mov    rdi,rax
   0x401177 <main+38>:	call   0x401122 <func>
   0x40117c <main+43>:	mov    rax,QWORD PTR [rbp-0x10]
   0x401180 <main+47>:	mov    rdi,rax
   0x401183 <main+50>:	call   0x401030 <puts@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
0008| 0x7fffffffe198 --> 0x402008 --> 0x3b031b0100000041 
0016| 0x7fffffffe1a0 --> 0x401190 (<__libc_csu_init>:	push   r15)
0024| 0x7fffffffe1a8 --> 0x7ffff7e14bbb (<__libc_start_main+235>:	mov    edi,eax)
0032| 0x7fffffffe1b0 --> 0x0 
0040| 0x7fffffffe1b8 --> 0x7fffffffe288 --> 0x7fffffffe566 ("/root/work/2/test2")
0048| 0x7fffffffe1c0 --> 0x100040000 
0056| 0x7fffffffe1c8 --> 0x401151 (<main>:	push   rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000000000401174 in main ()
gdb-peda$ s
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe280 --> 0x1 
RBX: 0x0 
RCX: 0x7ffff7ed9904 (<__GI___libc_write+20>:	cmp    rax,0xfffffffffffff000)
RDX: 0x7ffff7faa580 --> 0x0 
RSI: 0x405260 --> 0xa41 ('A\n')
RDI: 0x7fffffffe280 --> 0x1 
RBP: 0x7fffffffe1a0 --> 0x401190 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
RIP: 0x401177 (<main+38>:	call   0x401122 <func>)
R8 : 0x2 
R9 : 0x7ffff7e92f90 (<__wcpcpy>:	lea    rax,[rdi-0x4])
R10: 0x410 
R11: 0x246 
R12: 0x401040 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe280 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40116b <main+26>:	call   0x401030 <puts@plt>
   0x401170 <main+31>:	mov    rax,QWORD PTR [rbp-0x10]
   0x401174 <main+35>:	mov    rdi,rax
=> 0x401177 <main+38>:	call   0x401122 <func>
   0x40117c <main+43>:	mov    rax,QWORD PTR [rbp-0x10]
   0x401180 <main+47>:	mov    rdi,rax
   0x401183 <main+50>:	call   0x401030 <puts@plt>
   0x401188 <main+55>:	mov    eax,0x0
Guessed arguments:
arg[0]: 0x7fffffffe280 --> 0x1 
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe190 --> 0x7fffffffe280 --> 0x1 
0008| 0x7fffffffe198 --> 0x402008 --> 0x3b031b0100000041 
0016| 0x7fffffffe1a0 --> 0x401190 (<__libc_csu_init>:	push   r15)
0024| 0x7fffffffe1a8 --> 0x7ffff7e14bbb (<__libc_start_main+235>:	mov    edi,eax)
0032| 0x7fffffffe1b0 --> 0x0 
0040| 0x7fffffffe1b8 --> 0x7fffffffe288 --> 0x7fffffffe566 ("/root/work/2/test2")
0048| 0x7fffffffe1c0 --> 0x100040000 
0056| 0x7fffffffe1c8 --> 0x401151 (<main>:	push   rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000000000401177 in main ()
gdb-peda$ info registers 
rax            0x7fffffffe280      0x7fffffffe280
rbx            0x0                 0x0
rcx            0x7ffff7ed9904      0x7ffff7ed9904
rdx            0x7ffff7faa580      0x7ffff7faa580
rsi            0x405260            0x405260
rdi            0x7fffffffe280      0x7fffffffe280
rbp            0x7fffffffe1a0      0x7fffffffe1a0
rsp            0x7fffffffe190      0x7fffffffe190
r8             0x2                 0x2
r9             0x7ffff7e92f90      0x7ffff7e92f90
r10            0x410               0x410
r11            0x246               0x246
r12            0x401040            0x401040
r13            0x7fffffffe280      0x7fffffffe280
r14            0x0                 0x0
r15            0x0                 0x0
rip            0x401177            0x401177 <main+38>
eflags         0x246               [ PF ZF IF ]
cs             0x33                0x33
ss             0x2b                0x2b
ds             0x0                 0x0
es             0x0                 0x0
fs             0x0                 0x0
gs             0x0                 0x0

# 最後に

未初期化変数は不安定なので今回のようなことが起きる。変数は初期化するべきである

また, 今回の問題の解決に@zunzun_meow (opens new window)さん, @Nperair (opens new window)さん, @yuki_obuchi (opens new window)さん, onokatio_ (opens new window)さん, @xrekkusu (opens new window)さんにお手伝いいただきました。この場をお借りして感謝申し上げます。

以上。

Last Updated: 9ヶ月前