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

29 Окт в 09:26
4 +1
0
Ответы
1
Короткий разбор и рекомендации.
Что делает код (упрощённо)
char *s = malloc(10);
strcpy(s, "This string is definitely longer than ten bytes");
free(s);
printf("%s", s);
Основные проявления неопределённого поведения и уязвимости
- Буферный переполнение при копировании: строка "This string..." имеет длину 474747 символов (без учёта нуль-терминатора), с нуль-терминатором — 484848 байт. Выделено только 101010 байт, поэтому происходит переполнение на 48−10=3848 - 10 = 384810=38 байт при вызове strcpy — это неопределённое поведение и классическая уязвимость (heap overflow). Последствия: крах программы, повреждение кучи, возможная эксплуатация (подмена данных, управление потоком выполнения, раскрытие памяти).
- Use-after-free: после free(s) вызов printf("%s", s) использует освобождённую память — это UB. Результат непредсказуем: чтение мусора, крах, утечка данных, возможность корректно намеренно перезаписать блок и затем получить контролируемое содержимое.
- Отсутствие проверки результата malloc: если malloc вернёт NULL, strcpy(NULL, ...) приведёт к падению программы.
- (Опционально) Отсутствие заголовочных файлов/неправильные объявления функций в старых стандартах C могут привести к ещё одному UB (но современные компиляторы это обычно ловят).
Почему это опасно с точки зрения безопасности
- Heap overflow может коррумпировать метаданные аллокатора (в старых реализациях это вело к «unlink»‑эксплойтам), или соседние буферы — путь к RCE.
- Use-after-free позволяет атакующему перехватить контроль, если он может выделить управляемый блок в освобождённом месте.
- Непроверенный возврат malloc — простой способ вызвать DoS.
Как это исправить в C (рекомендуемые варианты)
1) Выделять нужный объём и проверять malloc:
const char *src = "This string is definitely longer than ten bytes";
size_t len = strlen(src); // len == 47
char *s = malloc(len + 1); // выделяем strlen + 1
if (s == NULL) { /* обработать ошибку */ }
memcpy(s, src, len + 1); // копируем с нуль-терминатором
printf("%s\n", s);
free(s);
s = NULL; // избегаем use-after-free
(в тексте: используйте выражение strlen(src)+1strlen(src) + 1strlen(src)+1 при расчёте размера)
2) Использовать безопасные вспомогательные функции:
- strdup/strndup: char *s = strdup(src); проверять NULL.
- asprintf (гибко формирует строку и выделяет память): if (asprintf(&s, "%s", src) == -1) {...}
3) Если копирование ограничено известной длиной, использовать безопасные функции и явную терминацию:
size_t bufsize = 10;
char *s = malloc(bufsize);
if (s) {
strncpy(s, src, bufsize - 1);
s[bufsize - 1] = '\0';
/* но strncpy не идеален — лучше заранее вычислить нужный размер */
}
4) Никогда не использовать указатель после free: печатать до free или обнулять указатель после free: s = NULL.
Практические меры защиты при разработке на C
- Включить проверки и отладочные средства: compile with -Wall -Wextra и использовать AddressSanitizer (-fsanitize=address), Valgrind, UBSan.
- Включить системные механизмы: ASLR, NX, PIE, glibc hardening, fortify source.
- По возможности использовать проверяемые интерфейсы и динамические строки (asprintf/strdup) вместо сырого strcpy.
Варианты на более безопасных языках
- Rust (без UB при корректном коде):
let s = String::from("This string is definitely longer than ten bytes");
println!("{}", s);
- Go:
s := "This string is definitely longer than ten bytes"
fmt.Println(s)
- C++ (std::string):
std::string s = "This string is definitely longer than ten bytes";
std::cout << s << '\n';
Во всех этих языках строки управляются автоматически, размеры проверяются внутри библиотек, и невозможны классические heap overflow/USE-AFTER-FREE при корректном использовании.
Инструменты для поиска уязвимостей
- AddressSanitizer, Valgrind, clang-tidy, static analyzers (Coverity, clang static analyzer), fuzzing (libFuzzer).
Краткий вывод
Наличие переполнения буфера (копирование 484848 байт в буфер 101010 байт) и использование указателя после free — оба приводят к неопределённому поведению и серьёзным уязвимостям. Исправление: выделять корректный размер (strlen(src)+1strlen(src) + 1strlen(src)+1), проверять malloc, не использовать указатель после free (или обнулять), либо переходить на безопасные абстракции (strdup/asprintf в C или строки в Rust/Go/C++).
29 Окт в 10:25
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир