Тестирование указателей для законности (C/C++)

Чтобы преобразовать XML в класс C #:

  • Перейдите в Microsoft Visual Studio Marketplace: - https://marketplace.visualstudio.com
  • В строке поиска введите текст: - xml в инструмент кода класса
  • Загрузите, установите и используйте приложение

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

80
задан starblue 15 February 2009 в 18:05
поделиться

19 ответов

Обновление для разъяснения: проблема не с устаревшим, освобожденным или деинициализирует указатели; вместо этого, я реализую API, который берет указатели от вызывающей стороны (как указатель на строку, дескриптор файла, и т.д.). Вызывающая сторона может отправить (в цели или по ошибке) недопустимое значение как указатель. Как я предотвращаю катастрофический отказ?

Вы не можете осуществить ту проверку. Нет просто никакого способа, которым можно проверить, "допустим" ли указатель. Необходимо положить, что, когда люди используют функцию, которая берет указатель, те люди знают то, что они делают. Если они передают Вас 0x4211 как значение указателя, то необходимо положить, что он указывает для обращения к 0x4211. И если бы они "случайно" поражают объект, затем даже если бы Вы использовали бы некоторую страшную функцию операционной системы (IsValidPtr или безотносительно), Вы все еще проскользнули бы в ошибку и не перестали бы работать быстро.

Начинают использовать нулевых указателей для сигнальной такого рода вещи и говорят пользователю Вашей библиотеки, что они не должны использовать указатели, если они имеют тенденцию случайно передавать недопустимые указатели, серьезно :)

71
ответ дан Peter Mortensen 5 November 2019 в 17:33
поделиться

Приложение к принятому ответу (ответам):

Предполагают, что Ваш указатель мог содержать только три значения - 0, 1 и-1, где 1 показывает допустимый указатель,-1 недопустимый и 0 другой недопустимый. Какова вероятность, что Ваш указатель ПУСТОЙ УКАЗАТЕЛЬ, при этом все значения одинаково вероятны? 1/3. Теперь, выньте допустимый случай, таким образом, для каждого недопустимого случая, Вы имеете 50:50 отношение для фиксации всех ошибок. Взгляды хорошее право? Масштабируйте это для 4-байтового указателя. Существуют 2^32 или 4294967294 возможных значения. Из них ТОЛЬКО ОДНО значение правильно, каждый является ПУСТЫМ, и Вас все еще оставляют с 4294967292 другими недопустимыми случаями. Повторно вычислите: у Вас есть тест для 1 из (4294967292 + 1) недопустимые случаи. Вероятность 2.xe-10 или 0 для наиболее практических целей. Такова тщетность ПУСТОЙ проверки.

0
ответ дан dirkgently 5 November 2019 в 17:33
поделиться

Вы знаете, новый драйвер (по крайней мере, на Linux), который способен к этому, вероятно, не было бы это трудно для записи.

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

0
ответ дан dicroce 5 November 2019 в 17:33
поделиться

Нет никакого способа сделать ту регистрацию C++. Что необходимо сделать, если другой код передает Вас недопустимый указатель? необходимо отказать. , Почему? Проверьте эту ссылку: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx

0
ответ дан zildjohn01 5 November 2019 в 17:33
поделиться

Технически можно переопределить оператор , новый удаляют ), и соберите информацию обо всей выделенной памяти, таким образом, у Вас может быть метод, чтобы проверить, допустима ли память "кучи". но:

  1. Вам все еще нужен способ проверить, выделяется ли указатель на стеке ()

  2. , необходимо будет определить то, что является 'допустимым' указателем:

a) память на том адресе выделяется

, b) память в том адресе , запускаются адрес объекта (например, адрес не посреди огромного массива)

, c) память в том адресе , запускаются , адрес объекта [1 114] ожидал Нижняя строка типа

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

0
ответ дан 5 November 2019 в 17:33
поделиться

Я видел, что различные библиотеки используют некоторый метод для проверки на не имеющую ссылки память и такой. Я полагаю, что они просто "переопределяют" методы выделения памяти и освобождения (malloc/free), который имеет некоторую логику, которая отслеживает указатели. Я предполагаю, что это - излишество для Вашего варианта использования, но это был бы один способ сделать это.

0
ответ дан sebnow 5 November 2019 в 17:33
поделиться

IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () для Windows.
Они занимают время пропорциональные длине блока, таким образом, для проверки работоспособности я просто проверяю начальный адрес.

0
ответ дан pngaz 5 November 2019 в 17:33
поделиться

