process reaperの実験

procctlという、プロセスの属性の一部を制御するAPIFreeBSD 10で追加されたものですが、10.2 でそれに reaper facility と呼ばれている機能が追加されました。基本的な機能性はLinuxを参考に(Linuxでは prctl(2) というAPIです)、APIの設計はDragonflyBSDをベースにしており、manページもFreeBSDのものよりDragonflyのほうがわかりやくす書かれていますから、そちらを参考にすると良いでしょう。こちらのメイルから始まるスレッドもご参考ください https://lists.freebsd.org/pipermail/freebsd-arch/2014-December/016437.html

process reaperとは、子プロセスを作った場合の親プロセスのように、子プロセスが終了してゾンビになった後で、それに対応するwaitシステムコールを発行するプロセスのことを指します(ゾンビの魂(?)を、死神の鎌で刈る(reap)というイメージですね)。しかしこれらのAPIでは、Unixにおいて PID 1 の、通常は init が(あるいは近年のmacOSLinuxでは違いますが)受け持つ、親プロセスが先に終了したようないわゆる orphan プロセスの親となり、それらが終了した後に wait を発行する、という権利と義務(?)について、init になり代わる、という機能を扱っています。

procctlという関数名ですが、manを見るときは同名のコマンドと被ってますので、man 2 procctlのようにして参照する必要があります。

次にサンプルコードを示します。

最後のほうでforkしていて、子プロセスのほうからはxtermを起動していますので(xtermが無い環境の方は適宜修正してお試しください)、そのxtermの中のシェルから sleep 100 & など適当に時間のかかるプロセスを起動しておいてからxtermを終了し、別のターミナルなどから ps -d などで確認すると、orphan プロセスの親になっているのが確認できるかと思います。

この機能、軽量コンテナのインスタンス毎のinitとして、とかに使うためのものだろうかとか思ってますが、どうなんでしょうか? (あるいはデーモンマネジャのようなものを実装するのにも使えそうですが)

総和の計算は、なぜどのように難しいのか

プログラミング入門で、forループを使う例題としてよく出てきたりするように、総和計算は一見簡単そうですが、実はどのように難しいのか、という点を書いてみます。

まず、対象が整数であれば、大きな問題はありません。昔のようにintが16ビットの場合は、160*160=25600 でぎりぎりに近く 200*200=40000 ではもう 32767 を超えて溢れますから、intではなくlongを使うといった注意が、以前は必要だったというくらいでしょうか。

一方、対象が浮動小数点の総和は、場合によってはそう簡単ではありません。

場合として、まず、符号が正か負の片方だけであれば簡単です(代表的なものとしては内積など)。ソートして絶対値順に並べ、絶対値が小さい側の端2個のペアを取り出して、その和を計算し、結果をソートされている列の入るべき場所に挿入する、という操作を繰り返せば、基本的には問題はありません。

符号が両方ある場合は、正と負に振り分けてそれぞれ計算し、最後に差をとれば良い、と思うかもしれませんが、それではうまくない場合があります。極端な場合として、正側と負側のそれぞれの合計の絶対値が、2倍以内という近い値になってしまうかもしれません。そうなると桁落ちになります。

桁落ちという現象は、しばしば誤解が見られますが、それ自身は誤差を発生させません。正確には、絶対誤差は増えないが、値が小さくなるために相対誤差が(場合によっては文字通り「桁違いに」)大きくなる、という現象です。

そういったような場合、その絶対誤差は、そこに至るまでの計算過程での情報落ちによる誤差の積み残しの集積になっています。

直感的には、桁落ちの英語が「キャンセル」であるように、総和をとる対象中の、正負それぞれの絶対値の大きい値どうしでキャンセルできるものがあれば、それぞれでキャンセルすれば、「桁落ちは起きているが、情報は失われていない」という状態を維持できるので、それを繰り返してゆけば、正確な計算ができるはずです。

