アドレス0000000Cは特別なアドレスですか?


32

プログラミングするとき、時々物事が壊れます。間違いを犯し、プログラムが間違ったアドレスから読み取ろうとしています。

多くの場合、これらの例外は次のようなものであることを私に際立たせます:

Access violation at address 012D37BC in module 'myprog.exe'. Read of address 0000000C.

今、私は多くのエラーログを見て、私にとって際立っているのは、0000000Cです。これは「特別な」住所ですか?不正な読み取りを伴う他のアクセス違反が表示されますが、アドレスはランダムに見えますが、これはまったく異なる状況で繰り返し表示されます。


1
また、私は気づいた0000000Cある方法よりも一般的で00000008はなく、答えのどれもまったくそのアドレスに思えません:/
ダックMooing

2
おそらく、それSystem.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData12=0x0Cこのオフセットがより一般的である理由です。
マークハード

1
@MarkHurdそれは怖いです。管理されていないアプリケーションが非常に多く、意図的に.NET文字列を読み書きするので、これがアクセス違反の主な原因になると本当に思いますか?
ルアーン

回答:


57

00000000特別なアドレス(nullポインター)です。0000000Cヌルポインターに12のオフセットを追加したときに得られるものですz。これは、実際にヌルであるポインターを介して、次のような構造体のメンバーを取得しようとした可能性が高いためです。

struct Foo {
    int w, x, y; // or anything else that takes 12 bytes including padding
    // such as: uint64_t w; char x;
    // or: void *w; char padding[8];
    // all assuming an ordinary 32 bit x86 system
    int z;
}

29
または、何らかの小さな整数値が誤ってポインタであるかのように逆参照されたためかもしれません。小さな値は巨大な値よりもはるかに一般的であるため、たとえば0x43FCC893ではなく、0X0000000Cのような不正なアドレスが生成される傾向があります。
キリアンフォス

3
私がこの質問をした理由は、0000000Cが他のアドレスに比べて頻繁に戻ってくるためです。オフセット12は、オフセット4、8、または16よりも一般的なのはなぜですか?
ピーターB

5
さらなる調査の後、この答えは完全に正しいです。私のソースでは、クラスの「タグ」プロパティが広く使用されています(良いか悪いかに対処する必要があります)。私の場合のタグプロパティは低レベルの基本クラスの一部であり、常にそのオフセットで作成されます。
ピーターB

1
素晴らしい点。おそらくNULLポインターのケースはカバーされましたが、NULLポインター++は単なる通常の(この場合は無効な)アドレスであるため、アクセスしたときにのみ失敗します。
ニール

8
@Leushenkoはい、メモリ保護は通常、ページ全体で機能し、0のみをキャッチできたとしても、次のアドレスも保護することをお勧めします。 OPの場合)。

11

Windowsでは、それは間接参照することは違法であり、全体の最初と最後のページを他の言葉で、プロセスメモリの最初または最後の64 KiBの(範囲0x00000000まで0x0000ffff0xffff0000する0xffffffff32ビット・アプリケーションで)。

これは、nullポインターまたはインデックスをnull配列に逆参照する未定義の動作をトラップするためです。また、ページサイズは64 KiBであるため、Windowsは最初または最後のページに有効な範囲が割り当てられないようにする必要があります。

これは、任意の値(有効なアドレスを含む)を持つ可能性のある初期化されていないポインターを保護しません。


7
Windowsは実際にそれを行うことはできません。ページテーブルは、x86で定義および要求される構造であり、小さなページは4KBに固定されています。それは石で(より正確には、シリコンで)設定されています。64KBは、おそらく便宜上のものです。
ElderBug

12
この場合、2のべき乗のサイズが関係するため、65 kBではなく64 KiBと書きます。
CodesInChaos

4
64KBの範囲は、NTのAplhaバージョンからの残りです。そして、それはページサイズではなく、割り当ての粒度です。 blogs.msdn.com/b/oldnewthing/archive/2003/10/08/55239.aspx
shf301

3
@CodesInChaos:大文字の "M"、 "G"、および "T"はあいまいですが、10 ^ 3の "k"と2 ^ 10の "K"の使用を非推奨にする理由はありません。
supercat

2
@MooingDuck確かに、そういうわけで私は小さなページを精査しました。ほとんどのx64 CPUは1GiBページもサポートしています。私の知る限り、Windowsは特別なAPIで割り当てられていない限り、常に4KBページでページングします。
ElderBug

2

なぜ0x0Cより一般的であるように見えるのか0x08(実際ですか?わかりません。どのような種類のアプリケーションですか?)、これは仮想メソッドテーブルポインターに関係している可能性があります。これは本当にコメント(ワイルドマス推測:)ですが、やや大きいので、ここに行きます...仮想メソッドを持つクラスがある場合、その独自のフィールドはによってシフトされ0x04ます。たとえば、別の仮想クラスから継承するクラスには、次のようなメモリレイアウトがあります。

0x00 - VMT pointer for parent
0x04 - Field 1 in parent
0x08 - VMT pointer for child
0x0C - Field 1 in child

これは一般的なシナリオですか、それとも近いですか?よく分かりません。ただし、64ビットアプリケーションでは、これがさらに興味深いことに0x0C値にシフトする可能性があることに注意してください。

0x00 - VMT parent
0x08 - Field 1 parent
0x0C - VMT child
0x14 - Field 2 child

そのため、実際には、アプリケーションでヌルポインターオフセットが大幅に重複する場合が多くあります。これは、子クラスの最初のフィールド、またはその仮想メソッドテーブルポインターである場合があります-インスタンスで仮想メソッドを呼び出すたびに必要なので、nullポインターで仮想メソッドを呼び出すと、そのアクセス違反が発生しますVMTオフセット。この特定の値の普及は、同様の継承パターンを持つクラスを提供する一般的なAPI、またはおそらく特定のインターフェイス(DirectXゲームなどのアプリケーションのクラスでかなり可能)に関係している可能性があります。このようないくつかの単純な一般的な原因を追跡することは可能かもしれませんが、nullの逆参照を非常に迅速に行うアプリケーションを取り除く傾向があるので...


1
コメントに目を通すと、推測をかなり減らすことができます。
デュプリケータ

@Deduplicatorまあ、マネージド.NET文字列は手動のポインター操作で安全でないコードで使用されるという考えは恐ろしく、これがアクセス違反の主な原因であるとさらに考えています。「ええ、これは完全にメモリセーフです。心配しないで、C#を使用しました。C++からメモリを手動で変更するだけですが、C#では安全です。」
ルアーン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.