天泣記

2005-11-02 (Wed)

#1

GCC Bugzilla で、Mark Mitchell がさまざまなバグの優先順位をつけ直した。そーか、優先順位ってのはそう使うのか。

GCC Bug 22127GCC Bug 22429 はめでたく(?) P1 になり、とくに後者は showstopper だそうなので、4.1 までには直るようだ。(まぁ、showstopper なのはこれ以外にもたくさんあるようだけど)

前者も修正が入りそうな気配なのはなかなか良い。

2005-11-03 (Thu)

#1

さっそく GCC Bug 22429 が直ったようなので、試してみる... が、直ってない?

... がー。cvs から svn に移行してやがった。

chkbuild の sample/build-gcc-ruby で、とりあえず gcc を svn で取って来るようにしてもう一度試すと、直っているようである。

#2

取って来るだけじゃなくて、cvs でやっているようにいろいろと情報を出すにはどうしたらいいか。ViewCVS による diff へのリンクとか。

でも、svn だと同じ場所にあっても同じファイルとは限らないし、適切な表示とはそもそもどういうものだろうな。

しばらく考えて適切であろうという表示形式を思いつく。でも、やはり svn up 前後でどうファイルが移動したかの情報が必要だなぁ。.svn をちょっと覗いた限りではその情報は見当たらないのだが、どうしたもんかね。

2005-11-05 (Sat)

#1

とある文庫本の後書きで、ページ数をオーバーして本文二段組になったという話があり、二段組にするとどうしてページ数を減るのか考える。

直観的に、ページ下部の空白が減るからという理由を思いつく。

さらにしばらく考えて、段落末尾における文末から行端までの空白が減るからと、理由を考え直す。とすれば、段落が多いほど減りやすいはずである。

ここで、件の文庫本を含む、ひとつの段落が数行以下であることが多いとあるジャンルを考えると、かなり効きそうである。

2005-11-09 (Wed)

#1

ふと唐突に思いついたのだが、(言語内) DSL っぽくしたい場合と関数呼び出しの括弧を使いたくない場合に相関はあるだろうか。

2005-11-10 (Thu)

#1

svn には working directory に lock があることに気がつく

chkbuild で gcc を svn update するところで timeout して殺してしまったら、lock が残ってうまくない

SIGINT, SIGTERM, SIGKILL の順に (5秒待ちつつ) 送るのだが、SIGINT, SIGTERM では死んでくれなくて、SIGKILL までいってしまったようだ

行儀良く死んでくれないのはなぜ? 5秒では足りないということか?

なお、lock を外すのは (lock がかかっているときのエラーメッセージに出て来るが) svn cleanup であった。

2005-11-11 (Fri)

#1

おぉ、いつのまにか returns_twice という attribute が出来ていたのか。

2005-11-12 (Sat)

#1

ViewCVS は ViewVC に改名した模様

#2

ふと

