Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #71, ОКТЯБРЬ 2006 г.

утечка мозга

КРИС КАСПЕРСКИ АКА МЫЩЪХ

Спецвыпуск: Хакер, номер #071, стр. 071-020-2


Простейшая тестовая программа все это наглядно подтверждает:

программа, демонстрирующая невозможность определения размера блока по указателю

// функция, принимающая указатель

// и пытающаяся определить размер соответствующего ему блока

foo(char *p){printf("sizeof says = %Xh\n_msize says = %Xh\n",sizeof(p),_msize(p));}

main()

{

char buf[0x666]; char *p = malloc(0x999);

// передаем указатель на начало блока

foo(buf);foo(p);

// передаем указатель на середину блока

foo(&buf[6]);foo(p+9);

}

После запуска мы получим следующие весьма неутешительные данные:

результат работы программы, определяющий размер блока по указателю

// указатель на начало стекового буфера

sizeof says = 4h, _msize says = 12E8CEh // sizeof и _msize провалились

// указатель на начало динамического буфера

sizeof says = 4h, _msize says = 9A0h // sizeof провалилась, _msize - почти ОК

// указатель на середину стекового буфера

sizeof says = 4h, _msize says = FFFFFFFFh // sizeof и _msize провалились

// указатель на середину динамического буфера

sizeof says = 4h, _msize says = D200h // sizeof и _msize провалились

Мы видим, что _msize ведет себя очень странно и когда не может определить размер блока, возвращает какой-то мусор, никак не сигнализируя об ошибке. Поэтому выполнять контроль должна вызывающая функция, передавая вызываемой размер буфера как аргумент. Отсюда и появились char *fgets(char *string, int n, FILE *stream); char *strncpy(char *strDest, const char *strSource, size_t count) и другие подобные функции. Теоретически, они на 100% застрахованы от переполнения, но вот практически... значение n приходится рассчитывать вручную, а, значит, существует риск ошибиться! К тому же, если длина строки превышает n, в буфер копируется лишь «огрызок», что само по себе является нехилым источником проблем и вторичных ошибок. Приходится навешивать специальный обработчик, выделяющий дополнительную память и считывающий оставшийся «хвост», что значительно усложняет реализацию, делает код более громоздким и менее наглядным. Обычно стараются выбрать n так, чтобы его значение превышало размер наибольшей строки, выделяя память с запасом и не обращая внимания на то, что большинство строк использует лишь малую часть буфера...

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

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

struct slist

{

unsigned char c;

struct slist *prev;

Назад на стр. 071-020-1  Содержание  Вперед на стр. 071-020-3