陀倖セキュリティ保蚌

゚ラヌ凊理の゚ラヌは、最も䞀般的な゚ラヌの原因です。



この蚘事を曞くずきに気になったでたらめ



Cでプログラミングする際に䜿甚する方が良いものに関する䞻な戊い-䟋倖たたは凊理甚のリタヌンコヌドは遠い過去にありたす*が、別の皮類の戊いはただ収たりたせんはい、たあ、䟋倖凊理に萜ち着きたしたが、どのように「正しく」凊理したすか



「正しい」こずに぀いおは倚くの芳点があり、その倧郚分は、凊理可胜な䟋倖のみをキャッチし、残りを呌び出し元のコヌドにスロヌする必芁があるずいう事実に芁玄されたす。 わかりにくい䟋倖が倧胆な方法で䞊䜍レベルに到達した堎合、最愛の人が䞀貫した状態であるかどうかが明確ではないため、アプリケヌション党䜓を撮圱したす。



䟋倖をキャッチしお凊理するこの方法には倚くの長所ず短所がありたすが、今日は少し異なるトピックを怜蚎したす。 ぀たり、䟋倖の発生に照らしおアプリケヌションの䞀貫した状態を確保するずいうトピックは、䟋倖に察する3぀のセキュリティレベルです。





3皮類の保蚌





1990幎代埌半、Dave Abrahamsは、3぀のレベルの䟋倖セキュリティを提䟛したした。基本保蚌、厳密保蚌、および䟋倖なし保蚌です。 このアむデアは、開発者のC ++コミュニティに倧歓迎され、Herb Sutterによる普及および䞀郚の倉曎の埌、䟋倖セキュリティ保蚌がブヌスト、暙準C ++ラむブラリ、およびアプリケヌション開発で広く䜿甚され始めたした。



圓初、これらの保蚌はC ++でSTLPortラむブラリを実装するためにDave Abrahamsによっお提案されたしたが、䟋倖の安党性の考え方は特定のプログラミング蚀語に結び付けられおおらず、JavaやCなどの䟋倖を䞻芁な゚ラヌ凊理メカニズムずしお䜿甚する他の蚀語で䜿甚できたす。 さらに、珟圚、䟋倖セキュリティ定矩には2぀のバヌゞョンがありたす1Dave Abrahamsによっお提案された元のバヌゞョンず2SatterおよびStraustrupによっお普及した修正バヌゞョンであり、ラむブラリだけでなくアプリケヌションアプリケヌションにも適しおいたす。



基本保蚌




初期定矩 「 䟋倖が発生した堎合、リ゜ヌスの挏掩はありたせん 。」



珟代の定矩は次のずおりです 。「 特定のメ゜ッドで䟋倖が発生した堎合、プログラムの状態は䞀貫しおいる必芁がありたす 。」 これは、リ゜ヌスリヌクがないこずだけでなく、クラスの䞍倉条件の保持も意味したす。これは、基本的な定矩ず比范しおより䞀般的な基準です。



これら2぀の定匏化の違いは、この保蚌はもずもずC ++でラむブラリを実装するために提案されたものであり、アプリケヌションアプリケヌションずは関係がないずいう事実によるものです。 しかし、より䞀般的なケヌス぀たり、ラむブラリだけでなく、アプリケヌションに぀いおもに぀いお話すず、リ゜ヌスリヌクはバグの原因の1぀に過ぎず、バグの原因ずは限りたせん。 任意の安定した時点**で䞍倉匏を保存するこずは、倖郚コヌドがアプリケヌションの䞍䞀臎状態を「芋る」こずができないずいう保蚌です。これは、リ゜ヌスリヌクがないこずず同じくらい重芁です。 ある口座から別の口座に送金するずきに、ある口座からお金を「残す」こずはできおも、別の口座に「届かない」堎合、銀行アプリケヌションのナヌザヌはほずんどメモリリヌクに興味を持ちたせん。



厳栌な保蚌




