Apakah alamat 0000000C adalah alamat khusus?

32

Ketika pemrograman kadang-kadang hal-hal rusak. Anda membuat kesalahan dan program Anda mencoba membaca dari alamat yang salah.

Satu hal yang menonjol bagi saya bahwa seringkali pengecualian itu seperti:

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

Sekarang saya melihat banyak log kesalahan dan apa yang menonjol bagi saya adalah: 0000000C. Apakah ini alamat "khusus"? Saya melihat pelanggaran akses lainnya dengan pembacaan yang buruk tetapi alamatnya tampak acak, tetapi yang ini terus kembali dalam situasi yang sama sekali berbeda.

Pieter B
sumber
1
Saya juga telah memperhatikan bahwa 0000000Citu jauh lebih umum daripada 00000008, tetapi tidak ada jawaban yang tampaknya sama sekali: /
Mooing Duck
2
Mungkin itu System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringDataadalah 12=0x0Calasan mengapa offset ini lebih umum.
Mark Hurd
1
@ MarkHurd Itu menakutkan. Apakah Anda benar-benar berpikir bahwa ada begitu banyak aplikasi yang tidak dikelola yang dengan sengaja membaca / menulis string .NET sehingga ini akan menjadi sumber utama pelanggaran akses?
Luaan

Jawaban:

57

00000000adalah alamat khusus (penunjuk nol). 0000000Chanya apa yang Anda dapatkan ketika Anda menambahkan offset 12 ke pointer nol, kemungkinan besar karena seseorang mencoba untuk mendapatkan zanggota struktur seperti yang di bawah ini melalui pointer yang sebenarnya nol.

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;
}

sumber
29
Atau mungkin karena beberapa nilai integral kecil disalahartikan seolah-olah itu adalah pointer. Nilai kecil jauh lebih umum daripada nilai besar, jadi ini cenderung menghasilkan alamat ilegal seperti 0X0000000C daripada, misalnya, 0x43FCC893.
Kilian Foth
3
Alasan saya mengajukan pertanyaan ini adalah karena 0000000C kembali begitu sering dibandingkan dengan alamat lainnya. Mengapa offset 12 lebih besar daripada offset 4, 8 atau 16?
Pieter B
5
Setelah diselidiki lebih lanjut, jawaban ini sepenuhnya benar. Di sumber saya, properti "tag" kelas digunakan secara luas (baik atau buruk saya harus menghadapinya.) Properti tag dalam kasus saya adalah bagian dari kelas dasar tingkat rendah dan selalu dibuat pada offset itu.
Pieter B
1
Poin luar biasa. Mungkin null pointer case telah dicakup, tetapi null pointer ++ hanya alamat yang normal (dan dalam hal ini tidak valid), sehingga gagal hanya saat mengaksesnya.
Neil
8
@Leushenko Ya, perlindungan memori biasanya berfungsi pada seluruh halaman, dan bahkan jika mungkin hanya menangkap 0, lebih baik juga melindungi alamat berikut karena mereka cenderung diakses jika aritmatika pointer dengan pointer nol terjadi (seperti pada Kasus OP).
11

Di Windows adalah ilegal untuk melakukan dereferensi seluruh halaman pertama dan terakhir , dengan kata lain 64 KiB pertama atau terakhir dari memori proses (rentang 0x00000000ke 0x0000ffffdan 0xffff0000ke 0xffffffffdalam aplikasi 32-bit).

Ini untuk menjebak perilaku undereferencing dari pointer nol atau indeks ke dalam array nol. Dan ukuran halaman adalah 64 KiB sehingga Windows hanya harus mencegah halaman pertama atau terakhir ditetapkan rentang yang valid.

Ini tidak akan melindungi terhadap petunjuk yang tidak diinisialisasi yang dapat memiliki nilai apa pun (termasuk alamat yang valid).

ratchet freak
sumber
7
Windows tidak bisa melakukan itu. Tabel halaman adalah struktur yang ditentukan dan dipersyaratkan oleh x86, dan halaman-halaman kecil diperbaiki pada 4KB. Ini diatur dalam batu (lebih tepatnya, dalam silikon). 64KB mungkin untuk kenyamanan.
ElderBug
12
Saya lebih suka menulis 64 KiB daripada 65 kB dalam hal ini, karena kekuatan dua ukuran relevan.
CodesInChaos
4
Kisaran 64KB merupakan sisa dari versi Aplha NT. Dan itu bukan ukuran halaman, tetapi perincian alokasi. blogs.msdn.com/b/oldnewthing/archive/2003/10/08/55239.aspx
shf301
3
@CodesInChaos: Sementara huruf besar "M", "G", dan "T" adalah ambigu, saya tidak melihat alasan untuk mencabut penggunaan "k" untuk 10 ^ 3 dan "K" untuk 2 ^ 10.
supercat
2
@ MoooDuck Ya memang, itu sebabnya saya membuka halaman kecil. Kebanyakan x64 CPU juga mendukung halaman 1GiB. Sejauh yang saya tahu, Windows selalu halaman dengan halaman 4KB, kecuali dialokasikan dengan API khusus.
ElderBug
2

Adapun mengapa 0x0Ctampaknya lebih umum daripada 0x08(apakah itu benar-benar? Saya tidak tahu; dan dalam jenis aplikasi apa?), Ini mungkin ada hubungannya dengan pointer tabel metode virtual. Ini benar-benar lebih dari komentar (menebak-nebak massa :), tetapi ini agak lebih besar, jadi begini ... Jika Anda memiliki kelas dengan metode virtual, bidangnya sendiri akan digeser oleh 0x04. Misalnya, kelas yang mewarisi dari kelas virtual lain mungkin memiliki tata letak memori seperti ini:

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

Apakah ini skenario umum, atau bahkan dekat? Saya tidak yakin. Namun, perhatikan bahwa dalam aplikasi 64-bit, ini bisa menjadi lebih menarik ketika digeser ke 0x0Cnilai:

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

Jadi sebenarnya ada banyak kasus di mana aplikasi mungkin memiliki tumpang tindih yang signifikan dalam offset null-pointer. Ini mungkin bidang pertama dalam kelas anak, atau penunjuk tabel metode virtual - diperlukan setiap kali Anda memanggil metode virtual apa pun, jadi jika Anda memanggil metode virtual pada nullpenunjuk, Anda akan mendapatkan pelanggaran akses pada Offset VMT. Prevalensi nilai khusus ini mungkin ada hubungannya dengan beberapa API umum yang menyediakan kelas yang memiliki pola pewarisan yang serupa, atau lebih mungkin, antarmuka tertentu (sangat mungkin untuk beberapa kelas aplikasi, seperti game DirectX). Mungkin saja bisa melacak beberapa penyebab umum sederhana seperti ini, tetapi saya cenderung menyingkirkan aplikasi yang melakukan null dereferencing dengan cepat, jadi ...

Luaan
sumber
1
Jika Anda melihat melalui komentar, Anda dapat mengurangi tebakannya.
Deduplicator
@Dupuplikator Yah, saya menemukan ide yang dikelola. NET string digunakan dalam kode yang tidak aman dengan operasi pointer manual menakutkan, dan pemikiran bahwa ini akan menjadi penyebab utama pelanggaran akses lebih dari itu. "Ya, ini benar-benar memori aman, jangan khawatir, kami menggunakan C #. Kami hanya secara manual mengubah memori dari C ++, tapi aman di C #."
Luaan