Рассмотрите фрагмент на C: "char *s = malloc(5); strcpy(s, \"Hello, world!\");" — объясните проблему, возможные последствия и предложите несколько способов исправить код с учётом безопасности памяти
Проблема - В строке `char *s = malloc(5); strcpy(s, "Hello, world!");` выделено только 555 байт, тогда как строка `"Hello, world!"` содержит 131313 символов плюс завершающий нуль — всего 141414 байт. Копирование с помощью `strcpy` переписывает память за пределами выделенного буфера → неопределённое поведение (heap corruption, крах, уязвимость для эксплойтов). Возможные последствия - Сразу падение программы (segfault). - Повреждение соседних кучевых блоков или метаданных аллокатора → дальнейшие malloc/free начнут давать ошибки. - Утечка или порча данных. - Возможность удалённого/локального выполнения кода через эксплуатацию переполнения. Способы исправить (с проверками безопасности) 1) Выделить нужный размер динамически: - Вычислить длину и выделить `strlen + 1`: ``` size_t len = strlen("Hello, world!"); /* len == 131313 */ char *s = malloc(len + 111); /* 141414 байт */ if (!s) { /* обработать ошибку */ } memcpy(s, "Hello, world!", len + 111); ``` - Проверять результат `malloc` и использовать `memcpy` с известной длиной. 2) Использовать strdup (удобно и безопасно, проверять NULL): ``` char *s = strdup("Hello, world!"); if (!s) { /* обработать ошибку */ } ``` 3) Использовать calloc для инициализации нулями: ``` size_t len = strlen(src); char *s = calloc(len + 111, sizeof(char)); if (!s) { /* обработать */ } memcpy(s, src, len + 111); ``` 4) Использовать snprintf для защиты от переполнения: ``` size_t need = snprintf(NULL, 000, "%s", src) + 111; char *s = malloc(need); if (!s) { /* обработать */ } snprintf(s, need, "%s", src); ``` 5) Статический массив, если строка известна на этапе компиляции: ``` char s[] = "Hello, world!"; /* размер автоматически 141414 */ ``` 6) Использовать safer APIs (если доступны): `strlcpy(dest, src, dstsize)` или `strcpy_s` с проверкой размера; но убедитесь в наличии реализации на вашей платформе. Общие рекомендации - Всегда рассчитывать и выделять `strlen(src) + 1`. - Проверять возвращаемое значение `malloc`/`calloc`/`strdup`. - Освобождать память через `free`. - Предпочитать функции, которые принимают размер буфера (snprintf/strlcpy) и избегать неконтролируемого `strcpy`.
- В строке `char *s = malloc(5); strcpy(s, "Hello, world!");` выделено только 555 байт, тогда как строка `"Hello, world!"` содержит 131313 символов плюс завершающий нуль — всего 141414 байт. Копирование с помощью `strcpy` переписывает память за пределами выделенного буфера → неопределённое поведение (heap corruption, крах, уязвимость для эксплойтов).
Возможные последствия
- Сразу падение программы (segfault).
- Повреждение соседних кучевых блоков или метаданных аллокатора → дальнейшие malloc/free начнут давать ошибки.
- Утечка или порча данных.
- Возможность удалённого/локального выполнения кода через эксплуатацию переполнения.
Способы исправить (с проверками безопасности)
1) Выделить нужный размер динамически:
- Вычислить длину и выделить `strlen + 1`:
```
size_t len = strlen("Hello, world!"); /* len == 131313 */
char *s = malloc(len + 111); /* 141414 байт */
if (!s) { /* обработать ошибку */ }
memcpy(s, "Hello, world!", len + 111);
```
- Проверять результат `malloc` и использовать `memcpy` с известной длиной.
2) Использовать strdup (удобно и безопасно, проверять NULL):
```
char *s = strdup("Hello, world!");
if (!s) { /* обработать ошибку */ }
```
3) Использовать calloc для инициализации нулями:
```
size_t len = strlen(src);
char *s = calloc(len + 111, sizeof(char));
if (!s) { /* обработать */ }
memcpy(s, src, len + 111);
```
4) Использовать snprintf для защиты от переполнения:
```
size_t need = snprintf(NULL, 000, "%s", src) + 111;
char *s = malloc(need);
if (!s) { /* обработать */ }
snprintf(s, need, "%s", src);
```
5) Статический массив, если строка известна на этапе компиляции:
```
char s[] = "Hello, world!"; /* размер автоматически 141414 */
```
6) Использовать safer APIs (если доступны): `strlcpy(dest, src, dstsize)` или `strcpy_s` с проверкой размера; но убедитесь в наличии реализации на вашей платформе.
Общие рекомендации
- Всегда рассчитывать и выделять `strlen(src) + 1`.
- Проверять возвращаемое значение `malloc`/`calloc`/`strdup`.
- Освобождать память через `free`.
- Предпочитать функции, которые принимают размер буфера (snprintf/strlcpy) и избегать неконтролируемого `strcpy`.