分散型計算とは、時間と状態に関するものです。つまり、複数のコンポーネントが通信するためには、状態を共有しなければならず、そのためには時間がかかります。
ほとんどのプログラマーは、自分の仕事を人であるかのように考えています。彼らは、自分で仕事をしなければならない場合、自分がするのと同じように、1 つのスレッドのコントロールでプログラム全体を実行することを考えます。しかし、最近のコンピュータは、タスクの切り替えが非常に速く、マルチコア、マルチ CPU、分散システムなどでは、2 つのイベントが全く同時に発生することもあります。不具合は、プログラマーが考えたプログラムの実行方法のモデルと、現実に起きていることとのギャップを埋めるために押し寄せます。これらの欠陥は、スレッド、プロセス、時間、および情報の間の予期せぬ相互作用に関連しています。これらの相互作用は、セマフォ、変数、ファイル システムなど、基本的には情報を保存できるあらゆるものを含む共有状態を通じて行われます。
HttpSessionState
属性として保存すると、アプリケーションの信頼性を損なう場合があります。HttpSessionState
オブジェクト、その属性、および参照するすべてのオブジェクトがメモリに保存されます。この処理モデルでは、アクティブセッションの状態が 1 台のマシンのシステムメモリに保存できる範囲に制限されます。これらの制限を取り除き、容量を拡張するために、複数のサーバーに対して永続的なセッション状態情報が設定される場合がよくあります。これにより容量が拡張され複数のマシン間における複製が許可されるため、全体的なパフォーマンスが向上します。セッション状態を保持するために、サーバーは HttpSessionState
オブジェクトをシリアライズする必要があり、このオブジェクトをシリアライズするためには、保存されるすべてのオブジェクトがシリアライズ可能である必要があります。[Serializable]
属性を宣言する必要があります。さらに、オブジェクトで独自のシリアライズメソッドが必要となる場合には、ISerializable
インターフェイスを実装する必要があります。
public class DataGlob {
String GlobName;
String GlobValue;
public void AddToSession(HttpSessionState session) {
session["glob"] = this;
}
}
sleep()
をコールすると、パフォーマンスの低下を招いたり、デッドロックが発生したりする可能性があります。sleep()
をコールすると、他のすべてのスレッドがそのリソースが解放されるのを待機するため、パフォーマンスが低下しデッドロックが発生する可能性があります。 sleep()
をコールしています。
ReentrantLock rl = new ReentrantLock();
...
rl.lock();
Thread.sleep(500);
...
rl.unlock();
if (fitz == null) {
synchronized (this) {
if (fitz == null) {
fitz = new Fitzer();
}
}
}
return fitz;
Fitzer()
オブジェクトが 1 つのみ割り当てられていることを保証したいと考えますが、このコードのコールのたびに同期化させるコストをかけたくはありません。この用法は Double-Checked Locking として知られます。Fitzer()
オブジェクトが割り当てられます。詳細は「Double-Checked Locking is Broken」宣言を参照してください [1]。pthread_mutex_unlock()
をコールしてミューテックスをアンロックする必要がありますシグナルを送信しているスレッドがミューテックスをアンロックできない場合は、2 番目のスレッドにある pthread_cond_wait()
コールは返されず、スレッドは実行されません。 pthread_cond_signal()
をコールしてミューテックスを待機している他のスレッドにシグナルを送信していますが、他のスレッドが待機しているミューテックスのアンロックに失敗しています。
...
pthread_mutex_lock(&count_mutex);
// Signal waiting thread
pthread_cond_signal(&count_threshold_cv);
...
...
if (tmpnam_r(filename)){
FILE* tmp = fopen(filename,"wb+");
while((recv(sock,recvbuf,DATA_SIZE, 0) > 0)&&(amt!=0))
amt = fwrite(recvbuf,1,DATA_SIZE,tmp);
}
...
tmpnam()
、tempnam()
、mktemp()
などの C ライブラリ関数と、先頭に _
(アンダースコア) が付けられた、それぞれに対応する C++ 関数、および Windows API に属する GetTempFileName()
関数です。このグループの関数は、選択されたファイル名の Race Condition の脆弱性があります。関数は、ファイル名が選択されたときに一意であることを保証しますが、ファイルの選択後、アプリケーションがファイルを開こうと試みる前に別のプロセスまたは攻撃者が同一の名前を持つファイルを作成するのを阻止するメカニズムがありません。同じ関数への別のコールによって発生する正当な衝突の危険のほかに、攻撃者が悪意ある衝突を作成する可能性も十分あります。これらの関数によって生成されたファイル名が十分にランダムでなく、推測が難しくないためです。O_CREAT
フラグや O_EXCL
フラグを使用した open()
のコールや、CREATE_NEW
属性を使用した CreateFile()
のコールでファイルが開かれるのは、ファイルが既に存在する場合に失敗するため、前述のタイプの攻撃は阻止されます。ただし、攻撃者が一連のテンポラリファイル名を正確に予測できる場合、アプリケーションは必要とするテンポラリストレージを開こうとしても阻止され、Denial of Service (DoS) 攻撃が発生する可能性があります。これらの関数によって生成されたファイル名の選択に使用されているランダム度が小さければ、このタイプの攻撃を準備するのは容易です。tmpfile()
などの C ライブラリ関数と、先頭に _
(アンダースコア) が付けられた、それに対応する C++ 関数、および多少動作が優れている C ライブラリ関数 mkstemp()
です。tmpfile()
スタイル関数は一意のファイル名を作成して、"wb+"
フラグが渡された場合の fopen()
と同じ方法、つまり読み書きモードでバイナリ ファイルとしてそのファイルを開きます。ファイルが既に存在する場合、tmpfile()
により、サイズがゼロに切り詰められます。これは、一意であるはずのファイル名を選択してから、選択されたファイルをその後で開くまでの間に存在する Race Condition に関する、前述したセキュリティ上の懸念を緩和しようとするものです。ただし、この動作は関数のセキュリティ上の問題を明確に解決するわけではありません。まず、攻撃者は事前にアクセス許可を緩くしたファイルを作成でき、その許可が tmpfile()
により開かれるファイルによって変更される可能性はあまりありません。さらに、UNIX ベースのシステムの場合、攻撃者がファイルを別の重要なファイルへのリンクとして事前に作成すると、アプリケーションは昇格した許可を使用してそのファイルを切り詰める可能性があり、結果的に攻撃者の意図した損傷を与えます。最後に、tmpfile()
が実際に新しいファイルを作成する場合、そのファイルに適用されるアクセス許可はオペレーティングシステムにより異なります。つまり、攻撃者が事前にファイル名を予測できない場合も、アプリケーションデータは脆弱な状態に保たれる可能性があります。mkstemp()
は、テンポラリファイルを作成する上で十分に安全性が確保された方法です。ユーザーが提供したファイル名テンプレートに基づき、ランダムに生成した一連の文字を組み合わせて一意のファイルを作成して開こうとします。ファイルを作成できなかった場合は、失敗して -1
を戻します。最近のシステムでは、ファイルはモード 0600
で開かれます。つまり、ユーザーがアクセス許可を明示的に変更しない限り、ファイルは改竄に対して安全です。しかし、mkstemp()
は予測可能なファイル名が使用されているため攻撃されやすく、攻撃者が使用されるファイル名を予測して事前に作成することで mkstemp()
の失敗を引き起こした場合、アプリケーションは Denial of Service 攻撃にさらされる可能性があります。
...
try:
tmp_filename = os.tempnam()
tmp_file = open(tmp_filename, 'w')
data = s.recv(4096)
while True:
more = s.recv(4096)
tmp_file.write(more)
if not more:
break
except socket.timeout:
errMsg = "Connection timed-out while connecting"
self.logger.exception(errMsg)
raise Exception
...
os.O_CREAT
フラグや os.O_EXCL
フラグを使用した open()
のコールでファイルが開かれる最良のケースでは、ファイルがすでに存在する場合は処理が失敗するため、ここで説明したタイプの攻撃は阻止されます。ただし、攻撃者が一連のテンポラリファイル名を正確に予測できる場合、アプリケーションは必要とするテンポラリストレージを開こうとしても阻止され、Denial of Service (DoS) 攻撃が発生する可能性があります。これらの関数によって生成されたファイル名の選択に使用されているランダム度が小さければ、このタイプの攻撃を準備するのは容易です。tmpfile()
のような関数が含まれます。tmpfile()
スタイル関数は一意のファイル名を作成して、"wb+"
フラグが渡された場合の open()
と同じ方法、つまり読み書きモードでバイナリ ファイルとしてそのファイルを開きます。ファイルが既に存在する場合、tmpfile()
により、サイズがゼロに切り詰められます。これは、一意であるはずのファイル名を選択してから、選択されたファイルをその後で開くまでの間に存在する Race Condition に関する、前述したセキュリティ上の懸念を緩和しようとするものです。ただし、この動作は関数のセキュリティ上の問題を明確に解決するわけではありません。まず、攻撃者は事前にアクセス許可を緩くしたファイルを作成でき、その許可が tmpfile()
により開かれるファイルによって変更される可能性はあまりありません。さらに、UNIX ベースのシステムの場合、攻撃者がファイルを別の重要なファイルへのリンクとして事前に作成すると、アプリケーションは昇格した許可を使用してそのファイルを切り詰める可能性があり、結果的に攻撃者の意図した損傷を与えます。最後に、tmpfile()
が実際に新しいファイルを作成する場合、そのファイルに適用されるアクセス許可はオペレーティングシステムにより異なります。つまり、攻撃者が事前にファイル名を予測できない場合も、アプリケーションデータは脆弱な状態に保たれる可能性があります。
...
HttpSession sesssion = request.getSession(true);
sesssion.setMaxInactiveInterval(-1);
...