䟋倖の厳密な保蚌の定矩に぀いおは、元の定矩ず最新の定矩は類䌌しおおり、次のように芁玄されたす。「 操䜜䞭に䟋倖が発生した堎合、これはアプリケヌションの状態に圱響を䞎えたせん 」



蚀い換えれば、厳栌な䟋倖保蚌により、すべおを受け取ったずき、たたは䜕も受け取っおいないずきにトランザクションがトランザクションされるこずが保蚌されたす。 この堎合、䟋倖が発生するず、操䜜前のアプリケヌションの状態にロヌルバックし、操䜜党䜓が正垞に完了した堎合にのみ新しい状態に倉曎する必芁がありたす。



陀倖は保蚌されおいたせん




䟋倖が存圚しないこずの保蚌は、「 どのような状況でも䟋倖がスロヌされるこずはありたせん 」に限定されたす 。



この保蚌は定矩の点では最も単玔ですが、芋た目ほど単玔ではありたせん。 たず、䞀般的な堎合、特に.Net環境では、アプリケヌションのほがすべおのポむントで䟋倖が発生する可胜性があるため、提䟛するこずはほずんど䞍可胜です。 実際には、運甚単䜍のみがこの保蚌に埓い、以前のレベルの保蚌が構築されるのはそのような運甚に基づいおいたす。 Cでは、この保蚌を提䟛する数少ない操䜜の1぀はリンクの割り圓おであり、C ++では、倀の亀換を実装するスワップ関数です。 これらの関数に基づいお、すべおの「ダヌティな䜜業」が䞀時オブゞェクトで実行され、結果の倀に割り圓おられるずきに、䟋倖の厳密な保蚌がしばしば実珟されたす。



第二に、䟋倖が存圚しないずいう保蚌に埓わない操䜜がある堎合、他の機胜の正垞な操䜜を保蚌できない堎合がありたす。 したがっお、たずえば、C ++では、コンテナの䟋倖たたはリ゜ヌスリヌクの基本的な保蚌さえ提䟛するために、ナヌザヌタむプのデストラクタヌが䟋倖をスロヌしないこずが必芁です。



䞊蚘で説明した3぀の䟋倖のセキュリティ保蚌は、最も匱いものから最も匷いものぞず進みたす。 以降の各保蚌は、前の保蚌のスヌパヌセットです。 これは、厳密な保蚌の履行は自動的に基本的な保蚌の履行を䌎い、䟋倖の䞍圚の保蚌は厳密な保蚌の履行を䌎うこずを意味したす。 コヌドが䟋倖の基本的な保蚌さえも満たさない堎合、それはアプリケヌションの時限爆匟であり、遅かれ早かれ䞍快な結果に぀ながり、その状態を地獄に壊したす。



次に、いく぀かの䟋を芋おみたしょう。



基本保蚌違反の䟋





C ++でのメモリおよびリ゜ヌスリヌクを防ぐ䞻な方法はRAII Resource Acquisition Is Initializationむディオムです。これは、オブゞェクトがコンストラクタヌでリ゜ヌスを取埗し、デストラクタヌでそれを解攟するずいう事実から成りたす。 たた、䟋倖が発生したずきなど、䜕らかの理由でオブゞェクトがスコヌプを離れるずデストラクタが自動的に呌び出されるため、䟋倖の安党性を確保するために同じむディオムが䜿甚されるこずは驚くこずではありたせん。



Cでは、このむディオムはIDisposableむンタヌフェむスずしお移行し、コンストラクトを䜿甚したすが、C ++ずは異なり、特定のスコヌプ内のリ゜ヌスのラむフタむムの制埡に適甚でき、コンストラクタヌでキャプチャされた倚くのリ゜ヌスの管理には適しおいたせん。



䟋を芋おみたしょう



// ,

class DisposableA : IDisposable

{

public void Dispose() {}

}



//

class DisposableB : IDisposable

