C standardı, boş işaretçilerin makinenin sıfır adresinde olmasını gerektirmez. ANCAK, 0
bir işaretçi değerine bir sabit değer NULL
atamak bir işaretçi (§6.3.2.3 / 3) ile sonuçlanmalı ve boş göstericiyi bir boolean olarak değerlendirmek yanlış olmalıdır. Bu gerçekten eğer biraz garip olabilir do sıfır adresi istiyor ve NULL
sıfır adresi değil.
Bununla birlikte, derleyicide ve standart kitaplıkta (ağır) değişiklikler yapıldığında NULL
, standart kitaplığa tam anlamıyla uyumlu kalırken alternatif bir bit örüntüsü ile temsil edilmek imkansız değildir . Öyle değil sadece tanımını değiştirmek için yeterli NULL
sonra da, ancak kendisi NULL
gerçek olarak değerlendirilir.
Özellikle şunları yapmanız gerekir:
- Atamalarda işaretçiler (veya işaretçiler için atmalar) gibi başka bir sihirli değere dönüştürülecek sıfırları düzenleyin
-1
.
0
Bunun yerine sihirli değeri kontrol etmek için işaretçiler ve sabit bir tam sayı arasında eşitlik testleri düzenleyin (§6.5.9 / 6)
- Sıfır için kontrol etmek yerine sihirli değere eşitliği kontrol etmek için bir işaretçi türünün boole olarak değerlendirildiği tüm bağlamları düzenleyin. Bu eşitlik testi anlambiliminden kaynaklanır, ancak derleyici bunu dahili olarak farklı şekilde uygulayabilir. Bkz. §6.5.13 / 3, §6.5.14 / 3, §6.5.15 / 4, §6.5.3.3 / 5, §6.8.4.1 / 2, §6.8.5 / 4
- Caf'in belirttiği gibi, yeni boş gösterici temsilini yansıtmak için statik nesnelerin (§6.7.8 / 10) ve kısmi bileşik başlatıcıların (§6.7.8 / 21) başlatılması için anlambilimini güncelleyin.
- Gerçek sıfır adresine erişmek için alternatif bir yol oluşturun.
Halletmeniz gerekmeyen bazı şeyler var. Örneğin:
int x = 0;
void *p = (void*)x;
Bundan sonra p
boş gösterici olacağı garanti EDİLMEZ. Yalnızca sabit atamaların ele alınması gerekir (bu, gerçek sıfır adresine erişmek için iyi bir yaklaşımdır). Aynı şekilde:
int x = 0;
assert(x == (void*)0); // CAN BE FALSE
Ayrıca:
void *p = NULL;
int x = (int)p;
x
olması garanti edilmez 0
.
Kısacası, bu koşul görünüşe göre C dili komitesi tarafından değerlendirildi ve NULL için alternatif bir temsil seçecek olanlar için değerlendirmeler yapıldı. Şimdi yapmanız gereken tek şey, derleyicinizde büyük değişiklikler yapmak ve hey, işiniz bitti :)
Bir yan not olarak, bu değişiklikleri derleyici uygun olmadan önce bir kaynak kodu dönüştürme aşaması ile uygulamak mümkün olabilir. Yani, önişlemci -> derleyici -> assembler -> bağlayıcının normal akışı yerine, bir önişlemci -> NULL dönüşüm -> derleyici -> assembler -> bağlayıcı eklersiniz. O zaman aşağıdaki gibi dönüşümler yapabilirsiniz:
p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }
Bu, tam bir C ayrıştırıcısının yanı sıra, bir tür ayrıştırıcısı ve hangi tanımlayıcıların işaretleyicilere karşılık geldiğini belirlemek için tiplerin ve değişken bildirimlerinin analizini gerektirir. Ancak, bunu yaparak, derleyicinin kod oluşturma bölümlerinde uygun şekilde değişiklik yapmak zorunda kalmazsınız. clang bunu uygulamak için yararlı olabilir - bunun gibi dönüşümler düşünülerek tasarlandığını anlıyorum. Elbette standart kitaplıkta da değişiklik yapmanız gerekebilir.
mprotect
güvenliğini sağlayabileceğiniz adresler olacaktır . Veya platformda ASLR veya benzeri yoksa, platformun fiziksel belleğinin ötesinde adresler. İyi şanslar.