Проблема: - В функции есть два независимых дефекта: локальный массив `buf` размером \(\(8\)\) и копирование в него строки `"This is long"` длиной \(\(12\)\) символов (без\0) — с терминатором выходит \(\(13\)\) байт. Фактически выполняется переполнение буфера на \(\(5\)\) байт, и одновременно возвращается указатель на стековую память (локальный массив), которая после выхода из функции недействительна (dangling pointer). Последствия: - Переполнение буфера приводит к переписыванию соседних областей памяти — возможен крах программы, corrupted return address, перезапись локальных переменных и т.п. - Возврат указателя на локальную память — неопределённое поведение при использовании результата: чтение/запись по этому указателю может привести к аварии или к уязвимости безопасности. - В сумме это потенциально критическая уязвимость: удалённое выполнение кода, эскалация прав или просто немедленный крах. Варианты исправления (коротко, с преимуществами и недостатками): 1) Выделять динамическую память (caller должен освобождать): - Пример: выделить `malloc(strlen(s)+1)` или использовать `strdup`. - Плюс: безопасно и гибко. Минус: ответственность за `free`. 2) Попросить вызывающего предоставить буфер: - Функция принимает `char *buf, size_t bufsize` и пишет ограниченно (`snprintf`, `strlcpy`, `strncpy` с ручной терминацией). - Плюс: нет внутреннего аллока; управление размером у вызывающего. Минус: обязан контролировать размеры. 3) Использовать статическое или статически инициализированное хранилище: - `static char buf[] = "This is long";` - Плюс: просто вернуть безопасно. Минус: не потокобезопасно для записи, одна копия для всех вызовов. 4) Вернуть структуру по значению, содержащую массив нужного размера: - Плюс: безопасно, без динамики. Минус: может быть громоздко при больших буферах. 5) Увеличить буфер и использовать безопасные функции: - Если действительно нужно помещать эту строку в локальный буфер, установить размер не меньше \(\(13\)\) (для данной строки) и использовать `snprintf`/`strlcpy`. - Но даже при увеличении нельзя возвращать адрес локальной памяти — этот пункт решает только переполнение, не проблему возврата. Рекомендуемое исправление (вариант с malloc): - Код: char *good(void) { const char *s = "This is long"; char *buf = malloc(strlen(s) + 1); if (!buf) return NULL; strcpy(buf, s); return buf; } // Вызывать и затем free(). Краткое резюме: - Исправьте оба дефекта: избегайте переполнения (проверка размеров / безопасные API) и не возвращайте указатель на локальную стековую память (используйте динамику, статическое хранилище, буфер от вызывающего или возврат struct).
- В функции есть два независимых дефекта: локальный массив `buf` размером \(\(8\)\) и копирование в него строки `"This is long"` длиной \(\(12\)\) символов (без\0) — с терминатором выходит \(\(13\)\) байт. Фактически выполняется переполнение буфера на \(\(5\)\) байт, и одновременно возвращается указатель на стековую память (локальный массив), которая после выхода из функции недействительна (dangling pointer).
Последствия:
- Переполнение буфера приводит к переписыванию соседних областей памяти — возможен крах программы, corrupted return address, перезапись локальных переменных и т.п.
- Возврат указателя на локальную память — неопределённое поведение при использовании результата: чтение/запись по этому указателю может привести к аварии или к уязвимости безопасности.
- В сумме это потенциально критическая уязвимость: удалённое выполнение кода, эскалация прав или просто немедленный крах.
Варианты исправления (коротко, с преимуществами и недостатками):
1) Выделять динамическую память (caller должен освобождать):
- Пример: выделить `malloc(strlen(s)+1)` или использовать `strdup`.
- Плюс: безопасно и гибко. Минус: ответственность за `free`.
2) Попросить вызывающего предоставить буфер:
- Функция принимает `char *buf, size_t bufsize` и пишет ограниченно (`snprintf`, `strlcpy`, `strncpy` с ручной терминацией).
- Плюс: нет внутреннего аллока; управление размером у вызывающего. Минус: обязан контролировать размеры.
3) Использовать статическое или статически инициализированное хранилище:
- `static char buf[] = "This is long";`
- Плюс: просто вернуть безопасно. Минус: не потокобезопасно для записи, одна копия для всех вызовов.
4) Вернуть структуру по значению, содержащую массив нужного размера:
- Плюс: безопасно, без динамики. Минус: может быть громоздко при больших буферах.
5) Увеличить буфер и использовать безопасные функции:
- Если действительно нужно помещать эту строку в локальный буфер, установить размер не меньше \(\(13\)\) (для данной строки) и использовать `snprintf`/`strlcpy`.
- Но даже при увеличении нельзя возвращать адрес локальной памяти — этот пункт решает только переполнение, не проблему возврата.
Рекомендуемое исправление (вариант с malloc):
- Код:
char *good(void) {
const char *s = "This is long";
char *buf = malloc(strlen(s) + 1);
if (!buf) return NULL;
strcpy(buf, s);
return buf;
}
// Вызывать и затем free().
Краткое резюме:
- Исправьте оба дефекта: избегайте переполнения (проверка размеров / безопасные API) и не возвращайте указатель на локальную стековую память (используйте динамику, статическое хранилище, буфер от вызывающего или возврат struct).