В приведённом фрагменте на C найдите все ошибки управления памятью и уязвимости, объясните последствия (включая возможное поведение на разных компиляторах/платформах) и предложите безопасную версию кода:
char *foo() {
char buf[10]

12 Окт в 14:14
2 +1
0
Ответы
1

Фрагмент, который вы привели, неполный, но очевидная проблема уже видна:

char *foo {
char buf101010

Если предположить, что функция затем что‑то записывает в buf и возвращает его адрес например,returnbuf;например, return buf;например,returnbuf;, то это типичная ошибка — возврат указателя на локальный автоматический буфер. Разберу все возможные ошибки/уязвимости, последствия и дам безопасные варианты.

1) Ошибки и уязвимости

Возврат адреса локального массива charbuf[10]char buf[10]charbuf[10] — UB undefinedbehaviorundefined behaviorundefinedbehavior. После выхода из foo память buf находится в стеке вызывающей функции и дальнейшее обращение через возвращённый указатель приведёт к неопределённому поведению.Возможный переполнение буфера. Если функция записывает >=10 байт включаянуль−терминаторвключая нуль-терминаторвключаянультерминатор, произойдёт запись за границы buf — это повреждение стека, которое может приводить к краху, к перезаписи локальных данных, к перезаписи указателя возврата и т.д.Неинициализированные данные. Если возвращённый буфер не был заполнен полностью, там может быть мусор — утечка данных информационноераскрытиеинформационное раскрытиеинформационноераскрытие.Если вместо возврата указателя код использует buf после выхода — use-after-return, UB.Если buf делается static, это устраняет UB с возвратом, но вводит другие проблемы: не потокобезопасно sharedstateshared statesharedstate, повторные вызовы перезаписывают один буфер.Если вместо автоматического буфера используется malloc, но вызывающий не освобождает — утечка памяти.

2) Последствия и поведение на разных компиляторах/платформах

UB означает, что компилятор не обязан выдавать предупреждение, и поведение может быть любым: «кажется работает», данные прежние, крах segfaultsegfaultsegfault, случайные искажения данных.На некоторых системах стек не сразу перезаписывается, поэтому возвращённый указатель может «работать» некоторое время — это создаёт ложное чувство безопасности.На платформах с SSP/Canary stackprotectorstack protectorstackprotector переполнение буфера может быть обнаружено и программа завершится с сообщением об обнаружении нарушения стека.ASLR/DEP/NX и другие защиты не делают UB безопасным; они меняют лишь вероятные векторы и последствия эксплуатации.Оптимизирующий компилятор может проявлять неожиданные эффекты: он может переиспользовать пространство стека, переместить значения в регистры и т.д. Всё это усиливает непредсказуемость.

3) Как исправить — варианты безопасного кода

Вариант A — выделять память в куче и возвращать её callerдолженосвобождатьcaller должен освобождатьcallerдолженосвобождать:
char foovoidvoidvoid {
const char s = "Hello";
size_t n = strlensss + 1;
char p = mallocnnn;
if !p!p!p return NULL;
memcpyp,s,np, s, np,s,n;
return p;
}
/ Использование:
char r = foo;
if rrr { putsrrr; freerrr; }
/

Плюсы: простая семантика, безопасно еслиправильновыделитьиосвободитьесли правильно выделить и освободитьеслиправильновыделитьиосвободить. Минусы: ответственность за free на вызывающем.

Вариант B — функцию запрашивают заполнить буфер, предоставленный вызывающим рекомендованныйстильAPIрекомендованный стиль APIрекомендованныйстильAPI:
int foo_fillchar<em>out,sizetoutlenchar <em>out, size_t out_lenchar<em>out,sizet outl en {
const char s = "Hello";
if !out∣∣outlen==0!out || out_len == 0!out∣∣outl en==0 return -1;
size_t need = strlensss + 1;
if (need > out_len) return -1; // или копировать усечённо
memcpyout,s,needout, s, needout,s,need;
return 0;
}
/ Использование:
char buf646464;
if foofill(buf,sizeofbuf)==0foo_fill(buf, sizeof buf) == 0foof ill(buf,sizeofbuf)==0 putsbufbufbuf;
/

Плюсы: нет утечек, вызывающий контролирует буфер и его размер.

Вариант C — вернуть структуру, содержащую массив копированиевыполняетсяповозвратукопирование выполняется по возвратукопированиевыполняетсяповозврату:
typedef struct { char buf101010; } Buf;
Buf foovoidvoidvoid {
Buf b = {0};
strncpy(b.buf, "Hello", sizeof b.buf - 1);
return b;
}
/ Использование:
Buf b = foo;
putsb.bufb.bufb.buf;
/

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

Вариант D — статический буфер еслидействительнонужно,носоговоркамиесли действительно нужно, но с оговоркамиеслидействительнонужно,носоговорками:
const char foovoidvoidvoid {
static char buf101010;
strncpy(buf, "Hello", sizeof buf - 1);
bufsizeofbuf−1sizeof buf - 1sizeofbuf1 = '\0';
return buf;
}
/ Внимание: не потокобезопасно, повторные вызовы перезаписывают буфер. */

4) Дополнительные рекомендации по безопасности

Всегда проверяйте границы при копировании/форматировании строк. Используйте snprintf/strlcpy гдедоступногде доступногдедоступно или memcpy + контроль длины.Всегда обрабатывайте ошибки malloc проверяйтеNULLпроверяйте NULLпроверяйтеNULL.Документируйте, кто отвечает за освобождение памяти.При экспонировании API избегайте возврата указателей на внутренние автоматические буферы.Пользуйтесь статическими анализаторами/включайте предупреждения компилятора gcc−Wall−Wextragcc -Wall -WextragccWallWextra, они обычно сигнализируют о возврате адреса локальной переменной.

Если хотите, можно показать конкретную «безопасную версию» вашей функции, но мне нужно знать, что именно функция должна возвращать строкуфиксированногосодержания?содержимоеизаргументов?динамическийрезультат?строку фиксированного содержания? содержимое из аргументов? динамический результат?строкуфиксированногосодержания?содержимоеизаргументов?динамическийрезультат?. Напишите ожидаемое поведение — предложу оптимальный вариант кода.

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