Сбой программы C ++ (нарушение прав доступа C0000005) при вызове - PullRequest
0 голосов
/ 30 декабря 2010

У меня есть сбой, который меня озадачил и который я до сих пор считал невозможным постоянно воспроизводить.Код скомпилирован с Visual Studio 2008.

(упрощенный, конечно) исходный код выглядит следующим образом:

class AbstractParentClass 
{
private:
    /* data members */
public:
    AbstractParentClass();
    /* 
       virtual functions ...
     */
}; 

class ChildClass : public AbstractParentClass
{
private:
    /* data members */
public:
    ChildClass();
    /* 
       overridden/implemented virtual functions ...
     */
};

void DifferentClass::func(const char ** strs)
{
     ChildClass child_class;
     int i = 0;
     [...]
}

Разборка из аварийного дампа выглядит следующим образом:

Library!DifferentClass::func:
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10  
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]

Сопоставляя источник функции func () с разборкой, он выглядит примерно так:

Library!DifferentClass::func:
void DifferentClass::func(const char ** strs)
{
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
     ChildClass child_class;
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10 
     int i = 0;
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]
}

При успешном запуске (на другой машине, хотя даже на той же машине происходит сбой)не является надежно воспроизводимым), единственное отличие в разборке - это инструкция вызова, которая корректно отображает адрес конструктора по умолчанию ChildClass, например:

00404e8b  call        ChildClass::ChildClass (40a3d0h)

, а не как:

612cab2b  call        a06cff10

Таким образом, в аварийном прогоне тот адрес a06cff10, который служит параметром инструкции вызова, кажется, приходит от who-знает-где и не отображается ни на что конкретно.И поэтому, как и следовало ожидать, попытка доступа к этому адресу (чтобы получить конструктор по умолчанию ChildClass) приводит к нарушению доступа:

EXCEPTION_RECORD:  0012f688 -- (.exr 0x12f688)
ExceptionAddress: a06cff10
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: a06cff10
Attempt to read from address a06cff10

Любая попытка просмотреть этот адрес в дампе сбоя действительно указывает на то, что адреснаходится за пределами процесса.

ОБНОВЛЕНИЕ: Таким образом, после прочтения приведенного ниже ответа от zvrba и дальнейшего его изучения проблемный вызов представляется первым из дюжины или около того вызовов функций в статической библиотеке (которая в свою очередь загружается библиотекой DLL).) что все имеют неправильное смещение функции.Они не все функции в одном классе.Существует три или четыре разных класса с затронутыми функциями, хотя все классы (как вызывающие, так и вызываемые) живут в одной и той же статической библиотеке.В этом первом вызове, в котором произошел сбой, инструкция была e8e053403f, а смещение 3F4053E0 в этой инструкции должно было быть смещением всего 53E0.Все остальные экземпляры имеют одинаковую проблему смещения.Смещение в инструкции составляет 3F40XXXX, когда оно должно быть просто XXXX.Дополнительный 3F400000, конечно, отправляет вещи в Never Never Land.До сих пор я не определил, какие адреса функций в разборке действительны, а какие - нет.Одна функция-член DifferentClass в библиотеке будет иметь все свои вызовы ChildClass как плохие, тогда как другая функция-член DifferentClass будет иметь другой вызов в ChildClass, выглядят просто отлично.

Кто-нибудь там видел что-то подобное / есть мысли о вероятных причинах этого?

Ответы [ 3 ]

2 голосов
/ 30 декабря 2010

Возможно, вы реализовали конструктор дочернего класса в другой DLL?Я подозреваю, что при аварийном запуске DLL загружается по другому адресу, чем его предпочтительный адрес - вы можете проверить это в окне модулей в отладчике VS.Это, в свою очередь, приводит к неправильному расчету цели вызова (эта конкретная инструкция вызова является относительной).Смещение в сборке (4 байта после кода операции E8) также очень странное и выглядит скорее как перемещение, которое не было исправлено, чем допустимое смещение.Как вы загружаете эту DLL?

2 голосов
/ 30 декабря 2010

Трудно понять, что происходит, если большая часть исходного кода пропущена, хотя, судя по вашим комментариям и разборке, адрес vtable-таблицы ChildClass поврежден. Это может иметь несколько возможных причин, например,

  • Как уже упоминалось @contactmatt, массив / буфер переполняется
  • с использованием разрушенного объекта
  • с использованием унитарной переменной
  • неправильно приведение / преобразование переменной / указателя
  • загрузка данных из буфера в объект без упаковки / проверки выравнивания байтов
  • и т.д.

Сначала найдите адрес виртуальной таблицы и попробуйте пройти через отладчик, проверяя, не перезаписана ли виртуальная память. Вероятно, это на несколько байт выше местоположения (40a3d0h), которое вы указали:

  call        ChildClass::ChildClass (40a3d0h)

Затем найдите код, который может выполняться в этот момент времени.

Предостережение: поскольку причина в значительной степени неизвестна, обнаружение фактического исправления будет означать чтение большого количества кода / просмотр версий вашего контроля версий, чтобы увидеть возможные риски. Исходя из опыта, проблема (например, одна из упомянутых), вызывающая повреждение, может даже не быть рядом с строкой кода с нарушением доступа.

0 голосов
/ 30 декабря 2010

Не то чтобы это помогло, но там, где я работаю, я исправлял ошибку Access Violation из-за доступа к массиву out of bounds в C-программе.Я мог воспроизводить его только время от времени на своей машине.Единственное исправление, которое мы могли сделать, - это поставить множество проверок «массив вне границ».

...