{

public DisposableB()

{

disposableA = new DisposableA();

throw new Exception( "OOPS!" );

}



public void Dispose() {}



private DisposableA disposableA;

}



// -

using ( var disposable = new DisposableB())

{

// ! Dispose

// DisposableB, DisposableA

}



* This source code was highlighted with Source Code Highlighter .








そのため、 DisposableAずDisposableBの 2぀の䜿い捚おクラスがありたす。各クラスは、コンストラクタヌで䞀郚の管理察象リ゜ヌスをキャプチャし、 Disposeメ゜ッドで解攟したす。 ファむナラむザは、リ゜ヌスの確定的なリリヌスを保蚌する助けにはならないため、珟時点ではファむナラむザを考慮しないようにしたしょう。これは堎合によっおは䞍可欠です。



この堎合、 DisposableBクラスのコンストラクタヌによっお䟋倖をスロヌするずき、 䜿い捚おオブゞェクトが存圚しなかったため、 Disposeメ゜ッドを呌び出すこずはありたせん。 この点で、ほずんどの䞻流のプログラミング蚀語の動䜜はほが同じですが、いく぀かの違いがありたす。 類䌌点は、コンストラクタヌが「クラッシュ」した堎合、呌び出し元のコヌドはただ構築されおいないオブゞェクトぞのリンクを取埗できず、そのリ゜ヌスを明瀺的に解攟できないこずです。 ただし、完党に構築されたフィヌルドのデストラクタが自動的に呌び出されるC ++蚀語ずは異なり、これは「マネヌゞド」C蚀語***では発生したせん。DisposableBクラスのコンストラクタが䟋倖をスロヌし、以前にキャプチャしたリ゜ヌスを解攟しない堎合、 「リ゜ヌスの流出」たたは、少なくずも非決定的リリヌスを受け取りたす。



同じ問題は、より埮劙な圢で珟れたす。 前に怜蚎したケヌスでは、䜿い捚おオブゞェクトのむンスタンスを䜜成し、その埌䟋倖がスロヌされるこずがはっきりずわかりたす。 ただし、基本的な䟋倖保蚌がないこずを確認するのがもう少し難しい堎合がありたす。



class Base : IDisposable

{

public Base()

{

//

}

public void Dispose() {}

}



class Derived : Base, IDisposable

{

public Derived( object data)

{

if (data == null )

throw new ArgumentNullException( "data" );

// OOPS!!

}

}

// -

using ( var derived = new Derived( null ))

{}



* This source code was highlighted with Source Code Highlighter .








Derivedクラスのコンストラクタヌで䟋倖を生成するず、基本クラスのDisposeメ゜ッドが呌び出されないため****、基本的な䟋倖保蚌に違反し、リ゜ヌスリヌクが発生したす。 繰り返したすが、コンパむラはusing構造のプリズムを通しおのみIDisposableむンタヌフェむスを知っおいるため、すべおの堎合、䜿い捚おオブゞェクトが別のクラスのフィヌルドである堎合、プログラマのみがDisposeメ゜ッドを呌び出したす。



基本クラスに加えお、フィヌルドのいずれかのコンストラクタヌが䟋倖をスロヌできる堎合、フィヌルド初期化子は同様のゞョヌクを再生できたす。



class ComposedDisposable : IDisposable

{

public void Dispose() {}



private readonly DisposableA disposableA = new DisposableA();

// , DisposableB ? OOPS!!

private readonly DisposableB disposableB = new DisposableB();

}



* This source code was highlighted with Source Code Highlighter .








この堎合、 DisposableBクラスのコンストラクタヌが、 disposableBフィヌルドの初期化時に䟋倖をスロヌするず 、それをキャッチしお、既にキャプチャヌしたリ゜ヌスを解攟するこずはできたせん。 C ++には、初期化リストで発生した䟋倖をキャッチするようなものがありたすが 䟋倖ずメンバヌの初期化を参照、Cにはそのような可胜性はないため、この状況から抜け出す方法は1぀しかありたせん。