% grep '\<regist\>' **/*(.)
doc/ChangeLog-1.8.0:    * ext/marshal/marshal.c (r_object): remove temporal regist for
doc/ChangeLog-1.8.0:    * ext/marshal/marshal.c (r_object): forgot to re-regist structs in
ext/tk/MANUAL_tcltklib.eng:       : to _eval and regist the command once, after that, the
lib/drb/extserv.rb:      @invoker = ro.regist(name, DRbObject.new(self, @server.uri))
lib/drb/extservm.rb:    def regist(name, ro)

2005-11-14 (Mon)

#1

request-response 型プロトコルにおけるサーバにおいて、signal で安全にサーバを停止するにはどうしたらいいか?

次のような構造のサーバを考える。

def process_connection(s)
  while req = read_request(s)
    process_request(req, s)
  end
  s.close
end

TCPServer.open(...) {|serv|
  loop {
    Thread.new(serv.accept) {|s|
      process_connection(s)
    }
  }
}

ここで、要求は、signal (仮に SIGTERM としよう) を受け取った場合に、新しいコネクションを accept するのを終了し、また、既存のコネクションに対応するスレッドはすでに始まっているリクエストの処理が終りしだい終了し、すべてのリクエストの処理が終ったら全体が終了するというものである。

accept するのを停止するのは難しくなさそうである。accept のループを独立したスレッドで行って、SIGTERM のハンドラからそのスレッドを殺してしまえば良い。殺すタイミングによって accept が行われたり行われなかったりするかも知れないが、その違いはクライアントからは見えないので問題ない。

各コネクションの処理を止めるのは難しい。リクエストを待っているとき (もしくは読んでいる途中) には殺しても良いが、実際に処理をしている (または結果をクライアントに書き出している) ときには殺せない。外部からその違いをテストして殺すとレースコンディションになるので、そのスレッド自身が適切なタイミングで自殺する必要がある。すぐに思いつくのはフラグを用意して通信することだが、リクエストを読むのにブロックしているときにフラグをセットしてもそのスレッド自身はフラグを検査できないのでうまくいかない。

さて?

#2

Linux 2.6 ってパイプのサイズが 64K になってる?

% ruby -rio/nonblock -e 'r, w = IO.pipe; w.nonblock = true; p w.syswrite("a" * (1024 * 1024))'
65536
% uname -a
Linux nute 2.6.12-1-686 #1 Tue Sep 27 12:52:50 JST 2005 i686 GNU/Linux

2005-11-15 (Tue)

#1

signal についてしばらく考えて、3つ方法を考え付く。

#2

スクリプト言語Rubyの拡張可能な多言語テキスト処理の実装, 松本行弘, 縄手雅彦, 情報処理学会論文誌 Vol.46, No. 11

2005-11-17 (Thu)

#1 アルゴリズミックアタック [CODE blog]
#2 最悪オーダじゅーよー [CODE blog]

2005-11-22 (Tue)

#1

Gauche のScheme:初心者の質問箱というので不完全文字列というものの話が出ている。

探してみると、<URL:http://www.shiro.dreamhost.com/scheme/gauche/memo-str-j.html> に説明がある。

不正なバイトを文字扱いするという話は、なんというか、Emacs 20.3 を腐る程落してレポートした経験を思い出すものがある。

実際に同じ性質になるかどうかはわからないけれど。

#2

WEBrick と XMLRPC の脆弱性情報が公開されたが、公開に至るまでの感想としては、催促するのに飽きたというのが実感である。

IPA 経由にするともしかしたら自分で催促しなくてもいいかも知れないので、今度何かあればそっちにしよう。

#3

TypeKey API を読んでみる

うぅむ、seconds since the epoch を使ってるなぁ

leap second は無いものとして扱っている模様

#4 daemon と制御端末 [CODE blog]

4.4BSD には daemon という関数がある。これは

int daemon(int nochdir, int noclose);

という形式で、呼び出したプロセス自体をデーモン化する。

最近の NetBSD での定義は次のようになっている。

int
daemon(nochdir, noclose)
        int nochdir, noclose;
{
        int fd;

        switch (fork()) {
        case -1:
                return (-1);
        case 0:
                break;
        default:
                _exit(0);
        }

        if (setsid() == -1)
                return (-1);

        if (!nochdir)
                (void)chdir("/");

        if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
                (void)dup2(fd, STDIN_FILENO);
                (void)dup2(fd, STDOUT_FILENO);
                (void)dup2(fd, STDERR_FILENO);
                if (fd > STDERR_FILENO)
                        (void)close(fd);
        }
        return (0);
}

http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/lib/libc/gen/daemon.c?rev=1.9&content-type=text/x-cvsweb-markup

つまり、(カレントディレクトリと標準入出力のあたりを除けば) fork して setsid するというものである。

さて、ここでデーモンを作る作法は昔からいろいろといわれているが、そのなかに、間違って制御端末を得てしまわないようにひとつ余計に fork する、という話がある。

でも、この実装はそうしていないようなんだけどいいのだろうか?

Unix では、プロセスは高々一つの制御端末を持つ。で、制御端末を持っていると、その端末から ^C という文字で SIGINT を送ったりしてプロセスを制御できる。でも、デーモンが不用意に制御端末を持ってしまうと、意図せずしてその端末から制御されてしまうかも知れない。デーモンを制御する権限を持っていない人がそういう端末を持てるとこれは権限上昇になるので危険である。

ここで、プロセスが制御端末を得る方法は、たしか規格では決まっていないのだが、伝統的には、制御端末を持っていないプロセスリーダーが端末を open することである。 (もっと条件があったかも?) したがって、端末を ~/public_html 下にシンボリックリンクして HTTP サーバに open させるなどの攻撃が考えられる。

もし、daemon 関数が余計に fork していれば、プロセスがプロセスリーダではなくなるので大丈夫なのだが、そうしてないわけで、daemon 関数でデーモンにした場合、大丈夫なのだろうか?

と、疑問に思ったわけであるが、調べてみると、 4.4BSD で制御端末を得るには ioctl で TIOCSCTTY を使わなければならず、 open では行えない事がわかった。(つまり O_NOCTTY には効果が無く、マニュアルにも記載されていない) つまり、不用意に open するというのは考えられるが、不用意に TIOCSCTTY するというのは考えられないので、どうも危険はなさそうである。

ところで、daemon は glibc にも移植されている。

int
daemon(nochdir, noclose)
        int nochdir, noclose;
{
        int fd;

        switch (__fork()) {
        case -1:
                return (-1);
        case 0:
                break;
        default:
                _exit(0);
        }

        if (__setsid() == -1)
                return (-1);

        if (!nochdir)
                (void)__chdir("/");

        if (!noclose) {
                struct stat64 st;

                if ((fd = open_not_cancel(_PATH_DEVNULL, O_RDWR, 0)) != -1
                    && (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0)
                        == 0)) {
                        if (__builtin_expect (S_ISCHR (st.st_mode), 1) != 0
#if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
                            && (st.st_rdev
                                == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR))
#endif
                            ) {
                                (void)__dup2(fd, STDIN_FILENO);
                                (void)__dup2(fd, STDOUT_FILENO);
                                (void)__dup2(fd, STDERR_FILENO);
                                if (fd > 2)
                                        (void)__close (fd);
                        } else {
                                /* We must set an errno value since no
                                   function call actually failed.  */
                                close_not_cancel_no_status (fd);
                                __set_errno (ENODEV);
                                return -1;
                        }
                } else {
                        close_not_cancel_no_status (fd);
                        return -1;
                }
        }
        return (0);
}

