Где же просиходит нарушение доступа?

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Где же просиходит нарушение доступа?

Сообщение Хакер » 09.08.2007 (Чт) 10:57

Имеется функция, которую нужно оттестировать. Функция принимает строку и строит по ней дерево (но это не важно).

На начальном этапе тестирования функция работала нормально. Работает и точка, - подумал я. Какого-то фига (вскоре выяснилось - что ой как не зря) мне пришла идея подкинуть функции длинную строку (эдак символов 27).

Среда пала. c000005 -- Access violation.

Каким образом размер входных данных мог привести к Access violation ? Ведь я сам писал библу и на 90% уверен, что это невозможно.

Единственное слабое место - запись в LFT (Length Filter Table - специальную таблицу, которая может при необходимости расширяться).

Изначально LFT имеет размер 16 ячеек. Если длина входной строки больше - LFT требуется увеличивать. Поэтому моя функция (KrmAddKwd), (вызов которой теперь почему-то приводит к GPF) в самом начале вызывает KrmEnlargeLFT и передаёт ей длину входной строки.

Если длина > размера LFT, LFT расширяется до нужного размера (HeapReAlloc-ом), и нарощенная часть заполняется валидными числами (LFT - таблица указателей на функции). Если длина строки меньше или равна (т.е. LFT достаточно велика), ничего не происходит, KrmEnlargeLFT завершается.

Поэтому возможно для недлинных входных строк LFT достаточно велик, а для 27-символьной происходит попытка расширить LFT, и, возможно, происходит сбой. Я так думал. (Многие сейчас подумают - нафиг гадать - возьми да прогони под отладчиком! Погодите, дойдёт дело и до отладчика.)

Проверить моё предположение было проще простого.

LFT создаётся при вызове KrmFinalize (которая проверяет, все ли параметры инициализации валидны, инициализирует и ставит флаг "инициализация произведена", разрешающий другим функциям выполняться). Перед KrmFinalize ставим KrmSetLFTInitialSize 40.

Запускаем. Среда падает :)

Значит дело не в LFT. А в чём же? В остальном коде нет никаких зависимостей на длину взодной строки.

Теперь перейдём к отладчику.
Компилирую. На всякий случай запускаю скомпилированный exe-шник (русское "авось"). Облом - GPF.

(Сразу после вызова KrmAddKwd находится такой код:
Код: Выделить всё
MsgBox "Функция успешно отработала!"
End
)


Запускаю любимый OllyDbg. Ставим брекпоинт на входе в KrmAddKwd. Запускаем. Выполнение останавливается на брекпоинте. Дальше пошагово трассируем код.

Функция выполняется... выполняется... и что вы думаете? Выполнение успешно доходит до конца функции, где происходит retn в вызвавший её VB-шный Sub Main.

Трассируем дальше: выполнение доходит до rtcMsgBox и я наблюдаю сообщение "Функция успешно отработала!" :)

Далее идёт вызов _vbaEnd - т.е. тот самый End, который стоит после MsgBox-а.

Итог: каким-бы образом я не извращался, под отладчиком всё работает как часы. И следовательно причину возникновение GPF найти не удастся. Облом :x

Причём я совершенно не могу понять, почему так. (Может кто знает, почему?)

Ладно, моё решение следующее: вставлю в свою KrmAddKwd вызов MessageBox-а, и буду двигать его дальше и дальше по направлению выполнения процедуры, пока сообщение будет появляться до GPF. Как только сообщение перестанет появляться - вызов MessageBox-а ушёл за "больную" инструкцию.

Вставил - работает. Подвинул - работает. Ещё подвинул - работает. Надоело двигать, вставил в самый конец:

Код: Выделить всё
        push 0
        push 0
        push pp1
        push 0
        call [MessageBoxA]
        ret     12d   


О чудо! Сообщение вывелось. Значит в процедуре всё валидно, и никаких ошибок нет (я был в этом уверен на 90%).

Значит, ошибка происходит уже в коде VB-программы. Но почему? Вчера у меня уже было такое - но в отличие от этого раза, тогда была тупейшая ошибка - я в процедуре (совсем другой) поюзал EBX, но не восстановил его перед возвратом (ибо код копипастился) - но это обнаружить не составило труда.

В KrmAddKwd с EBX всё в порядке (перед этими четырьмя пушами стоит pop ebx), а других регистров, которые по соглашению stdcall функция должна восстанавливать, я не юзаю.

В чём же дело?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 09.08.2007 (Чт) 12:40

Было (перед финализацией)
Код: Выделить всё
KrmSetActiveRange Asc("A"), Asc("Z")

написал
Код: Выделить всё
KrmSetActiveRange Asc("0"), Asc("Z")


Баги исчезли. Но почему? Размер JAT (тоже таблица указателей), никак не связан с длиной строки. При том, для JAT[A-Z] функция падала при длине строки большее > 20d.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 10.08.2007 (Пт) 1:48

Проблема решена 8)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.


Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 47

    TopList