今日の役に立たない一言 - Today’s Trifle! -

古い記事ではさまざまなテーマを書いていますが、2007年以降はプログラミング関連の話がほとんどです。

ソートフレームワークのセキュリティホール

専門学校でJavaを教えるために、この本をテキストに選んだ。そして授業を進めているいて、困ったのが演習問題3.11。3章ではクラスの拡張を学習するんだけど、ここの演習問題3.11が難しい。

演習問題 3.11:ソートアルゴリズムが、気付かれることなくメトリックスに関して不正を行える SortDouble のセキュリティホールを少なくとも 1つ見つけなさい。そのセキュリティホールをなくすように修正しなさい。この場合、ソートアルゴリズムの作成者は main を書かないと想定してください。

SortDouble と SortMetrics のコードをまるごと引用する。
SortDouble は、メトリックス付きソートのフレームワークを提供している。このクラスを継承してソートアルゴリズムを実装することを期待している。SortMetrics はそのメトリックスそのもの。

abstract class SortDouble {
    private double values;
    private final SortMetrics curMetrics = new SortMetrics();

    /** 全ソートをするために呼び出される */
    public final SortMetrics sort(double data) {
        values = data;
        curMetrics.init();
        doSort();
        return getMetrics();
    }

    public final SortMetrics getMetrics() {
        return curMetrics.clone();
    }

    /** 拡張したクラスが要素の数を知るため */
    protected final int getDataLength() {
        return values.length;
    }

    /** 拡張したクラスが要素を調べるため */
    protected final double probe(int i) {
        curMetrics.probeCnt++;
        return values[i];
    }

    /** 拡張したクラスが要素を比較するため */
    protected final int compare(int i, int j) {
        curMetrics.compareCnt++;
        double d1 = values[i];
        double d2 = values[j];
        if (d1 == d2)
            return 0;
        else
            return (d1 < d2 ? -1 : 1);
    }

    /** 拡張したクラスが要素を交換するため */
    protected final void swap(int i, int j) {
        curMetrics.swapCnt++;
        double tmp = values[i];
        values[i] = values[j];
        values[j] = tmp;
    }

    /** 拡張したクラスが実装する -- ソートをするのに使用される */
    protected abstract void doSort();
}

final class SortMetrics implements Cloneable {
    public long probeCnt,   // 単純な出0他の値調査
                compareCnt, // 2つの要素の比較
                swapCnt;    // 2つの要素の交換

    public void init() {
        probeCnt = swapCnt = compareCnt = 0;
    }

    public String toString() {
        return probeCnt + " probes " +
               compareCnt + " compares " +
               swapCnt + " swaps";
    }

    /** このクラスは、clone をサポートしている */
    public SortMetrics clone() {
        try {
            // デフォルトの仕組みで十分
            return (SortMetrics) super.clone();
        } catch (CloneNotSupportedException e) {
            // 起こり得ない。このクラスと Object は複製できる
            throw new InternalError(e.toString());
        }
    }
}

SortDouble を継承したサブクラスでは、doSort() だけがオーバーライドできるのよね。SortDouble の中で SortMetrics は private final なので、継承した側から直接アクセスできないし、probe()・compare()・swap() の各メソッドも final 宣言されているのでオーバーライドして実装を変更することができない。getMetrics() ではきちんと clone() した SortMetrics を返してるし。元データも private 変数に参照を持ってて、サブクラスからは直接アクセスすることができない。いろいろと考えてみたけど、doSort() の中から適当なデータを作って sort() を呼び出すくらいの姑息なやり方しか思いつかない。
そこで訳者の柴田さんにメールで質問してみた。すると、メトリックスの各値がオーバーフローするまでprobe()・compare()・swap()のメソッドを繰り返し呼ぶ方法もあるとの回答。それは・・・long型なだけに現実的じゃないような。。。メトリックスの値とはまったく違う処理時間がかかるに違いない。
ほかのセキュリティホールが分かる人、いますか〜?
あ、リフレクションを使うのはダメよ。もっと後の章で出る話なので。

プログラミング言語Java第4版 楽天ブックス
プログラミング言語Java 第4版 Amazon