Последствия этого переполнения буфера?
1411 просмотра
11 ответа
Поэтому здесь я считаю, что у меня возникла небольшая проблема с переполнением буфера, которую я обнаружил при просмотре чужого кода. Это сразу показалось мне неправильным и потенциально опасным, но по общему признанию я не мог объяснить ФАКТИЧЕСКИЕ последствия этой «ошибки», если таковые имеются.
Я написал тестовое приложение, чтобы продемонстрировать ошибку, но обнаружил (к моему ужасу), что оно работает правильно, независимо от переполнения. Я хочу верить, что это просто случайно, но хотел получить обратную связь, чтобы определить, было ли мое мышление неправильным или действительно есть проблема, которая просто не проявляет себя в моем тестовом приложении.
Код проблемы (думаю, так или иначе):
char* buffer = new char[strlen("This string is 27 char long" + 1)];
sprintf(buffer, "This string is 27 char long");
Теперь, причина, по которой это выделилось мне, и я хочу пометить его как возможное переполнение буфера, заключается в первом strlen
. Из-за арифметики с указателями «неправильное» размещение символа + 1
приведет strlen
к возврату 26
вместо 27
(принимая длину «его строка равна 27 символам»). sprintf
, Я полагаю, затем печатает 27 символов в буфер и вызывает переполнение буфера.
Это правильная оценка?
Я написал тестовое приложение, чтобы продемонстрировать это для человека, чей код я просматривал, и обнаружил, что даже в отладчике строка будет печататься правильно. Я также пытался поместить другие переменные в стек и кучу до и после этого кода, чтобы посмотреть, смогу ли я повлиять на соседние области памяти, но все еще получал правильный вывод. Я понимаю, что моя вновь выделенная память кучи может не быть смежной, что объясняет отсутствие полезного переполнения, но я просто очень хотел подтвердить мнение других, если это действительно проблема.
Поскольку это довольно простой «вопрос», было бы неплохо, если бы вы могли подкрепить свой ответ какими-то ссылками. Хотя я ценю и приветствую ваш вклад, я не собираюсь принимать «да, это так» в качестве окончательного ответа. Заранее благодарю.
Обновление: много хороших ответов с большим количеством дополнительной информации. К сожалению, я не могу принять их всех. Спасибо, что поделились своими знаниями и за то, что были моим «вторым мнением». Я ценю помощь.
Автор: KevenK Источник Размещён: 12.11.2019 09:13Ответы (11)
14 плюса
Ваша оценка верна. [править] с добавлением исправления, упомянутого Джеймсом Керраном. [/ edit]
Вероятно, ваше тестовое приложение не показало проблему, потому что выделение округлено до следующего кратного 4, 8 или 16 (которые являются общими гранулярностями выделения).
Это означает, что вы должны быть в состоянии продемонстрировать строку длиной 31 символ.
В качестве альтернативы используйте «инструментальный» собственный профилировщик памяти, который может разместить защитные байты вокруг такого выделения.
Автор: peterchen Размещён: 20.07.2010 02:226 плюса
Ваша оценка верна, за исключением того, что springf поместит 28 символов в буфер, считая NUL в конце строки в конце (именно поэтому вам понадобился неуместный "+1" в первую очередь)
Обратите внимание, что по моему опыту, если что-то выходит из строя вне отладчика, но работает с пошаговым переходом в отладчике, в 100% случаев вы переполняете локальный буфер. Отладчики помещают в стек намного больше, поэтому менее вероятно, что что-то важное было перезаписано.
Автор: James Curran Размещён: 20.07.2010 02:213 плюса
Проблема в том, что вы пишете где-то в памяти, а не в стеке. Поэтому трудно понять, что на самом деле не так. Если вы хотите увидеть повреждения, попробуйте выделить строку в стеке
char buffer[strlen("This string is 27 char long" + 1)];
и написать мимо него. Будут записаны другие переменные, вы также можете добавить некоторый код для выполнения, если вы действительно знаете, как работает двоичный файл.
Чтобы использовать подобное переполнение буфера, вам нужно записать нужные данные, а затем найти способ «перейти» к этим данным, которые будут выполнены.
Автор: Scharron Размещён: 20.07.2010 02:241 плюс
Да вы правы. Выделенный буфер будет на 2 байта слишком маленьким, чтобы содержать строку.
Так как это распределяется в куче, это может привести к повреждению кучи. Однако вероятность этого зависит от того, какие другие выделения и освобождения памяти произошли до этого момента, а также от того, какой менеджер кучи используется. См. Переполнение кучи для более подробной информации.
Автор: torak Размещён: 20.07.2010 02:231 плюс
Многие исторические malloc
реализации помещают бухгалтерские данные непосредственно перед и / или после выделенного блока. Возможно, вы перезаписываете такие данные, и в этом случае вы не увидите никаких ошибок / сбоев, пока не попытаетесь освободить память (или, возможно, освободить независимо от того, каким будет следующий блок). Аналогично, возможно, что бухгалтерская информация для последующего распределения позже перезапишет вашу строку.
Я подозреваю, что современные malloc
реализации предпринимают некоторые усилия для защиты от повреждения кучи путем заполнения выделений данными проверки целостности, поэтому, если вам повезет, ничего плохого не произойдет, или вы можете получить предупреждающее сообщение во время последующей операции выделения / освобождения.
1 плюс
Вы правы в том, что арифметика указателей в этом примере приведет к неверной (более короткой) длине, передаваемой new. Наиболее вероятная причина, по которой вы не можете вызвать этот сбой, заключается в том, что существует некоторая неопределенность относительно того, сколько буферного пространства фактически обеспечивается выделением памяти.
Библиотеке разрешено предоставлять больший буфер, чем было запрошено. Кроме того, также возможно, что все, что следует за вашим буфером, имеет префикс заголовка выделения, который подчиняется правилам выравнивания машинных слов. Это означает, что до следующего заголовка выделения может быть до трех байтов заполнения (в зависимости от платформы).
Даже если вы перезаписали следующий заголовок выделения (который используется для управления выделенными блоками памяти), он не проявит себя как проблема, пока владелец этого следующего блока не попытается вернуть его в кучу.
Автор: Amardeep AC9MF Размещён: 20.07.2010 02:261 плюс
Я попробовал это с распределением кучи, переменные не являются непрерывными в памяти в этом случае. Вот почему в этом случае сложно переполнить буфер.
Купить попробуйте с переполнением стека
#include "stdio.h"
#include "string.h"
int main()
{
unsigned int y = (0xFFFFFFFF);
char buffer[strlen("This string is 27 char long" + 1)];
unsigned int x = (0xFFFFFFFF);
sprintf(buffer, "This string is 27 char long");
printf("X (%#x) is %#x, Y (%#x) is %#x, buffer '%s' (%#x) \n", &x, x,&y, y, buffer, buffer);
return 0;
}
Вы увидите, что Y поврежден.
Автор: Yousf Размещён: 20.07.2010 02:380 плюса
Как утверждают другие, вы совершенно правы, полагая, что это нехорошо, и причина, по которой вы этого не видите, заключается в заполнении. Попробуйте valgrind
на это, это должно окончательно найти эту ошибку.
0 плюса
Ваша настоящая проблема в том, что вы пишете
char* buffer = new char[strlen("This string is 27 char long" + 1)];
вместо
char* buffer = new char[strlen("This string is 27 char long") + 1];
Это означает, что в первом случае вы даете strlen () адрес, который не является началом вашей строки .
Попробуйте этот код:
const char szText[] = "This string is 27 char long";
char* buffer = new char[strlen(szText) + 1];
sprintf(buffer, szText);
Автор: Poni
Размещён: 20.07.2010 02:37
0 плюса
Причина, по которой строка отлично печатается в отладчике, заключается в том, что как часть sprintf, завершающий символ NULL записывается в память (в данном случае за пределами выделенного буфера), а когда дело доходит до чтения строки, присутствует символ NULL. завершить строку, как ожидалось.
Проблема в том, что байт, содержащий символ NULL, не был выделен как часть оригинала new
и поэтому может быть использован для другого распределения позже. В этом случае, когда вы придете, чтобы прочитать строку впоследствии, вы, скорее всего, получите исходную строку с добавленным мусором.
0 плюса
Правильное утверждение. Поскольку вы передаете адрес второго символа строки в strlen (), в результате вы получаете длину на один символ меньше. Кроме того, основная проблема связана с sprintf (), это одна из причин того, что это небезопасно.
Даже это компилируется и выполняется (также может произойти сбой).
char* x = new char;
sprintf(x, "This is way longer than one character");
printf("%s", x);
Чтобы избежать этой опасной проблемы, вы должны использовать безопасные версии этой функции, такие как snprintf () или asprintf () в GCC или sprintf_s () в MSVC.
В качестве ссылок, пожалуйста, ознакомьтесь с документацией библиотеки GNU C по этому вопросу, а также с примечанием по безопасности статьи sprintf () MSDN.
Автор: Hamid Nazari Размещён: 20.07.2010 03:07Вопросы из категории :
- c++ What are the barriers to understanding pointers and what can be done to overcome them?
- c++ Какой самый простой способ для анализа файла INI в C ++?
- c++ Когда вы должны использовать «друг» в C ++?
- c++ Как вы очищаете переменную stringstream?
- c++ В C ++ конструктор и деструктор могут быть встроенными функциями?
- c Как вы форматируете unsigned long long int, используя printf?
- c Как реализовать продолжения?
- c Как вы передаете функцию в качестве параметра в C?
- c Как получить список каталогов в C?
- printf Печать ведущих 0 в C?
- printf Избегайте конечных нулей в printf ()
- printf Левый блокнот с пробелами
- printf JavaScript эквивалентен printf / String.Format
- buffer-overflow Есть ли в Java переполнение буфера?
- buffer-overflow Что такое переполнение буфера и как его вызвать?
- buffer-overflow Почему функция get так опасна, что ее нельзя использовать?
- buffer-overflow Написание идиом Secure C и Secure C
- buffer-overflow Последствия этого переполнения буфера?