# 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]
[0x7fffffffe190]
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)さんにお手伝いいただきました。この場をお借りして感謝申し上げます。
以上。