これたでのすべおのケヌスに関しお、䟋倖の基本的な保蚌の提䟛は開発者に完党に委ねられおいたす。Cはこれらの目的のための「砂糖」を提䟛しないからです。 残っおいるのは、サブオブゞェクトを正しい順序で 䜜成し、コンストラクタヌの最埌に䜿い捚おフィヌルドを䜜成するか、その䜜成をtry / catchブロックでラップしお、䟋倖が発生した堎合にすべおのリ゜ヌスをクリアするこずです。



䟋倖の厳密な保蚌の䟋。 オブゞェクト初期化子およびコレクション初期化子





䞊蚘の䟋倖の基本的な保蚌の違反の䟋は、それらはそれほどフェッチされおいたせんが、それほど䞀般的ではありたせん。 たた、耇数の管理察象リ゜ヌスを含むオブゞェクトを䜜成する堎合、C蚀語コンパむラヌが圹に立たない堎合は、オブゞェクトやコレクションを䜜成するずきなど、他のいく぀かの堎合に圹立ちたす。



オブゞェクトおよびコレクションの初期化子オブゞェクト初期化子およびコレクション初期化子は、オブゞェクトの䜜成ず初期化、たたはコレクションに芁玠のリストを蚭定する原子性を保蚌したす。 次の䟋を芋おみたしょう。



class Person

{

public string FirstName { get ; set ; }

public string LastName { get ; set ; }

public int Age { get ; set ; }

}



var person = new Person

{

FirstName = "Bill" ,

LastName = "Gates" ,

Age = 55,

};



* This source code was highlighted with Source Code Highlighter .








䞀芋、以䞋の構文糖衣のように思えるかもしれたせん。



var person = new Person();

person.FirstName = "Bill" ;

person.LastName = "Gates" ;

person.Age = 55;



* This source code was highlighted with Source Code Highlighter .








ただし、実際には、オブゞェクト初期化子が呌び出されるず、䞀時倉数が䜜成され、この特定の倉数のプロパティが倉曎されおから、新しいオブゞェクトに割り圓おられたす 。



var tmpPerson = new Person();

tmpPerson.FirstName = "Bill" ;

tmpPerson.LastName = "Gates" ;

tmpPerson.Age = 55;

var person = tmpPerson;



* This source code was highlighted with Source Code Highlighter .








これにより、オブゞェクトを䜜成するプロセスの原子性ず、セッタヌの1぀によっお䟋倖が発生した堎合に郚分的に初期化されたオブゞェクトを䜿甚できなくなりたす。 同様の原則は、コレクションの初期化子にありたす。オブゞェクトが䞀時コレクションに远加され、それが曞き蟌たれた埌にのみ、䞀時倉数が新しいオブゞェクトに割り圓おられたす。



これらの2぀の抂念の根底にある原則は、ネむティブコヌドでの䟋倖の厳密な保蚌の独自の実装で簡単に䜿甚できたす。 これを行うには、特定の䞀時倉数でオブゞェクトの内郚状態のすべおの倉曎を実行するだけで十分であり、完了埌にのみ実際の状態がアトミックに倉曎されたす。



おわりに





䟋倖を正しく凊理するこずは簡単なこずではなく、いく぀かの䟋が瀺しおいるように、䟋倖の基本的な保蚌さえ難しい堎合がありたす。 ただし、そのような保蚌を提䟛するこずは、アプリケヌション党䜓の薄い局でリ゜ヌスを塗り぀ぶすよりも、1か所でリ゜ヌスを操䜜する耇雑さを隠す方がはるかに簡単であるため、アプリケヌションの開発に䞍可欠な条件です。 10幎前にScott Meyersによっお策定された黄金のルヌルは今でも有効です。 正しく䜿いやすく、間違っお䜿いにくいクラスを䜜成し 、䟋倖の保蚌が明らかに重芁な圹割を果たしたす。



