すべてのソフトウェア、システム、電子デバイスは、特定のプログラム、順序、アルゴリズムに基づいて動作します。この順序やアルゴリズムから外れると、エラーや正常に動作しなくなる場合があります。多重スレッドのアプリを利用している場合、競合状態が予期せぬ問題を引き起こすことがあります。以下で詳しく説明します。
多重スレッドのアプリでよく見られる問題の一つに、競合状態攻撃があります。これは、複数のタスクを同時に実行しようとする際に発生する望ましくない事象を指します。しかし、プログラムやデバイスは決められた順序に従って動作するため、通常はこのような状況は起こりません。
競合状態攻撃が起こりやすいのは、複数のプログラム、アプリ、スレッドが同時に特定の資産やリソースへアクセスしようとする場合です。
多重スレッドのアプリを利用しているなら、競合状態の抜け穴に何度か遭遇したことがあるかもしれません。この問題はソフトウェアや技術の領域を超えて一般的に見られますが、必ずしも全員がその存在に気づいているわけではありません。
競合状態の例としてどんなものが考えられるか? 以下に2例を示します:
一つの照明に複数のスイッチが接続されているのを見たことはありませんか? これは非常に一般的な状況ですが、これらの回路はスイッチの位置に依存していません。照明が点いていれば、どのスイッチでも消すことが可能です。
次に、二人が同時に照明を点けようとする状況を考えてみてください。二人は異なるスイッチを使用しますが、一方の操作がもう一方の操作を打ち消し、回路が正常に動作しなくなります。これが競合状態の例です。
プログラマがデバイスにビッグデータの読み書きを指示している間に、別の機器がデータベース全体または一部を書き換えようとすると、競合状態が発生しやすくなります。
この状態は以下のような事象を引き起こす可能性があります。
ごく稀に、命令が誤った順序で処理されるケースも見受けられます。
競合状態の仕組みを深く理解すると、複数の種類が存在することが分かります。ここでは主な種類を説明します。
上記の競合状態はプログラミングや電子機器に限らず発生しますが、プログラミング分野ではコードに関する別の2種類の競合状態も存在します。以下に示します。
同じ値を取り込み、新たな結果を返す2つの独立したタスクを考えてみてください。この種類はソフトウェアの不具合や動作の妨げになる原因となります。
例 – 当座預金口座で複数の小切手があらかじめ決められた順序で処理される場合を考えます。銀行の小切手処理システムでは、十分な資金があると確認された場合のみ小切手が処理され、まず小切手Aが処理された後、再度資金がチェックされた上で小切手Bが処理されます。
しかし、2つの小切手が同時に処理されると、読み取り・変更・書き込みの競合状態が発生し、両方の小切手に対して同じ金額が読み取られるため、口座残高が正しく表示されなくなります。
この状況は、2つのワークフローが同じ値を確認し、それぞれ異なる動作を行う場合に発生します。両プロセスが値を確認しますが、一方のみがその値を用いて処理を進め、もう一方はnullとして扱われ、結果として古い値が使用されます。
例
地図アプリで2つのプロセスが同時に進行している場合、どちらも同じ位置データを要求します。この競合状態では、一方が先に値を取得してしまい、もう一方は値にアクセスできず、nullが返される結果となります。
競合状態の脆弱性、またはサイバー脅威はTime-of-Check to Time-of-Use (TOCTTOU)とも呼ばれます。これは、コンピュータが処理時に決められた順序を守る必要がある点を突いています。前のタスクが成功した場合にのみ次が実行されるため、2つの処理間に生じる隙間を攻撃者が悪意ある内容を混入するために利用する可能性があります。
通常、競合状態の脆弱性は、攻撃者がシステムを意図的に操作し、正常な処理の裏で不正な動作を実行させる際に発生します。
これらの処理を実行させる方法は、以下の2通りです。
競合状態の脆弱性が直接影響するのはシステムやデバイスの性能ですが、デバイスのセキュリティにも大きな影響を及ぼします。すべてのソフトウェアは決められた順序に従って動作するよう設計されており、その順序が守られないと必ず何らかの不具合が発生します。
熟練の攻撃者は、この不具合と競合による遅延を利用してデバイスを攻撃する可能性があります。その結果、スレッドロックやデッドロック状態が生じ、デッドロックの脆弱性はDoS攻撃の原因となり、脅威のブロックが対象デバイスの性能を低下させます。
双方とも多重スレッド環境で発生し、デバイスの性能に影響を与えるという共通点がありますが、全く同じ現象ではありません。
競合状態は、2つのスレッドが同時に同じ変数を使用する場合に発生します。一方、デッドロックは2つのスレッドが同時に1つのロックを待つことで起こります。
この状況では、両方のスレッドが処理や機能の実行を停止します。競合状態では、2つのタスクが互いに先んじようと競い合いますが、デッドロックでは、双方が相手の処理完了を待つ状態となります。
競合状態の脆弱性を見つけるのは難しいですが、不可能ではありません。静的および動的なツールを用いることで、その発生の可能性を検出できます。静的テストソフトはプログラムが使用されていなくても自動でスキャンしますが、この方法では誤検知が起こる場合があります。
そのため、動的テストツールの利用が推奨されます。しかし、動的ツールも完璧ではなく、間接的な競合状態の発生を捉えるのはほぼ難しいです。
競合状態はデータ競合の結果として現れることが多いため、データ競合検知ツールを利用して最終的に競合状態を把握する方法も有効です。
デジタル資産を問題なく動作させるには、競合状態の脆弱性を抑え、有効な予防策を講じる必要があります。以下は代表的な防止策です。
アプリやデバイスが共有する資源が増えるほど、競合状態が発生する可能性は高まります。そのため、最も簡単な対策は共有を減らすことです。たとえコードなどの資源を共有する場合でも、原子操作に悪影響が出ないか十分に確認することが大切です。これにより、共有されたコードが独立して動作できます。
もう一つの対策は、変更不可能なオブジェクトを使用することです。こうしたオブジェクトは、一度作成されると値を変えることができません。
スレッド同期は競合状態に対処する有効な方法です。これにより、特定のプログラムが一度に一つのスレッドのみを実行できます。
ストレージまたはメモリのシリアライズを試みましょう。これは、読み取りと書き込みのコマンドが同時に到着した場合、読み取りを優先して実行することを意味します。
ネットワークにおいても競合状態はよく見られる問題です。複数のユーザが同一のネットワークチャネルに同時アクセスしようとする状況では、コンピュータはその状況を認識できません。特に、遅延が大きいネットワークでこの問題が顕著です。
対策としては、優先順位付けの方式を導入することが推奨されます。この方式により、特定のユーザに専用アクセスが許可され、同時に複数のユーザが1つのネットワークにアクセスすることがなくなります。
競合状態のプログラミングは、システムの性能に影響を及ぼすため注意が必要です。途切れのないネットワーク、デバイス、多重スレッドのアプリを求める貴社は、この問題が発生するシナリオを把握し、早期の予防策を講じることが重要です。これにより、競合状態の脆弱性の発生を抑えることが可能となります。
最新情報を購読