しかし、そのようなヒューリスティック的な計算をいちいちやっていては、総和計算に期待されるような O(n) に近い効率はとても実現できませんから、実際に提案されている手法は、何らかの方法で積み残しを全部残すであるとか、倍精度浮動小数点で表現可能な値の範囲全体をカバーする巨大な固定小数点で計算するとか、そういった手法になっています。

Ruby 2.4 の新機能, Array#sum

Ruby 2.4 で追加されている新機能のひとつに Array#sum があります。

(参: ドキュメントは http://ruby-doc.org/core-2.4.0/Array.html#method-i-sum

共立『アルゴリズム辞典』でも「総和計算」を立項しているように、情報落ち*1*2による「積み残し」が場合によっては著しいのが、対象が浮動小数点の時の総和計算の問題点です。

Array#sum は、Kahan summation algorithm(wikipedia:カハンの加算アルゴリズム、summation なので -総和アルゴリズム ないし -総和計算アルゴリズム、としたほうが訳としては正確)の改良版などの手法を実装しており、かなりの高精度な結果が得られます。

次のようなスクリプトで試してみると(実験に適した浮動小数点乱数を生成するため、拙作の RandomFloat を使用)、

# sum sample
require_relative 'random_float'

r_f = RandomFloat.new gt:1, lt:2**38

loop {
  arr = []
  (1000000).times {
    x = r_f.rand
    arr << x
    arr << -x
  }
  arr.shuffle!
  p arr.sum
}

そこそこ過酷な条件の総和計算ですが、次のように、ほぼ正確な結果を得られています(完全に正確ではない結果も、時折出ています)。

$ ruby-trunk -v sum_sample.rb
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-freebsd10.3]
0.0
0.0
0.0
0.0
0.0
-1.3322676295501878e-15
0.0
1.4654943925052066e-14
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0

Python の math.fsum との違い

ネットを捜すと、Python の math.fsum との関連で議論されていることがありますが、両者は基本的なコンセプトの点で別物です。今回実装された Array#sum は、あくまでも「高精度」であり、誤差が発生する可能性自体はあります。一方(現在の?)Python の math.fsum は、たとえて言うなら、入力や途中の計算を全て Rational に変換してから行い(実際の実装は異なります*3)、最後に浮動小数点に戻したような「正確」な結果を返すもので、前述で見たような誤差が発生することはありません。

もしそのような「正確な総和」が Ruby でどうしても必要な場合は、Rational を使ってユーザが実装すれば良いでしょう。そのような sum も(私見では、現在の sum と別の実装として、違う名前でメソッドを追加すべきだと思っています)もしかしたら今後実装されるかもしれません(関連するチケットは存在しています)。

*1:正負の要素があると、桁落ちが発生することもありますが、総和計算における主要な問題は桁落ちではありません。むしろ、正しく計画された桁落ちであれば情報は欠けないのであり、情報が積み残されて消えてしまった、という「結果が露呈する(ことがある)」のが、総和計算における桁落ちです。

*2:この桁落ちに関しては、以前のPythonのドキュメントの和訳に、原文には相当するcancellationという語は実のところ無いのですが、手違いにより「桁落ち」という表現があったため、誤解している人ももしかしたらいるかもしれません。

*3:色々な手法が提案されています。

拡張ライブラリでキーワード引数を処理する定番プラクティス(ベストじゃないかも)

ruby 2.4 リリースおめでとうございます。つい先日まで「拡張モジュール」と「拡張ライブラリ」の、どちらの呼称がobsoletedなのかいまいちはっきり認識していなかったきしもとです(ClassクラスのスーパークラスであるModuleのほうのモジュールがあるので、「拡張モジュール」がobsoletedです)。

いつのまにやら内蔵ライブラリでも結構いろいろ使われているなど、一般的になっていたRubyのキーワード引数ですが、じつはつい先日まで、C API で提供されているユーティリティ関数にバグがあり、だいぶ悩ませられていた人もあったようです。

さいわい、良いタイミングで修正されて、本日リリースされた 2.4 からは普通に使えるようになっていますので、拡張ライブラリでキーワード引数を処理する定番プラクティス(ベストじゃないかも)をまとめておきます。

