誤動作の原因の二つ目が判明しました。
符号なし2進数と符号付2進数の比較
後述するSOFUB_MAPの実装に符号なし2進数と符号付2進数を比較しているコードがありました。直接の比較ではありませんでしたが、符号なし整数の値を符号付整数の変数に代入し、その変数と定数を比較していたため、意図した通りには動作しない場合がありました。
問題発生のシナリオは以下の通りです。
- メモリ不足のためプロセスのテキスト領域(サイズ>=32768)のswap領域への書き出し処理が始まる
- buf構造体のb_bcountにサイズが格納されてsofub_alloc()に渡される
- sofub_alloc()では符号付整数のcountにb_bcountが代入される
- 関数の中でcountを正の定数と比較する(if文)
- countは負の値となるため条件が不成立となる(条件が成立した場合はテキスト領域のコピー処理が実行される)
- 5.で条件が不成立となった結果テキスト領域がコピーされずに過去にコピーされた別のデータがswap領域に書き込まれる
- 後にテキスト領域がswap領域から読み込まれるが、無関係のデータが復元されるためプログラムが誤動作する
2.11BSDのCプログラムのスタートアップコードはpure executable用とseparate executable用の二種類あるのですが、誤動作していたプログラムテキストの先頭部分はpure用ではなくseparate用に置き換わっていました。先頭以外の部分にも他のプログラムのヒープ領域と思しきデータがありました。
SOFUB_MAPとは
UNIBUS用のデバイスはDMAアドレスが18bitなので、メインメモリのアドレスが18bitを越える領域に直接アクセスすることができません。このため、PDP-11ではUNIBUS用のMMUが実装されているのですが、TangConsoleDCJ11MEMにはDMA用のMMUはありません。
そこで登場するのがSOFUB_MAPです。18bitのDMAアドレスでアクセスできる範囲に中間バッファを設け、このバッファで中継することでメインメモリ全領域とのI/Oを実現しています。ISAバスのバスマスタデバイスで1MBを越えるメインメモリを扱う際の技法であるbounce bufferと同類です。
SOFUB_MAPではDMAの前後に必要に応じてメモリコピーを実行するのですが、上述のシナリオでこのコピー処理がスキップされた結果、バッファに残っていた別のデータがディスクに書き込まれていました。
通常のファイルI/Oでは転送サイズが32KB以上になることはないので正常に動作していましたが、swapデバイスとのI/Oでは32KB以上になることがあるため、問題が発生していました。
修正内容
問題の原因がわかってしまえば修正は簡単で、変数countの型を符号なしにしました。修正パッチ
なお、DMA完了時に呼ばれるsofub_relse()ではcountはunsignedで宣言されているので修正の必要はありませんでした。
0 件のコメント:
コメントを投稿