まぁ、関数名に _ が付いていたり、/dev/null をいろいろ検査してたりもするようであるが、 NetBSD のとよく似ている。

4.4BSD で問題なかったのだから glibc も問題ないかというとそこが問題である。どのシステムコールが制御端末に影響するかというのはカーネル側の話なので、 4.4BSD カーネルではなく、Linux (や Hurd) の挙動を調べなければならない。

で、少なくとも Linux では open で制御端末を得られるような気がするのだが... いいんかね?

なお、uClibc では、次のように、setsid の後に再度 fork している。

int daemon( int nochdir, int noclose )
{
 int fd;

 switch (fork()) {
         case -1:
                 return(-1);
         case 0:
                 break;
         default:
                 _exit(0);
 }

 if (setsid() == -1)
         return(-1);

 /* Make certain we are not a session leader, or else we
  * might reacquire a controlling terminal */
 if (fork())
         _exit(0);

 if (!nochdir)
         chdir("/");

 if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
         dup2(fd, STDIN_FILENO);
         dup2(fd, STDOUT_FILENO);
         dup2(fd, STDERR_FILENO);
         if (fd > 2)
                 close(fd);
 }
 return(0);
}

http://www.uclibc.org/cgi-bin/viewcvs.cgi/trunk/uClibc/libc/unistd/daemon.c?rev=11757&view=auto

#5 Linux と Hurd 以外での glibc [CODE blog]

Linux と Hurd 以外で glibc が使われている事ってあるのかな?

そっか、Ging があるか。

http://slashdot.jp/bsd/05/11/10/2128242.shtml