1. Rubyレイヤで処理できないか検討

いきなりタイトルから外れるようなことを書きますが、doc/extension.rdoc の英語版にはあるように(日本語版では省略されている)、

  Be warned, handling keyword arguments in the C API is less efficient
  than handling them in Ruby.  Consider using a Ruby wrapper method
  around a non-keyword C function.
  ref: https://bugs.ruby-lang.org/issues/11339

といったように効率上は、Rubyで実装されたメソッドに対するキーワード引数の処理のほうが効率がよく(参照先によれば数倍?)、プログラムの見やすさのこともありますから、キーワード引数はRubyレイヤで受けて、拡張ライブラリ側で処理するのは避ける、というのも一つの手です。

2. アリティの確認

ここから拡張ライブラリの話に入ります。

まずメソッド定義の C API であるrb_define_methodなどには、キーワード引数の扱いは現れてきません。rb_define_methodの場合、第4引数( argc )を -1 にして、可変長引数のメソッドとして定義します。

そして、Rubyでキーワード引数付きのメソッドを定義した場合、アリティ(引数の個数)が、定義側(仮引数)と呼出側(実引数)で一致しているか否かは、どちらの側もキーワード引数が無いものとしてチェックされています(必須なキーワードがある場合も)。ですから、拡張ライブラリの場合もそれに合わせます。rb_define_methodで登録すべき C 関数の骨組みは次のようになります。

/* 必須な引数の個数が 1 個で、キーワード引数も取るメソッド */
static VALUE
my_kw_method(int argc, VALUE argv[], VALUE obj)
{
    VALUE h;

    /* アリティチェックよりも前にキーワード引数の存在をチェックする */
    h = rb_check_hash_type(argv[argc-1]);
    if ( ! NIL_P(h)) {
        --argc;
    }
    rb_check_arity(argc, 1, 1);  /* 必須な引数は1個で */
        /* (キーワード引数以外の)オプショナルな引数は無し */

    if ( ! NIL_P(h)) {
        /*
         *
         * ここでキーワード引数取得の処理
         *
         */
    }

    /*
     * ここにメソッドの処理の本体
     */
}

3. rb_get_kwargs

キーワード引数の処理のために用意されている C API のrb_get_kwargsですが、先日までバグっていた上に、(仕様の変遷があったため)ドキュメントがコードと一致しなくなっていました。ですので、以前にドキュメントを読んで実装してハマったというようなかたは、まずドキュメントを読み直してみてください。

「ここでキーワード引数取得の処理」となっている部分のコードは、次のようになります。

    /* 必須キーワードが 3、省略可能が 2 個、余ったキーワードがあったらエラー */
    VALUE foo, bar, baz,  quux, hoge;
     :
     :
     :
        ID table[5];
        VALUE values[5];
        table[0] = rb_intern("foo");
        table[1] = rb_intern("bar");
        table[2] = rb_intern("baz");
        table[3] = rb_intern("quux");
        table[4] = rb_intern("hoge");
        rb_get_kwargs(h, table, 3, 2, values);  // ここの「2」は、余ったキーワードを無視するなら -3 に変える
        foo = values[0];
        bar = values[1];
        baz = values[2];
        quux = ( (values[3] != Qundef) ? (values[3]) : デフォルト値 ) ;
        hoge = ( (values[4] != Qundef) ? (values[4]) : デフォルト値 ) ;

ここで、デフォルト値についてはあらかじめ変数をその値に設定(初期化)しておいて、3項演算子ではなく if による分岐で書く、というようなバリエーションもありえますが、デフォルト値にオブジェクトの生成などがあると、無駄に余計なオブジェクトを作ってしまうことになります。

一方で、必須のキーワードが無い場合に、キーワード引数がまるごと無いと、この例のように書いてしまうと変数が未初期化のままになります。そういうわけで、そのあたりはケースバイケースで書き分ける感じになるでしょう。

4. 余ったキーワードの扱い

