Проанализируйте следующий фрагмент на C: char *s = malloc(10); strcpy(s, "This is a very long string"); free(s); — найдите все ошибки, объясните возможные последствия во время выполнения (UB, переполнение буфера), и предложите безопасную альтернативу
Ошибки в фрагменте - Недостаточно памяти: выделено malloc(101010) байт, а копируется строка длиной 262626 символов плюс терминатор NUL — требуется 272727 байт. Переполнение: 27−10=1727 - 10 = 1727−10=17 байт. - Не проверяется результат malloc (возможен NULL). - Использование небезопасной функции strcpy — она не ограничивает копирование и приведёт к переполнению. - После переполнения последующий free(s) может работать с повреждённой службой аллокации — UB (undefined behavior). Возможные последствия при выполнении - Переполнение буфера (heap overflow) — повреждение соседних областей памяти, метаданных аллокатора. - Краш программы при malloc/free/other heap ops. - Трудноотлавливаемые баги: неожиданные изменения данных, логические ошибки. - Уязвимость безопасности: исполнение произвольного кода, эксплуатация через перезапись указателей/метаданных. Конкретные числа (для наглядности) - Длина литерала "This is a very long string" = 262626. - Нужно байт = 26+1=2726 + 1 = 2726+1=27. - Выделено = 101010. - Переполнение = 27−10=1727 - 10 = 1727−10=17. Безопасные альтернативы (короткие примеры) 1) Если нужна только константная строка (нет модификации) — не копировать: char const *s = "This is a very long string"; 2) Если нужен динамический копируемый буфер — вычислять размер и проверять malloc: const char *src = "This is a very long string"; size_t need = strlen(src) + 111; /* need = 27 */ char *s = malloc(need); if (s == NULL) { /* обработка ошибки */ } memcpy(s, src, need); /* или strcpy если уверены в размере */ 3) Использовать strdup (POSIX) — проще: char *s = strdup("This is a very long string"); if (s == NULL) { /* обработка */ } 4) Использовать безопасные ограниченные функции: const char *src = "This is a very long string"; size_t need = strlen(src) + 111; char *s = malloc(need); if (s) { /* гарантированно не выйдем за пределы буфера */ snprintf(s, need, "%s", src); } Дополнительно - Предпочтительнее вычислять размер через strlen + 111 и проверять malloc. - Избегайте strcpy/strcat без контроля размеров; используйте strncpy/strlcpy/snprintf/strdup в зависимости от платформы и целей. - Всегда обрабатывать ошибку выделения памяти и избегать доверия к неограниченному копированию.
- Недостаточно памяти: выделено malloc(101010) байт, а копируется строка длиной 262626 символов плюс терминатор NUL — требуется 272727 байт. Переполнение: 27−10=1727 - 10 = 1727−10=17 байт.
- Не проверяется результат malloc (возможен NULL).
- Использование небезопасной функции strcpy — она не ограничивает копирование и приведёт к переполнению.
- После переполнения последующий free(s) может работать с повреждённой службой аллокации — UB (undefined behavior).
Возможные последствия при выполнении
- Переполнение буфера (heap overflow) — повреждение соседних областей памяти, метаданных аллокатора.
- Краш программы при malloc/free/other heap ops.
- Трудноотлавливаемые баги: неожиданные изменения данных, логические ошибки.
- Уязвимость безопасности: исполнение произвольного кода, эксплуатация через перезапись указателей/метаданных.
Конкретные числа (для наглядности)
- Длина литерала "This is a very long string" = 262626.
- Нужно байт = 26+1=2726 + 1 = 2726+1=27.
- Выделено = 101010.
- Переполнение = 27−10=1727 - 10 = 1727−10=17.
Безопасные альтернативы (короткие примеры)
1) Если нужна только константная строка (нет модификации) — не копировать:
char const *s = "This is a very long string";
2) Если нужен динамический копируемый буфер — вычислять размер и проверять malloc:
const char *src = "This is a very long string";
size_t need = strlen(src) + 111; /* need = 27 */
char *s = malloc(need);
if (s == NULL) { /* обработка ошибки */ }
memcpy(s, src, need); /* или strcpy если уверены в размере */
3) Использовать strdup (POSIX) — проще:
char *s = strdup("This is a very long string");
if (s == NULL) { /* обработка */ }
4) Использовать безопасные ограниченные функции:
const char *src = "This is a very long string";
size_t need = strlen(src) + 111;
char *s = malloc(need);
if (s) {
/* гарантированно не выйдем за пределы буфера */
snprintf(s, need, "%s", src);
}
Дополнительно
- Предпочтительнее вычислять размер через strlen + 111 и проверять malloc.
- Избегайте strcpy/strcat без контроля размеров; используйте strncpy/strlcpy/snprintf/strdup в зависимости от платформы и целей.
- Всегда обрабатывать ошибку выделения памяти и избегать доверия к неограниченному копированию.