#6 glibc を cvs でとって来る方法 [CODE blog]
% cvs -z 9 -d :pserver:anoncvs@sources.redhat.com:/cvs/glibc co libc
#7 シェルスクリプトの伝統的脆弱性 [CODE blog]
・ テンポラリファイル
・ IFS
・ setuid-script
・ 他には?
#8 tDiary の (Wikiスタイルの) ソースの例 [CODE blog]
! daemon と制御端末

4.4BSD には daemon という関数がある。
これは

  int daemon(int nochdir, int noclose);

という形式で、呼び出したプロセス自体をデーモン化する。

最近の NetBSD での定義は次のようになっている。

    int
    daemon(nochdir, noclose)
            int nochdir, noclose;
    {
            int fd;

            switch (fork()) {
            case -1:
                    return (-1);
            case 0:
                    break;
            default:
                    _exit(0);
            }

            if (setsid() == -1)
                    return (-1);

            if (!nochdir)
                    (void)chdir("/");

            if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
                    (void)dup2(fd, STDIN_FILENO);
                    (void)dup2(fd, STDOUT_FILENO);
                    (void)dup2(fd, STDERR_FILENO);
                    if (fd > STDERR_FILENO)
                            (void)close(fd);
            }
            return (0);
    }

http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/lib/libc/gen/daemon.c?rev=1.9&content-type=text/x-cvsweb-markup

つまり、(カレントディレクトリと標準入出力のあたりを除けば) fork して setsid するというものである。

さて、ここでデーモンを作る作法は昔からいろいろといわれているが、
そのなかに、間違って制御端末を得てしまわないようにひとつ余計に fork する、という話がある。

でも、この実装はそうしていないようなんだけどいいのだろうか?

Unix では、プロセスは高々一つの制御端末を持つ。
で、制御端末を持っていると、その端末から ^C という文字で SIGINT を送ったりしてプロセスを制御できる。
でも、デーモンが不用意に制御端末を持ってしまうと、意図せずしてその端末から制御されてしまうかも知れない。
デーモンを制御する権限を持っていない人がそういう端末を持てるとこれは権限上昇になるので危険である。

ここで、プロセスが制御端末を得る方法は、たしか規格では決まっていないのだが、
伝統的には、制御端末を持っていないプロセスリーダーが端末を open することである。
(もっと条件があったかも?)
したがって、端末を ~/public_html 下にシンボリックリンクして HTTP サーバに open させるなどの攻撃が
考えられる。

もし、daemon 関数が余計に fork していれば、プロセスがプロセスリーダではなくなるので大丈夫なのだが、
そうしてないわけで、daemon 関数でデーモンにした場合、大丈夫なのだろうか?

と、疑問に思ったわけであるが、調べてみると、
4.4BSD で制御端末を得るには ioctl で TIOCSCTTY を使わなければならず、
open では行えない事がわかった。(つまり O_NOCTTY には効果が無く、マニュアルにも記載されていない)
つまり、不用意に open するというのは考えられるが、
不用意に TIOCSCTTY するというのは考えられないので、どうも危険はなさそうである。

ところで、daemon は glibc にも移植されている。

    int
    daemon(nochdir, noclose)
            int nochdir, noclose;
    {
            int fd;

            switch (__fork()) {
            case -1:
                    return (-1);
            case 0:
                    break;
            default:
                    _exit(0);
            }

            if (__setsid() == -1)
                    return (-1);

            if (!nochdir)
                    (void)__chdir("/");

            if (!noclose) {
                    struct stat64 st;

                    if ((fd = open_not_cancel(_PATH_DEVNULL, O_RDWR, 0)) != -1
                        && (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0)
                            == 0)) {
                            if (__builtin_expect (S_ISCHR (st.st_mode), 1) != 0
    #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
                                && (st.st_rdev
                                    == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR))
    #endif
                                ) {
                                    (void)__dup2(fd, STDIN_FILENO);
                                    (void)__dup2(fd, STDOUT_FILENO);
                                    (void)__dup2(fd, STDERR_FILENO);
                                    if (fd > 2)
                                            (void)__close (fd);
                            } else {
                                    /* We must set an errno value since no
                                       function call actually failed.  */
                                    close_not_cancel_no_status (fd);
                                    __set_errno (ENODEV);
                                    return -1;
                            }
                    } else {
                            close_not_cancel_no_status (fd);
                            return -1;
                    }
            }
            return (0);
    }