上記のコードでは、余ったキーワードがあると "unknown keyword" ArgumentError の例外が上がります。第4引数(省略可能なキーワードの個数)を 2 から -2-1 に変えると(-1は、ゼロ個の時のための調整用(でしょう))、余ったキーワードがあっても無視されるようになります。その場合、余ったキーワード(と値のペア)はハッシュ h の中に残されます(rb_get_kwargsは、処理できたキーワードを h から取り除きます)。

余ったキーワード(意図的に任意のキーワードを、意味をもって受け付けるような場合を除いて)の扱いをどうするかは、cruby開発陣の中で、まだプラクティスが確立していません(内蔵ライブラリや、標準添付ライブラリでもまちまちになっています)。もしこの件に関心があるようであれば、時々気にしてみてください(確かチケットには(まだ)なっていません)。

5. その他

values としてヌルポインタを渡すと、値を取り出さずにキーワード引数の存在のチェックのみを行います。

移植性が(ほとんど)あり性能問題が無い(かもしれない)、負の値に対応した算術右シフトのCコード

普通は、自分のマシンでどのようなコードが生成されるかを確認して、うまくいっていればそれで良しとする所ですが、右シフトは凝ったコードを書こうとするCプログラマにとって悩みの種です。

しかし、手元の環境( FreeBSD clang version 3.4.1 )で、次のような、「あらゆる環境で2の補数マシンをエミュレーションするコード」を、

(追記: ビットパターンが 0x800...000 のようになるような最小の負の値の時には、このコードでも「未定義」を踏んでいます)と思ったのですが、最初に +1 することで回避してますねコレ。それでも未定義に落とせる抜け穴あるかな?

int
signed_shift_right(int x, unsigned a)
{
    return ( (x < 0) ? (-(-(x+1)>>a)-1) : (x >> a) ) ;
}

-O3 オプション付きでコンパイルしてみたところ、次のように「完全に」最適化されました。

$ clang -W -Wall -Wextra -pedantic -O3 -S signed_shift_right.c
$ cat signed_shift_right.s
	.file	"signed_shift_right.c"
	.text
	.globl	signed_shift_right
	.align	16, 0x90
	.type	signed_shift_right,@function
signed_shift_right:                     # @signed_shift_right
	.cfi_startproc
# BB#0:
	pushq	%rbp
.Ltmp2:
	.cfi_def_cfa_offset 16
.Ltmp3:
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
.Ltmp4:
	.cfi_def_cfa_register %rbp
	movb	%sil, %cl
	sarl	%cl, %edi
	movl	%edi, %eax
	popq	%rbp
	ret
.Ltmp5:
	.size	signed_shift_right, .Ltmp5-signed_shift_right
	.cfi_endproc


	.ident	"FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512"
	.section	".note.GNU-stack","",@progbits

static inline を付けて、インライン展開まで効けば、おそらく完全にオーバヘッド0のコードが生成されるものと思われます。

SICP的な状態を共有する複数のクロージャをJavaScriptでどう書くか

某展示会の学術系ブースで、このようなSICP的な状態を共有する複数のクロージャJavaScriptで書きたいわけだが、

"use strict"

var [getVal, setVal] = (function () {
    var v
    return [function () {
        return v
    }, function (new_v) {
        v = new_v
    }]
})()

setVal(42)
console.log(getVal())

JavaScriptの動作確認には node を使っています)

流石にこのように無名関数をバリバリ書こうとは思わないだろうので、それを解消する提案、というようなものを見ました。

提案されていた手法は、特別な記法でこの例の v のような変数をマークアップし、プリプロセッサのようなもので、もっとプレーンな書き方をしたJavaScriptから、このようなコードに変換する、というものでしたが、それについてはこの記事の本題ではありません。

Pythonで書くと、こんな感じになるでしょうか。

#! /usr/local/bin/python3

def mk_val():
    v = None
    def get_val():
        return v
    def set_val(new_v):
        nonlocal v
        v = new_v
    return get_val, set_val

get_val, set_val = mk_val()

set_val(42)
print(get_val())

Pythonではlamdaがあえてかなり使いにくく設計されており、このように明示的に手続きに名前を付けるスタイルになります*1