В целом невозможно сделать. Вот один особенно противный случай:

struct Point2d {
    int x;
    int y;
};

struct Point3d {
    int x;
    int y;
    int z;
};

void dump(Point3 *p)
{
    printf("[%d %d %d]\n", p->x, p->y, p->z);
}

Point2d points[2] = { {0, 1}, {2, 3} };
Point3d *p3 = reinterpret_cast<Point3d *>(&points[0]);
dump(p3);

На многих платформах, это распечатает:

[0 1 2]

Вы вынуждаете систему во время выполнения неправильно интерпретировать биты памяти, но в этом случае она не собирается отказывать, потому что биты все имеют смысл. Это - часть дизайна языка (взгляд на полиморфизм C-стиля с struct inaddr, inaddr_in, inaddr_in6), таким образом, Вы не можете надежно защитить от него ни на какой платформе.

1
ответ дан Peter Mortensen 5 November 2019 в 17:33
поделиться

AFAIK там не является никаким путем. Необходимо стараться избегать этой ситуации, всегда устанавливая указатели в NULL после освобождения памяти.

15
ответ дан Ferdinand Beyer 5 November 2019 в 17:33
поделиться

На Win32/64 существует способ сделать это. Попытайтесь считать указатель и поймать получающийся SEH exeception, который будет брошен на отказ. Если это не бросает, то это - допустимый указатель.

проблема с этим методом, хотя то, что это просто возвращается, можно ли считать данные с указателя. Это не делает гарантии о безопасности типов или любом количестве других инвариантов. В целом этот метод еще хорош для мало кроме сказать "да, я могу читать, что конкретное место в памяти за один раз, которая теперь передала".

Короче говоря, не делайте этого ;)

, у Raymond Chen есть сообщение в блоге на этом предмете: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx

28
ответ дан Adam Rosenfield 5 November 2019 в 17:33
поделиться

Предотвращение катастрофического отказа, вызванного вызывающей стороной, отправляющей в недопустимом указателе, является хорошим способом сделать тихие ошибки, которые трудно найти.

Не это лучше для программиста, использующего Ваш API для получения сообщения открытым текстом, что его код является поддельным путем катастрофического отказа его вместо того, чтобы скрыть его?

30
ответ дан Nailer 5 November 2019 в 17:33
поделиться

Относительно ответа немного в этом потоке:

IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () для Windows.

Мой совет состоит в том, чтобы избегать их, кто-то уже отправил этого: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx

Другое сообщение по той же теме и тем же автором (я думаю) является этим: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx ("IsBadXxxPtr должен действительно быть назван CrashProgramRandomly").

, Если пользователи Вашего API отправляет в неправильных данных, позвольте ему отказать. Если проблема состоит в том, что данные передали, не используется, до позже (и это мешает находить причину), добавьте режим отладки, где строки и т.д. зарегистрированы при записи. Если они будут плохи, то это будет очевидно (и вероятно откажет). Если это происходит путь к часто, это могло бы стоить переместить Ваш API из процесса и позволить им разрушить процесс API вместо основного процесса.

7
ответ дан Fredrik 5 November 2019 в 17:33
поделиться

Как другие сказали, Вы не можете надежно обнаружить недопустимый указатель. Рассмотрите некоторые формы, которые мог бы принять недопустимый указатель:

у Вас мог быть нулевой указатель. Это - то, на которое Вы могли легко проверить и сделать что-то о.

у Вас мог быть указатель на где-нибудь за пределами допустимой памяти. То, что составляет допустимую память, варьируется в зависимости от того, как среда выполнения Вашей системы настраивает адресное пространство. В системах Unix это обычно - виртуальное адресное пространство, запускающееся в 0 и идущее в некоторое большое количество мегабайтов. Во встроенных системах это могло быть довольно маленьким. Это не могло бы запуститься в 0 в любом случае. Если Ваше приложение, оказывается, работает в привилегированном режиме или эквиваленте, то Ваш указатель мог бы сослаться на действительный адрес, который может или не может быть сохранен с реальной памятью.

у Вас мог быть указатель на где-нибудь в Вашей допустимой памяти, даже в Вашем сегменте данных, bss, стеке или "куче", но не указывающий на доступный объект. Вариант этого является указателем, который раньше указывал на доступный объект, прежде чем что-то плохо произошло с объектом. Плохие вещи в этом контексте включают освобождение, повреждение памяти или повреждение указателя.

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

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