これらの保蚌の実際の適甚に぀いお話す堎合、芚えおおくべきいく぀かのポむントがありたす。 たず、基本的な䟋倖保蚌を満たさないコヌドは正しくありたせん。 それに基づいお、䜿甚たたは倉曎されたずきに状態が壊れないアプリケヌションを䜜成するこずは䞍可胜です*****。 第二に、偏執狂にならず、最倧限の保蚌を求めたす。 非同期䟋倖が存圚するため、䟋倖がないこずを100保蚌するこずはほずんど䞍可胜ですが、厳密な保蚌を実装するこずでさえ、倚くの堎合、䞍圓に高䟡になる可胜性がありたす。



結論ずしお、次のように蚀えたす。 䟋倖の安党性の保蚌は䞇胜薬ではなく、信頌できるアプリケヌションを構築するための優れた基盀です 。



----------------------



*実際には、1぀の単玔な理由のために「ホットな」議論はありたせんでした。䟋倖を凊理せずに.Netプラットフォヌムでプログラミングするこずはできたせん。 このような議論は、特に䜎レベルのプログラミングに関しおは、C ++蚀語などに関連しおいたす。



**䞀般に、䞍倉保存を垞に必芁ずする人はいたせん。 通垞、 openメ゜ッドの呌び出しの䞍倉の「前」ず「埌」が必芁ですが、䜜業の「䞀郚」のみを実行する閉じたメ゜ッドの呌び出し埌に保存する必芁はありたせん。



***Cのようなより掗緎された蚀語は、叀いC ++のようなこずをしないかもしれないこずは非垞に面癜いように思えるかもしれたせんが、実際はそうです。 䟋ずしお、前述のコヌドをCからC ++に曞き換えたしょう。



class Resource1

{

public :

Resource1()

{

// , -

//

}

~Resource1()

{

//

}

};



class Resource2

{

public :

Resource2()

{

// resource1_

throw std::exception( "Yahoo!" );

}

private :

Resource1 resource1_;

};





// - Resource2

Resource2 resource2;




* This source code was highlighted with Source Code Highlighter .








C ++で前述したようにCずは異なり、クラスコンストラクタヌで䟋倖がスロヌされるず、既に構築されたフィヌルド぀たり、サブオブゞェクトのデストラクタヌが自動的に呌び出されたす。 ぀たり、この堎合、 Resource1オブゞェクトのデストラクタヌの呌び出しは自動的に行われ、リ゜ヌスリヌクは発生したせん。



C蚀語ずC ++蚀語のこのような動䜜の違いは簡単に説明できたす。 C ++では、リ゜ヌスは動的に割り圓おられたメモリを含むすべおのものであり、これがリ゜ヌス管理ツヌルがより高いレベルにある理由です。 C蚀語で䜜業する応甚プログラマヌは、コンストラクタヌでリ゜ヌスをキャプチャヌするのではなく、usingブロックでリ゜ヌスを䜿甚するこずが非垞に倚くありたす。 そしお、もし圌がそのような問題に盎面しおいるなら、圌はコンパむラの助けなしに自分でそれを解決しなければなりたせん。



ずころで、Herb Sutterは圌の蚘事ですでにこれに぀いお䞀床話したした 「C ++、C、およびJavaのコンストラクタ䟋倖」 。



****すでにこれらのメモでそれを入手しおいるかもしれたせんが、それは非垞に重芁であり、最埌から2番目のメモです。 圌らはしばしばむンタビュヌでそのような䟋を蚭定するこずを奜むので、今、私の読者はそれに察する正しい答えを知っおいたす



***** OutOfMemoryExceptionやThreadAbortExceptionなどの「非同期」䟋倖の䞀貫した発生を保蚌するこずはほずんど䞍可胜であるため、この蚘事で述べられおいるこずはすべお同期䟋倖にのみ適甚されたす。 ここでの蚌拠「 Thread.Abortメ゜ッドの危険性に぀いお。 」



All Articles