このPythonの場合を踏まえて、最初の例を考えてみると、こういうスタイルが出てきます。

"use strict"

function mkVal() {
    var v
    function getVal() {
        return v
    }
    function setVal(new_v) {
        v = new_v
    }
    return [getVal, setVal]
}

var [getVal, setVal] = mkVal()

setVal(42)
console.log(getVal())

SICP中でも、どうしてもlambdaが必要な場合は使っていますが、それよりもネストしたdefineのほうが自然な場合はそちらが多用されているような気がします。どうでしょうか?

*1:Python3以降ならこのようにnonlocal文でこのような変数の書き換えを共有できますが、2以前はJavaでやるようにboxingが必要で、そもそもこういうのはPythonではあまりencourageされていない、と言えばそういうことなのかもしれませんが。

「Trusting Trust」攻撃に対抗する

シュナイアーさんによるblogエントリ( https://www.schneier.com/blog/archives/2006/01/countering_trus.html )は、もう10年も前ですので、その世界では特に新しい話題というわけではないようですが、私が知ったのは今年のはじめのことでしたので*1、この機会に紹介する文章を書いてみたいと思います。

Unixを作ったケン・トンプソンさんがチューリング賞を受賞された際の講演「信用を信用することができるだろうか」( Reflections on Trusting Trust )は、その暴露的な部分(UNIXのloginコマンドにバックドアがあった、とする)の真偽はさておき、もしコンパイラのバイナリが「信用できない」ものだったら、という場合における、潜在的な脅威の可能性を否定することの難しさの指摘でした。*2

以下で述べる話題は、直接的な「コンパイラのバイナリを精読する」といった手段と比べると、大幅に省力的で、また、そのような可能性に対してある種の「相対的」安全性という考え方を示すものにもなっています。

詳細は、デイヴィッド・ホイーラーさん*3D論 Fully Countering Trusting Trust through Diverse Double-Compiling( http://www.dwheeler.com/trusting-trust/dissertation/html/wheeler-trusting-trust-ddc.html )となっていて、だいぶ大部ですが(私も全部は追いきれていません)、基本的なアイディアのスケッチは、けっこう以前からUSENETで議論されていたという程度には、簡単なものです。

まず基本的な問題について確認

トンプソンさんの講演の詳細を知っている方は、この節は飛ばして問題ありません。

しばしば、オープンソースのプロダクトは、たくさんの目でソースコードがチェックされているのだから安心だ、と主張されます。その当否はともかくとして、その大前提に、コンパイラは絶対に信用できるものだ、という仮定があります。

しかし、本当にコンパイラは信用できるのでしょうか? 噂では、性能比較に使われるベンチマークプログラムはその目的上、共通のものが広く公開されているので、そのパターンを検出して特別な最適化を掛ける、というチートが仕込まれていたコンパイラがあった、といいます。もしそれが単なる最適化ではなく、対象にバックドアを仕込むようなものだったら?

コンパイラだってオープンソースコンパイラなんだから大丈夫! でもそのコンパイラコンパイルするコンパイラに何か仕込まれていたら? 理屈では、一種の自己再生プログラム(いわゆるクワイン)のテクニックを利用して、ソースコードには何の痕跡も残さずに、コンパイラのバイナリから、それによってコンパイルされた、次の世代のコンパイラのバイナリへ、と、悪意を持ったコード片を「感染」させ続けることが可能です(プログラムのテクニック自体としては、一種のパズルのようなものとして知られていたものではあるものの、そのようにセキュリティに対する深刻な脅威として、可能性があると(広く注目されるような場で)示したのが、トンプソンさんのチューリング賞講演でした)*4*5

すぐに思いつく対抗法

コンパイラのバイナリを精読してチェックする(ブラックボックスチェック)、あるいはソースコードとの対応が取れているかチェックする(ホワイトボックスチェック)というあたりが、まず思い浮かぶでしょう。それができればそれに越したことはありませんが、たとえばGCCなど、実際に使われているコンパイラはそれなりに大きく、大変な作業です。プログラムでできないか、と考えるかもしれません。いい着目点ではありますが、やはりそのプログラムは信頼できるのか、ということになります。

あるいは、コンパイラではなくインタプリタを使おう、と思うかもしれません。しかし、そのインタプリタコンパイルするコンパイラが、ということになります。問題の場所は移動しますが、必要な作業はそんなに変わらない、ということになります。

ここで、GCCの昔話を

今では多くのUnixライク環境で、バイナリパッケージによるインストールは当たり前のものとなりました。何らかのプログラミング言語の処理系を「野良ビルド」するにあたり、いわゆるセルフホスティングであったり、そうでなくても自分自身のビルドに自分自身が必要であっても、まずバイナリパッケージを使って簡単に必要な環境を作ることができます。

昔は、なんでも野良ビルドが当然であり、わざわざ「野良」などと付けるようになったのもある時代以降ではなかったか、というような記憶があります。そして、難しさの点で大物*6のひとつが「プロプライエタリUNIX環境へのGCCの導入」でした。

普通のやりかたとしては、GCCのバイナリを、同じ環境を使っている他のユーザからGNUの助け合いの精神にもとづきコピーしてもらってくれば良いわけですが、全く同じ環境というのもなかなか無かったりするわけで、いわゆる「言語処理系のブートストラップ」の一種のような作業で「最初のGCCのバイナリ」を得ることになります。

今でもGCCのビルドの説明には、ステージという表現が使われていますが、この時代には次のような意味がありました。

stage1で得られるのはGCCによるコンパイル結果ではありませんから、バイナリ的にも違ったものなはずです(一種のキメラ的存在と言えるかもしれない)。一方、stage2とstage3は、どちらもGCCによるコンパイル結果ですから、結果は一致するはずです(理論的には一種の「不動点」)。結果が一致しても、必ずしも問題が無いとは言い切れませんが、結果が一致しなければ高い確率で問題が存在するはずです。

ここで注目すべきなのは、このプロセスは「何らかの既存のGCCバイナリ」に依存していない、という点です。これを利用したのが Diverse Double-Compiling による、Trusting Trust 攻撃への対抗だ、ということになります。

図法の導入

伝統的な「T図式」( T diagram )というものもありますが、ここでの議論には少し向いてない面があるので、次のような図法を使います( Fully Countering Trusting Trust through Diverse Double-Compiling の、§4.1 にある図)。

中央の四角がコンパイルプロセスを表現していて、上からの矢印がそのコンパイルに使用するコンパイラのバイナリ、左からの矢印がコンパイル対象のソースコード、右からの矢印がコンパイルオプションなどの「ソースコード以外」のコンパイル結果に影響を与える要素(以下では省略します)、下に出ている矢印がコンパイル結果であるバイナリ生成物、という感じになります。

この図法で、前節で話題にした、プロプラUNIX環境へのGCCの導入をあらわすと、次のようになります。

Diverse Double-Compiling

提案手法では、次の図のような手続きで、調査対象のコンパイラの2種類のバイナリを作ります。

右側は、自分自身をコンパイルできる(セルフホスティングな)コンパイラの、通常のコンパイル手続きです。対称性のために2回コンパイルしていますが、原理を示す上では1回でかまいません(2回とも、同じ結果が得られているはずです。説明のため、ここではGCCとします)。

左側は、GCCの導入において「プロプライエタリUNIX付属のcc」であった部分を「何らかの信頼できるコンパイラ」に変えたもの、という感じになります。最初のコンパイラが信頼できるコンパイラですから、stage1のコンパイラソースコードときちんと対応しているはずです。ですからstage2のコンパイラは、(ソースコードGCCだとすれば)最初の「信頼できるコンパイラ」と「GCCソースコード」を信じられる限りにおいて、信じられるはず、ということになります。

そして、この信じられるコンパイラは、右のほうのプロセスで作られたコンパイラと、バイナリ的に一致するはずです。もし一致しなければ、何らかのおかしな仕掛けが仕込まれている可能性がある、ということになります(もちろん、これまで触れてきませんでしたが、コンパイルされた時刻を埋め込む、などといった特異な要因が無ければ*7*8)。

ここで大事なことは、GCCのような大きなCコンパイラのバイナリコードをチェックする、というような大変な仕事ではなく、「信頼できて、GCCをとにかくコンパイルできれば良い(コンパイルが遅くても、最適化が全く無くても)コンパイラを用意すること」と「2つのファイルの単なるバイナリ比較」という、(比較すればそれなりに)単純な仕事で、問題点の検出が可能になった、ということです。

相対的な信頼性

またこの手法は、LLVM clangでGCC(ここではコンパイラコレクションの意)のCコンパイラをビルドする、あるいはGCCのCコンパイラLLVM clangをビルドする、といったようなプロセスで、「絶対に信頼できるCコンパイラ」が無くても、ある程度相対的には信頼できると確認できる、ということも示しています。

まとめ

現実的には考えにくいとはいえ、その危険性から脅威と考えられる「Trusting Trust」攻撃に対して、(世界的なセキュリティ界隈では以前から知られていたもののようですが)有効な対抗手段があることを紹介しました。

世界にコンパイラの実装が1種類ではまずく、少なくとも2つ以上、できればもっと多数あるべき、という「多様性の善」を示すものにもなっているように思います。また、セルフホスティングができると、自分の非標準なオレオレ拡張を、自分自身の実装に使いたくなるんじゃないか、という気がしますが、それをやってしまうとこのような検証ができなくなる、ということもわかります*9。あと、極小Cコンパイラとして8ccという実装がありますが( https://github.com/rui314/8ccGCCコンパイルできれば、もしかして「最初の信頼できるコンパイラ」として有用だったりしないでしょうか? (これについては全く私は手を付けてません)

*1:追記1: どうも10年前に、こちら http://www.radiumsoftware.com/0603.html#060329 の最後の段落で、話題自体は読んでいたようです。

*2:(2018年8月追記)また、講演録を読むと最後のほうで、クラッカー(特に、今で言ういわゆる「スクリプトキディ」)について、きつい語が並んでいますが、それも背景の説明が必要なようです。受賞は1983年で、その当時は、本格的なパーソナルコンピュータが広まる前である一方、映画『ウォー・ゲーム』が公開された年でもあり、フィクションとないまぜの「コンピュータを操る少年」像がしばしば「神童」的に扱われ、マスコミを賑わしていた、というような背景があります。また、いわゆる「ハッカーはクラッカーじゃない」問題の源流の一つでもあり、講演後に(以下出典未確認)CACM誌でRMSと論争になった、という話もあります。

*3:BW変換のWであるデイヴィッド・ホイーラーさんとは特に無関係のようです。

*4:講演録では、トンプソンさんが知ったネタ元は、Multicsのセキュリティに関する空軍の文書とありますが、その後確認されたところによれば Karger, P.A., and Schell, R.R. Multics Security Evaluation: Vulnerability Analysis. ESD-TR-74-193, Vol II, June 1974, p 52. という文献だとネットの情報にはあります。

*5:余談ですが、Multicsは高水準言語をOSの記述に使おうとしたという点でも先進的でした(言語はPL/I)。日本でその影響を受けて開発されたHITAC 5020 TSSも、PL/IWというPL/IのサブセットをPL/IWで書いて、人力コンパイルでブートストラップしたと伝えられていますから、この記事とも少々関係があります。

*6:現代と違って、昔はC言語処理系の標準準拠もいろいろ怪しかったりすると、けっこう一筋縄ではなかったりした。

*7:あるいは、近年のセキュリティ技術という観点からは、アドレスを決め打ちするような攻撃に対抗するべくランダム化を掛ける手法がありますが、そういうのとも相性が悪いでしょう。

*8:追記2(参): Debianプロジェクトなどによる、ReproducibleBuilds日本語解説)として、バイナリ再現性の重視について推進するプロジェクトがあります。

*9:追記3: これも http://www.radiumsoftware.com/0603.html#060329 で指摘されているのを、どこかで覚えていたようです。