Ловля странных ошибок адресной арифметики с указателями C

Я недавно столкнулся с очень подлой ошибкой, в которой я забываю разыменовывать указатель на строку (массив символов) и таким образом иногда перезаписывал один байт на стеке.

Плохо:

char ** str;
(*str) = malloc(10);
...
str[2] = 'a'; //overwrites 3 bytes from the location in which str is stored

Исправленный:

char ** str;
(*str) = malloc(10);
...
(*str)[2] = 'a'; 

GCC не произвел предупреждений, и эта ошибка приведет к очень серьезному и реальному использованию как значение, которое это иногда перезаписывало, содержал размер буфера. Я только поймал эту ошибку, потому что я получил luckly, и это вызвало очевидный отказ.

  • Кроме доверия удаче и/или никогда использования C для чего-нибудь, какие защитные методы кодирования и приемы Вы используете для ловли странных ошибок C?

  • Я думаю о перемещении в MemCheck valgrind, кто-либо использовал его? Я подозреваю, что это не поймало бы эту ошибку. Кто-либо знает?

  • Есть ли инструменты для ловли разыменования указателя или арифметических ошибок? Это даже возможно?

ОБНОВЛЕНИЕ

Вот требуемый пример кода, он не бросает предупреждений.

#include 

void test(unsigned char** byteArray){
    (*byteArray) = (unsigned char*)malloc(5);
    byteArray[4] = 0x0;
}

int main(void){
    unsigned char* str;
    test(&str);  
    return 0;
}

Компиляция не вызывает ошибок:

gcc -Wall testBug.c -o testBug

Выполнение вызывает отказ seg:

./testBug
Segmentation fault

Это - версия GCC, который я использую:

gcc -v

Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.1-4ubuntu9' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9) 

7
задан Ethan Heilman 2 February 2010 в 19:08
поделиться

5 ответов

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

char **outStr;
*outStr = malloc(10);
char *str = *outStr;
str[2] = 10;

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

3
ответ дан 7 December 2019 в 10:01
поделиться

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

После просмотра OpenID появляется что-то называемое «немедленным запросом» ( http://openid.net/specs/openid-authentication-2_0.html#anchor28 ).

При запросе аутентификации Доверяющая сторона МОЖЕТ запросить, чтобы ОП не взаимодействовала с конечным пользователем. В этом случае ОП ДОЛЖЕН немедленно ответить либо утверждением, что аутентификация успешна, либо ответом, указывающим, что запрос не может быть выполнен без дальнейшего взаимодействия с пользователем.

Из-за этого я думаю, что я мог бы просто сохранить URL-адрес openID пользователя в файле cookie и использовать немедленный запрос, чтобы увидеть, аутентифицирован ли пользователь или нет. Таким образом, мне не нужно ничего делать с моей базой данных, или реализовать любую логику для предотвращения захвата сеанса долгоживущего файла cookie.

Этот метод выполнения, по-видимому, является способом, который OpenID предлагает сделать с помощью документа Best Practices .

-121--2308818-

В целом сайт становится проще переваривать для остальной части Интернета. Данные можно использовать повторно, объединять, создавать перекрестные ссылки и сохранять.

Простым примером было бы иметь в любом месте на сайт широту и долготу (гео). С помощью микроформатов любой, кто ищет эту широту и долготу, может быть легко связан со своим веб-сайтом, увеличивая трафик, узнавая об этом человеке/компании и позволяя пользователям легко сохранять эту информацию. (Хотя я сталкивался с этим мало лично, это скорее «будущее» вещей, чем настоящее. Но всегда хорошо быть в курсе событий).

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

-121--4903969-

Я использую Valgrind, это спасатель жизни!

valgrind --tool=memcheck -v ./yourapp

И MemCheck обнаружит недопустимую запись с «str [2] =» a «;».

1
ответ дан 7 December 2019 в 10:01
поделиться

Нет встроенного класса, подобного HashSet < T > , за исключением этого одиночного поведения.

Если вам это нужно, я бы порекомендовал прокрутить свой собственный. Однако подкласс HashSet < T > не рекомендуется. Ни один из методов (например, Add , который явно требуется изменить) не является виртуальным, поскольку он не был разработан с учетом подкласса. Это приведет к странному использованию, так как вы скрываете унаследованные методы.

Просто инкапсулируйте HashSet < T > и откройте необходимые членов. Единственный реальный «код», который нужно добавить, это единственная проверка NULL для метода Add - в противном случае просто передайте все методы инкапсулированному экземпляру.

Если вы хотите, чтобы это был общий класс, вам нужно будет добавить дополнительное ограничение только для работы с классами, так как вы хотите иметь проверку null:

public class ValueSet<T> : ICollection<T>, IEnumerable<T>, ICollection
    where T : class
{
     private HashSet<T> hashSet = new HashSet<T>();
     // ... Implement all members as needed...
-121--2396249-

Я думаю о семействе продуктов, которые включают Ms Access, Excel, Clarion для DOS и т.д. Где можно создавать приложения с 0 исходным кодом и без программистов. Не то чтобы они способны выполнять операции AI качества , но они могут делать очень удобные приложения.

-121--4180350-

GCC должен дать вам

 warning: assignment makes pointer from integer without a cast

Нет?

1
ответ дан 7 December 2019 в 10:01
поделиться

Пожалуйста, используйте VALGRIND. Это одна из лучших инструментов проверки памяти, которые я наткнулся. Это, безусловно, обнаружит вашу ошибку.

Помимо обнаружения ошибок памяти, VALGRIND также помогает обнаруживать утечки памяти, в использовании блоков памяти и т. Д.

Даже IBM Rational Purify поможет вам при обнаружении таких ошибок. Хотя мой личный любимый валгринд.

1
ответ дан 7 December 2019 в 10:01
поделиться

Мое предложение - не инструмент, а лучшая практика: тестирование. Такие ошибки обычно очень легко найти при тщательном тестировании кода, начиная с самого нижнего уровня модульного тестирования.

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

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

0
ответ дан 7 December 2019 в 10:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: