Управление значением заполнения (например, 0xCC) для неинициализированных переменных стека? - PullRequest
2 голосов
/ 21 декабря 2011

Во-первых, немного предыстории: у нас есть фреймворк, который будет проходить серию тестов несколько раз и гарантировать, что состояние будет одинаковым при каждом запуске.Это ловит ряд случаев, которые приводят к недетерминированному поведению, включая случаи, вызванные многопоточностью или сортировкой по значениям указателя.Эти тесты выполняются в Debug в Visual Studio 2008 (скоро будет в версии 2010).

Проблема: к сожалению, тесты не отлавливают использование неинициализированных переменных так часто, как хотелось бы.Рассмотрим эти случаи:

struct Foo{ int m_a; int m_b; };
void doStuff( struct Foo& f);
...
Foo* bar = new Foo();
// Uninitialized in ctor, but heap initialized to 0xCD,
// so appears "deterministic"
if (bar->m_a) 
{ ... }

Foo baz;
// may or may not initialize all of baz
// uninitialized members are left to 0xCC
doStuff( baz );
if (baz.m_b)
{ ... }

Что бы я хотел сделать, это сделать неинициализированные значения разными для каждого прогона, чтобы отловить эти случаи, например, известный мусор при первом запуске и 0 во втором.Таким образом, любые вычисления для неинициализированных членов будут давать разные результаты, и проверка их в операторе if также будет иметь противоположную ветвь.

У меня есть контроль над первым случаем, потому что мы направляем new и delete через нашу собственную кучу.Однако я не смог найти никакой информации о том, как управлять значением заполнения для переменных стека. Возможно ли это вообще?Самый близкий вопрос, который я мог найти здесь, был Может ли g ++ заполнить неинициализированные переменные POD известными значениями? , но это для g ++.Мне не нужно портативное решение;трюк для Visual Studio подойдет.

Примечание # 1: я знаю, что lint / Rational Purify / Valgrind / [вставьте волшебную пулю статического анализа кода] поймает это, и, вероятно, более надежно.Но я ищу небольшое изменение, которое я могу внести в нашу существующую платформу, и для их интеграции, вероятно, потребуется больше времени, чем я готов потратить, поэтому, пожалуйста, не предлагайте это.

Примечание #2: у нас уже установлен уровень предупреждения max и предупреждения как ошибки включены, что отлавливает некоторые случаи неинициализированных переменных, но это не охватывает все случаи, когда функция doStuff забывает инициализировать некоторые элементы структуры.

Примечание # 3: меня не слишком беспокоит производительность, так как она уже запущена в Debug и используется только для внутреннего тестирования.

Примечание # 4: тот же исполняемый файл используется длятесты (запускаются один раз в режиме «записи», затем снова в режиме «проверки» для сравнения результатов), поэтому, к сожалению, другие параметры компиляции на данный момент недоступны.

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 21 декабря 2011

Я считаю, что компилятор может быть очень полезен для отслеживания таких проблем, используя что-то вроде:

class checked_int_t {
    int i_;
public:
    checked_int_t(int const i) : i_ (i) { }
    operator int() const { return i_; }
};

Теперь ваш код может измениться на что-то вроде следующего, что приведет к ошибке времени компиляции, когда выВыделите объект Foo, потому что m_a не инициализируется в конструкторе по умолчанию.

struct Foo{
    checked_int_t m_a;
    int m_b;
};

Обратите внимание, что вы можете делать это постепенно, чтобы проверять наличие унитарных переменных, поэтому вам не нужно вносить большие изменения.Просто найдите переменную, которая, по вашему мнению, не инициализируется должным образом, и измените ее тип.Если вам абсолютно необходимо установить значение позже, вы могли бы сделать что-то вроде:

class checked_int_t {
    int i_;
    bool set_;
public:
    checked_int_t() : set_ (false) { }
    checked_int_t(int const i) : i_ (i), set_ (true) { }
    operator int() const { assert(set_); return i_; }
};

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

#ifdef NDEBUG
typedef int checked_int_t;
#else
class checked_int_t {
    int i_;
public:
    checked_int_t(int const i) : i_ (i) { }
    operator int() const { return i_; }
};
#endif
1 голос
/ 21 декабря 2011

Вы можете попытаться выяснить, какой код выполняет наполнение, и исправить его во время выполнения. Если dllversion исправлена, это должно быть приемлемо для отладочной сборки. Вы, вероятно, должны иметь возможность обрабатывать различные адреса загрузки DLL, так как они могут быть загружены в произвольных местах по соображениям безопасности.

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