ソートフレームワークのセキュリティホール
専門学校で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