Когда и почему ОС инициализирует память в 0xCD, 0xDD и т. Д. В malloc / free / new / delete? - PullRequest
116 голосов
/ 16 декабря 2008

Я знаю, что ОС иногда инициализирует память с помощью определенных шаблонов, таких как 0xCD и 0xDD. То, что я хочу знать, это , когда и , почему это происходит.

Когда

Это специфично для используемого компилятора?

Работают ли malloc / new и free / delete одинаково в этом отношении?

Это зависит от платформы?

Произойдет ли это в других операционных системах, таких как Linux или VxWorks?

Почему

Насколько я понимаю, это происходит только в конфигурации отладки Win32 и используется для обнаружения переполнения памяти и помогает компилятору перехватывать исключения.

Можете ли вы привести какие-либо практические примеры того, как эта инициализация полезна?

Я помню, как читал что-то (возможно, в Code Complete 2), что при выделении его полезно инициализировать память известным шаблоном, и определенные шаблоны будут вызывать прерывания в Win32, что приведет к исключениям, отображаемым в отладчике.

Насколько это портативно?

Ответы [ 9 ]

177 голосов
/ 16 декабря 2008

Краткий обзор того, что компиляторы Microsoft используют для различных битов неизвестной / неинициализированной памяти при компиляции в режиме отладки (поддержка может варьироваться в зависимости от версии компилятора):

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. 
                         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 detect 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(). 

Отказ от ответственности: таблица основана на некоторых заметках, которые у меня лежат - они могут быть не на 100% правильными (или связными).

Многие из этих значений определены в vc / crt / src / dbgheap.c:

/*
 * 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 reproducable).  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 */

Есть также несколько раз, когда среда отладки заполняет буферы (или части буферов) известным значением, например, «свободное пространство» в выделении std::string или буфер, переданный fread(). В этих случаях используется значение с именем _SECURECRT_FILL_BUFFER_PATTERN (определено в crtdefs.h). Я не уверен точно, когда это было введено, но это было во время выполнения отладки по крайней мере VS 2005 (VC ++ 8).

Первоначально значение, используемое для заполнения этих буферов, было 0xFD - то же самое значение, которое использовалось для ничейной земли. Однако в VS 2008 (VC ++ 9) значение было изменено на 0xFE. Я предполагаю, что это потому, что могут быть ситуации, когда операция заполнения будет выполняться после конца буфера, например, если вызывающая сторона передала размер буфера, который был слишком велик для fread(). В этом случае значение 0xFD может не инициировать обнаружение этого переполнения, так как, если размер буфера будет слишком велик только для одного, значение заполнения будет таким же, как и значение безлюдной земли, используемое для инициализации этой канарейки. Никакие изменения на ничейной земле не означают, что переполнение не будет замечено.

Таким образом, значение заполнения было изменено в VS 2008, так что такой случай изменит ничейную сухопутную канарейку, что приведет к обнаружению проблемы во время выполнения.

Как отмечали другие, одно из ключевых свойств этих значений заключается в том, что переменная-указатель с одним из этих значений разыменовывается, что приведет к нарушению доступа, поскольку в стандартной 32-разрядной конфигурации Windows режим пользователя адреса не будут превышать 0x7fffffff.

33 голосов
/ 16 декабря 2008

Одним из приятных свойств значения заполнения 0xCCCCCCCC является то, что в сборке x86 код операции 0xCC представляет собой код операции int3 , который является программным прерыванием точки останова. Поэтому, если вы когда-нибудь попытаетесь выполнить код в неинициализированной памяти, заполненной этим значением заполнения, вы сразу же достигнете точки останова, и операционная система позволит вам подключить отладчик (или завершить процесс).

7 голосов
/ 16 декабря 2008

Это зависит от компилятора и ОС, Visual Studio устанавливает разные типы памяти для разных значений, так что в отладчике вы можете легко увидеть, переполнены ли вы в недопустимую память, фиксированный массив или неинициализированный объект. Кто-нибудь опубликует подробности, пока я буду гуглить их ...

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

4 голосов
/ 16 декабря 2008

Это не ОС - это компилятор. Вы также можете изменить поведение - см. Внизу этого поста.

Microsoft Visual Studio создает (в режиме отладки) двоичный файл, который предварительно заполняет память стека 0xCC. Он также вставляет пробел между каждым кадром стека, чтобы обнаружить переполнение буфера. Вот очень простой пример того, где это полезно (на практике Visual Studio обнаружит эту проблему и выдаст предупреждение):

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

Если Visual Studio не инициализирует переменные до известного значения, то эту ошибку потенциально сложно найти. С предварительно инициализированными переменными (или, скорее, с предварительно инициализированной стековой памятью) проблема воспроизводима при каждом запуске.

Однако есть небольшая проблема. Значение, которое использует Visual Studio, равно TRUE - все, кроме 0, будет. На самом деле вполне вероятно, что когда вы запускаете свой код в режиме Release, эти унифицированные переменные могут быть выделены для части стековой памяти, которая содержит 0, что означает, что вы можете иметь ошибку с унифицированными переменными, которая проявляется только в режиме Release.

