Java言語仕様では、64ビットのlong型およびdouble型の値を、2つの32ビット値として扱うことが許されている。たとえば、64ビット値の書込み操作は、2つの独立した32ビット値の操作として実行される可能性がある。
Java言語仕様の17.7節「doubleとlongの非アトミックな扱い」には以下のように記されている。[JLS 2005]
... こうした振る舞いは実装依存である。つまり、Java仮想マシンはlong値やdouble値の書込みをアトミックな動作として実行するか、あるいは、二つの動作として実行するかを自由に決定することが許されている。プログラミング言語Javaメモリモデルでは、volatileでないlong値やdouble値への単一の書込みは、それぞれ32ビットずつの二つの書込みとして扱われる。結果的に、ある64ビット値の書込みの最初の32ビットと、他の書込みによる次の32ビットの組み合わせをスレッドが参照しうる。
この動作が原因で、スレッドセーフであることが要求されるコードにおいて、未確定の値が読み取られてしまうかもしれない。それゆえ、マルチスレッドプログラムでは、64ビット値の読み書きがアトミックに行われることを保証しなくてはならない。
違反コード
以下の違反コードで、あるスレッドがassignValue()メソッドを繰り返し呼び出し、別のスレッドがprintLong()メソッドを繰り返し呼び出す場合、printLong()メソッドは0でも引数jの値でもないiの値を出力することがある。
class LongContainer { private long i = 0; void assignValue(long j) { i = j; } void printLong() { System.out.println("i = " + i); } }
iがdouble型で宣言される場合にも同様の問題が発生しうる。
適合コード (volatile 変数)
以下の適合コードでは、iをvolatile宣言している。long型およびdouble型の volatile 変数の読み書きは、常にアトミックに行われる。
class LongContainer { private volatile long i = 0; void assignValue(long j) { i = j; } void printLong() { System.out.println("i = " + i); } }
なお、assignValue()メソッドへ渡す引数がvolatile変数から取得される、もしくはアトミックな読取りの結果取得されることを保証するのは重要である。さもないと、引数の読取りそのものが脆弱性に繋がってしまう。
volatile宣言は、値のインクリメントのような、読取り、変更、書込みを伴う一連の複合操作のアトミック性を保証しない。詳しくは「VNA02-J. 共有変数への複合操作のアトミック性を確保する」を参照。
例外
VNA05-EX0: 64ビットのlong型またはdouble型の値の全ての読み書きが同期されたブロック内で実行される場合、読み書きのアトミック性は保証される。読み書きのアトミック性を保証するには、クラス内の同期されたメソッドのみが64ビット値を公開し、その値が他のコードから(直接的にも間接的にも)アクセスできないことが求められる。(詳細は、ガイドライン「VNA02-J. 共有変数への複合操作のアトミック性を確保する」を参照。)
VNA05-EX1: このルールは、64ビットのlong型およびdouble型の値がアトミックに読み書きされることが保証されているシステムにおいては無視できる。しかし、別のプラットフォームでこれが保証されるとは限らない。
リスク評価
マルチスレッドアプリケーションにおいて64ビット値を含む操作のアトミック性を保証しないと、未確定の値を読み書きする場合がある。言語仕様上は要求されていないが、多くのJVMは64ビット値の読み書きをアトミックに行う。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
VNA05-J | 低 | 低 | 中 | P2 | L3 |
自動検出
このルールの違反を検知することができる静的解析ツールが存在する。
関連ガイドライン
MITRE CWE | CWE-667. Improper Locking |
参考文献
[Goetz 2006] | 3.1.2, Non-atomic 64-Bit Operations |
[Goetz 2004c] | |
[JLS 2005] | §17.7, Non-atomic Treatment of double and long |
翻訳元
これは以下のページを翻訳したものです。
VNA05-J. Ensure atomicity when reading and writing 64-bit values (revision 90)