Борьба с утечками ресурсов и переполняющимися буферами

дизассемблерный фрагмент


Выделение памяти на стеке осуществляется путем "приподнимания" регистра-указателя стека на некоторую величину, что можно сделать командой "SUB ESP, n", где  n – количество выделяемых байт. Поскольку, компилятор адресует локальные переменные через регистр EBP, то изменение ESP не нарушит работы функции и все будет ОК, но… так будет продолжаться недолго. При выходе из функции она попытается восстановить регистры, сохраненные на входе (в данном случае — это регистр ESI), но на вершине перемещенного стека их не окажется! В регистр ESI попадет мусор и материнская функция рухнет.

Существует по меньшей мере два решения проблемы: либо вручную опускаем регистр ESP при выходе из функции (если, конечно, не забудем это сделать), либо копируем на вершину выделенного блока порядка 20h байт памяти с макушки старого стека (обычного этого более чем достаточно, даже если функция сохраняет все регистры общего назначения ей требуется всего лишь 1Сh байт). В этом случае о ручном освобождении выделенной памяти можно не заботиться. Это сделает машинная команда MOV ESP, EBP, помещенная компилятором в эпилог функции.

Ниже приведена пара макросов для динамического выделения стековой памяти по первому сценарию:

#define stack_alloc(p,n,total) { __asm{sub esp,n};\

                           __asm{mov dword ptr ds:[p],esp};\

                           total += n;}

#define stack_free(total) {__asm{add esp,total};}



Содержание раздела