Monday, April 28, 2008

Access violation

На днях довелось чинить одну из программ которую я поддерживаю. Я знал что код падает в определенном модуле с Access violation ошибкой. Очень выборочно падает. Тоесть тостер не мог мне сказать как повторить этот баг. Вместо этого мне дали имя файла где этот код падает и примерное место где. Также дали:

Состояние регистров

EAX: 0 EBX: 0 ECX: 334068 EDX: 334068 ESI: 38ae908 EDI: 32 ESP: 12e65c EBP: 12e6d8 EIP: 684356a8

Код на ассемблере

6843569e 85c0             test    eax,eax
684356a0 6a32             push    0x32
684356a2 5f               pop     edi
684356a3 7510             jnz     684356b5
684356a5 ff7508           push    dword ptr [ebp+0x8]
684356a8 8b0b             mov     ecx,[ebx]
684356aa e871f5ffff       call    68434c20
684356af 84c0             test    al,al
684356b1 7409             jz      684356bc
684356b3 8bc1             mov     eax,ecx
684356b5 e80af5ffff       call    68434bc4
684356ba 8bf8             mov     edi,eax
684356bc ff7648           push    dword ptr [esi+0x48]

А также строчку на ассемблере где падает

684356a8 8b0b             mov     ecx,[ebx]

Я давно ничего не писал на ассемблере и было приятно вспомнить. Тем более что баг серьезный и было непонятно как его найти так что надо было использовать все что есть.

Я не помню всех команд но по push 0x32 видно что в стэк занесена константа которая в десятиричной системе представляется как 50. Дальше идет сравнение того что в регистре eax с нулем. И если 0 то идет прыжок на адрес 684356b5 потом сохраняется значение локальной переменной и ... мы падаем на mov ecx,[ebx] Эта строчка копирует содержимое по адресу который находится в регистре ebx в регистр ecx. Смотрим что у нас в евх, ага бинго! 0! Теперь ясно. Нулевой указатель. Поэтому и падаем. Процесс решил залезть в чужое адресное пространство. Дальше я нашел это место в С-шном коде. Оно выглядит примерно так:

void foo(X* pX, Y* pY)
{
    if(pX)
    {
        if (pY)
        {
            use(pY);
        }
        else
        {
            int err = 50;
            if (pX->error != 0)
            {
                err = bar(pX->error);
            }
            else
            {
                if(boo(pY->error))
                {
                    err = bar(pY->error);
                }
            }
        }
    }
}

Ну, господа, программисты на С. Видите где ошибка?

В резюме добавлю что во многих универах на факультетах информатики перестали преподавать не то что ассемблер а даже С и С++. Теперь один сплошной managed code: Java, .NET и тд. Но это одна сторона медали - темная. Другая, это то что у тех кто знает ассемблер и С - неплохая job security. Или как это по русски? Уверенность в рабочем месте?

4 comments:

Deniss said...

Надо отключить комментарии иначе можно будет подсмотреть ответ в комментах

Vlad said...

Да это больше риторический вопрос. Там на самом деле очень очевидно. Если бы я посмотрел на эту функцию внимательнее я бы и так увидел но с ассемблером оказалось быстрее.

Roman said...

А я, кстати, только по С коду увидел ушибку, а с Ассемблером ничего не понял. Старею :-)

Vlad said...

У нас бывает что ты не знаешь где в С коде ошибка. Только ассемблер и есть и по нему надо искать где.