私は nix-Haters Handbook にリンクされていて、つまずいた(149ページ):
件名:関連するUnixのバグ
1991年10月11日
W4115xの仲間の学生—
アクティベーションレコード、引数の受け渡し、呼び出し規約について説明しているときに、次のように入力することをご存知でしたか。
!xxx%s%s%s%s%s%s%s%s
cシェルに接続すると、すぐにクラッシュしますか?なぜなのかご存知ですか?
考えるべき質問:
!xxx
と入力すると、シェルは何をしますか?!xxx%s%s%s%s%s%s%s%s
と入力すると、入力に対して何をする必要がありますか?- なぜこれがシェルをクラッシュさせるのですか?
- この問題が発生しないように、シェルの問題のある部分を(かなり簡単に)どのように書き直すことができますか?
純粋に好奇心から、誰かが問題が何であったかを説明できますか?当然のことながら、Googleで文字列を検索しても役に立ちません。メッセージから他の引用を検索すると、メッセージの他のコピーが表示されただけで、説明はありませんでした。
25年前の貝殻の出所を掘り下げる気はしませんが
フォーマット文字列の脆弱性 である可能性があります。
シェルに次のようなコードが含まれている場合
printf(str);
ここで、str
はユーザーの入力から取得した文字列であり、文字列の内容はprintf
が使用するフォーマット文字列になります。 %s
は、引数が指す文字列を出力するようにprintf
に指示します。引数が指定されていない場合(上記のように、フォーマット文字列のみがあります)、関数はスタックから他のデータを読み取り、それらをポインターとして追跡します。おそらく、マップされていないメモリにアクセスし、プロセスをクラッシュさせます。
ある意味、メッセージの言い回しもこのような解決策を示唆していると思います。 !xxx
と入力すると、シェルが目に見える形で!xxx: event not found
のようなエラーメッセージを出力します。そこから、!xxx%s%s%s%s%s%s%s%s: event not found
も印刷しようとすることは大きな飛躍ではなく、フォーマット文字列の脆弱性が関係しています。
持ってはいけませんが、ソースを覗いてみました ここ (4.3BSD-Tahoe/usr/src/bin/csh
、日付は1988年のものです)。
findev(cp, anyarg)
in sh.Lex.c
一致する履歴イベントを見つける関数である可能性があります:Histlist
と呼ばれるstruct Hist
のリンクリストをウォークスルーします。何も見つからない場合は、seterr2(cp, ": Event not found");
からnoev()
を呼び出します。ここでのcp
は、履歴で検索されている文字列のようです。
seterr2()
は、変数err
を引数の連結として設定し、err
は、 sh.c
のif (err) error(err);
のいくつかの場所でprocess()
として使用されます。最後に、error()
( sh.err.c
)には、古典的なフォーマット文字列の脆弱性が含まれています:if (s) printf(s, arg), printf(".\n");
他のいくつかの場所では、error()
はerror("Unknown user: %s", gpath + 1);
のような引数で呼び出されるため、error()
の最初の引数はフォーマット文字列である可能性があることは明らかです。
履歴置換機能を完全に理解したと言ったら正直ではありません。 Cでのコメントなしの手動文字列処理です。%
は履歴置換で特別な意味を持ちますが、最初の文字として(!%
のように)、またはfindev()
が呼び出された後にのみ特別に処理されることがわかります。