パスワードを忘れた? アカウント作成
この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。

ZDNetのポインタ解説記事で勘違い」記事へのコメント

  • gccに限った話になるのかもしれませんが、"-O0"(最適化なし)では予想通りSEGVしましたが、"-O2"するとコケないみたいです。



    もしかして無意識にオプティマイザに助けられていて意識するのを忘れていたとか?
    --
    -- やさいはけんこうにいちば〜ん!
    • by okky (2487) on 2008年04月01日 18時25分 (#1323348) ホームページ 日記
      いや、こけないわけではなくて…。

          movl    $5, %eax
          movl    %eax, 4(%esp)
          movl    $.LC0, (%esp)
          call    printf
      *n に入るべき値を %eax に代入しなおして、そっちを printf() に渡しているだけで。

      実はその前の1行:

          movl    $5, (%eax)
      が死ぬほどやばい。
      %eax は main() 関数に入ってきて以来、初期化されていない。
      その「どこを指しているんだかよく判らないもの」が指している所にドガンッと5を書いちゃってる

      これがヒープを壊しているのか、スタックを壊しているのかは判らないが(なんかヒープのどっかを指しているっぽい)、とにかく、どこかを壊しているのは間違いない。今回は小さなプログラムだったので、破綻が表出する前に終了しているのに過ぎない。
      --
      fjの教祖様
      親コメント
      • by Anonymous Coward
        gccの癖を見ると、スタートアップコードがmainを呼ぶときにeaxを使っていて、おそらくその場所は&argcか、argvあたりなんではないかと。
        • by Anonymous Coward on 2008年04月01日 23時01分 (#1323535)
          オプティマイズされていないコードでは,5を代入する前にnの値を初期化しています。

          movl -8(%ebp), %eax
          movl $5, (%eax)

          何処のアドレスを入れているのか興味を持ったので調べてみました。
          ちなみに環境は,openbsd+gcc version 3.3.5 (propolice)です。

          gcc -g -O test.cでコンパイルした場合。
          nは初期化されず,textセグメント内の__finiを指し示していたので,当然Segmentation fault.で落ちました。

          (gdb) b 6
          Breakpoint 1 at 0x1c0005e1: file test.c, line 6.
          (gdb) r
          Starting program: /home/fooooooooo/a.out

          Breakpoint 1, main () at test.c:6
          (gdb) p n
          $1 = (int *) 0x1c000620
          (gdb) x/2i 0x1c000620
          0x1c000620 <__fini>: call 0x1c0005b0
          0x1c000625 <__fini+5>: ret
          (gdb)
          (gdb) c
          Continuing.

          Program received signal SIGSEGV, Segmentation fault.
          main () at test.c:6
          gcc -g test.cでコンパイルした場合。
          nはargcらしき領域のアドレスで初期化されていました。argcの内容が上書きされるだけなので,とりあえず動作はする。
          でも,なんでこんな初期化をしているのかと言う,疑問は残ります??

          (gdb) b 6
          Breakpoint 1 at 0x1c0005e8: file test.c, line 6.
          (gdb) r barrrr
          Starting program: /home/fooooooooo/a.out barrrr

          Breakpoint 1, main () at test.c:6
          (gdb) p n
          $1 = (int *) 0xcfbe87e8
          (gdb) x/2s 0xcfbe87e8
          0xcfbe87e8: "/home/fooooooooo/a.out"
          0xcfbe87ff: "barrrr"
          (gdb) c
          Continuing.
          5

          Program exited normally.
          親コメント
          • by Anonymous Coward

            オプティマイズされていないコードでは,5を代入する前にnの値を初期化しています。

            movl -8(%ebp), %eax
            movl $5, (%eax)

            バーカ -8(%ebp) が n だっつーの。

            • by Anonymous Coward
              あ,オプティマイズしていない時は,nはスタック取られていたのね。
              両方とも,レジスタに割り付けられているものと,勘違いしてました。
              出直してきます.... orz.
        • gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33)
          Fedora8 x86_64

          という環境に移って実行したところ、

          main:
          .LFB13:
                          .file 1 "afo.c"
                          .loc 1 4 0
                          subq $8, %rsp
          .LCFI0:
                          .loc 1 7 0
                          movl $5, %esi
                          .loc 1 6 0
                          movl $5, (%rax)
          .LVL0:
                          .loc 1 7 0
                          movl $.LC0, %edi
                          xorl %eax, %eax
          .LVL1:
                          call printf
                          .loc 1 9 0
                          xorl %eax, %eax
                          addq $8, %rsp
                          ret
          .LFE13:


          というコードになりました。で、%rax が指しているのは environ です。
          どうやら環境変数の開始アドレスを示しているポインタの末尾4 byte を 0x00000005 に変更しているようです。
          # 32bit だと、environ アドレスそのものを 0x00000005 に変更していることになるかと予測される。

          (gdb) break 4
          Breakpoint 1 at 0x4004c0: file afo.c, line 4.
          (gdb) run
          Starting program: /home/okuyama/temp/a.out
           
          Breakpoint 1, main () at afo.c:4
          4       {
          Missing separate debuginfos, use: debuginfo-install glibc.x86_64
          (gdb) info register rax rip
          rax            0x33331539f0     219900361200
          rip            0x4004c0 0x4004c0 <main>
          (gdb) disass
          Dump of assembler code for function main:
          0x00000000004004c0 <main+0>:    sub    $0x8,%rsp
          0x00000000004004c4 <main+4>:    mov    $0x5,%esi
          0x00000000004004c9 <main+9>:    movl   $0x5,(%rax)
          0x00000000004004cf <main+15>:   mov    $0x4005e8,%edi
          0x00000000004004d4 <main+20>:   xor    %eax,%eax
          0x00000000004004d6 <main+22>:   callq  0x4003b8 <printf@plt>
          0x00000000004004db <main+27>:   xor    %eax,%eax
          0x00000000004004dd <main+29>:   add    $0x8,%rsp
          0x00000000004004e1 <main+33>:   retq
          End of assembler dump.
          (gdb) x/4x 0x33331539f0
          0x33331539f0 <environ>: 0x611d6868      0x00007fff      0x00000000      0x00000000
          (gdb) stepi 3
          7         printf("%d\n", *n);
          (gdb) info register rax rip
          rax            0x33331539f0     219900361200
          rip            0x4004cf 0x4004cf <main+15>
          (gdb) x/4x 0x33331539f0
          0x33331539f0 <environ>: 0x00000005      0x00007fff      0x00000000      0x00000000
          というわけで、この代入実行後、32bit 環境で、環境変数を参照したら一撃で絶命ですね。

          コンパイラが助けてくれたわけではないようです。単に破綻に気がつく前に終了しているだけ。
          --
          fjの教祖様
          親コメント
      • by Anonymous Coward
        そんな処理系依存の話をするのならば、実行コードとスタックとヒープが完全に別のメモリ空間になっていて、ヒープ空間はint*の値が如何様な値を取ろうがメモリ空間が存在する処理系で、プロセス毎にそのメモリ空間のどこをアクセスしようが問題ない処理系であるなら、修正前のソースコードでもまったく問題ないわけだよ。

        しかし、多くの人はそんな過保護な環境ではないのだけどね。
        • by Anonymous Coward

          実行コードとスタックとヒープが完全に別のメモリ空間になっていて、ヒープ空間はint*の値が如何様な値を取ろうがメモリ空間が存在する処理系で、プロセス毎にそのメモリ空間のどこをアクセスしようが問題ない処理系であるなら、修正前のソースコードでもまったく問題ない


          int* p1;
          int* p2;

          *p1=1;
          *p2=2;
          printf("%d",*p1); /* 先生!! メモリが破壊されました!! */

          #if 0
          まさか全てのポインタは自動的に異なるアドレスで初期化されて、
          そのアドレスは絶対他の変数にアロケートされない環境ですか!?
          なんて過保護な(w
          #end if
    • gcc4.0 on Leopardだと 無印、-O -O2 -O3 -Os すべて期待通り bus error です。
      --
      Best regards, でぃーすけ
      親コメント
    • by Anonymous Coward
      アホか。
      スタックフレーム上に変数こさえるかレジスタに割り当てるかの違いだろ。
      -O2 でも eax の値の番地壊してるよ。

192.168.0.1は、私が使っている IPアドレスですので勝手に使わないでください --- ある通りすがり

処理中...