Это меня раздражало, поэтому я написал скрипт для изменения значения предварительного заполнения путем непосредственного редактирования двоичного файла, что позволило мне найти неинициализированные проблемы с переменными, которые появляются, только когда стек содержит ноль. Этот сценарий изменяет только предварительное заполнение стека; Я никогда не экспериментировал с предварительным заполнением кучи, хотя это должно быть возможно. Может потребоваться редактирование исполняемой библиотеки DLL, возможно, нет.

3 голосов
/ 19 ноября 2016

Это специфично для используемого компилятора?

На самом деле, это почти всегда функция библиотеки времени выполнения (например, библиотеки времени выполнения C). Время выполнения обычно сильно коррелирует с компилятором, но есть некоторые комбинации, которые вы можете поменять.

Я полагаю, что в Windows, в куче отладки (HeapAlloc и т. Д.) Также используются специальные шаблоны заполнения, отличающиеся от тех, которые взяты из malloc, и бесплатных реализаций в библиотеке времени выполнения отладки C. Так что это может быть и функция ОС, но в большинстве случаев это просто библиотека времени выполнения языка.

Работают ли malloc / new и free / delete одинаково в этом отношении?

Часть управления памятью new и delete обычно реализуется с помощью malloc и free, поэтому память, выделенная с помощью new и delete , обычно , имеет те же функции.

Это зависит от платформы?

Подробности зависят от времени выполнения. Используемые фактические значения часто выбираются таким образом, чтобы они не только выглядели необычно и очевидно при рассмотрении шестнадцатеричного дампа, но и имели определенные свойства, которые могут использовать преимущества процессора. Например, часто используются нечетные значения, потому что они могут вызвать ошибку выравнивания. Используются большие значения (в отличие от 0), потому что они вызывают неожиданные задержки, если вы зацикливаетесь на неинициализированном счетчике. На x86 0xCC - это инструкция int 3, поэтому, если вы выполните неинициализированную память, она будет перехвачена.

Произойдет ли это в других операционных системах, таких как Linux или VxWorks?

Это в основном зависит от используемой вами библиотеки времени выполнения.

Можете ли вы привести какие-либо практические примеры того, как эта инициализация полезна?

Я перечислил несколько выше. Значения обычно выбираются, чтобы увеличить вероятность того, что что-то необычное случится, если вы делаете что-то с недопустимыми частями памяти: длинными задержками, перехватами, ошибками выравнивания и т. Д. Менеджеры кучи также иногда используют специальные значения заполнения для промежутков между распределениями. Если эти шаблоны когда-либо изменяются, он знает, что где-то произошла плохая запись (например, переполнение буфера).

Я помню, что читал что-то (возможно, в Code Complete 2), что при выделении его полезно инициализировать память известным шаблоном, и определенные шаблоны будут вызывать прерывания в Win32, что приведет к исключениям, отображаемым в отладчике.

Насколько это портативно?

Написание твердого кода (и, возможно, Code Complete ) рассказывает о вещах, которые следует учитывать при выборе шаблонов заливки. Я упомянул некоторые из них здесь, и статья Википедии о Магическое число (программирование) также суммирует их. Некоторые приемы зависят от специфики используемого вами процессора (например, требует ли он выровненных операций чтения и записи и какие значения отображаются в инструкции, которые будут перехватываться). Другие приемы, такие как использование больших значений и необычных значений, выделяющихся в дампе памяти, более переносимы.

2 голосов
/ 13 мая 2010

В этой статье описываются необычные битовые комбинации памяти и различные методы, которые можно использовать, если вы встретите эти значения.

2 голосов
/ 16 декабря 2008

Легко видеть, что память изменилась по сравнению с начальным значением, как правило, во время отладки, но иногда и для кода выпуска, поскольку вы можете присоединять отладчики к процессу во время его работы.

Это не просто память, многие отладчики устанавливают содержимое регистра в значение часового значения при запуске процесса (некоторые версии AIX устанавливают для некоторых регистров значение 0xdeadbeef, что немного смешно).

2 голосов
/ 16 декабря 2008

Очевидная причина «почему» заключается в том, что у вас есть такой класс:

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

private:
    SomeObject *_obj;
}

И затем вы создаете экземпляр a Foo и вызываете SomeFunction, это даст нарушение прав доступа при попытке прочитать 0xCDCDCDCD. Это означает, что вы забыли что-то инициализировать. Это «почему часть». Если нет, то указатель мог бы быть связан с какой-то другой памятью, и отладку было бы сложнее. Он просто сообщает вам причину нарушения прав доступа. Обратите внимание, что этот случай был довольно простым, но в более крупном классе эту ошибку легко совершить.

AFAIK, это работает только на компиляторе Visual Studio в режиме отладки (в отличие от выпуска)

1 голос
/ 19 декабря 2008

Компилятор IBM XLC имеет опцию «initauto», которая присваивает автоматическим переменным указанное вами значение. Я использовал следующее для моих отладочных сборок:

-Wc,'initauto(deadbeef,word)'

Если бы я посмотрел на хранилище неинициализированной переменной, он был бы установлен в 0xdeadbeef

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...