Bir derleyici, malloc / free / new / delete üzerinde belleği 0xCD, 0xDD, vb. Olarak ne zaman ve neden başlatır?


129

Ben derleyici bazen gibi belirli desenleri ile bellek başlatılır biliyorum 0xCDve 0xDD. Bilmek istediğim şey bunun ne zaman ve neden olduğu.

Ne zaman

Bu kullanılan derleyiciye özel mi?

Bununla ilgili olarak aynı şekilde mi çalışıyor malloc/newve free/deleteçalışıyor?

Platforma özel mi?

LinuxVeya gibi diğer işletim sistemlerinde gerçekleşecek VxWorksmi?

Neden

Anladığım kadarıyla, bu yalnızca Win32hata ayıklama yapılandırmasında gerçekleşir ve bellek taşmalarını tespit etmek ve derleyicinin istisnaları yakalamasına yardımcı olmak için kullanılır.

Bu ilklendirmenin ne kadar yararlı olduğuna dair pratik örnekler verebilir misiniz?

Hafızayı tahsis ederken bilinen bir modele göre başlatmanın iyi olduğunu söyleyen bir şeyi okuduğumu (belki Kod Tamamlandı 2'de) hatırlıyorum ve bazı kalıplar kesintileri tetikleyecek ve Win32bu da hata ayıklayıcıda istisnaların gösterilmesine neden olacak.

Bu ne kadar taşınabilir?

Yanıtlar:


191

Microsoft derleyicilerinin, hata ayıklama modu için derlendiğinde çeşitli sahipsiz / başlatılmamış bellek bitleri için ne kullandıklarının hızlı bir özeti (destek, derleyici sürümüne göre değişebilir):

Value     Name           Description 
------   --------        -------------------------
0xCD     Clean Memory    Allocated memory via malloc or new but never 
                         written by the application. 

0xDD     Dead Memory     Memory that has been released with delete or free. 
                         It is used to detect writing through dangling pointers. 

0xED or  Aligned Fence   'No man's land' for aligned allocations. Using a 
0xBD                     different value here than 0xFD allows the runtime
                         to detect not only writing outside the allocation,
                         but to also identify mixing alignment-specific
                         allocation/deallocation routines with the regular
                         ones.

0xFD     Fence Memory    Also known as "no mans land." This is used to wrap 
                         the allocated memory (surrounding it with a fence) 
                         and is used to detect indexing arrays out of 
                         bounds or other accesses (especially writes) past
                         the end (or start) of an allocated block.

0xFD or  Buffer slack    Used to fill slack space in some memory buffers 
0xFE                     (unused parts of `std::string` or the user buffer 
                         passed to `fread()`). 0xFD is used in VS 2005 (maybe 
                         some prior versions, too), 0xFE is used in VS 2008 
                         and later.

0xCC                     When the code is compiled with the /GZ option,
                         uninitialized variables are automatically assigned 
                         to this value (at byte level). 


// the following magic values are done by the OS, not the C runtime:

0xAB  (Allocated Block?) Memory allocated by LocalAlloc(). 

0xBAADF00D Bad Food      Memory allocated by LocalAlloc() with LMEM_FIXED,but 
                         not yet written to. 

0xFEEEFEEE               OS fill heap memory, which was marked for usage, 
                         but wasn't allocated by HeapAlloc() or LocalAlloc(). 
                         Or that memory just has been freed by HeapFree(). 

Sorumluluk reddi: Tablo, etrafta yatan bazı notlardan geliyor -% 100 doğru (veya tutarlı) olmayabilir.

Bu değerlerin çoğu vc / crt / src / dbgheap.c dosyasında tanımlanmıştır:

/*
 * The following values are non-zero, constant, odd, large, and atypical
 *      Non-zero values help find bugs assuming zero filled data.
 *      Constant values are good, so that memory filling is deterministic
 *          (to help make bugs reproducible).  Of course, it is bad if
 *          the constant filling of weird values masks a bug.
 *      Mathematically odd numbers are good for finding bugs assuming a cleared
 *          lower bit.
 *      Large numbers (byte values at least) are less typical and are good
 *          at finding bad addresses.
 *      Atypical values (i.e. not too often) are good since they typically
 *          cause early detection in code.
 *      For the case of no man's land and free blocks, if you store to any
 *          of these locations, the memory integrity checker will detect it.
 *
 *      _bAlignLandFill has been changed from 0xBD to 0xED, to ensure that
 *      4 bytes of that (0xEDEDEDED) would give an inaccessible address under 3gb.
 */

static unsigned char _bNoMansLandFill = 0xFD;   /* fill no-man's land with this */
static unsigned char _bAlignLandFill  = 0xED;   /* fill no-man's land for aligned routines */
static unsigned char _bDeadLandFill   = 0xDD;   /* fill free objects with this */
static unsigned char _bCleanLandFill  = 0xCD;   /* fill new objects with this */

Ayrıca, hata ayıklama çalışma zamanının arabellekleri (veya arabellek parçalarını) bilinen bir değerle dolduracağı birkaç kez vardır, örneğin, std::string's ayırmadaki ' gevşek 'boşluk veya geçirilen arabellek fread(). Bu durumlarda, ad verilen _SECURECRT_FILL_BUFFER_PATTERN(içinde tanımlanan crtdefs.h) bir değer kullanılır . Tam olarak ne zaman tanıtıldığından emin değilim, ancak en azından VS 2005 (VC ++ 8) tarafından hata ayıklama çalışma zamanındaydı.

Başlangıçta, bu tamponları doldurmak 0xFDiçin kullanılan değer - hiç kimsenin arazisi için kullanılan değerin aynısıydı. Ancak, VS 2008'de (VC ++ 9) değer olarak değiştirildi 0xFE. Sanırım bunun nedeni, doldurma işleminin arabelleğin sonundan sonra çalışacağı durumlar olabileceğidir, örneğin, arayan çok büyük bir arabellek boyutunda geçerse fread(). Bu durumda değer0xFD bu taşmayı algılamayı tetiklemeyebilir çünkü arabellek boyutu sadece bir kadar büyükse, doldurma değeri o kanaryayı başlatmak için kullanılan no man's land değeriyle aynı olacaktır. Hiç kimsenin toprağında değişiklik olmaması, taşmanın fark edilmeyeceği anlamına gelir.

Böylece VS 2008'de doldurma değeri değiştirildi, böylelikle böyle bir durum kimsenin olmadığı kara kanaryasını değiştirecek ve sorunun çalışma zamanı tarafından tespit edilmesine yol açacaktı.

Diğerlerinin de belirttiği gibi, bu değerlerin temel özelliklerinden biri, bu değerlerden birine sahip bir işaretçi değişkeninin referansı kaldırılırsa, standart bir 32 bit Windows yapılandırmasında kullanıcı modu adresleri olduğundan erişim ihlaline neden olacağıdır. 0x7fffffff'den daha yükseğe çıkmaz.


1
MSDN'de mi bilmiyorum - onu oradan oradan bir araya getirdim ya da başka bir web sitesinden edindim.
Michael Burr

2
Oh evet - bir kısmı DbgHeap.c'deki CRT kaynağından.
Michael Burr

Bazıları MSDN'de ( msdn.microsoft.com/en-us/library/bebs9zyz.aspx ), ancak hepsi değil. İyi liste.
sean e

3
@seane - FYI bağlantınız ölmüş görünüyor. Yenisine
Simon Mourier

Bu blokların adı nedir? Bellek engeli, zar, bellek çiti veya çit talimatı mı ( en.wikipedia.org/wiki/Memory_barrier )?
kr85

36

0xCCCCCCCC dolgu değeri ile ilgili güzel bir özellik, x86 derlemesinde 0xCC opcode'un , yazılım kesme noktası kesmesi olan int3 işlem kodu olmasıdır. Bu nedenle, bu dolgu değeriyle doldurulmuş başlatılmamış bellekte kod çalıştırmayı denerseniz, hemen bir kesme noktasına ulaşırsınız ve işletim sistemi bir hata ayıklayıcı eklemenize (veya işlemi sonlandırmanıza) izin verir.


6
Ve 0xCD komuttur, bu intyüzden 0xCD 0xCD'nin çalıştırılması bir int CDtuzak oluşturacak bir üretecektir .
Tad Marshall

2
Günümüz dünyasında, Veri Yürütme Engellemesi, CPU'nun yığından bir talimat almasına bile izin vermiyor. Bu cevap, XP SP2'den beri güncel değil.
MSalters

2
@MSalters: Evet, varsayılan olarak, yeni ayrılan belleğin çalıştırılamayacağı doğrudur, ancak birileri belleği kolayca çalıştırabilir VirtualProtect()veya mprotect()çalıştırılabilir hale getirebilir.
Adam Rosenfield

Bir veri bloğundan kod yürütemezsiniz. HİÇ. Tekrar tahmin et.
Dan

9

Derleyici ve işletim sistemine özgüdür, Visual Studio, farklı bellek türlerini farklı değerlere ayarlar, böylece hata ayıklayıcıda, malloced belleğe, sabit bir diziye veya başlatılmamış bir nesneye aşırı yüklenip yüklenmediğinizi kolayca görebilirsiniz. Ben googlederken biri ayrıntıları gönderecek ...

http://msdn.microsoft.com/en-us/library/974tc9t1.aspx


Tahminim, dizelerinizi de düzgün bir şekilde sonlandırmayı unutup unutmadığınızı kontrol etmek için kullanılıyor (çünkü bu 0xCD'ler veya 0xDD'ler yazdırılıyor).
strager

0xCC = başlatılmamış yerel (yığın) değişken 0xCD = başlatılmamış sınıf (yığın?) Değişken 0xDD = silinmiş değişken
FryGuy

@FryGuy Burada açıkladığım gibi, bu değerleri (bazılarını) belirleyen pratik bir neden var .
Glenn Slayden

4

İşletim sistemi değil - derleyici. Davranışı da değiştirebilirsiniz - bu yazının altına bakın.

Microsoft Visual Studio, yığın belleği 0xCC ile önceden dolduran bir ikili dosya oluşturur (Hata Ayıklama modunda). Ayrıca, arabellek taşmalarını algılamak için her yığın çerçevesi arasına bir boşluk ekler. Bunun yararlı olduğu çok basit bir örnek burada (pratikte Visual Studio bu sorunu tespit eder ve bir uyarı verir):

...
   bool error; // uninitialised value
   if(something)
   {
      error = true;
   }
   return error;

Visual Studio değişkenleri bilinen bir değere önceden başlatmadıysa, bu hatanın bulunması potansiyel olarak zor olabilir. Önceden hazırlanmış değişkenlerle (veya daha doğrusu, önceden başlatılmış yığın belleği), sorun her çalışmada yeniden üretilebilir.

Ancak ufak bir sorun var. Visual Studio'nun kullandığı değer TRUE'dur - 0 dışındaki her şey olur. Aslında, kodunuzu Release modunda çalıştırdığınızda, birimleştirilmiş değişkenlerin 0 içeren bir yığın belleği parçasına tahsis edilebilmesi oldukça olasıdır, bu da yalnızca Release modunda kendini gösteren birimselleştirilmiş bir değişken hatasına sahip olabileceğiniz anlamına gelir.

Bu beni rahatsız etti, bu yüzden doğrudan ikiliyi düzenleyerek ön doldurma değerini değiştirmek için bir komut dosyası yazdım , bu da yalnızca yığın sıfır içerdiğinde ortaya çıkan büyük harfsiz değişken problemlerini bulmama izin verdi. Bu komut dosyası yalnızca yığın ön dolgusunu değiştirir; Mümkün olsa da, yığın ön dolgusunu hiç denemedim. Çalışma zamanı DLL'sini düzenlemeyi içerebilir, olmayabilir.


1
VS, GCC gibi bir değeri başlatılmadan önce kullanırken bir uyarı vermez mi?
strager

3
Evet, ama her zaman değil, çünkü statik analize bağlı. Sonuç olarak, işaretçi aritmetiği ile karıştırmak oldukça kolaydır.
Airsource Ltd

3
"İşletim sistemi değil - derleyici." Aslında, derleyici değil - çalışma zamanı kitaplığıdır.
Adrian McCarthy

Hata ayıklarken, Visual Studio hata ayıklayıcı, 0 veya 1 değilse, true (204) gibi bir şeyle bir bool değerini gösterecektir . Dolayısıyla, kodu izlerseniz bu tür bir hatayı görmek nispeten kolaydır.
Phil1970

4

Bu kullanılan derleyiciye özel mi?

Aslında, neredeyse her zaman çalışma zamanı kitaplığının bir özelliğidir (C çalışma zamanı kitaplığı gibi). Çalışma zamanı genellikle derleyici ile güçlü bir şekilde ilişkilidir, ancak değiştirebileceğiniz bazı kombinasyonlar vardır.

Windows'ta hata ayıklama öbeğinin (HeapAlloc, vb.) Ayrıca malloc'tan gelenlerden farklı özel dolgu desenleri ve hata ayıklama C çalışma zamanı kitaplığındaki ücretsiz uygulamalar kullandığına inanıyorum. Dolayısıyla bir işletim sistemi özelliği de olabilir, ancak çoğu zaman yalnızca dil çalışma zamanı kitaplığıdır.

Malloc / new ve free / delete bu konuda aynı şekilde mi çalışıyor?

New ve delete öğelerinin bellek yönetimi kısmı genellikle malloc ve free ile uygulanır, bu nedenle new ve delete ile ayrılan bellek genellikle aynı özelliklere sahiptir.

Platforma özel mi?

Ayrıntılar çalışma zamanına özeldir. Kullanılan gerçek değerler, genellikle bir onaltılık döküme bakıldığında alışılmadık ve açık görünmek için değil, aynı zamanda işlemcinin özelliklerinden yararlanabilecek belirli özelliklere sahip olacak şekilde tasarlanır. Örneğin, bir hizalama hatasına neden olabildikleri için genellikle tek değerler kullanılır. Başlatılmamış bir sayaca dönerseniz şaşırtıcı gecikmelere neden oldukları için (0'ın aksine) büyük değerler kullanılır. X86'da, 0xCC bir int 3talimattır, bu nedenle başlatılmamış bir bellek çalıştırırsanız, yakalanır.

Linux veya VxWorks gibi diğer işletim sistemlerinde de meydana gelecek mi?

Çoğunlukla kullandığınız çalışma zamanı kitaplığına bağlıdır.

Bu ilklendirmenin ne kadar yararlı olduğuna dair herhangi bir pratik örnek verebilir misiniz?

Bazılarını yukarıda listeledim. Değerler genellikle, geçersiz bellek bölümleriyle bir şey yaparsanız olağandışı bir şeyin olma olasılığını artırmak için seçilir: uzun gecikmeler, tuzaklar, hizalama hataları, vb. Yığın yöneticileri de bazen ayırmalar arasındaki boşluklar için özel doldurma değerleri kullanır. Bu kalıplar değişirse, bir yerlerde kötü bir yazma (tampon taşması gibi) olduğunu bilir.

Belleği ayırırken bilinen bir modele başlatmanın iyi olduğunu ve bazı kalıpların hata ayıklayıcıda gösterilen istisnalara neden olacak şekilde Win32'de kesintileri tetikleyeceğini (belki Kod Tamamlandı 2'de) okuduğumu hatırlıyorum.

Bu ne kadar taşınabilir?

Katı Kod Yazmak (ve belki de Kod Tamamlandı ), dolgu desenlerini seçerken dikkate alınması gereken şeylerden bahseder. Burada bazılarından bahsetmiştim ve Magic Number (programlama) hakkındaki Wikipedia makalesi de bunları özetliyor. Bazı püf noktaları, kullandığınız işlemcinin özelliklerine bağlıdır (örneğin, hizalı okuma ve yazma gerektirip gerektirmediği ve hangi değerlerin yakalanacak talimatlarla eşleştiği gibi). Bellek dökümünde öne çıkan büyük değerler ve olağandışı değerler kullanmak gibi diğer hileler daha taşınabilirdir.



2

"Neden" in açık nedeni, böyle bir sınıfınız olduğunu varsaymanızdır:

class Foo
{
public:
    void SomeFunction()
    {
        cout << _obj->value << endl;
    }

private:
    SomeObject *_obj;
}

Ve sonra birini başlatır Foove çağırırsınız SomeFunction, okumaya çalışırken bir erişim ihlali verir0xCDCDCDCD . Bu, bir şeyi başlatmayı unuttuğunuz anlamına gelir. Bu "neden kısmı". Değilse, işaretçi başka bir bellekle aynı hizaya gelmiş olabilir ve hata ayıklaması daha zor olacaktır. Sadece erişim ihlali almanızın nedenini bilmenizi sağlar. Bu vakanın oldukça basit olduğunu unutmayın, ancak daha büyük bir sınıfta bu hatayı yapmak kolaydır.

AFAIK, bu yalnızca Visual Studio derleyicisinde hata ayıklama modundayken çalışır (sürümün aksine)


Açıklamanız takip etmiyor, çünkü okumaya çalışırken de bir erişim ihlali alacaksınız 0x00000000, bu da aynı derecede yararlı (veya daha çok, kötü bir adres olarak) olacaktır. Bu sayfadaki başka bir yorumda da belirttiğim gibi, bunun 0xCD(ve 0xCC) gerçek nedeni , bunların bir yazılım kesintisini tetikleyen yorumlanabilir x86 işlem kodları olmalarıdır ve bu, yalnızca tek bir spesifik ve nadir hata türünde hata ayıklayıcıya zarif bir kurtarma sağlar. yani, CPU yanlışlıkla kod olmayan bir bölgede bayt çalıştırmaya çalıştığında. Bu işlevsel kullanımın dışında, doldurma değerleri, not ettiğiniz gibi yalnızca tavsiye niteliğindeki ipuçlarıdır.
Glenn Slayden

2

Belleğin başlangıçtaki başlangıç ​​değerinden, genellikle hata ayıklama sırasında, ancak bazen kod için de değiştiğini kolayca görmek için, çünkü çalışırken işleme hata ayıklayıcılar ekleyebilirsiniz.

Bu sadece bellek de değil, birçok hata ayıklayıcı, işlem başladığında yazmaç içeriğini bir sentinel değere ayarlayacaktır (AIX'in bazı sürümleri 0xdeadbeef, biraz komik olan bazı yazmaçları ayarlayacaktır ).


1

IBM XLC derleyicisinin, otomatik değişkenlere sizin belirlediğiniz bir değeri atayan bir "initauto" seçeneği vardır. Hata ayıklama yapılarım için aşağıdakileri kullandım:

-Wc,'initauto(deadbeef,word)'

Başlatılmamış bir değişkenin depolanmasına baksaydım, 0xdeadbeef olarak ayarlanacaktı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.