Мысленный эксперимент с __stdcall и поврежденным стеком (C++)

StackOverflow https://stackoverflow.com/questions/695734

Вопрос

Сегодня мои мысли блуждали по теме указателей на функции, и я придумал следующий сценарий:

__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 Если преобразованный указатель используется для вызова функции, тип которого не совместим с типом, поведение не определен.

Редактировать:В большинстве операционных систем ошибки такого типа не вызовут проблем во всей операционной системе.Но это вызовет неопределенные проблемы в вашей программе.Программе пользовательского режима было бы очень сложно вызвать синий экран.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top