Моя компания начала приглашать нескольких клиентов, потому что наша программа рушится с нарушением прав доступа в своих системах.
Сбой происходит в SQLite 3.6.23.1, который мы отправляем как часть нашего приложения. (Мы отправляем пользовательскую сборку, чтобы использовать те же библиотеки VС++, что и остальные приложения, но это код SQLite на складе.)
Сбой происходит, когда pcache1Fetch
выполняет call 00000000
, как показано в стеке WinDbg:
0b50e5c4 719f9fad 06fe35f0 00000000 000079ad 0x0
0b50e5d8 719f9216 058d1628 000079ad 00000001 SQLite_Interop!pcache1Fetch+0x2d [sqlite3.c @ 31530]
0b50e5f4 719fd581 000079ad 00000001 0b50e63c SQLite_Interop!sqlite3PcacheFetch+0x76 [sqlite3.c @ 30651]
0b50e61c 719fff0c 000079ad 0b50e63c 00000000 SQLite_Interop!sqlite3PagerAcquire+0x51 [sqlite3.c @ 36026]
0b50e644 71a029ba 0b50e65c 00000001 00000e00 SQLite_Interop!getAndInitPage+0x1c [sqlite3.c @ 40158]
0b50e65c 71a030f8 000079ad 0aecd680 071ce030 SQLite_Interop!moveToChild+0x2a [sqlite3.c @ 42555]
0b50e690 71a0c637 0aecd6f0 00000000 0001edbe SQLite_Interop!sqlite3BtreeMovetoUnpacked+0x378 [sqlite3.c @ 43016]
0b50e6b8 71a109ed 06fd53e0 00000000 071ce030 SQLite_Interop!sqlite3VdbeCursorMoveto+0x27 [sqlite3.c @ 50624]
0b50e824 71a0db76 071ce030 0b50e880 071ce030 SQLite_Interop!sqlite3VdbeExec+0x14fd [sqlite3.c @ 55409]
0b50e850 71a0dcb5 0b50e880 21f9b4c0 00402540 SQLite_Interop!sqlite3Step+0x116 [sqlite3.c @ 51744]
0b50e870 00629a30 071ce030 76897ff4 70f24970 SQLite_Interop!sqlite3_step+0x75 [sqlite3.c @ 51806]
Соответствующая строка кода C:
if( createFlag==1 ) sqlite3BeginBenignMalloc();
Компилятор inlines sqlite3BeginBenignMalloc
, который определяется как:
typedef struct BenignMallocHooks BenignMallocHooks;
static SQLITE_WSD struct BenignMallocHooks {
void (*xBenignBegin)(void);
void (*xBenignEnd)(void);
} sqlite3Hooks = { 0, 0 };
# define wsdHooksInit
# define wsdHooks sqlite3Hooks
SQLITE_PRIVATE void sqlite3BeginBenignMalloc(void){
wsdHooksInit;
if( wsdHooks.xBenignBegin ){
wsdHooks.xBenignBegin();
}
}
И сборка для этого:
719f9f99 mov esi,dword ptr [esp+1Ch]
719f9f9d cmp esi,1
719f9fa0 jne SQLite_Interop!pcache1Fetch+0x2d (719f9fad)
719f9fa2 mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]
719f9fa7 test eax,eax
719f9fa9 je SQLite_Interop!pcache1Fetch+0x2d (719f9fad)
719f9fab call eax ; *** CRASH HERE ***
719f9fad mov ebx,dword ptr [esp+14h]
Регистры:
eax=00000000 ebx=00000001 ecx=000013f0 edx=fffffffe esi=00000001 edi=00000000
eip=00000000 esp=0b50e5c8 ebp=000079ad iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
Если eax
равно 0 (оно есть), флаг нуля должен быть установлен test eax, eax
, но он отличен от нуля. Поскольку флаг нуля не установлен, je
не перескакивает, а затем приложение выходит из строя, пытаясь выполнить call eax (00000000)
.
Обновление: eax
всегда должно быть здесь 0, потому что sqlite3Hooks.xBenignBegin
не задано в нашей сборке кода. Я мог бы перестроить SQLite с помощью SQLITE_OMIT_BUILTIN_TEST
, который включил бы #define sqlite3BeginBenignMalloc()
в коде и полностью опустил бы этот код. Это может решить проблему, но это не похоже на "реальное" исправление; что бы остановить это в другом коде кода?
До сих пор общим фактором является то, что все клиенты работают под управлением Windows 7 Home Premium 64-bit (6.1, Build 7601) с пакетом обновления 1 (SP1) и имеют один из следующих процессоров (в соответствии с DxDiag):
- AMD A6-3400M APU с графикой Radeon (tm) HD (4 процессора), ~ 1,4 ГГц
- AMD A8-3500M APU с графикой Radeon (tm) HD (4 процессора), ~ 1,5 ГГц
- AMD A8-3850 APU с графикой Radeon (tm) HD (4 процессора), ~ 2,9 ГГц
Согласно статье Wikipedia AMD Fusion, это все модели Llano для чипов AMD Fusion на базе ядра K10 и выпущены в июне 2011 года, когда мы сначала начали получать отчеты.
Наиболее распространенной клиентской системой является Toshiba Satellite L775D, но у нас также есть отчеты о сбоях от систем HP Pavilion dv6 и dv7 и Gateway.
Может ли этот сбой произойти из-за ошибки ЦП (см. Исправления для AMD 12h процессоров AMD), или есть некоторые другие возможные объяснения, которые я пропускаю? (По словам Раймонда, он может разгоняться, но странно, что на это влияет только эта конкретная модель процессора.)
Честно говоря, не представляется возможным, что это действительно ошибка процессора или ОС, потому что клиенты не получают bluescreens или сбои в других приложениях. Должно быть какое-то другое, более вероятное объяснение, но что?
Обновление 15 августа: я приобрел ноутбук Toshiba L745D с процессором AMD A6-3400M и способен последовательно воспроизводить авария при запуске программы. Авария всегда выполняется по одной и той же инструкции; .time
сообщает от 1 м до 7 м от времени пользователя перед сбоем. Один факт (который может иметь отношение к проблеме), который я забыл упомянуть в оригинальной публикации, заключается в том, что приложение является многопоточным и имеет как высокий уровень использования ЦП, так и ввода-вывода. Приложение порождает четыре рабочих потока по умолчанию и загружает 80%% использования ЦП (есть некоторая блокировка для ввода-вывода, а также для мьютексов в коде SQLite) до тех пор, пока он не завершится сбой. Я изменил приложение, чтобы использовать только два потока, и он по-прежнему разбился (хотя потребовалось больше времени). Теперь я тестирую только один поток, и он еще не разбился.
Обратите также внимание на то, что это не является проблемой загрузки CPU; Я могу запустить Prime95 без ошибок в системе, и это повысит температуру процессора до > 70 ° C, в то время как мое приложение едва достигает температуры выше 50 ° C во время работы.
Обновление 16 августа: Воспрепятствование инструкциям немного заставляет проблему "уйти". Для eaxmple замена загрузки памяти (mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]
) на xor eax, eax
предотвращает сбой. Изменение исходного кода C для добавления дополнительной проверки в оператор if( createFlag==1 )
изменяет относительные смещения различных переходов в скомпилированном коде (а также расположение операторов test eax, eax
и call eax
), а также, как представляется, проблема.
Самый странный результат, который я нашел до сих пор, заключается в том, что изменение jne
at 719f9fa0
на две команды nop
(так что управление всегда переходит к команде test eax, eax
, независимо от того, что значение createFlag
/esi
) позволяет программе работать без сбоев.