Что произойдет, если я попытаюсь получить доступ к памяти за пределами области malloc ()? - PullRequest
5 голосов
/ 01 ноября 2009

Я выделил кусок памяти с помощью char* memoryChunk = malloc ( 80* sizeof(char) + 1); Что мешает мне записать в область памяти более 81 единицы? Что я могу сделать, чтобы предотвратить это?

void testStage2(void) {
 char c_str1[20] = "hello";
 char* ut_str1;
 char* ut_str2;

 printf("Starting stage 2 tests\n");
 strcat(c_str1, " world");
 printf("%s\n", c_str1); // nothing exciting, prints "hello world"

 ut_str1 = utstrdup("hello ");
 ut_str1 = utstrrealloc(ut_str1, 20);
 utstrcat(ut_str1, c_str1);
 printf("%s\n", ut_str1); // slightly more exciting, prints "hello hello world"

 utstrcat(ut_str1, " world");
 printf("%s\n", ut_str1); // exciting, should print "hello hello world wo", 'cause there's not enough room for the second world
}

char* utstrcat(char* s, char* suffix){
 int i = strlen(s),j;
 int capacity = *(s - sizeof(unsigned) - sizeof(int));
 for ( j =0; suffix[j] != '\0'; j++){
  if ((i+j-1) == 20)
   return s;
  s[i+j] = suffix[j];
 }
 //strcpy(s, suffix);
 s[i + j] = '\0';
 return s;
}// append the suffix to s

Ответы [ 5 ]

16 голосов
/ 01 ноября 2009

Что мешает мне записать в память более 81 единицы?

Ничего. Тем не менее, это приводит к неопределенному поведению . Это означает, что может произойти все, что угодно , и вы не должны зависеть от того, что он делает одно и то же дважды. 99,999% случаев это ошибка.

Что я могу сделать, чтобы предотвратить это?

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

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

подход stdlib

Для вашего кода у вас может быть utstrncat, который действует как utstrcat, но принимает максимальный размер (то есть размер буфера).

подход stdc ++

Вы также можете создать массив struct / class или использовать std::string в C ++. Например:

typedef struct UtString {
    size_t buffer_size;
    char *buffer;
} UtString;

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

Подход маркера конца буфера

Другой подход состоит в том, чтобы иметь маркер конца буфера , аналогичный маркеру конца строки . Когда вы сталкиваетесь с маркером, не пишите в это место или перед ним (в конце маркера строки) (или вы можете перераспределить буфер, чтобы было больше места).

Например, если у вас есть "hello world\0xxxxxx\1" в качестве строки (где \0 - маркер конца строки, \1 - маркер конца буфера, а x - случайные данные). добавление " this is fun" будет выглядеть следующим образом:

hello world\0xxxxxx\1
hello world \0xxxxx\1
hello world t\0xxxx\1
hello world th\0xxx\1
hello world thi\0xx\1
hello world this\0x\1
hello world this \0\1
*STOP WRITING* (next bytes are end of string then end of buffer)

Ваша проблема

Проблема с вашим кодом здесь:

  if ((i+j-1) == 20)
   return s;

Несмотря на то, что вы останавливаетесь перед переполнением буфера, вы не отмечаете конец строки.

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

Кроме того, я боюсь, что в вашем распределении может быть ошибка. У вас есть + 1, чтобы выделить размер перед строкой, правильно? Есть проблема: unsigned обычно не 1 символ; Вам понадобится + sizeof(unsigned) для этого. Я также написал бы utget_buffer_size и utset_buffer_size, чтобы вам было легче вносить изменения.

3 голосов
/ 01 ноября 2009

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

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

0 голосов
/ 01 ноября 2009

Карл предлагает strncpy () , что является началом в правильном направлении. Основная идея состоит в том, чтобы выработать привычку избегать переполнения буфера, применяя конкретные методы. Более преднамеренная библиотека для этого представлена ​​в strlcpy и strlcat - согласованное, безопасное, копирование строк и объединение .

0 голосов
/ 01 ноября 2009

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

Как уже упоминалось другими авторами, вам просто нужно тщательно программировать. Не используйте вызовы типа strlen, strcpy - используйте ограниченные по длине versoins, такие как strncpy и т. Д.

0 голосов
/ 01 ноября 2009

Что происходит: ничего, или ваша программа получит SIGSEGV. Что вы должны сделать: напишите вашу программу тщательно. Используйте такие инструменты, как valgrind.

...