まぁ、関数名に _ が付いていたり、/dev/null をいろいろ検査してたりもするようであるが、
NetBSD のとよく似ている。

4.4BSD で問題なかったのだから glibc も問題ないかというとそこが問題である。
どのシステムコールが制御端末に影響するかというのはカーネル側の話なので、
4.4BSD カーネルではなく、Linux (や Hurd) の挙動を調べなければならない。

で、少なくとも Linux では open で制御端末を得られるような気がするのだが... いいんかね?

なお、uClibc では、次のように、setsid の後に再度 fork している。

  int daemon( int nochdir, int noclose )
  {
          int fd;

          switch (fork()) {
                  case -1:
                          return(-1);
                  case 0:
                          break;
                  default:
                          _exit(0);
          }

          if (setsid() == -1)
                  return(-1);

          /* Make certain we are not a session leader, or else we
           * might reacquire a controlling terminal */
          if (fork())
                  _exit(0);

          if (!nochdir)
                  chdir("/");

          if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
                  dup2(fd, STDIN_FILENO);
                  dup2(fd, STDOUT_FILENO);
                  dup2(fd, STDERR_FILENO);
                  if (fd > 2)
                          close(fd);
          }
          return(0);
  }

http://www.uclibc.org/cgi-bin/viewcvs.cgi/trunk/uClibc/libc/unistd/daemon.c?rev=11757&view=auto

! Linux と Hurd 以外での glibc

Linux と Hurd 以外で glibc が使われている事ってあるのかな?

そっか、Ging があるか。

http://slashdot.jp/bsd/05/11/10/2128242.shtml

! glibc を cvs でとって来る方法

  % cvs -z 9 -d :pserver:anoncvs@sources.redhat.com:/cvs/glibc co libc

! シェルスクリプトの伝統的脆弱性

* テンポラリファイル
* IFS
* setuid-script
* 他には?

2005-11-24 (Thu)

#1
% google-count 風邪が{移る,写る,映る,うつる}
612     風邪が移る
3       風邪が写る
3       風邪が映る
564     風邪がうつる

2005-11-25 (Fri)

#1

飲み会があったと仮定しよう。

帰りがけに本屋に寄ったと仮定しよう。(飲み会の後にもあいてる本屋に幸いあれ)

スーパーダッシュ文庫の新刊を発見したと仮定しよう。

Holy☆Hearts! と蘭堂家の人々の新刊を見つけたにも関わらず折込広告では銀盤カレイドスコープの新刊も出ているはずなのに発見できないことを発見してしまったと仮定しよう。

そのときの気分はいかがばかりか。(酔っているという前提で)

2005-11-27 (Sun)

#1

パス名には名前の並びという側面がある。あくまでも側面であってそれが全てではないが。

名前の並びをバラすのに、dirname と basename が使えるかというとこれは難しい。basename はいいのだが、dirname はお節介が過ぎる。

#2

php-5.1 は tzdata を直接読んでる?

むぅ、データ自体をソースに埋め込んでんのか?

<URL:http://cvs.php.net/php-src/ext/date/lib/timezonedb.h>

なかなか思い切ってるな

2005-11-30 (Wed)

#1

svn はメモリが足りないと abort する (SIGABRT を自分自身に送って終了する) ようだ。

そうなった場合、working directory は svn cleanup が必要になる。

#2

64bit にすると、メモリがたくさん扱えるが、メモリをたくさん使ってしまうというのも確かではある

まぁ、ふたつの「たくさん」では、前者のほうが圧倒的に大きいのだが、それがいくら大きかろうが実メモリという都合もあるわけで


[latest]


田中哲