StackExchangeで見た
システムプログラミング案件
八重樫 剛史
https://twitter.com/hogegashi
https://github.com/yaegashi
自己紹介
仕事とか
趣味とか
StackExchange 見てますか?
StackExchange 効能
私の主戦場: Unix & Linux
私の戦績: Unix & Linux
U&L システムプログラミング案件
各質問にはタグがついており、自分が回答した質問もタグで検索できる
U&L システムプログラミング案件
むかし自分が回答した /dynamic-linking タグの質問
U&L システムプログラミング案件
拙い語彙の英語でも通じるし upvote や accept がもらえます
U&L システムプログラミングおもしろ案件
U&L システムプログラミングおもしろ案件
Q: 実行中にシステムコールを全く行わない unix コマンドってある?
A: Unix プロセスって終了するのにも exit() を呼んで�システムコールしないといけないから、ないよ。
・・・本当だろうか?
Q: システムコールを使わずに終了するプログラム書ける?
安直な回答
int main()
{
return 0;
}
Q: システムコールを使わずに終了するプログラム書ける?
実際には libc ランタイムが main() の前後でたくさんシステムコール呼んでる
$ echo 'int main(){return 0;}' | gcc -static -x c -�$ strace ./a.out�execve("./a.out", ["./a.out"], [/* 56 vars */]) = 0�uname({sysname="Linux", nodename="hf", ...}) = 0�brk(NULL) = 0xeec000�brk(0xeed1c0) = 0xeed1c0�arch_prctl(ARCH_SET_FS, 0xeec880) = 0�readlink("/proc/self/exe", "/home/yaegashi/a.out", 4096) = 20�brk(0xf0e1c0) = 0xf0e1c0�brk(0xf0f000) = 0xf0f000�access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)�exit_group(0) = ?�+++ exited with 0 +++
Q: システムコールを使わずに終了するプログラム書ける?
libc ランタイムなしで 0 を返す関数だけのプログラムを作って実行すると…
$ echo 'int _start(){return 0;}' | gcc -x c - -nostdlib -nostartfiles�$ strace ./a.out�execve("./a.out", ["./a.out"], [/* 56 vars */]) = 0�--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x1} ---�+++ killed by SIGSEGV (core dumped) +++�Segmentation fault (core dumped)
Q: システムコールを使わずに終了するプログラム書ける?
いまのところの結論
ところでシグナルって何種類あるのか?
・・・システムコールを使わずに、何種類のシグナルを出せるだろうか?
とりあえず Linux on x86_64 という条件でチャレンジしてみよう!
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
SIGSEGVをシステムコールなしで出す
もっとも簡単?
$ echo 'void _start(){}' | gcc -x c - -nostdlib -nostartfiles�$ strace ./a.out�execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0�--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x1} ---�+++ killed by SIGSEGV (core dumped) +++�Segmentation fault (core dumped)
SIGSEGVをシステムコールなしで出す
ちなみに空の関数を最適化つきでコンパイルすると、�GCCは repz retq という見慣れないコードを吐く
http://repzret.org というサイトに解説がある
$ echo 'void _start(){}' | gcc -O -x c - -nostartfiles -nostdlib�$ objdump -d�a.out: file format elf64-x86-64�Disassembly of section .text:�0000000000400144 <_start>:� 400144: f3 c3 repz retq
SIGFPEをシステムコールなしで出す
整数ゼロ除算などで発生する
$ echo 'int _start(){return 1/0;}' | gcc -x c - -nostartfiles -nostdlib�<stdin>: In function '_start':�<stdin>:1:22: warning: division by zero [-Wdiv-by-zero]�$ strace ./a.out�execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0�--- SIGFPE {si_signo=SIGFPE, si_code=FPE_INTDIV, si_addr=0x40014f} ---�+++ killed by SIGFPE (core dumped) +++�Floating point exception (core dumped)
SIGFPEをシステムコールなしで出す
あれ、でも 1/0 って定数・即値じゃないのかね?
$ echo 'int _start(){return 1/0;}' | gcc -O -x c - -nostartfiles -nostdlib�<stdin>: In function '_start':�<stdin>:1:22: warning: division by zero [-Wdiv-by-zero]�$ objdump -d�a.out: file format elf64-x86-64�Disassembly of section .text:�0000000000400144 <_start>:� 400144: b8 01 00 00 00 mov $0x1,%eax� 400149: b9 00 00 00 00 mov $0x0,%ecx� 40014e: 99 cltd � 40014f: f7 f9 idiv %ecx� 400151: c3 retq
SIGFPEをシステムコールなしで出す
Clang は GCC とはまた違ったコードを吐くぞ?
$ echo 'int _start(){return 1/0;}' |
clang -O -x c - -nostartfiles -nostdlib�<stdin>:1:22: warning: division by zero is undefined [-Wdivision-by-zero]�int _start(){return 1/0;}� ^~�1 warning generated.�$ objdump -d�a.out: file format elf64-x86-64�Disassembly of section .text:�0000000000400150 <_start>:� 400150: c3 retq
SIGILLをシステムコールなしで出す
このへんからアセンブリ言語を使う必要がでてくる
$ echo '.global _start;_start:mov %ax,%cs' |
gcc -x assembler - -nostdlib -nostartfiles�$ strace ./a.out�execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0�--- SIGILL {si_signo=SIGILL, si_code=ILL_ILLOPN, si_addr=0x4000d4} ---�+++ killed by SIGILL (core dumped) +++�Illegal instruction (core dumped)
SIGTRAPをシステムコールなしで出す
有名問題 (BINARY HACKS にも書かれている)
$ echo 'int3' | gcc -x assembler - -nostdlib -nostartfiles�$ strace ./a.out�execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0�--- SIGTRAP {si_signo=SIGTRAP, si_code=SI_KERNEL} ---�+++ killed by SIGTRAP (core dumped) +++�Trace/breakpoint trap (core dumped)
SIGBUSをシステムコールなしで出す
SIGBUSをシステムコールなしで出す
実は x86 は EFLAGS の AC ビットというのを立てると�アラインメントなしのメモリアクセスで例外を出すようになるらしい
stackoverflow.com: How to get a “bus error”?�orchistro.tistory.com: How to generate SIGBUS on x86 processors
$ echo 'pushf;orl $0x40000,(%rsp);popf;movl $0,1' |� gcc -x assembler - -nostdlib -nostartfiles�$ strace ./a.out�execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0�--- SIGBUS {si_signo=SIGBUS, si_code=BUS_ADRALN, si_addr=0} ---�+++ killed by SIGBUS (core dumped) +++�Bus error (core dumped)
SIGKILLをシステムコールなしで出す
ちょっとずるい方法�無限ループするプログラムを作って CPU time に limit を設定して殺してもらう
$ echo 'jmp .' | gcc -O -x assembler - -nostdlib -nostartfiles�$ ulimit -t 1�$ strace -rt ./a.out� 0.000000 execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0� 0.997027 +++ killed by SIGKILL +++�Killed
SIGXCPUをシステムコールなしで出す
おまけ�CPU time に soft limit を設定すると SIGKILL の前に SIGXCPU を送ってもらえる
$ echo 'jmp .' | gcc -O -x assembler - -nostdlib -nostartfiles�$ ulimit -t 10�$ ulimit -S -t 1�$ strace -rt ./a.out� 0.000000 execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0� 0.997119 --- SIGXCPU {si_signo=SIGXCPU, si_code=SI_KERNEL} ---� 0.118958 +++ killed by SIGXCPU (core dumped) +++�CPU time limit exceeded (core dumped)
システムコールなしで出せるシグナルはまだあるだろうか?
他にもうまい方法を考案された方はぜひ教えてください!!
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
まとめ
おわり