Проанализируйте следующий фрагмент на C: char *buf = malloc(10); strcpy(buf, "This string is definitely longer than ten"); free(buf); printf("%s", buf); — объясните все возможные UB (undefined behavior), последствия для безопасности и надёжности приложения, и опишите корректные способы работы с динамической памятью в C

8 Окт в 11:36
7 +1
0
Ответы
1

Код
char *buf = malloc(10);
strcpy(buf, "This string is definitely longer than ten");
free(buf);
printf("%s", buf);
содержит несколько независимых неопределённых поведений (UB). Ниже — подробный разбор, последствия для безопасности и надёжности, и корректные варианты работы с динамической памятью.

1) Какие именно UB здесь есть (и почему)

Переполнение буфера (heap buffer overflow):

malloc(10) выделяет 10 байт, а strcpy копирует строку длиной значительно больше 10 (включая нуль-терминатор). Запись выходит за пределы выделенной области — это UB.Возможные последствия: порча соседних данных в куче, повреждение метаданных malloc (chunk headers, бинты и т.д.), порча других объектов программы.

Использование памяти после освобождения (use-after-free):

free(buf); printf("%s", buf); — чтение памяти через указатель, который уже освобождён. Это UB.Возможные последствия: чтение мусора, чтение памяти, отданной уже другому объекту, крах (segfault), эксплуатация.

Непроверенный результат malloc:

malloc может вернуть NULL при недостатке памяти. На NULL передаётся в strcpy — будет попытка записать по NULL (скорее всего crash). Непроверенный NULL — тоже UB в контексте программы.

Дополнительные потенциальные UB/риски (вне прямого кода, но релевантно):

Если перед копированием не обеспечено место для '\0' — строка может быть не закрыта корректно (хотя в данном случае strcpy записывает '\0', но он будет записан за пределами).Освобождённый указатель не обнуляется: последующее повторное free(buf) где-то ещё — double-free (UB).При вычислении размера выделения нужно избегать переполнения арифметики (size_t len + 1).

2) Последствия для безопасности

Переполнение кучи может позволить:
Испортить указатели, структуры данных, функции обратного вызова — привести к отказу в обслуживании (DoS) или выполнению произвольного кода.Повредить метаданные malloc/free, что в старых реализациях можно эксплуатировать для подмены указателей при free (heap exploitation: fastbin dup, unlink и т. п.).Организовать информационный утёк — чтение и вывод содержимого чужих областей памяти.Use-after-free:
Утечка секретов (если освобождённый буфер позже читает другой поток или печатается).Возможность подмена выделенной области другим кодом и затем выполнение/чтение данных, контролируемых атакующим — прямой вектор для RCE.Непроверенный NULL:
Легко получить crash, что снижает устойчивость сервиса.

3) Последствия для надёжности и отладки

Поведение программы станет непредсказуемо: иногда работает, иногда падает через долгое время, что сильно затрудняет отладку.Портируемые баги: в разных сборках/платформах/реализациях malloc последствия могут отличаться.Мелкие повреждения данных приводят к трудноуловимым логическим ошибкам.

4) Корректные способы работы с динамической памятью (практические рекомендации)

Выделяйте ровно столько, сколько нужно: size_t len = strlen(src); buf = malloc(len + 1); проверяйте buf != NULL; memcpy(buf, src, len + 1);
Пример:
size_t len = strlen(src);
char buf = malloc(len + 1);
if (!buf) { perror("malloc"); exit(1); }
memcpy(buf, src, len + 1); / копируем нуль-терминатор /
printf("%s\n", buf);
free(buf);
buf = NULL; / предотвращает use-after-free / double-free */

Используйте strdup/strndup, если доступны:
char buf = strdup(src);
if (!buf) { / обработка ошибки */ }

Если нужно ограничить копирование в фиксированный буфер — используйте безопасные функции, которые учитывают размер:
snprintf(buf, buf_size, "%s", src); / гарантирует нуль-терминатор /
или
strlcpy(buf, src, buf_size); / где доступно /

Проверяйте результат malloc/realloc/calloc:
if (ptr == NULL) { / обработать OOM / }

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

Защита от переполнения счёта при вычислении размера:
size_t n = strlen(src);
if (n >= SIZE_MAX - 1) { / ошибка / }
size_t alloc = n + 1;

При динамическом расширении используйте realloc с проверкой возврата:
char tmp = realloc(buf, new_size);
if (!tmp) { / обработать — старый buf всё ещё валиден */ } else buf = tmp;

Предпочитайте более безопасные абстракции:

В С: библиотеки (glib GString), в C++ — std::string, в новых проектах — Rust/Go для большей безопасности памяти.

5) Инструменты и меры защиты

Во время разработки и тестирования используйте:
AddressSanitizer (-fsanitize=address), UndefinedBehaviorSanitizer.Valgrind, Electric Fence.Компиляторные опции: -fstack-protector-strong, -D_FORTIFY_SOURCE=2, -Wformat, -Wall -Wextra.Статический анализ: clang-tidy, cppcheck.На продакшен-системах используйте современные средства защиты: ASLR, DEP/NX, современные аллокаторы (jemalloc), hardened malloc, и регулярные обновления.

6) Небольшие исправленные примеры

Полное копирование строки (динамически):
const char src = "This string is definitely longer than ten";
size_t len = strlen(src);
char buf = malloc(len + 1);
if (!buf) { perror("malloc"); exit(1); }
memcpy(buf, src, len + 1);
printf("%s\n", buf);
free(buf);
buf = NULL;

Если нужно ограничить размер буфера (без переполнения):
const char src = "...";
size_t bufsize = 10;
char buf = malloc(bufsize);
if (!buf) { / обработка / }
snprintf(buf, bufsize, "%s", src); / строка будет усечена и нуль-терминатор гарантирован /
printf("%s\n", buf);
free(buf);

Итого: в исходном фрагменте как минимум два грубых UB — heap overflow и use-after-free — которые легко приводят к краху и/или уязвимости. Правильный подход — выделять нужный объём (strlen+1 или strdup), проверять возвращаемое значение malloc/realloc, не читать память после free и использовать безопасные/строго ограничивающие функции копирования либо более высокоуровневые абстракции. Используйте санитайзеры и статический анализ для поиска таких ошибок на ранней стадии.

8 Окт в 12:11
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир