コンピューティングにおいて、ヌル ポインターまたはヌル参照は、ポインターまたは参照が有効なオブジェクトを参照していないことを示すために保存される値です。プログラムでは、長さが不明なリストの終了や、何らかのアクションの実行の失敗などの状況を表すためにヌル ポインターが日常的に使用されます。このヌル ポインターの使用は、 null 許容型やオプション型のNothing値に例えることができます。
ヌル ポインターを初期化されていないポインターと混同しないでください。ヌル ポインターは、有効なオブジェクトを指すどのポインターとも等しくないことが保証されています。ただし、一般に、ほとんどの言語では、初期化されていないポインターに対してこのような保証はありません。他の有効なポインターと比較して等しい場合もあれば、ヌル ポインターと比較して等しい場合もあります。異なるタイミングで両方が行われる場合もあれば、比較が未定義の動作になる場合もあります。また、このようなサポートを提供する言語では、正しい使用方法は各開発者の個々の経験とリンター ツールによって異なります。適切に使用された場合でも、ヌル ポインターは意味的に不完全です。これは、「該当なし」の値と「不明」の値、または「将来」の値の違いを表現できないためです。
ヌルポインタは意味のあるオブジェクトを指していないため、その(無効な)メモリ位置に格納されているデータにアクセスしようとすると、実行時エラーが発生したり、プログラムが即座にクラッシュしたりする可能性があります。これがヌルポインタエラーです。これは最も一般的なソフトウェアの脆弱性の1つであり、[1]この概念を導入したトニー・ホーアはこれを「10億ドルの間違い」と呼んでいます。[2]
C
Cでは、任意の型の 2 つの null ポインタを比較すると、必ず等しいことが保証されています。[3]プリプロセッサ マクロは、NULLで実装定義の null ポインタ定数として定義されています。 [4]これは、C99では、型に変換された整数値として移植可能に表現できます( void 型へのポインタを参照)。[5] C 標準では、null ポインタがメモリ アドレス0へのポインタと同じであるとは述べられていませんが 、実際にはそうである可能性があります。C では、null ポインタの逆参照は未定義の動作であり、[6]準拠の実装では、逆参照されるポインタが null ではないと想定できます。
<stdlib.h>((void *)0)0 void*
実際には、ヌル ポインターを逆参照すると、マップされていないメモリからの読み取りまたは書き込みが試行され、セグメンテーション違反またはメモリ アクセス違反が発生する可能性があります。これは、プログラム クラッシュとして現れるか、プログラム コードでキャッチできるソフトウェア例外に変換される可能性があります。ただし、そうではない状況もあります。たとえば、x86 リアル モードでは、アドレスは0000:0000読み取り可能で、通常は書き込みも可能なため、そのアドレスへのポインターを逆参照することは完全に有効ですが、通常は望ましくないアクションであり、アプリケーションで未定義ではあるがクラッシュしない動作につながる可能性があります。ヌル ポインターがそのアドレスへのポインターとして表されている場合、それを逆参照するとその動作が発生します。アドレス 0 へのポインターを逆参照することが意図的で明確に定義されている場合もあります。たとえば、16 ビット リアル モード x86 デバイス用に C で記述されたBIOSコードは、書き込み用のヌル ポインターと同じ値を持つポインターを逆参照することにより、マシンの物理アドレス 0 に割り込み記述子テーブル(IDT) を書き込む場合があります。コンパイラがヌルポインタの逆参照を最適化してセグメンテーション違反を回避し、他の望ましくない動作を引き起こす可能性もあります。[7]
C++
C++では、NULLマクロはCから継承されていますが、ゼロの整数リテラルは伝統的にヌルポインター定数を表すために好まれてきました。[8]しかし、C++11では代わりに
明示的なヌルポインター定数nullptrと型が導入されました。nullptr_t
その他の言語
一部のプログラミング言語環境(たとえば、少なくとも 1 つの独自の Lisp 実装)では、[要引用]ヌル ポインターとして使用される値 ( Lispnilでは と呼ばれる) は、実際には実装に役立つ内部データ ブロックへのポインターである可能性があります (ただし、ユーザー プログラムからは明示的にアクセスできません)。そのため、同じレジスタを便利な定数として使用し、実装内部にすばやくアクセスすることができます。これはベクトルと呼ばれます。
nil
タグ付きアーキテクチャを持つ言語では、おそらく null のポインターを、例外的なケースの明示的な処理を強制するタグ付きユニオンに置き換えることができます。実際、おそらく null のポインターは、計算されたタグを持つ タグ付きポインターと見なすことができます。
プログラミング言語は、ヌルポインターに異なるリテラルを使用します。たとえば、Python では、ヌル値は と呼ばれます。PascalNoneと Swift では、ヌルポインターは と呼ばれますnil。Eiffel では、参照と呼ばれます。
void
ヌル参照
ヌルポインタは意味のあるオブジェクトを指していないため、ヌルポインタを逆参照(つまり、そのメモリ位置に格納されているデータにアクセス)しようとすると、通常は(必ずではありませんが)実行時エラーが発生したり、プログラムが即座にクラッシュしたりします。MITREは、ヌルポインタエラーを最も一般的に悪用されるソフトウェアの脆弱性の1つとして挙げています。[9]
- Cでは、ヌルポインタの逆参照は未定義動作です。[6]多くの実装では、このようなコードを実行するとアクセス違反でプログラムが停止します。これは、ヌルポインタ表現が、オブジェクトを格納するためにシステムによって割り当てられることのないアドレスとして選択されるためです。ただし、この動作は普遍的ではありません。また、コンパイラは未定義動作がないという前提でプログラムを最適化することが許可されているため、保証されていません。
- Delphiおよび他の多くの Pascal 実装では、定数はメモリの最初のアドレスへの NULL ポインターを表し、マネージ変数の初期化にも使用されます。これを逆参照すると、ユニットが句にリンクされている場合は、
nilPascal 例外インスタンスにマップされる外部 OS 例外が発生します。EAccessViolationSystem.SysUtilsuses - Javaでは、null 参照にアクセスすると
NullPointerException(NPE) が発生しますが、これはエラー処理コードによってキャッチできますが、このような例外が発生しないようにすることが推奨されます。 - Lispでは、はファーストクラス オブジェクト
nilです。慣例により、はと同様に です。したがって、これらのコンテキストで逆参照してもエラーは発生しませんが、不適切に記述されたコードは無限ループに陥る可能性があります。(first nil)nil(rest nil)nil - .NETでは、null 参照にアクセスすると が
NullReferenceExceptionスローされます。これらをキャッチすることは一般的に悪い習慣と考えられていますが、この例外タイプはプログラムによってキャッチされ、処理される可能性があります。 - Objective-Cでは、
nilプログラムを中断させることなく、オブジェクト(ヌルポインタ)にメッセージを送信することができます。メッセージは単に無視され、戻り値(ある場合)は型に応じてnilまたは になります。 [10]0 - スーパーバイザモードアクセス防止(SMAP)が導入される前は、ページゼロを攻撃者のアドレス空間にマッピングし、その結果、ヌルポインタがその領域を指すようにすることで、ヌルポインタ参照バグを悪用される可能性がありました。これにより、場合によってはコードが実行される可能性があります。 [11]
緩和
ヌルのない言語もありますが、ほとんどの言語ではヌルの可能性が存在するため、ヌルポインターの参照を回避したりデバッグを支援したりするテクニックがあります。[12] Bondら[12]は、 Java仮想マシン(JVM)を変更してヌル伝播を追跡することを提案しています。
有効性の順に、null 参照の処理には 3 つのレベルがあります。
1. nullのない言語。
2. 実行時に null 参照の可能性を回避するためにコードを静的に分析できる言語。
3. 実行時に null 参照が発生する可能性がある場合、デバッグを支援するツール。
純粋関数型言語は、ポインタへの直接アクセスが提供されず、すべてのコードとデータが不変であるため、レベル 1 の例です。インタープリタ型言語または仮想マシン言語で実行されるユーザー コードでは、通常、ヌル ポインタの逆参照の問題は発生しません。[疑わしい–議論する]
言語がvoidになる可能性のあるポインタを提供または利用する場合、静的解析やその他の技術によるコンパイル時のチェックを提供することで、実行時のnull参照を回避することができます。これには、null参照を回避するためのVoid安全性を備えたEiffelプログラミング言語[13]、D、[14]、Rust [15]などの言語機能による構文上の支援が含まれます。
一部の言語では外部ツールを使用して分析を実行できますが、これらは言語定義自体によって制限されるため、コンパイラ チェックによる直接的な言語サポートと比較すると弱くなります。
レベル 3 の最後の手段は、実行時に null 参照が発生した場合に、デバッグ支援が役立つことです。
ヌルポインタの代替
経験則として、構造体またはクラスの各タイプに対して、ビジネス ロジックの状態を表すオブジェクトをいくつか定義し、null の未定義の動作を置き換えます。たとえば、構造体内の現在は使用できないフィールド (ただし、将来定義されることが事前にわかっているフィールド) を示すには「future」、正規化されていない構造体内のフィールドを示すには「not applicable」、フィールドを初期化できなかったことを示すには「error」、「timeout」 (おそらく、プログラム全体、スレッド、要求、またはコマンドの通常の実行が停止する) を使用します。
歴史
2009年、トニー・ホーアは[16] [17] 、1965年にALGOL W言語 の一部としてヌル参照を発明したと述べています。2009年のその文献で、ホーアは彼の発明を「10億ドルの間違い」と表現しています。
私はこれを 10 億ドルの失敗と呼んでいます。それは 1965 年のヌル参照の発明でした。当時、私はオブジェクト指向言語 (ALGOL W) における参照用の最初の包括的な型システムを設計していました。私の目標は、コンパイラによって自動的にチェックが実行され、参照の使用がすべて絶対に安全であることを保証することでした。しかし、実装が非常に簡単であるという理由だけで、ヌル参照を入れたいという誘惑に抗うことができませんでした。これにより、無数のエラー、脆弱性、システム クラッシュが発生し、過去 40 年間でおそらく 10 億ドルの苦痛と損害が発生しました。
参照
注記
- ^ 「CWE-476: NULL ポインタ参照」。MITRE。
- ^ 「Null References: The Billion Dollar Mistake」。InfoQ 。 2024年9月5日閲覧。
- ^ ISO/IEC 9899、6.3.2.3項、第4段落。
- ^ ISO/IEC 9899、条項 7.17、段落 3: NULL... これは実装定義の null ポインター定数に展開されます...
- ^ ISO/IEC 9899、条項6.3.2.3、段落3。
- ^ ISO/IEC 9899、条項6.5.3.2、段落4、特に脚注87。
- ^ Lattner, Chris (2011-05-13). 「未定義の動作についてすべての C プログラマが知っておくべきこと #1/3」。blog.llvm.org。2023-06-14 にオリジナルからアーカイブ。2023-06-14に取得。
- ^ Stroustrup, Bjarne (2001 年 3 月)。「第 5 章:修飾子(§5.4) は、 の偶発的な再定義を防ぎ、定数が必要な場所で が確実に使用されるようにします。」C++ プログラミング言語(第 3 版の第 14 刷)。米国およびカナダ: Addison–Wesley。p. 88。ISBN
constNULLNULL0-201-88954-4。 - ^ 「CWE-476: NULL ポインタ参照」。MITRE。
- ^ Objective-C 2.0 プログラミング言語、「nil へのメッセージの送信」セクション。
- ^ 「OS X の AppleGraphicsDeviceControl におけるカーネル NULL ポインタ参照の脆弱性」
- ^ ab Bond, Michael D.; Nethercote, Nicholas; Kent, Stephen W.; Guyer, Samuel Z.; McKinley, Kathryn S. (2007). 「Tracking bad apples」。第22回 ACM SIGPLAN 会議の議事録、オブジェクト指向プログラミング システムとアプリケーション - OOPSLA '07。p. 405。doi :10.1145/1297027.1297057。ISBN 9781595937865. S2CID 2832749。
- ^ 「ボイドセーフティ:背景、定義、ツール」。2021年11月24日閲覧。
- ^ Bartosz Milewski. 「SafeD – Dプログラミング言語」 。 2014年7月17日閲覧。
- ^ “Fearless Security: Memory Safety”. 2020年11月8日時点のオリジナルよりアーカイブ。2020年11月4日閲覧。
- ^ Tony Hoare (2009-08-25). 「Null 参照: 10 億ドルの損失」. InfoQ.com.
- ^ Tony Hoare (2009-08-25). 「プレゼンテーション: 「Null 参照: 10 億ドルの損失」」. InfoQ.com.
参考文献
- 合同技術委員会 ISO/IEC JTC 1、小委員会 SC 22、ワーキンググループ WG 14 (2007-09-08)。国際規格 ISO/IEC 9899 (PDF) (委員会草案)。
{{cite book}}: CS1 maint: 複数名: 著者リスト (リンク) CS1 maint: 数値名: 著者リスト (リンク)
