Мысленный эксперимент с __stdcall и поврежденным стеком (C ++) - PullRequest
0 голосов
/ 30 марта 2009

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

__stdcall int function (int)
{
    return 0;
}

int main()
{
    (*(int(*)(char*,char*))function)("thought", "experiment");
    return 0;
}

AFAIK этот код может повредить стек, так что за проблемы, на которые я мог бы обратить внимание при запуске этого кода?

Я бы проверил это сам, однако я на неделю отошел от своей машины.

РЕДАКТИРОВАТЬ: Подожди секунду, я думал немного больше. Как было отмечено в комментариях, целью этого кода было оставить параметр в стеке, когда все сказано и сделано (вызывающая сторона помещает в стек два параметра, вызываемая - ожидая только один параметр - выдает только один параметр) ). Однако, так как мой актер не упоминает соглашение о вызовах, я отбрасываю стандартный вызов, по крайней мере, с точки зрения вызывающего абонента? int function (int) по-прежнему выталкивает параметр из стека, но вызывает ли вызывающая сторона мысль, что функция __cdecl (по умолчанию) из-за приведения? (т. е. три общих параметра выскочили?)

РЕДАКТИРОВАТЬ 2: Ответ на этот второй вопрос, как подтвердил Роб, да. Я должен был бы повторить __stdcall, если бы я хотел оставить параметр в стеке:

(*(__stdcall int(*)(char*,char*))function)("thought", "experiment");

Ответы [ 3 ]

2 голосов
/ 30 марта 2009

Вы вызываете функцию, как если бы это было _cdecl, что означает, что вызывающая сторона выдвигает аргументы и очищает стек.

Принимающей функцией является _stdcall, которая подразумевает, что вызываемый объект очищает стек. Вызываемый ожидает один аргумент, поэтому выгрузит 4 байта из стека.

Когда функция возвращает вызывающую функцию, она выскакивает из двух указателей (предварительно вставив два указателя), поэтому ваш стек поврежден на 4 байта.

Оба соглашения о вызовах используют один и тот же механизм возврата и имеют одинаковые правила регистров (eax, ecx и edx не сохраняются). См. Википедия для получения более подробной информации.

В зависимости от расположения и выравнивания кадров стека это несоответствие может вызвать ряд эффектов. Если вам повезет, то вам это сойдет с рук. Если нет, вы можете испортить адрес возврата вашей основной функции, что приведет к аварийному завершению работы программы, когда она переходит к кто-знает-где. Если компилятор внедрил какой-то стековый сторож для обнаружения повреждения, он, скорее всего, обнаружит это и прервет работу программы.

1 голос
/ 30 марта 2009

Нет, это точно не вызовет синий экран. Ни один пользовательский режим не может этого сделать. Даже если бы такая ошибка была в коде режима ядра, BSOD возникнет только после обращения к недопустимой памяти или передачи неверных аргументов функции.

Вы просто повреждаете личную память вашего процесса, и повреждение может позже (или не может) привести к неправильной операции (например, разыменование указателя, указывающего на недействительную память). Когда это происходит, ОС завершает ваш процесс, но не раньше.

0 голосов
/ 30 марта 2009

Я думаю, что в этом случае у вас будет «неопределенное поведение».

Из C стандарта : (я бы предположил, что то же самое в C ++)

768 Если преобразованный указатель используется для вызова функция, тип которой не совместим с указанным типом, поведение не определено.

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

...