clockcount というライブラリをリリースしてみる
という対象で動くかもしれない
なお、サポートしていないが、S/390 にもあるようである
また、MIPS にもだいたいあるのかもしれない (でも、64bit かどうかわからない)
ちなみに Alpha を扱っていないのは、32bit で気に入らないから
ぷらっとホームが「検証ルーム」なる設備を持っていることを知る
Alpha の最後のチップは EV7z というやつで、これは 1.3GHz である。
<URL:http://www.itmedia.co.jp/news/articles/0408/17/news015.html>
ここで、1.3GHz でカウンタが増加していくと、32bit (4G) があふれるまでには 3秒かかる。
ということは、それ以上の頻度でカウンタを覗いて、あふれていること (値が減っていること) を検出し、あふれた回数を数えれば、数え始めた時点からは連続的に数えられるわけである。
というわけで試しにそうしてみた。(Ruby のスレッドを使うので、Thread.exclusive=true とかやられたり、あるいはプロセスが suspend されたりすると、数え損ねるかもしれない)
なお、原理的にいえば 64bit のカウンタでもあふれた回数を数えれば連続性を伸ばせるという事情は同じわけであるが、64bit だと 3GHz で増加していっても、あふれるまでに 6万年以上かかるので気にしなくてもよいであろう。
しかし、アセンブリ言語っていうのは、疑似命令の文法はけっこう似ているくせに、なんでコメントの文法がちがうのだろう?
MIPS64 も、Count Register は 32bit らしい (MIPS64 Architecture For Programmers, Volume III: The MIPS64 Privileged Resource Architecture)
まぁ、試せる環境が無いので実装できないが
そういえば、32bit カウンタがあふれるのを検出するスレッドというのは、fork したときに殺されないほうがいい例のひとつだな。
あふれた回数を保持している変数が fork で複製されるからだが。
SPARC-V8+ 用のコードは SPARC-V9 では動かないことに気が付く。64bit な返値を渡す方法が違うのである
SPARC-V8+ だと %o0 に上位 32bit, %o1 に下位 32bit を入れなければならないが、SPARC-V9 だと %o0 に素直に入れておけばよい
(仮定の話として、SPARC-V8+ で上位と下位が逆だったら両方で動くコードを作れる?)
ということは、どうにかして区別して、生成するのを変化させないといけない。コード自体も変えないといけないし、アセンブラを呼び出すコマンドライン引数も変えないといけない
しかし、32bit か 64bit かというのは、ruby を build するときの CFLAGS (LDFLAGS, DLDFLAGS) に -m64 が入っているかどうかから決まるのだが、コンパイラのオプションを解析するのは避けたい
なんかいい方法はあるか?
アセンブラを $(CC) 経由で動かして、プリプロセッサで __sparcv9 を調べるのがいいか?
アセンブリソースをプリプロセスするには... 拡張子を s じゃなくて S にする? うぅむ。試してみると gcc では動くようだが、これって他のコンパイラドライバでは動くんだろうか。
あと、SPARC-V8 で ruby が build されている時には、同じオプションでは SPARC-V8+ にアセンブルできないな。うぅ。
extconf.rb で調べるしか無いか。
SPARC V9 には Stack Bias なるものがあって、stack pointer や frame pointer は実際のアドレスから 2047bytes ずれているらしい
<URL:http://docs.sun.com/app/docs/doc/806-0477/6j9r2e2bb?a=view>
なんで?
現在、アセンブリ言語で記述しているのは unsigned long long clockcount(void) という signature な関数であるが、これを void clockcount(unsigned long long *) とすれば、sparc では SPARC-V8+ と SPARC-V9 でコードを共通化できる気がする。
x86 と x86_64 もかな。違うかも?
64bit を引数や返値でうけわたすのは calling convention が変化しがちなところで、ポインタにして直接は扱わないようにすれば、そういうところを避けられる?
では、HP PA-RISC や PowerPC ではどうだろう?
size だけじゃなくて endian の違いもあり得るか。考えてみると、gcc の inline assembly はそういうところを扱わなくていいのが便利だなぁ。
bi-endian について調べる。
とりあえず、PowerPC のドキュメントは分かりやすい。(PowerPC User Instruction Set Architecture, Book I, Version 2.02)
しかし... 実効アドレスをいじって、メモリ上の endian を変更せずにプロセッサから見た場合の endian が変わったかのように見せかけるというのは、なんともトリッキー。
でも、MIPS でもインストラクションの説明を見るとそんなかんじだなぁ。(MIPS64 Architecture For Programmers, Volume II: The MIPS64 Instruction Set)
ふと思ったのだが、32bit アプリケーションを 64bit プロセッサで動かしたとき、レジスタの上位 32bit に変な値が残ってしまうとかいうことは無いのだろうか。
debootstrap の sarge というところに載っているパッケージを gonzui にいれる。
gonzui-import -a として apt でとってきたのを直接入れられるのは便利だ。
savannah から glibc と emacs のCVSリポジトリをとってきて、ViewCVS で公開してみる
savannah はリポジトリ rsync でとれるのでよろしい
system 関数の呼び出しを調べてみる
https://www.codeblog.org/gonzui/search?q=funcall%3Asystem&fm=all
いくつか眺めると、つぎのようなコードが見つかった
126: static int 127: http_rman(char *url) 128: { 129: char *pName; 130: char *pSection; 131: char buf[200]; 132: 133: /* parse URL: should be /man?command[?section] */ 134: pSection = strrchr(url, '?'); 135: if (!pSection) { 136: return -1; 137: } 138: pName = pSection-1; 139: *pSection++ = '\0'; 140: 141: pName = strrchr(url, '?'); 142: if (!pName) { 143: pName = pSection; 144: pSection = ""; 145: } 146: else 147: pName++; 148: 149: sprintf(buf, "man %s %s | rman -r \"man?%%s?%%s\" -n %s -f html", 150: pSection, pName, pName); 151: 152: return system(buf); 153: }
https://www.codeblog.org/gonzui/markup/xc/extras/rman/contrib/http-rman.c?q=package:xc%20funcall:system#l152
url なる変数から切り出した文字列をコマンドライン引数に直接埋め込んでいて怪しい。バッククォートとか考えてんのかね
その呼出元を見ると、あー、gets とか使ってるんですけど
174: /* read rest of command (just for the sake of it) */ 175: while (gets(url) && url[0] != '\r') 176: ; 177: 178: /* command should be GET <url> [HTTP/1.0] */ 179: if (sscanf(buf, "GET %s", url) == 1) { 180: 181: status = http_rman(url);
https://www.codeblog.org/gonzui/markup/xc/extras/rman/contrib/http-rman.c?q=funcall:http_rman#l181
ソフトウェアから電源のON/OFFを行いたいことがある。(それも安価に)
で、どうやって実現するか
そーいう要求のために作られた箱はたしかにあるが高価である。キットは面倒。というわけでUSB連動電源タップがうまく使えればいいのだが...
USB連動電源タップというのは、電源タップからUSBケーブルが出ているもので、USBから来る電源が落ちるとタップのスイッチが切れるというものである。提供元が想定している用途はPCの電源を切ったときに周辺機器の電源を自動的に切るためのものであろう。 (なお、USB的には認識されない)
さて、ここで提供元の想定は気にしないことにして、もし、PCの電源自体は切らずに、ソフトウェアでUSBの電源だけをON/OFFできるとすれば、電源タップのスイッチをソフトウェアで制御できることになる。
問題は PC の中で、USB だけを(理想的には各ポートごとに)ON/OFFできるか、というところである。
というところで個人的にはレイヤが下過ぎて手が出ないのであるが、もともとこの要求を持ってきたひと(g新部さん)の指定で次のように printk を入れて試す。
case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) { printk(KERN_INFO "USB_PORT_FEAT_POWER: has HCS_PPC\n"); writel (temp & ~(PORT_RWC_BITS | PORT_POWER), &ehci->regs->port_status [wIndex]); } else { printk(KERN_INFO "USB_PORT_FEAT_POWER: no HCS_PPC\n"); } break;
https://www.codeblog.org/gonzui/markup/linux-2.6.14.2/drivers/usb/host/ehci-hub.c?q=USB_PORT_FEAT_POWER#l369
はて? insmod, rmmod したが、dmesg にも /var/log/kern.log にも出て来ない。
IRC で尋ねてみると、hikidoc になったのならできるということなので、2.1.3 を試すもできない。
CVS からとってきて試すとできた。
さて、どうするか。
お、HP TestDrive の FreeBSD/IA64 なマシンが生き返っている
んー。make test まではきれいに行くが、test-all 内でブロックするな
調べてみると、BSD の FD_ISSET のバグだった
<URL:http://www.freebsd.org/cgi/query-pr.cgi?pr=ia64/91421>
うぅむ。web からバグレポートして、自動応答で返ってきたメールには次の URL が書いてあるのだが、それでは見えない... しばらくしてから見えるようになった?
<URL:http://www.freebsd.org/cgi/query-pr.cgi?pr=91421>
なお、NetBSD と OpenBSD にも同じ問題があるように見える
しかし、IA64 でなくても LP64 な BSD ならどこでも問題があるように思えるが、今まで誰も気がつかなかったのだろうか
なんか、すぐに commit されたようである。
そーいえば --enable-pthread でテストしていなかったと思いだして、FreeBSD/IA64 上でテストする、と、落ちる。
でも、調べてみたら、--enable-pthread における stack 領域の減少による stack overflow であった。
そーいや、yarv は --enable-pthread 強制なんだよなぁ。stack 領域の減少で使い物にならなくなったりして。
USB連動電源というものの存在を g新部さんに教えた結果、(ハードウェアはコンシューマデバイスだけで) ソフトウェアから AC 電源の ON/OFF ができるようになった模様
<URL:http://www.gniibe.org/log/2006/01/08#elecom-h2h-g4scr>
さて、なんに使えるか?
個人的には評価ボードは扱っていないので、XFD とかだろうか
コンセントを挿せば動き、抜けば止まる、というデバイスであればなんでもいいわけだが、さて
ふと、BSD の FD_ISSET に問題があることに気がついた
・ http://www.freebsd.org/cgi/query-pr.cgi?pr=91421 ・ http://www.FreeBSD.org/cgi/cvsweb.cgi/src/sys/sys/select.h?rev=1.20&content-type=text/x-cvsweb-markup
とりあえず見つけた対象の FreeBSD については報告して直してもらった
NetBSD, OpenBSD も同様な感じである、と最初は思ったが、long じゃなくて __int32_t を使っているから問題ないか
・ http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/sys/sys/fd_set.h?rev=1.2&content-type=text/x-cvsweb-markup ・ http://www.openbsd.org/cgi-bin/cvsweb/src/sys/sys/select.h?rev=1.8&content-type=text/x-cvsweb-markup
gonzui で検索してみると、FD_ISSETの用法はだいたい if の条件とかに直接使う感じで int にcastされることはないので、それほど問題にはならなさそうではある
https://www.codeblog.org/gonzui/search?q=FD_ISSET&fm=all
とはいえ、int な変数に代入しているとかもないわけではない
https://www.codeblog.org/gonzui/markup/linux-2.6.14.2/fs/fcntl.c?q=path:linux-2.6.14.2/fs/fcntl.c%20funcall:FD_ISSET#l45
linux kernel の中での FD_ISSET の定義はいろいろなようである
https://www.codeblog.org/gonzui/markup/linux-2.6.14.2/include/linux/time.h?q=fundef:FD_ISSET#l122
として __FD_ISSET で定義され、
https://www.codeblog.org/gonzui/search?q=package:linux-2.6.14.2%20fundef:__FD_ISSET
というように __FD_ISSET はアーキテクチャごとに定義されているようである。各定義は != 0 がついているのもあればついていないのもあるが、ついていないのがそのアーキテクチャではいらないことをわかったうえでそうしているのか、考えずにそうなっているのかはよくわからない
そういえば、UnixArchive を gonzui に入れたいと思っている
個人的には一般公開直後にダウンロードしてきて手元に保持しているのだが、検索が面倒なのである。また、FD_ISSET のような問題の起源がどこにあるのかを確認するのに必要である
というわけで、ひさしぶりに、 http://minnie.tuhs.org/ からたどって中身をちょっと見てみる
おや、なんか増えてる?以前ダウンロードしたときには net1.tar.gz とかなかったと思うんだけど (あるいはダウンロードに失敗していた?)
NetBSD が __int32_t を使うようになったのは OpenBSD の fork より前のようだ
http://www.levenez.com/unix/history.html をみながら、FreeBSD から矢印が出ているのを少し調べてみる
・ OpenDarwin は int32_t になっているhttp://cvs.opendarwin.org/cgi-bin/cvsweb.cgi/src/xnu/bsd/sys/types.h?rev=1.1.1.3&content-type=text/x-cvsweb-markup ・ DragonFly BSD は unsigned long のままhttp://www.dragonflybsd.org/cgi-bin/cvsweb.cgi/src/sys/sys/types.h?rev=1.11&content-type=text/x-cvsweb-markup
この問題の起源を考えてみる
まず、FD_ISSET を導入したのは誰だろう?
・ 4.2BSD の types.h には、fd_set はあるが、FD_ISSET はない。select(2) のマニュアルにも載っていない (かわりに、<< で指定する方法が書いてある) ・ 4.3BSD の types.h には、FD_ISSET があり、long (fd_mask型) を返す。ただし、マニュアルには返値の型の記載はない ・ SUSv2 には FD_ISSET は int を返すとある
というわけで、実装を提供したのは 4.3BSD である。しかし、その時点では仕様は曖昧であり、おそらく POSIX などで後から仕様を定義したときに、実装とは微妙にずれたものにしてしまったのであろう。
さて、ここで、sizeof(int) と sizeof(long) が同じであれば実用上の問題は起きないわけである。Unix ではずいぶんと長い間この条件が成り立っていたわけであるが、4.3BSDがリリースされた対象のマシンではどうだったのだろう? 16bit int という可能性はあるだろうか?
gonzui-import が失敗するものがあるので調べていたところ、テンポラリに作ったディレクトリを再帰的に 777 に chmod するコードがあった
とりあえず除去した後に尋ねてみると、削除できないものがあったからであるという
・ http://lists.sourceforge.jp/mailman/archives/gonzui-devel/2006-January/000405.html ・ http://lists.sourceforge.jp/mailman/archives/gonzui-devel/2005-April/000276.html
せめて 700 であればかなりましだったのに
Unix的伝統では、open や mkdir でファイルを作成するときには、666 や 777 を引数に指定し、umask で修正して使う、ということになっている (と思う)
しかし、考えてみれば 666, 777 を指定しておいて、安全なパーミッションになるかどうかは環境とユーザ次第、というのは責任をユーザに押しつけている感がしなくもない
DragonFly BSD 1.4 のリリースノートを読む
http://www.dragonflybsd.org/main/release1_4.cgi
・ closefrom システムコールの導入 ・ shutdown() がパイプをサポート
というのが気になった
たしかに、closefrom はユーザレベルでは書きにくい
ちゃんとやるんなら、RLIMIT_NOFILE を調べないといけないし、と考えたところで、RLIMIT_NOFILE を調べてもだめなんじゃないかと思いついた
つまり、open して大きな fd を得てから、setrlimit で小さくしたときには、limit より大きな fd が生きていることが有り得るのではないか、という思いつきである
% ruby -ve ' fs = (3..99).map { open("/dev/null") } Process.setrlimit(Process::RLIMIT_NOFILE, 20) p Process.getrlimit(Process::RLIMIT_NOFILE) p fs.last.fileno p fs.last.read' ruby 1.9.0 (2006-01-03) [i686-linux] [20, 20] 100 ""
やはり動くか
って、なんだっけ?
そういう慣習であることは覚えているが、そういえば理由を覚えていない
変なものを open していると、unmount できないというのはありそう
セキュリティ的な理由はあるだろうか?
gonzui をいじくりまわしてある程度安定化したので、DB を再構築した上でたまっていたrequestを片付ける
gonzui で、「gtk+-2.8.7 内の検索結果」というリンクをたどるとおかしいと大和さんに指摘を受けたので調べてみると、リンクが .../search?q=package%3Agtk+-2.8.7+gtk_main というようになっていた。
gtk+ の + がそのままになっているので + が空白に変換されてしまっているらしい。
実際、リンクをたどった後、URL を書き換えて gtk+ を gtk%2B に書き換えてアクセスすると、ちゃんと予期された内容が出てくる。
ソースを見ると、まぁそのとおりで、WEBrick::HTTPUtils.escape_form を使うべきところで WEBrick::HTTPUtils.escape を使っていたので変えたら直った模様。
https なページから http なページにリンクをたどったとき、ブラウザは Referer を送るべきでないという規則がある。
Clients SHOULD NOT include a Referer header field in a (non-secure) HTTP request if the referring page was transferred with a secure protocol. RFC 2616 15.1.3
ふと思いついて、ある http なサーバのログを見てみたところ、https な Referer がいくつか見つかった。対応する User-Agent は次の通り。
・ Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 ・ Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 ・ FAST Enterprise Crawler/6.4 (helpdesk at fast.no)
Safari は送るってことかな。SHOULD NOT だから、あえてそうするというのであればいけないというわけではないんだけど。
昨日、大和さんにいわれて気がついたわけであるが、べつに USB 連動電源タップを使わなくても、USB扇風機などの USB Bus Power な機器は制御できるわけである。
というわけでどんなものがあるか調べてみる。
まず、有名なのはイーレッツ (というか、線上のメリークリスマス) である。
http://www.e-lets.co.jp/product/aig-xmas5/index.htm
でも、製品紹介にある他の USB グッズは充電器ぐらいで見た目に面白くない。
http://www.e-lets.co.jp/product/
ほかのメーカはどうかということで探してみる。
・ サンコーレアモノショップ http://www.thanko.jp/products/usb.html ・ ロアス http://www.loas.co.jp/novelty/usb/ ・ サンワサプライ http://www.sanwa.co.jp/seihin_joho/usbtoy/index.html ・ ソリッドアライアンス (i-Duck わすれんぼう) http://www.solidalliance.com/products/products.html ・ ゲート http://www.gate.jp/top/makers/maker.php?ID=M041
デモとして見た目に面白そうなのは、光るものと扇風機あたりだろうか
コムサテライト3号店というところでは、USB駆動の改造デバイスを売っているらしい。
・ http://www.watch.impress.co.jp/akiba/hotline/20030607/etc_soumen.html ・ http://www.watch.impress.co.jp/akiba/hotline/20050702/etc_somen.html
現在売っているのは USBクリスタルというものだろうか。
http://www.comsate.co.jp/supercom/
なんとなく行って現物を見てみるが、明るいところで観賞するには光量が足りなくて、デモには向かない感じであった。
ついでに他の階を回ると XPort ACコントロールKit というものがあった。
http://www.wakamatsu.co.jp/psplaza/cgi-bin/goodslist.cgi?mode=view_goods&sort=&sub_id=0565&goods_id=0006
買ってみる。ふたつあわせて1000円。
ふと、sun というシンボルがいつ定義されるのか調べてみる。
Solaris 8 の cpp(1) には次のようにある。
... Hardware: interdata, pdp11, u370, u3b, u3b2, u3b5, u3b15, u3b20d, vax, ns32000, iAPX286, i386, sparc, and sun ... The symbols sun, sparc and unix are defined for all Sun systems. ...
マニュアル的には、ハードウェアを意味するシンボルのようである。
とすると 2つの疑問がわく。
・ Sun のハードウェア上で稼働する Sun でない OS では定義されるか ・ Sun でないハードウェア上で稼働する Sun の OS では定義されるか
前者の例は Linux/SPARC とかであり、後者は Solaris x86 である。
ここで、わざわざ Sun のハードウェア上で Sun 以外の OS を動かす商用ベンダがいるとも思えないので、前者で使われているコンパイラは主に gcc であろう。
gcc で探してみると、gcc/config/sol2.h で定義されている模様である。
57: #define TARGET_OS_CPP_BUILTINS() \ 58: do { \ 59: builtin_define_std ("unix"); \ 60: builtin_define_std ("sun"); \
https://www.codeblog.org/gonzui/markup/gcc-4.0.2/gcc/config/sol2.h#l60
TARGET_OS_CPP_BUILTINS の定義なので、OS によって決まるようである。つまり、Linux/SPARC とかでは定義されないのであろう。
Solaris x86 に関してはソースを参照できないが、探してみると、 Sun Studio 11 の cc(1) ではハードウェアを意味するとは書いていない。むしろ常に定義される感じである。とすると、Solaris x86 でも定義されるように思える。
とすると、結論としては、sun は SunOS を意味するということであろう。
これって何だろう?
<URL:http://gadgets.zive.cz/?q=node/929>
USB なチョキ。
<URL:http://wkcao.en.ec21.com/GC01073869/CA01073881/USB_Massage.html>
マッサージ器らしい。わかってしまえば面白くない。
日本以外にもUSB連動電源タップはある模様
http://www.cablestogo.com/product.asp?cat%5Fid=1004&sku=34006
Linux カーネルをいじる必要なく、ユーザスペースで USB の power control ができる模様
<URL:https://www.codeblog.org/blog/gniibe/?date=20060114#p02>
かなりお手軽感が増している (ここまで来ると Hub を選ぶという点がなんとも残念である)
とりあえず USB Fan, Light, Aquarium を ON/OFF してみた
Paul Eggert を見てきた
ON/OFF の制御が可能なら、時間的に制御してやれば連続的な制御も可能かもしれない。
# while : do ./hub-ctrl -b 4 -d 3 -P 4 -p 1 # ON sleep 0.01 ./hub-ctrl -b 4 -d 3 -P 4 -p 0 # OFF sleep 0.1 done
こころもち遅くなっている気がする。
インバータ?
時間的に... やってみると、ちらちらしてよろしくない。
Paul Eggert を生で見たので、gonzui で検索してみよう。
https://www.codeblog.org/gonzui/search?q=%22Paul+Eggert%22&fm=all
configure などの autoconf まわりでひっかかるのは個別の話ではないにしても、 xc, gcc, glibc, apache, linux, base-files, bash, coreutils, diffutils, findutils, gzip, perl, mawk (debian), sed, tar, bsdmainutils, cpio, gettext, groff, texinfo, nvi, emacs, gnutls, gnupg に残っている名前は個々の貢献に思える。
wget は glibc 経由、postgresql は tzdata 経由だろうか。
なんとも手広い。
Canon のプリンタ (LBP-2810) のトナーを交換し、メッセージに憤りを覚える。
まず、トナーがきれて、印刷不能になった時のメッセージは 「12G C トナーコウカン」というものであった。(メッセージの細部は違っていたかも)
まぁ、C が Cyan の略であるところまでは許すとしよう。わかりにくいが。しかもトナーのカタログの方には C じゃなくてシアンとして出ているし。
しかし、トナーを注文して品物が届き、C トナーを交換してみると、次に「M トナーコウカン」と出てきやがった。
ここで、トナーは C, M, Y, K が別売りである。
したがって、メッセージにしたがって C トナーだけを注文していたとしたら、プリンタは使えるようにならなかったのである。
しかし、幸いにして、C, M, Y, K の一式を注文しておいたので、M トナーも交換すると、(予想通り) 次に「Y トナーコウカン」と出て来る。
で、Y トナーも交換すると、プリンタが使えるようになった。
不幸にして K トナーが余ってしまったが、「K トナーチェック」とメッセージが出ているので、すぐに必要になることであろう。おそらく。
なお、交換の順番は C, M, Y だったが、プリンタ内部のトナーカートリッジの位置は上から K, M, Y, C であり、順序に一貫性がないことに気がついた。
しかし、考えてみると、この問題はなかなか面白いかも知れない。
逐次的にひとつづつ検査して、最初に失敗したところで全体を失敗させると、こういう挙動になるわけである。
期待される挙動は C, M, Y の 3つのトナーを交換するようメッセージを表示することであるが、これを実現するには、失敗が起きても可能な範囲で続きを実行しないといけない。ただし、失敗したところに依存する作業は行ってはいけない。
そういう動作を行う前例としては、make -k やコンパイラのエラーメッセージがある。make -k は失敗してもそれに依存しない所は動作するし、コンパイラは最初の失敗であきらめたりはしない (ものがある)。
でも、そういう動作を自分で書くのはなかなか厄介である。失敗したところに依存しているかどうかをちゃんと判断するのは面倒臭い。
しかし、C, M, Y, K のトナーの検査という例に限って考えれば、4つの検査は独立なため、それほど難しくない。
もし、そういう比較的簡単な例が多いのであれば、そういうのを支援することに意味があるかも知れない。
http://www.watch.impress.co.jp/akiba/hotline/20010421/etc_usbled.html
光るUSBケーブルというものも以前はあったようだが、いまはもうないのか。
testt
tDiary の graphviz plugin をためしにいじりまわしてみる。
http://www.in-ulm.de/~mascheck/various/
いろいろと調べられていて素晴らしいが、とくに
・ "#! /" の 4byte で認識するシステムがあったというのはガセ ・ ${1+"$@"} が必要になる状況 ・ directory の setuid bit に意味があるシステムは存在した
というのは以前疑問には思ったものの調べがつかなかった話なので、はっきりと調べられているのを読んでかなり感動。
graphviz を gonzui にいれて、system, popen, fork, exec family を探してみる。
tDiary の plugin をいくらいじっても、graphviz 自身に任意のコマンドを実行できる機能があるとまずいからである。
探してみた結果は fork がひとつ見つかった。
https://www.codeblog.org/gonzui/search?q=package%3Agraphviz-2.2.1+funcall%3Afork&fm=c
ただ、これは sfio の中で、この関数は graphviz 本体からはたどり着けなさそうである。
従って、graphviz に渡すデータはユーザが任意に指定できてもいけなくはないと思う。まぁ、仕様としては。
じゃんけん
ソースは次の通り
{{graphviz_neato(' digraph じゃんけん { ぐー->ちょき; ちょき->ぱー; ぱー->ぐー; } ')}}
しかし、graphviz はどうも怪しげな感じである。
テストしている最中に
% nkf -Ue a.dot digraph test { aaa -> aaa; aaa->ぱー; ぱー->ぐ; } % /usr/bin/dot < a.dot *** glibc detected *** malloc(): memory corruption: 0x08093970 ***
などといわれることもあった。 (テストした環境は i386 だけど)
だから、実装としては、安心して他人に使わせられる感じはしない。
む、商品が出るらしい。
http://www.watch.impress.co.jp/akiba/hotline/20060121/etc_ptu2f3.html
% google-count eth{0123456789} 1830000 eth0 1370000 eth1 270000 eth2 59400 eth3 24300 eth4 16200 eth5 11100 eth6 11200 eth7 744 eth8 870 eth9
% google-count wd0{abcdefghijklmnopqrstuvwxyz} 106000 wd0a 32000 wd0b 909 wd0c 18000 wd0d 26000 wd0e 12900 wd0f 12900 wd0g 979 wd0h 510 wd0i 601 wd0j 343 wd0k 402 wd0l 468 wd0m 240 wd0n 1020 wd0o 546 wd0p 796 wd0q 1020 wd0r 1310 wd0s 776 wd0t 1110 wd0u 924 wd0v 932 wd0w 254 wd0x 1100 wd0y 1080 wd0z
おー、OpenBSD rthread かぁ。
とある Debian 環境で apt-get upgrade したら
Errors were encountered while processing: kernel-image-2.4.27-2-686
といわれた。
調べてみようかと思ったが、面倒臭くなったので apt-get remove kernel-image-2.4.27-2-686 とした。
% dpkg -l|grep kernel-image rc kernel-image-2 2.4.27-10sarge Linux kernel image for version 2.4.27 on PPr %
残骸が残っているか。dpkg --purge kernel-image-2.4.27-2-686 として除去。
% dpkg -l|grep kernel-image zsh: done dpkg -l | zsh: exit 1 grep kernel-image
ついでに、apt-get --purge remove grub として grub も削除。
あれ、/boot/grub 以下は消えないのか。
% find /boot -ls 305218 4 drwxr-xr-x 3 root root 4096 Jan 25 14:42 /boot 96892 4 drwxr-xr-x 2 root root 4096 Jan 25 15:19 /boot/grub 96896 4 -rw-r--r-- 1 root root 15 Aug 5 01:43 /boot/grub/device.map 96893 4 -rw-r--r-- 1 root root 512 Aug 5 01:43 /boot/grub/stage1 96895 112 -rw-r--r-- 1 root root 108168 Aug 5 01:43 /boot/grub/stage2 96897 8 -rw-r--r-- 1 root root 7776 Aug 5 01:43 /boot/grub/e2fs_stage1_5 96898 8 -rw-r--r-- 1 root root 7504 Aug 5 01:43 /boot/grub/fat_stage1_5 96899 12 -rw-r--r-- 1 root root 8320 Aug 5 01:43 /boot/grub/jfs_stage1_5 96900 8 -rw-r--r-- 1 root root 7008 Aug 5 01:43 /boot/grub/minix_stage1_5 96901 12 -rw-r--r-- 1 root root 9216 Aug 5 01:43 /boot/grub/reiserfs_stage1_5 96902 12 -rw-r--r-- 1 root root 9288 Aug 5 01:43 /boot/grub/xfs_stage1_5 96904 4 -rw-r--r-- 1 root root 2710 Jan 25 15:19 /boot/grub/menu.lst 97219 4 -rw-r--r-- 1 root root 2710 Jan 25 15:19 /boot/grub/menu.lst~
そんで、reboot する。とくに問題はない。
やっぱこの環境では kernel はいらないんだよなぁ。
xc/config/imake/imake.c:1115-1124
1115: #ifdef CROSSCOMPILE 1116: if (CrossCompiling) { 1117: char cmd[PATH_MAX]; 1118: strcpy (cmd, CrossCompileDir); 1119: strcat (cmd,"/"); 1120: strcat (cmd,ld); 1121: ldprog = popen (cmd, "r"); 1122: } else 1123: #endif 1124: ldprog = popen (ld, "r");
code plugin を試す。
なお、PATH_MAX は GNU Hurd では無制限で定義されていないらしいので、こういうコードを書くと動かないようである。
http://www.gnu.org/software/hurd/faq.ja.html#q6-2
xc/config/imake/imake.c:1399-1407
1399: char buf[PATH_MAX]; 1400: char cmd[PATH_MAX]; 1401: char* ptr; 1402: 1403: strcpy(cmd,name); 1404: 1405: buf[0] = '\0'; 1406: strcat (cmd, " --print-libgcc-file-name"); 1407: if ((gccproc = popen (cmd, "r")) != NULL) {
なんというか、PATH_MAX の意味をわかっていない感じである。なんとなく十分に大きな数という認識だろうか。
xc/programs/Xserver/hw/xfree86/xf86cfg/cards.c:558-575
558: cmd = malloc(32 + (strlen(pattern) * 2) + strlen(Cards)); 559: 560: strcpy(cmd, "egrep -i '^NAME\\ .*"); 561: len = strlen(cmd); 562: ptr = pattern; 563: while (*ptr) { 564: if (!isalnum(*ptr)) { 565: cmd[len++] = '\\'; 566: } 567: cmd[len++] = *ptr++; 568: } 569: cmd[len] = '\0'; 570: strcat(cmd, ".*$' "); 571: strcat(cmd, Cards); 572: strcat(cmd, " | sort"); 573: /*sprintf(cmd, "egrep -i '^NAME\\ .*%s.*$' %s | sort", pattern, Cards);*/ 574: 575: if ((fp = popen(cmd, "r")) == NULL) {
シェルにあたえる引数をエスケープするのに、isalnum が偽になるバイトの前に \ をつけている。これは正しいだろうか?
それはそれとして、とりあえず、char が signed だと負の値を isalnum に渡してしまうのはよろしくない。
{{code "xc/programs/Xserver/hw/xfree86/xf86cfg/cards.c", "558-575"}}
と書いたのだが、「\」が「促」に化けてしまうようだ。
xc/programs/xrx/helper/GetUrl.c:53-60
53: GetUrl(char *url, char **reply_ret, int *len_ret) 54: { 55: char buf[BUFSIZ], *reply, *ptr; 56: FILE *fp; 57: int readbytes, size; 58: 59: sprintf(buf, "www -source \"%s\"", url); 60: fp = popen(buf, "r");
ここも怪しい。ただ、url の起源をたどっていくと、内部で生成しているので、危なくない可能性もある。
ここはソースを読むのと、xrx について調べるのと両方やるのが効率がいいところかもしれない。
xc/programs/xrx/testplugin/testplugin.c:650-662
650: NPError 651: NPN_GetURL(NPP instance, const char *url, const char *window) 652: { 653: AppData *data = (AppData *)instance->ndata; 654: char buf[BUFSIZ]; 655: FILE *fp; 656: 657: if (url == NULL) 658: return NPERR_INVALID_URL; 659: 660: /* perform request using "www" */ 661: sprintf(buf, "www -source \"%s\"", url); 662: fp = popen(buf, "r");
ここも同様。
http://www.freebsd.org/cgi/query-pr.cgi?pr=92110
http://www.NetBSD.org/cgi-bin/query-pr-single.pl?number=32609
ふと、ffs の用途を考えつき、他のひとは ffs を使っているのかどうか疑問になったので検索してみる。
https://www.codeblog.org/gonzui/search?q=funcall%3Affs&fm=all
X でけっこう使っているようだ。
ついでに、j0 も検索してみる。
https://www.codeblog.org/gonzui/search?q=funcall%3Aj0&fm=all
こちらはあまり使われていない。
まぁ、数学関数を使うようなアプリケーションを入れていないからそうなるのも当然か。
system や popen でコマンドに文字列を渡す場合、shell が解釈してしまわないように細工をしてやらなければならない。
まぁ、そんな細工をしなくてもいいようにそもそも shell を使わないというのが最良なこともままあるが、それではあまりに面倒なこともたしかにあるので、どう細工すべきか考えるのも重要ではある。
https://www.codeblog.org/blog/akr/?date=20060125#p03 のケースでは \ を使っていたが、残念ながらあれには穴がある。
SUSv3 を調べると、shell の escape/qoute には次の記法がある。
・ single quote: '' の内部に書いたすべての文字はその文字のままコマンドに渡される。なお、内部に ' を入れることはできない。 ・ double quote: "" の内部は、$, `, \ を除いてそのままコマンドに渡される。\ は直後の文字が $, `, ", \, <newline> のときだけ quote 外部の \ と同じ意味になる。 ・ escape: (quote の外部の) \ は直後の1文字をそのままコマンドに渡す。ただし、newline は例外で、\<newline> は継続行とみなされ、その2文字が除去される。
というわけで、\ だけでやろうとした先の例では、newline が消えてしまう。
ではどうすればいいかというと、いくつか考えられる。
・ 基本的には \ でやって、newline のところだけ '' または "" を使う ・ "" を使い、内部の $, `, \ に \ を前置する ・ '' を使い、' はいったん ' を閉じて、\' を使う
Ruby で書くと次のような感じである。
def shell_escape_1(str) str.gsub(/(\n+)|./) { $1 ? "'#{"\n"*$1.length}'" : "\\#{$&}" } end def shell_escape_2(str) "\"#{str.gsub(/[$`\\"]/, '\\\\\\&')}\"" end def shell_escape_3(str) str.gsub(/('+)|[^']+/) { $1 ? "\\'" * $1.length : "'#{$&}'" } end
そういえば、tDiary の graphviz plugin にも shell_escape_2 に類似したコードがあったが、! にも \ を前置するようになっていた。考えてみるとこれはうまくない。
sh-3.1$ echo "\!" |od -c 0000000 \ ! \n 0000003
double quote の内部で、$, `, ", \, <newline> 以外に \ をつけると、\ が残ってしまう。まぁ、わたしが書き換えたバージョンでは、 graphviz を起動するところには shell を使っておらず、エラーメッセージにコマンドラインを含めるところで使っているだけなので、あまりおおきな問題ではない (ということにしよう)。
gcov, lcov, valgrind, callgrind, kcachegrind
乱数については、以前調べたことがある。
結局、/dev/urandom があればそこから取り出して種にするというのがそこそこまともだという結論に達したのだが、世の中には /dev/urandom ではだめだというひともいるらしい。
・ http://www.securityfocus.com/bid/6855 ・ http://icat.nist.gov/icat.cfm?cvename=CAN-2001-0950
/dev/random を使っちゃうと、むしろブロックして危ないと思うのだが、/dev/urandom で本当にまずいケースはあるだろうか。
http://www.securityfocus.com/bid/6855 で指摘されている util-linux のソースは、次のようになっている。
util-linux-2.12r/misc-utils/mcookie.c:37-48,76,132-150
37: #define BUFFERSIZE 4096 38: 39: struct rngs { 40: const char *path; 41: int minlength, maxlength; 42: } rngs[] = { 43: { "/dev/random", 16, 16 }, /* 16 bytes = 128 bits suffice */ 44: { "/proc/interrupts", 0, 0 }, 45: { "/proc/slabinfo", 0, 0 }, 46: { "/proc/stat", 0, 0 }, 47: { "/dev/urandom", 32, 64 }, 48: };
...
76: unsigned char buf[BUFFERSIZE];
...
132: for (i = 0; i < RNGS; i++) { 133: if ((fd = open( rngs[i].path, O_RDONLY|O_NONBLOCK )) >= 0) { 134: int count = sizeof(buf); 135: 136: if (rngs[i].maxlength && count > rngs[i].maxlength) 137: count = rngs[i].maxlength; 138: r = read( fd, buf, count ); 139: if (r > 0) 140: MD5Update( &ctx, buf, r ); 141: else 142: r = 0; 143: close( fd ); 144: if (Verbose) 145: fprintf( stderr, _("Got %d bytes from %s\n"), r, rngs[i].path ); 146: if (rngs[i].minlength && r >= rngs[i].minlength) 147: break; 148: } else if (Verbose) 149: fprintf( stderr, _("Could not open %s\n"), rngs[i].path ); 150: }
つまり、/dev/random, /proc/interrupts, /proc/slabinfo, /proc/stat, /dev/urandom を順に O_NONBLOCK で読んでいる。
(そこで Vulnerable とされている Debian 3.0 の util-linux 2.11n を ftp://aist.ring.gr.jp/pub/linux/debian/debian/pool/main/u/util-linux/util-linux_2.11n.orig.tar.gz からとってきて確かめてもコードは同様に見える。)
たしかに、このコードでは /dev/random のエントロピーが枯渇して即座に読めなければ /dev/urandom から読む。でも、それってそんなにいけないことなのだろうか?
あと、そもそもエントロピーが枯渇しているときに /dev/urandom を読むのであれば、/dev/random を読む意味はあるのだろうか?
以前調べたのは ruby で使うためで、その結果、現在は次のようになっている。
ruby-1.8.3/random.c:256-306
256: random_seed() 257: { 258: static int n = 0; 259: struct timeval tv; 260: int fd; 261: struct stat statbuf; 262: 263: int seed_len; 264: BDIGIT *digits; 265: unsigned long *seed; 266: NEWOBJ(big, struct RBignum); 267: OBJSETUP(big, rb_cBignum, T_BIGNUM); 268: 269: seed_len = 4 * sizeof(long); 270: big->sign = 1; 271: big->len = seed_len / SIZEOF_BDIGITS + 1; 272: digits = big->digits = ALLOC_N(BDIGIT, big->len); 273: seed = (unsigned long *)big->digits; 274: 275: memset(digits, 0, big->len * SIZEOF_BDIGITS); 276: 277: #ifdef S_ISCHR 278: if ((fd = open("/dev/urandom", O_RDONLY 279: #ifdef O_NONBLOCK 280: |O_NONBLOCK 281: #endif 282: #ifdef O_NOCTTY 283: |O_NOCTTY 284: #endif 285: #ifdef O_NOFOLLOW 286: |O_NOFOLLOW 287: #endif 288: )) >= 0) { 289: if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) { 290: read(fd, seed, seed_len); 291: } 292: close(fd); 293: } 294: #endif 295: 296: gettimeofday(&tv, 0); 297: seed[0] ^= tv.tv_usec; 298: seed[1] ^= tv.tv_sec; 299: seed[2] ^= getpid() ^ (n++ << 16); 300: seed[3] ^= (unsigned long)&seed; 301: 302: /* set leading-zero-guard if need. */ 303: digits[big->len-1] = digits[big->len-2] <= 1 ? 1 : 0; 304: 305: return rb_big_norm((VALUE)big); 306: }
/dev/urandom を 4 * sizeof(long) だけ読んで種を生成している。つまり、long の長さによるが、128 ないし 256bit 読む。 (/dev/urandom がなかったときの保険というかごまかしと言うか以前のコードの名残として、時刻などいくつかの情報を xor している)
なお、ruby は Mersenne Twister を内部に抱えていて、こういう長い種もとくに問題なく扱えるし、システムによって生成される乱数が異なることもない。まぁ、ruby のバージョンが変われば変わる可能性はあるが。
こういうコードになったのは ruby-1.8.3 からで、/dev/urandom を使っていなかった他、いくつか問題があった。なので、1.8.3 でいろいろと変えたのだが、乱数だけあって気がつくひとはあまりいない。ただ、何回か、種を設定しても生成される乱数が異なることに気がついたひとはいたようである。あと、srand が以前の種を返すので、
% ruby-1.8.2 -e 'srand; p srand' 1138591551 % ruby-1.8.3 -e 'srand; p srand' 255108505494104843374021615775162945381
というようにあからさまに長くなっていることを気にするひとがいるか思ったが、いまのところ見かけた覚えがない。
perl も /dev/urandom を使っている。 (というか、ruby で使おうと思ったのは perl で使っていることに気がついたからである)
perl-5.8.7/util.c:4446-4525
4446: U32 4447: Perl_seed(pTHX) 4448: { 4449: /* 4450: * This is really just a quick hack which grabs various garbage 4451: * values. It really should be a real hash algorithm which 4452: * spreads the effect of every input bit onto every output bit, 4453: * if someone who knows about such things would bother to write it. 4454: * Might be a good idea to add that function to CORE as well. 4455: * No numbers below come from careful analysis or anything here, 4456: * except they are primes and SEED_C1 > 1E6 to get a full-width 4457: * value from (tv_sec * SEED_C1 + tv_usec). The multipliers should 4458: * probably be bigger too. 4459: */ 4460: #if RANDBITS > 16 4461: # define SEED_C1 1000003 4462: #define SEED_C4 73819 4463: #else 4464: # define SEED_C1 25747 4465: #define SEED_C4 20639 4466: #endif 4467: #define SEED_C2 3 4468: #define SEED_C3 269 4469: #define SEED_C5 26107 4470: 4471: #ifndef PERL_NO_DEV_RANDOM 4472: int fd; 4473: #endif 4474: U32 u; 4475: #ifdef VMS 4476: # include <starlet.h> 4477: /* when[] = (low 32 bits, high 32 bits) of time since epoch 4478: * in 100-ns units, typically incremented ever 10 ms. */ 4479: unsigned int when[2]; 4480: #else 4481: # ifdef HAS_GETTIMEOFDAY 4482: struct timeval when; 4483: # else 4484: Time_t when; 4485: # endif 4486: #endif 4487: 4488: /* This test is an escape hatch, this symbol isn't set by Configure. */ 4489: #ifndef PERL_NO_DEV_RANDOM 4490: #ifndef PERL_RANDOM_DEVICE 4491: /* /dev/random isn't used by default because reads from it will block 4492: * if there isn't enough entropy available. You can compile with 4493: * PERL_RANDOM_DEVICE to it if you'd prefer Perl to block until there 4494: * is enough real entropy to fill the seed. */ 4495: # define PERL_RANDOM_DEVICE "/dev/urandom" 4496: #endif 4497: fd = PerlLIO_open(PERL_RANDOM_DEVICE, 0); 4498: if (fd != -1) { 4499: if (PerlLIO_read(fd, &u, sizeof u) != sizeof u) 4500: u = 0; 4501: PerlLIO_close(fd); 4502: if (u) 4503: return u; 4504: } 4505: #endif 4506: 4507: #ifdef VMS 4508: _ckvmssts(sys$gettim(when)); 4509: u = (U32)SEED_C1 * when[0] + (U32)SEED_C2 * when[1]; 4510: #else 4511: # ifdef HAS_GETTIMEOFDAY 4512: PerlProc_gettimeofday(&when,NULL); 4513: u = (U32)SEED_C1 * when.tv_sec + (U32)SEED_C2 * when.tv_usec; 4514: # else 4515: (void)time(&when); 4516: u = (U32)SEED_C1 * when; 4517: # endif 4518: #endif 4519: u += SEED_C3 * (U32)PerlProc_getpid(); 4520: u += SEED_C4 * (U32)PTR2UV(PL_stack_sp); 4521: #ifndef PLAN9 /* XXX Plan9 assembler chokes on this; fix needed */ 4522: u += SEED_C5 * (U32)PTR2UV(&when); 4523: #endif 4524: return u; 4525: }
/dev/urandom 以外には時刻などを使う。
ただ、この Perl_seed という関数の返り値は U32 なので、(おそらく) 32bit しかない。32bit というのは現在の計算機の brute force attack に対抗するには危うい。
あと、perl の乱数アルゴリズムはシステムが提供している drand48, random, rand のどれかを使う。長い種を扱わないのはこのへんに起因するのかもしれない。
perl-5.8.7/pod/perl56delta.pod:390-396
390: =head2 Better pseudo-random number generator 391: 392: In 5.005_0x and earlier, perl's rand() function used the C library 393: rand(3) function. As of 5.005_52, Configure tests for drand48(), 394: random(), and rand() (in that order) and picks the first one it finds. 395: 396: These changes should result in better random numbers from rand().
そういえば、bash には $RANDOM という疑似変数がある。
bash-3.1/doc/bashref.texi:4694-4697
4694: @item RANDOM 4695: Each time this parameter is referenced, a random integer 4696: between 0 and 32767 is generated. Assigning a value to this 4697: variable seeds the random number generator.
せっかくなのでたどってみよう。
とりあえず RANDOM で検索すると次の行が見つかった。
bash-3.1/variables.c:1451
1451: INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
きっと get_random というのが乱数を生成する関数で、 assign_random はおそらく種をセットするのだろう。
bash-3.1/variables.c:1137-1161,1175-1191,1200-1201
1137: /* The random number seed. You can change this by setting RANDOM. */ 1138: static unsigned long rseed = 1; 1139: static int last_random_value; 1140: static int seeded_subshell = 0; 1141: 1142: /* A linear congruential random number generator based on the example 1143: one in the ANSI C standard. This one isn't very good, but a more 1144: complicated one is overkill. */ 1145: 1146: /* Returns a pseudo-random number between 0 and 32767. */ 1147: static int 1148: brand () 1149: { 1150: rseed = rseed * 1103515245 + 12345; 1151: return ((unsigned int)((rseed >> 16) & 32767)); /* was % 32768 */ 1152: } 1153: 1154: /* Set the random number generator seed to SEED. */ 1155: static void 1156: sbrand (seed) 1157: unsigned long seed; 1158: { 1159: rseed = seed; 1160: last_random_value = 0; 1161: }
...
1175: int 1176: get_random_number () 1177: { 1178: int rv; 1179: 1180: /* Reset for command and process substitution. */ 1181: if (subshell_environment && seeded_subshell == 0) 1182: { 1183: sbrand (rseed + getpid() + NOW); 1184: seeded_subshell = 1; 1185: } 1186: 1187: do 1188: rv = brand (); 1189: while (rv == last_random_value); 1190: return rv; 1191: }
...
1200: rv = get_random_number (); 1201: last_random_value = rv;
アルゴリズムは線形合同法で、種は pid と時刻という感じだろうか。
コメントにもあるようにそんなによくないというのは自覚しているようではある。しかし、last_random_value とかいうので、同じ値が連続して出てこないようにしているのはいかがなものかと思う。どちらかというと、見識があってこれで十分と判断したというよりは、素人っぽさを感じる。
Python には os.urandom というのがある。 (なお、Windows の場合は C で定義されるようである)
Python-2.4.2/Lib/os.py:710-725
710: if not _exists("urandom"): 711: def urandom(n): 712: """urandom(n) -> str 713: 714: Return a string of n random bytes suitable for cryptographic use. 715: 716: """ 717: try: 718: _urandomfd = open("/dev/urandom", O_RDONLY) 719: except: 720: raise NotImplementedError("/dev/urandom (or equivalent) not found") 721: bytes = "" 722: while len(bytes) < n: 723: bytes += read(_urandomfd, n - len(bytes)) 724: close(_urandomfd) 725: return bytes
疑似乱数は Mersenne Twister が random.py で定義される。
Python-2.4.2/Lib/random.py:29,46,108-112
29: General notes on the underlying Mersenne Twister core generator:
...
46: from os import urandom as _urandom
...
108: try: 109: a = long(_hexlify(_urandom(16)), 16) 110: except NotImplementedError: 111: import time 112: a = long(time.time() * 256) # use fractional seconds
種は、os.urandom を使って16バイト (128bit) 読んで作る。読めなければ時刻。
php-5.1.0/ext/standard/rand.c:58-82
58: /* {{{ php_rand 59: */ 60: PHPAPI long php_rand(TSRMLS_D) 61: { 62: long ret; 63: 64: if (!BG(rand_is_seeded)) { 65: php_srand(GENERATE_SEED() TSRMLS_CC); 66: } 67: 68: #ifdef ZTS 69: ret = php_rand_r(&BG(rand_seed)); 70: #else 71: # if defined(HAVE_RANDOM) 72: ret = random(); 73: # elif defined(HAVE_LRAND48) 74: ret = lrand48(); 75: # else 76: ret = rand(); 77: # endif 78: #endif 79: 80: return ret; 81: } 82: /* }}} */
疑似乱数には random, lrand48, rand のどれかを使っている。
php-5.1.0/ext/standard/php_rand.h:49-53
49: #ifdef PHP_WIN32 50: #define GENERATE_SEED() ((long) (time(0) * GetCurrentProcessId() * 1000000 * php_combined_lcg(TSRMLS_C))) 51: #else 52: #define GENERATE_SEED() ((long) (time(0) * getpid() * 1000000 * php_combined_lcg(TSRMLS_C))) 53: #endif
種は時刻と pid と、えーと、この lcg ってのはそれ自身が疑似乱数なのか。うぅむ。
あ、php_rand_r というのが使われる可能性もある?
php-5.1.0/main/reentrancy.c:278,318-333
278: #ifndef HAVE_RAND_R
...
318: static int 319: do_rand(unsigned long *ctx) 320: { 321: return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1)); 322: } 323: 324: 325: PHPAPI int 326: php_rand_r(unsigned int *ctx) 327: { 328: u_long val = (u_long) *ctx; 329: *ctx = do_rand(&val); 330: return (int) *ctx; 331: } 332: 333: #endif
うぅむ。これはあまりよろしくない。でも、HAVE_RAND_R というところからみると、rand_r があれば使われないか。でも、rand_r って rand と同程度で、それもそんなに良くない気がするなぁ。
どうも graphviz は Debian のが古い (古すぎる) のが問題らしい。 graphviz-2.7.20060126.0540.tar.gz をとってきて試しに install してみたら問題が起きない。
Debian BTS にも新しいのが欲しいというリクエストが出ているが、反応がないようだ。
http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=pkg&data=graphviz&archive=no&version=&dist=unstable
調べた中でいちばんしょぼいのは bash の $RANDOM である。
では、$RANDOM を使っているシェルスクリプトはあるだろうか?もしあったら危ないこともあるかもしれない。
まぁ、SUSv3 にも定義されていなくてポータブルじゃないからあまりないだろう、と思ったところが大違い。大量にヒット。
https://www.codeblog.org/gonzui/search?q=RANDOM&fm=sh
うぅむ。autoconf 関連のスクリプトとかに入っているのか。
たとえば、config.guess に入っている。ここに入っていると大量にヒットしてしまうのもしょうがない。
gcc-3.4.4/config.guess:107-110
107: { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || 108: { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || 109: { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || 110: { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
まぁ、mktemp があれば使わないし、注意深く考えてある感じはする。
const int a = 1; const int b = a;
というコードは gcc では error になるが、g++ ではそうならないことを知る。
popen を探して php の virtual_popen にいきあたる。
php-5.1.0/TSRM/tsrm_virtual_cwd.c:984-1044
984: #else /* Unix */ 985: 986: CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) 987: { 988: int command_length; 989: int dir_length, extra = 0; 990: char *command_line; 991: char *ptr, *dir; 992: FILE *retval; 993: 994: command_length = strlen(command); 995: 996: dir_length = CWDG(cwd).cwd_length; 997: dir = CWDG(cwd).cwd; 998: while (dir_length > 0) { 999: if (*dir == '\'') extra+=3;
1000: dir++; 1001: dir_length--; 1002: } 1003: dir_length = CWDG(cwd).cwd_length; 1004: dir = CWDG(cwd).cwd; 1005: 1006: ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1); 1007: if (!command_line) { 1008: return NULL; 1009: } 1010: memcpy(ptr, "cd ", sizeof("cd ")-1); 1011: ptr += sizeof("cd ")-1; 1012: 1013: if (CWDG(cwd).cwd_length == 0) { 1014: *ptr++ = DEFAULT_SLASH; 1015: } else { 1016: *ptr++ = '\''; 1017: while (dir_length > 0) { 1018: switch (*dir) { 1019: case '\'': 1020: *ptr++ = '\''; 1021: *ptr++ = '\\'; 1022: *ptr++ = '\''; 1023: /* fall-through */ 1024: default: 1025: *ptr++ = *dir; 1026: } 1027: dir++; 1028: dir_length--; 1029: } 1030: *ptr++ = '\''; 1031: } 1032: 1033: *ptr++ = ' '; 1034: *ptr++ = ';'; 1035: *ptr++ = ' '; 1036: 1037: memcpy(ptr, command, command_length+1); 1038: retval = popen(command_line, type); 1039: 1040: free(command_line); 1041: return retval; 1042: } 1043: 1044: #endif
読んでみると、カレントディレクトリを移動した上で指定したコマンドを実行(popen)するもののようだ。そのために、popen に渡す引数に cd '...' というのを前置している。
quote は '...' で、' 自体の処理もちゃんとしているのはよろしい。
ただ、なんでこんなことをせねばならんのかという嘆きたくはなる。
popen の中では fork して exec しているわけで、child process で exec するまえに chdir を呼ぶというのが本来やりたい処理である。もしそうできれば、quote は不要である。
だが、popen を使う限りにおいて、そうはできない。
では、(ここの部分は Unix 用だし) popen を使わずに fork/exec を直接使えばいいのかというと、おそらくそれはそう単純にはいかない。インターフェース上 virtual_popen の返値は pclose にわたされると想像されるが、pclose に渡せるものを作れるのは popen だけである。名前からしてあからさまに対になっていてそんな感じがするし、機能から考えても、pclose は child process を wait するから、pid を FILE構造体に入れなきゃいけないけど、fdopen ではそれはできない。
やはりなんというか popen という API はできがわるい。
さらに、php_escape_shell_cmd にいきあたる。
php-5.1.0/ext/standard/exec.c:255-324
255: /* {{{ php_escape_shell_cmd 256: Escape all chars that could possibly be used to 257: break out of a shell command 258: 259: This function emalloc's a string and returns the pointer. 260: Remember to efree it when done with it. 261: 262: *NOT* safe for binary strings 263: */ 264: char *php_escape_shell_cmd(char *str) { 265: register int x, y, l; 266: char *cmd; 267: char *p = NULL; 268: 269: l = strlen(str); 270: cmd = safe_emalloc(2, l, 1); 271: 272: for (x = 0, y = 0; x < l; x++) { 273: switch (str[x]) { 274: case '"': 275: case '\'': 276: #ifndef PHP_WIN32 277: if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) { 278: /* noop */ 279: } else if (p && *p == str[x]) { 280: p = NULL; 281: } else { 282: cmd[y++] = '\\'; 283: } 284: cmd[y++] = str[x]; 285: break; 286: #endif 287: case '#': /* This is character-set independent */ 288: case '&': 289: case ';': 290: case '`': 291: case '|': 292: case '*': 293: case '?': 294: case '~': 295: case '<': 296: case '>': 297: case '^': 298: case '(': 299: case ')': 300: case '[': 301: case ']': 302: case '{': 303: case '}': 304: case '$': 305: case '\\': 306: case '\x0A': /* excluding these two */ 307: case '\xFF': 308: #ifdef PHP_WIN32 309: /* since Windows does not allow us to escape these chars, just remove them */ 310: case '%': 311: cmd[y++] = ' '; 312: break; 313: #endif 314: cmd[y++] = '\\'; 315: /* fall-through */ 316: default: 317: cmd[y++] = str[x]; 318: 319: } 320: } 321: cmd[y] = '\0'; 322: return cmd; 323: } 324: /* }}} */
むー。文字列を escape している感じだが、もともと存在する quote を解釈する?しかも single quote と double quote の処理が同じ?
これは怪しい。
[latest]