лучшая вещь, которую можно сделать, позволяют катастрофический отказ и производят некоторую хорошую диагностическую информацию.

1
ответ дан John Grieggs 5 November 2019 в 17:33
поделиться

Нет никаких условий в C++ для тестирования на законность указателя как общий случай. Можно, очевидно, предположить, что ПУСТОЙ УКАЗАТЕЛЬ (0x00000000) является плохими, и различными компиляторами, и библиотекам нравится использовать "специальные значения" тут и там для создания отладки легче (Например, если я когда-нибудь вижу, что указатель обнаруживается как 0xCECECECE в Visual Studio, я знаю, что сделал что-то не так), но истина то, что, так как указатель является просто индексом в память, которую почти невозможно сказать только путем рассмотрения указателя, если это - "правильный" индекс.

существуют различные приемы, что можно сделать с dynamic_cast и RTTI такой, чтобы гарантировать, что объект указал, имеет тип, который Вы хотите, но они все требуют, чтобы Вы указали на что-то допустимое во-первых.

, Если Вы хотите удостовериться, чтобы Вы программировали, может обнаружить "недопустимые" указатели затем, мой совет - это: Установите каждый указатель, Вы объявляете или к ПУСТОМУ УКАЗАТЕЛЮ или к допустимому адресу непосредственно после создания и сразу устанавливаете его в NULL после освобождения памяти, на которую он указывает. Если Вы прилежны об этой практике, то проверка ПУСТОЙ УКАЗАТЕЛЬ - все, в чем Вы когда-либо нуждаетесь.

2
ответ дан Toji 5 November 2019 в 17:33
поделиться

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

2
ответ дан 5 November 2019 в 17:33
поделиться

Это не очень хорошая политика принять произвольные указатели как входные параметры в общедоступном API. Лучше иметь "простые данные" типы как целое число, строка или структура (я имею в виду классическую структуру с простыми данными внутри, конечно; официально что-либо может быть структурой).

, Почему? Хорошо, потому что, поскольку другие говорят, что нет никакого стандартного способа знать, были ли Вы даны допустимая подсказка или та, которая указывает на спам.

, Но иногда у Вас нет выбора - Ваш API должен принять указатель.

В этих случаях, это - обязанность вызывающей стороны передать хороший указатель. ПУСТОЙ УКАЗАТЕЛЬ может быть принят как значение, но не указатель на спам.

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

// API that does not allow NULL
void PublicApiFunction1(Person* in_person)
{
  assert(in_person != NULL);
  assert(in_person->Invariant());

  // Actual code...
}

// API that allows NULL
void PublicApiFunction2(Person* in_person)
{
  assert(in_person == NULL || in_person->Invariant());

  // Actual code (must keep in mind that in_person may be NULL)
}
2
ответ дан Daniel Daranas 5 November 2019 в 17:33
поделиться

Установка в NULL указателя прежде и после использования является хорошей техникой. Это легко сделать в C++ при управлении указателями в классе, например (строка):

class SomeClass
{
public:
    SomeClass();
    ~SomeClass();

    void SetText( const char *text);
    char *GetText() const { return MyText; }
    void Clear();

private:
    char * MyText;
};


SomeClass::SomeClass()
{
    MyText = NULL;
}


SomeClass::~SomeClass()
{
    Clear();
}

void SomeClass::Clear()
{
    if (MyText)
        free( MyText);

    MyText = NULL;
}



void SomeClass::Settext( const char *text)
{
    Clear();

    MyText = malloc( strlen(text));

    if (MyText)
        strcpy( MyText, text);
}
2
ответ дан Tim Ring 5 November 2019 в 17:33
поделиться

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

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

Мы используем много различных методов в зависимости от требований производительности/пропускной способности и защищенности. От самого предпочтительного:

  • отдельные процессы с помощью сокетов (часто передающие данные как текст).

  • отдельные процессы с помощью общей памяти (если большие объемы данных для передачи).

  • тот же процесс разделяют потоки через очередь сообщений (если частые короткие сообщения).

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

  • тот же процесс через прямой вызов процедуры - все переданные данные выделяются от пула памяти.

Мы пытаемся никогда не обратиться к тому, что Вы пытаетесь сделать при контакте с внешним программным обеспечением - особенно, когда нам дают плагины/библиотеку как двоичный а не исходный код.

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

6
ответ дан Dipstick 5 November 2019 в 17:33
поделиться

Смотрите к это и этот вопрос. Также смотрите к интеллектуальные указатели .

7
ответ дан Community 5 November 2019 в 17:33
поделиться