Мысленный эксперимент с __stdcall и поврежденным стеком (C++)
Вопрос
Сегодня мои мысли блуждали по теме указателей на функции, и я придумал следующий сценарий:
__stdcall int function (int)
{
return 0;
}
int main()
{
(*(int(*)(char*,char*))function)("thought", "experiment");
return 0;
}
AFAIK, этот код повредит стек, так какие типы проблем я мог бы обнаружить, если бы запустил этот код?
Я бы провел это расследование сам, однако меня не будет рядом с моей машиной для разработки на неделю.
РЕДАКТИРОВАТЬ:Подожди секунду, я подумал еще немного.Как было замечено в комментариях, цель этого кода заключалась в том, чтобы параметр оставался в стеке, когда все сказано и сделано (вызывающая сторона помещает в стек два параметра, вызываемая сторона - ожидая только одного параметра - извлекает только один ).Однако, поскольку в моем приведении не упоминается соглашение о вызовах, отбрасываю ли я stdcall, по крайней мере, с точки зрения вызывающего абонента? целочисленная функция (целое) по-прежнему извлекает параметр из стека, но возвращается ли вызывающая сторона к мысли, что функция является __cdecl (по умолчанию) из-за приведения?(т.е.выскочило всего три параметра?)
РЕДАКТИРОВАТЬ2:Ответ на второй вопрос, как подтвердил Роб, — да.Мне пришлось бы переформулировать __stdcall, если бы я хотел оставить параметр в стеке:
(*(__stdcall int(*)(char*,char*))function)("thought", "experiment");
Решение
Вы вызываете функцию так, как будто это _cdecl, что означает, что вызывающая сторона помещает аргументы и очищает стек.
Приемной функцией является _stdcall, что означает, что вызываемый объект очищает стек.Вызываемый объект ожидает один аргумент, поэтому извлекает из стека 4 байта.
Когда функция возвращает значение, вызывающая сторона извлекает два указателя (ранее переместив два указателя), поэтому ваш стек повреждается на 4 байта.
Оба соглашения о вызовах используют один и тот же механизм возврата и имеют одни и те же правила регистрации (eax, ecx и edx не сохраняются).Видеть Википедия Больше подробностей.
В зависимости от расположения и выравнивания кадра стека это несоответствие может вызвать ряд последствий.Если вам повезет, то вам это сойдет с рук.В противном случае вы можете испортить обратный адрес вашей основной функции, что приведет к сбою программы при переходе неизвестно куда.Если компилятор внедрил какую-то защиту стека для обнаружения повреждений, он, скорее всего, обнаружит это и прервет программу.
Другие советы
Нет, синего экрана это точно не вызовет.Ни один процесс пользовательского режима не может этого сделать.Даже если бы такая ошибка была в коде режима ядра, BSOD возник бы только после доступа к недопустимой памяти или передачи неправильных аргументов функции.
Вы просто повреждаете частную память вашего процесса, и это повреждение может (или не может) позже привести к недопустимой операции (например.разыменование указателя, указывающего на недопустимую память).Когда это произойдет, ОС завершит ваш процесс, но не раньше.
Я думаю, в этом случае у вас будет «неопределенное поведение».
Из Стандарт С:(Я предполагаю, что в C++ то же самое)
768 Если преобразованный указатель используется для вызова функции, тип которого не совместим с типом, поведение не определен.
Редактировать:В большинстве операционных систем ошибки такого типа не вызовут проблем во всей операционной системе.Но это вызовет неопределенные проблемы в вашей программе.Программе пользовательского режима было бы очень сложно вызвать синий экран.