以下のプログラムをWindows 2000,Windows XP上で実行するとOSごと
ハングアップするかあるいはリブートされます。
Windows 98上では特に不具合はおきません。Visual C++ 6.0 と
Borland C++ Builderのどちらでもコンパイルした場合でも起こります。
/*
* このプログラムを実行するとWin2kがハング or リブートするので
* 実行する際には十分注意してください。
*/
#include <stdio.h>
int main( void )
{
for(;;){
printf( "hung up\t\t\b\b\b\b\b\b" );
}
return 0;
}
--
-----------------------------
Masaru Tsuchiyama
-----------------------------
> 同僚が見つけたバグを代理で投稿します。
> #include <stdio.h>
>
> int main( void )
> {
> for(;;){
> printf( "hung up\t\t\b\b\b\b\b\b" );
> }
> return 0;
> }
ハングアップするのは OS が悪いといえば悪いですが、これプログラムも悪い
ですが。
まずは printf 関数のプロトタイプをじっくりと見直してみましょう。
#かなり多い間違いではあるが……
--
written by Takeharu Masuda <t...@kk.iij4u.or.jp>
with Thinkpad 235 - called "Chandra II" / "Clavius"
2001/10/24 00:02:33
printf 関数のプロトタイプは int printf( const char *format [,
argument]... );
なので特に間違っているようには思えないですがどこが間違っていますか?
私もリブートを確認しました。
これは恐るべき現象ですね。
どうせ既に判明している問題だろう、SP2が当たっていれば大丈夫さ~と
同プログラムをVC6でコンパイル、実行させて見て、ほ~ら、なんにも……
表示されなくなって、あごがカックンと落ちました。
コンソール自体の問題ではないか、と思われますが、いったい何が起こっているやら。
この記事が、悪質ないたずらプログラムなどに使用されないことを願っています。
----- Takeshi SHIGIHARA
Office cyg...@zero.ad.jp
Home cyg...@po.jah.ne.jp -----
> > まずは printf 関数のプロトタイプをじっくりと見直してみましょう。
> > #かなり多い間違いではあるが……
> >
>
> printf 関数のプロトタイプは int printf( const char *format [,
> argument]... );
> なので特に間違っているようには思えないですがどこが間違っていますか?
const char *format は書式指定の文字列であり、その後ろにそれぞれのパラ
メータがくることが前提になっています。
#ANSI C 定義では完全にオプションなので0個でも構わないのだが、結構トラブ
ルの種になる
つまり、今回のケースなら
printf( "%s", 文字列 );
って書くほうがより安全です。puts と同じ感覚で printf( 文字列 ) って書く
人は多いですが、これだと内部でどういうトラブルを起こすか分かったものでは
ありません。
#特に文字列に "%" を含んじゃった日には……
まあ、va_start と va_end マクロの扱いなんかでとらぶっているんだとは思
いますが、もともと printf( 文字列 ) って書くのはあまり感心しないですね。
--
written by Takeharu Masuda <t...@kk.iij4u.or.jp>
with Thinkpad 235 - called "Chandra II" / "Clavius"
2001/10/24 23:57:26
Masaru Tsuchiyama氏の記述は、C言語のライブラリの使い方として完全に正しいのです。
ところで、Takeharu Masudaさんは、かのコードを試してみましたか?
私はprintfをWin32の WriteFile または WriteConsole に書き換えてまで試しました。
結果は、暴走・リセットです。
これはC言語ライブラリの使い方の問題ではなく、Win2K/XP上のコンソール自体の問題
です。セキュリティホールと言っても良い。
非常に由々しき問題なんです。
---- Takeshi SHIGIHARA
OFFICE cyg...@zero.ad.jp
HOME cyg...@po.jah.ne.jp , cyg...@tka.att.ne.jp ------------
fj.comp.lang.cを加えて、フォロー先もfj.comp.lang.cです。
Takeharu Masuda wrote:
> On Wed, 24 Oct 2001 07:37:11 +0900
> "Masaru Tsuchiyama" <tsuch...@asahi-net.email.ne.jp> wrote
> at Re: Windows 2000がハングアップするバグについて
> (<3bd5f119$0$21719$44c9...@news2.asahi-net.or.jp>):
>
> > > まずは printf 関数のプロトタイプをじっくりと見直してみましょう。
> > > #かなり多い間違いではあるが……
> > >
> >
> > printf 関数のプロトタイプは int printf( const char *format [,
> > argument]... );
> > なので特に間違っているようには思えないですがどこが間違っていますか?
>
> const char *format は書式指定の文字列であり、その後ろにそれぞれのパラ
> メータがくることが前提になっています。
>
> #ANSI C 定義では完全にオプションなので0個でも構わないのだが、結構トラブ
> ルの種になる
>
ならば、パラメータがなくとも問題ないでしょう。どんなトラブルの種になるんでしょうか?
>
> つまり、今回のケースなら
>
> printf( "%s", 文字列 );
>
> って書くほうがより安全です。puts と同じ感覚で printf( 文字列 ) って書く
> 人は多いですが、これだと内部でどういうトラブルを起こすか分かったものでは
> ありません。
> #特に文字列に "%" を含んじゃった日には……
今回は"%"を含んでいないんだから、問題はありません。
確かに、文字列が可変である場合や、任意の文字列に変更される
可能性のある場合は、エンバグしやすい書き方かもしれませんが、
今回のコードそのものにはまったく問題はないでしょう。
>
>
> まあ、va_start と va_end マクロの扱いなんかでとらぶっているんだとは思
> いますが、もともと printf( 文字列 ) って書くのはあまり感心しないですね。
>
va_start,va_argマクロは関係ないと思いますが?
--
cog...@sp.hudson.co.jp
株式会社ハドソン
研究開発本部 研究開発課
熊岡 忍(Kumaoka Shinobu)
In article <3BD6E610...@tka.att.ne.jp>,
Takeshi SHIGIHARA <cyg...@po.jah.ne.jp> wrote:
>printf("constant string");
>これは、完全にlegalな記述であり、ここに変換文字列が含まれるかどうかはオプションで
>あって、より安全かどうかは意味をなしません。
>
>Masaru Tsuchiyama氏の記述は、C言語のライブラリの使い方として完全に正しいのです。
C を学習する時に真っ先に出て来る sample program の一つであ
ろう hello.c が実は規格違反であったというのは実に斬新な切口
で面白いのですが、それを証明すべく果敢にも OS を落してみせる
Windows 2000 というのもなかなかのものですね :-)
しかし、この提言を笑止扱いして終わりでは勿体ないので、原因
の切り分けに用いるというのは一つの策であると思います。
ここで「legal」と言われている printf("%s", "hungup...") な
ら本当に落ちないのか、はたまた puts("hungup...") なら大丈夫
なのか、fprintf() ならどうか、fprintf() の出力先を stdout 以
外に据えたらどうなのか、色々試してみる価値はあると思います。
また、console program として使う場合、shell で stdout を某
かの file に redirect してみるとどうなるのか、ANSI.SYS を設
定してみるとどうか、DOS アプリとして compile したものならど
うなのか、等々環境を変えて試してみてもいいでしょう。
このように興味は尽きないのですが、残念なことに私の周囲には
「落しても構わない Windows 2000 環境」が存在しないので、無闇
に試す訳にもいかなくてジレンマを抱えています。
>私はprintfをWin32の WriteFile または WriteConsole に書き換えてまで試しました。
>結果は、暴走・リセットです。
library の支障ではなくて端末の実装ミスのような気がしてきま
すね。となると、C で書かなくても perl でも BASIC でも似たよ
うな症状が起きるように思えます。
上記の試行例の中では ANSI.SYS など是非とも試してみたい部類
の検証だと思うのですが如何なものでしょう?Windows NT/2000 で
は %SystemRoot%\SYSTEM32\CONFIG.NT をいじるんすが、XP でも一
緒でしたっけ?
>これはC言語ライブラリの使い方の問題ではなく、Win2K/XP上のコンソール自体の問題
>です。セキュリティホールと言っても良い。
>非常に由々しき問題なんです。
割と良く使う console アプリとしては FTP.EXE なんかどうなん
だろうと思いますが、接続時の message に件の文字列を返す site
を用意しておくと、結構リスキーな結果が得られたりするんじゃな
いでしょうか?
頑張れば、「DIR」と打っただけで落ちる環境も構築出来るかも
知れませんね。
--
しらい たかし
perl でもハングアップしました。
for (;;) {
print "hung up\t\t\b\b\b\b\b\b";
}
Takashi SHIRAI wrote:
>
> library の支障ではなくて端末の実装ミスのような気がしてきま
> すね。となると、C で書かなくても perl でも BASIC でも似たよ
> うな症状が起きるように思えます。
--
川端 浩 (Hiroshi Kawabata)
mailto:falc...@muf.biglobe.ne.jp
> しかし、この提言を笑止扱いして終わりでは勿体ないので、原因
> の切り分けに用いるというのは一つの策であると思います。
さすが、しらいさんはsmartだ……私も見習わねば。
この現象のもたらす大問題に、私は熱くなりすぎていたように思います。
> library の支障ではなくて端末の実装ミスのような気がしてきま
> すね。となると、C で書かなくても perl でも BASIC でも似たよ
> うな症状が起きるように思えます。
Masaru Tsuchiyamaさんは comp.os.ms-windows.programmer.win32 にも同様の記事を
出していらっしゃいます。
Message ID 3bd56bdb$0$21707$44c9...@news2.asahi-net.or.jp から始まる、
一連のスレッドが出来上がっています。こちらもまた、興味深いものです。
そのスレッドにて、現象を再現できる最も短いものとして、Bart Kowalski氏が
int main(void)
{
printf("\t\b\b");
return 0;
}
というのを発表されています。また、これがasciiだろうがunicodeだろうが関係なく
現象が起こるという報告もあります。
ファイルにリダイレクトしたものをtypeしたものでも、やはり現象は起こったそうです。
私は、できる限りシステムに近い条件として、printfが最終的に使用しているAPIでも
あるWriteFile APIにて現象を確認しました。
ところで、WriteFileを行う前にコンソールのモードを
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), 0);
にてタブやBSを編集させないモードにして動作させたところ、リセットなどの暴走行為
を起こさないことを確認しています……あまり使い物にならない気がします。
> 上記の試行例の中では ANSI.SYS など是非とも試してみたい部類
> の検証だと思うのですが如何なものでしょう?Windows NT/2000 で
> は %SystemRoot%\SYSTEM32\CONFIG.NT をいじるんすが、XP でも一
> 緒でしたっけ?
これは試していませんでしたね。今、XP環境は無いのでWin2000だけですが。
せ~の……
ぬはぁ
むぅ。今、気づいたけど、コンソールで prompt $e[32m とかしても色が変わらない。
何か間違えてるかな。
NT kernelの問題らしいのであまり情報量がないですが、
VMWare 2.0.4上のW2Kでも同様の現象が再現しました。
テストするならその手の環境が便利ですね。
# このバグには呆れて笑うしかありませんね…
----
Muraoka Taro <ko...@tka.att.ne.jp>
> 以下のプログラムをWindows 2000,Windows XP上で実行するとOSごと
> ハングアップするかあるいはリブートされます。
Win32 コンソールで エスケープ sequence は使えない
と思います。command.com + ansi.sys では OK かも。
Masaru Tsuchiyama wrote:
>
> 同僚が見つけたバグを代理で投稿します。
>
> 以下のプログラムをWindows 2000,Windows XP上で実行するとOSごと
> ハングアップするかあるいはリブートされます。
> Windows 98上では特に不具合はおきません。Visual C++ 6.0 と
> Borland C++ Builderのどちらでもコンパイルした場合でも起こります。
>
この内容をセキュリティホール memo メーリングリストへ投稿しました。
http://memo.st.ryukoku.ac.jp/archive/200110.month/thread.html
の「コンソールプログラムで Windows 2000 がハングアップする」
以下のスレッドも見て下さい。。
--
小柳 雅明(Koyanagi...@nifty.ne.jp)
「人の足を止めるのは"絶望"ではなく"諦観"
人の足を進めるのは"希望"ではなく"意志"」
-- ARMS
> この内容をセキュリティホール memo メーリングリストへ投稿しました。
> http://memo.st.ryukoku.ac.jp/archive/200110.month/thread.html
> の「コンソールプログラムで Windows 2000 がハングアップする」
> 以下のスレッドも見て下さい。。
NTBugtrag にも流しました。
#元記事の無限ループを含むコードをそのまま書いたら、moderator に
#「そりゃ無限ループでマシンパワーが奪われてるだけだろ、プロセスを
#殺せば生き返るはずだ」という勘違いをされて reject されました。
#現在、Kowalski 氏のコードを書き添えて再投稿中。
========================================================================
飯嶋 浩光 / でるもんた http://www.ht.sakura.ne.jp/~delmonta/
IIJIMA Hiromitsu, aka Delmonta mailto:delm...@ht.sakura.ne.jp
mailto:delm...@pop01.odn.ne.jp
面白いコトがわかりました。以下のコード見て下さい。
(ハングしませんから、ご安心を)
int main(int argc, char* argv[])
{
printf("12345678901234567890\t\b\b");
return 0;
}
本来なら、
1234567890123456789
と表示されるはずですよね。ところが、
1234567890123456
までしか出力されません。
これは推測ですが、
Win2kでは、\bが「直前の\bでない文字のバイト数分」バックするようです。
上記の場合、\tは、コンソール上で4文字分ですので、
\b\bで"7890"+タブが削除されるわけです。
printf("12345漢\b\b");
では、"漢"が2バイトですから、\b\bで"45漢"が削除されます
つまり、4バイトバックするわけです。
結果として、\t\b\bによって、バッファが8バイト分後ろに戻り、
アクセス違反が発生してWin2kが落ちるものと思われます。
ちなみに、Win9xで落ちないのは、2kがDOSをエミュレーションしているのに対して、
9xが仮想モードで動作していることにあると思います。
--
Aritel <ari...@geocities.co.jp>
http://cgi.cx/aritel/
In article <3BD833E9...@tka.att.ne.jp>,
Takeshi SHIGIHARA <cyg...@po.jah.ne.jp> wrote:
>そのスレッドにて、現象を再現できる最も短いものとして、Bart Kowalski氏が
> int main(void)
> {
> printf("\t\b\b");
> return 0;
> }
>というのを発表されています。また、これがasciiだろうがunicodeだろうが関係なく
>現象が起こるという報告もあります。
UNICODE つっても色々あって、UTF-8 だとこの文字列のコードは
ASCII と全く同じですね。UCS-2 だと単に BYTE -> WORD にしたも
のになりますが。
多分言わんとしてるのは、"\0\t\0\b\0\b" という UCS-2 のコー
ドを吐かせた時か、もしくは UNICODE 文字列を引数に取るような
API で端末出力させた時という意味だと思います。
前者だとすると、UCS-2 の 1 byte 目である \0 は C の ASCIZ
の terminator なので、fputc() 辺りで一文字ずつ吐かないと出力
出来ませんが、それで試行したのなら単に端末が \0 を無視しただ
けで同じ効能が得られた結果なんでしょう。
後者だとすると、printf() のような ASCII を扱う library も、
UNICODE 文字列を扱う API も、結局は内部的には同じところに辿
り着く筈なので、再現して当たり前のような気がします。
どの道 console を実現する部分の MS library の bug っぽいで
すね。
>ファイルにリダイレクトしたものをtypeしたものでも、やはり現象は起こったそうです。
小島@龍谷大さんとこの memo を見ると他にも再現報告が挙がっ
てるんですが、その辺りも踏まえて総合的に判断すると、応用範囲
は思いのほか広そうですね。
command prompt を含めて console アプリは使用禁止にするとい
う、非常に不便な回避法しか今のところ防御策はないかも知れませ
ん。
# 代替品として、cygwin の端末で動く COMMAND.COM なんてもの
#は無いのかしらん?
>私は、できる限りシステムに近い条件として、printfが最終的に使用しているAPIでも
>あるWriteFile APIにて現象を確認しました。
>ところで、WriteFileを行う前にコンソールのモードを
> SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), 0);
>にてタブやBSを編集させないモードにして動作させたところ、リセットなどの暴走行為
>を起こさないことを確認しています……あまり使い物にならない気がします。
それってその console アプリが終ってしまうと当然無効になっ
てしまって、OS 起動後一度実行しておけばその後はずっと大丈夫
なんてことはありませんよね?
何かそういう手段が用意出来れば、自衛策として startup に含
めておけるのですが。
その SetConsoleMode() で回避出来たのにしたって、tab 処理を
無効にさせたことが効いているのではなくて、"\t\b\b" を出力す
る前に端末の初期化が正しく行なわれたので回避出来たということ
はありませんかね?
多分、"\t\b\b" がその console アプリの中で最初に出力される
文字列だからいけないと思うんですよ。そうでなければ、こんな単
純な文字列どこででも現れ得るのでもっと早期に発覚していた筈で
す。
その API って何も副作用無いって保証はありませんよね?引数
を 0 -> 1 に返るとちゃんと再現するようなら、そのモードの違い
が原因だと特定出来るかも知れませんが。
>> 上記の試行例の中では ANSI.SYS など是非とも試してみたい部類
>> の検証だと思うのですが如何なものでしょう?Windows NT/2000 で
>> は %SystemRoot%\SYSTEM32\CONFIG.NT をいじるんすが、XP でも一
>> 緒でしたっけ?
>
> これは試していませんでしたね。今、XP環境は無いのでWin2000だけですが。
> せ~の……
> ぬはぁ
COMMAND.COM + ANSI.SYS だと端末出力に関する初期化がもっと
丁寧に実行されるかなと思ったんですが駄目でしたか。
ふぅ。
# 駄目元で printf("%s", "\t\b\b") としてみると再現しなくな
#ったりなんかしてしまうと面白いんだけどなー :-)
--
しらい たかし
In article <9rej2j$4dc$1...@news0.hi-ho.ne.jp>,
aritel <ari...@geocities.co.jp> wrote:
>初投稿 aritel です
>これは推測ですが、
>Win2kでは、\bが「直前の\bでない文字のバイト数分」バックするようです。
>上記の場合、\tは、コンソール上で4文字分ですので、
>\b\bで"7890"+タブが削除されるわけです。
>
> printf("12345漢\b\b");
>では、"漢"が2バイトですから、\b\bで"45漢"が削除されます
>つまり、4バイトバックするわけです。
>
>
>結果として、\t\b\bによって、バッファが8バイト分後ろに戻り、
>アクセス違反が発生してWin2kが落ちるものと思われます。
もしそうなら、"漢\b\b" でも落ちそうな気がしますし、単に余
剰に backword しただけで落ちると言うなら "\b\b\b\b\b..." と
いう文字列でも落ちそうです。
"\t\b\b" の場合余剰に戻るのは 4 文字分ですから、もしこの数
が重要なのなら、「漢」なら "漢\b\b\b" で 4 文字分余剰に戻り
ますし、"\b\b\b\b" でも 4 文字分戻りますね。
もしかして、command prompt 起動して直後に Bs キーを 4 回叩
いただけで落ちたりして :-)
>ちなみに、Win9xで落ちないのは、2kがDOSをエミュレーションしているのに対して、
>9xが仮想モードで動作していることにあると思います。
95/98 で動いてるのはありゃ MS-DOS そのものです。DOS prompt
ってのは、DPMI を実装した MS-DOS ver. 7 として動いてます。
だから遅いんですね。16bit アプリと 32bit アプリを DPMI で
行き来する訳ですから。でも、DOS prompt ではこの本物の MS-DOS
のお蔭で、emulator の command prompt では動かないアプリでも
まぁ普通に動いてくれます。
95/98 では function call 部分の実装は MS-DOS と同じなので
すが、NT/2000 の場合はこの function call 部分の実装からして
MS-DOS 時代の emulation になってます。
今回の件で emulation がまずかったというのは、多分その部分
なんじゃないかと思いますけどね。
--
しらい たかし
これについては、以下で確認できます。
main(){
printf("\n\n");
printf("\b\b\b\b");
}
main(){
printf("\n\n");
printf("漢\b\b\b\b");
}
main(){
printf("\n\n");
printf("\t\b\b\b\b");
}
それぞれ、改行した後に問題のコードを入れてあります。動かして見るとわかります
が、\bだけ、漢\b... は前の行には戻りません。なぜなら、\bは行頭より前には戻ら
ない仕組み(というのが標準のはず)だからです。2バイトずつ戻ろうが、行頭より
前に戻るBSは切り捨てられます。
しかし、\t\b... の場合だけ、カーソルが前の行に移動します。なぜか、\tのときだ
け行頭より前へのBSが破棄されずに実行されてしまうんですね。
とも思ったんですが、外部と通信するようなコンソールアプリで、
外部からの入力をそのままコンソールに垂れ流すようなものだと、
それを通して攻撃される可能性がありますね。
# このバグを使った攻撃でなくても、\aを大量に送られたら…?(^^;;
> その SetConsoleMode() で回避出来たのにしたって、tab 処理を
> 無効にさせたことが効いているのではなくて、"\t\b\b" を出力す
> る前に端末の初期化が正しく行なわれたので回避出来たということ
> はありませんかね?
> 多分、"\t\b\b" がその console アプリの中で最初に出力される
> 文字列だからいけないと思うんですよ。
どうでしょう。コンソールアプリを実行するたびに新しいコンソールが
作成されるわけではありませんから、コンソールアプリが最初に出力する
文字列が"\t\b\b"かどうかは関係ないと思います。
> そうでなければ、こんな単
> 純な文字列どこででも現れ得るのでもっと早期に発覚していた筈で
> す。
いや、むしろめったに現れないのでは。タブを出力した後に
バックスペースを出力する、ってどんな状況でしょう。
元記事の方の同僚の方に、どういう状況で発見したのか訊いてみたいですね。
> その API って何も副作用無いって保証はありませんよね?引数
> を 0 -> 1 に返るとちゃんと再現するようなら、そのモードの違い
> が原因だと特定出来るかも知れませんが。
>
そうですね。それに、
http://msdn.microsoft.com/library/en-us/dllproc/conchar_25b9.asp
に、
> When a console is created, all input modes except
> ENABLE_WINDOW_INPUT are enabled by default.
と書いてあるように、コンソールのモードの初期値は、
ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT |
ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_OUTPUT |
ENABLE_WRAP_AT_EOL_OUTPUT
ですから、"PROCESSED_INPUT"を解除したことによる
効果だという保証もありませんね。
# そうだという予想はできるし、恐らくそうなんだろうけど
# もしかしたらWRAP_AT_EOL_OUTPUTだったりして