windbg: могу ли я доверять параметрам в стеке вызовов, когда он говорит, что следующие кадры могут быть неправильными?

Наше программное обеспечение C++ дает сбой либо из-за нарушения прав доступа, либо из-за повреждения кучи (согласно Windbg). Итак, стандартную кучу страниц мы включили и теперь анализируем дампы. Наше программное обеспечение записывает минидамп во время необработанного исключения. Я смотрю на это в Windbg, и стек вызовов выглядит довольно разумно. У меня есть символы для нашего кода и Windows, но нет промежуточного кода Oracle. Мой вопрос: могу ли я действительно верить параметрам, отображаемым в вызове kv, когда в стеке я получаю сообщение: «ВНИМАНИЕ: информация о раскрутке стека недоступна. Следующие кадры могут быть неправильными»? Вот стек после выполнения .ecxr и kv:

# ChildEBP RetAddr  Args to Child              
00 06f6ed20 7c863879 017c1000 06f6edd0 06f6edac ntdll!DbgBreakPoint (FPO: [0,0,0])
01 06f6ed30 7c8785ee 0000000e 7c8788ac 017c1000 ntdll!RtlpPageHeapStop+0x72 (FPO: [Non-Fpo])
02 06f6edac 7c878cec 017c1000 00000009 1214d208 ntdll!RtlpDphReportCorruptedBlock+0x154 (FPO: [Non-Fpo])
03 06f6ee10 7c879874 1e456fb0 00000000 04011000 ntdll!RtlpDphAddToDelayedFreeQueue+0x120 (FPO: [Non-Fpo])
04 06f6ee34 7c879a94 04011000 04110000 01001002 ntdll!RtlpDphNormalHeapFree+0x73 (FPO: [Non-Fpo])
05 06f6ee8c 7c87c96b 04010000 01001002 1e456fd0 ntdll!RtlpDebugPageHeapFree+0x146 (FPO: [Non-Fpo])
06 06f6eef4 7c85560a 04010000 01001002 1e456fd0 ntdll!RtlDebugFreeHeap+0x2c (FPO: [Non-Fpo])
07 06f6efcc 7c83e330 04010000 01001002 1e456fd0 ntdll!RtlFreeHeapSlowly+0x37 (FPO: [Non-Fpo])
08 06f6f0b0 7c34218a 04010000 01001002 1e456fd0 ntdll!RtlFreeHeap+0x11a (FPO: [Non-Fpo])
*** WARNING: Unable to verify checksum for sqora32.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for sqora32.dll - 
09 06f6f0f8 03f6dffc 1e456fd0 06f6f11c 03f67589 msvcr71!free+0xc3 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3052\vc\crtbld\crt\src\free.c @ 103]
WARNING: Stack unwind information not available. Following frames may be wrong.
0a 06f6f104 03f67589 1e456fd8 1dbed030 04833aa0 sqora32!SQLTablesW+0x434a0
0b 06f6f11c 03f5f010 04e209f8 1d879540 03f7a40c sqora32!SQLTablesW+0x3ca2d
0c 06f6f144 03f5eb44 04e209f8 1d879540 1dbed030 sqora32!SQLTablesW+0x344b4
0d 06f6f17c 03f481c8 04e209f8 000000d5 1d879540 sqora32!SQLTablesW+0x33fe8
0e 06f6f1b4 03f28522 04e209f8 1d879540 04833aa0 sqora32!SQLTablesW+0x1d66c
0f 06f6f1f0 03f24d80 04e209f8 02dd54f0 000000d5 sqora32!SQLPrepareW+0x37a
10 06f6f21c 4bf767f3 04e209f8 02dd54f0 000000d5 sqora32!SQLExecDirectW+0xa8
11 06f6f23c 4bf9464a 02dd54f0 000000d5 00000001 odbc32!SQLExecute+0x26f (FPO: [Non-Fpo])
12 06f6f260 4c631cff 4bfa0000 1225c3c0 000000d5 odbc32!SQLExecDirect+0x77 (FPO: [Non-Fpo])
13 06f6f278 4c6367a5 02dd99b0 1225c3c0 000000d5 odbccr32!CurSQLExecDirect+0x1e (FPO: [Non-Fpo])
14 06f6f29c 4c63549b 12256338 4c631ce1 02dd73f8 odbccr32!CSql::SubmitSql+0x161 (FPO: [Non-Fpo])
15 06f6f2d4 4c63774a 12256338 1697d508 000000d5 odbccr32!CSql::ExecDirect+0x43 (FPO: [Non-Fpo])
16 06f6f314 4bf767f3 02dd99b0 1697d508 000000d5 odbccr32!CLExecDirect+0x6a (FPO: [Non-Fpo])
17 06f6f334 4bf9464a 1697d508 000000d5 00000000 odbc32!SQLExecute+0x26f (FPO: [Non-Fpo])
18 06f6f358 01711385 4bfa0000 1697d508 000000d5 odbc32!SQLExecDirect+0x77 (FPO: [Non-Fpo])
19 06f6f380 01746a82 1697d508 00000001 06f6f6a0 odbcwrap!ODBCProcessSQLv+0x365 (FPO: [Uses EBP] [4,2,4])
1a 06f6fea8 01742474 1478d678 16604288 01540000 TransLog!DBInsertLogRecord+0x312 (FPO: [Uses EBP] [2,705,4])
1b 06f6fed4 003dbf7c 000000a8 003daf77 01540000 TransLog!LogManager::Run+0x204

Параметры нашей функции odbcwrap!ODBCProcessSQLv are: 1697d508 00000001 06f6f6a0

  • 2-й параметр = 00000001 - дескриптор оператора - что правильно
  • 3-й параметр = 06f6f6a0 - это строка SQL - что правильно
  • 1-й параметр = 1697d508. Это ДОЛЖНО быть указателем на наш класс ODBC. Но вместо этого этот указатель является другой строкой SQL!!

Это очень странно по нескольким причинам. Если бы это действительно было значение, переданное в ODBCProcessSQLv, наш код взорвался бы внутри ODBCProcessSQLv задолго до того, как он попал бы в Oracle. Итак, тогда я думаю, что класс ODBC был в порядке, но мы перезаписали его в какой-то момент после обращения к Oracle. Это будет означать, что мы перезаписываем стек, верно? Это вполне возможно, но мне трудно это доказать.

Но мне все еще любопытно узнать, могу ли я доверять этому значению 1697d508 в стеке, учитывая сообщение над этим в стеке, в котором говорится, что следующие кадры могут быть неправильными.

Еще одна странность — это вызов SQLExecDirect:

18 06f6f358 01711385 4bfa0000 1697d508 000000d5 odbc32!SQLExecDirect+0x77 (FPO: [Non-Fpo])

Вы можете видеть, что тот же 1697d508 передается в качестве второго параметра, который является правильной строкой SQL, а 000000d5 является длиной этой строки, что также является правильным. Но 4bfa0000 не имеет смысла. Этот параметр должен быть 02dd99b0 — это дескриптор инструкции. Я знаю это, потому что указатель класса ODBC выглядит нормально, когда передается в TransLog!DBInsertLogRecord, то есть 1478d678 (параметр № 1). Итак, когда я смотрю на 1478d678, это объект указателя класса ODBC, и я вижу, что дескриптор оператора должен быть 02dd99b0. Почему это 4bfa0000? Опять же, мы портим стек? Что тоже интересно, смотрю по стеку, всего 2 кадра, вижу, что 02dd99b0 перешло в CLExecDirect!!

16 06f6f314 4bf767f3 02dd99b0 1697d508 000000d5 odbccr32!CLExecDirect+0x6a (FPO: [Non-Fpo])

Таким образом, похоже, что мы передаем это значение 02dd99b0, но по какой-то причине либо windbg не может получить точную информацию, либо мы повреждаем стек.

Это действительно указывает на поврежденный стек, верно? Как это может произойти? Можете ли вы привести пример? Если он поврежден, то почему только первый параметр неверен для SQLExecDirect и ODBCProcessSQLv, а остальные параметры в порядке?

Любая помощь приветствуется...


person printf fan    schedule 05.02.2016    source источник
comment
когда символы неверны, очень высока вероятность того, что параметры также неверны, также обратите внимание на смещения, такие как name+0xveryhugeoffset, это указывает на то, что это может быть просто неправильно, не доверяйте им, если вы физически не сломаете их в своем коде src и не установите точку останова. в известной функции в вашем коде я предполагаю translog!dbxxxx и вхожу в код оракула, так как у вас есть трассировка стека, попробуйте установить точки останова в коде оракула, я думаю, что могу доверять только до sqora32!SQLPrepareW+0x37a, где смещение кажется разумным   -  person blabb    schedule 05.02.2016
comment
Для сборки отладки вы можете в значительной степени доверять параметру, показанному на WinDbg (применяется только к модулям сборки отладки). Для релизной сборки из-за различных оптимизаций компилятора места в памяти параметров часто используются повторно. Поэтому никогда не доверяйте стеку вызовов без обратной трассировки сборки вызывающего объекта. Поэтому, если вы сомневаетесь, воспроизведите дамп с помощью отладочной сборки.   -  person Peter    schedule 07.02.2016
comment
Если у кого-то с отсутствующими символами (sqora32 в вашем случае) есть оптимизации FPO, генерация трассировки стека действительно ненадежна, и отладчик покорно информирует вас об этом.   -  person Sergei Vorobiev    schedule 14.02.2016
comment
что касается аргументов, если они не находятся в стеке, отладчик не знает, как их извлечь. Они вполне могут быть в регистрах... которые вы можете найти, исследуя разборку... ub TransLog!DBInsertLogRecord+0x312 покажет, что на самом деле помещается в стек. Вероятно, первый параметр находится в ECX, поэтому вы можете изучить дизассемблирование odbcwrap!ODBCProcessSQLv, чтобы увидеть, что он делает с ECX...   -  person Sergei Vorobiev    schedule 14.02.2016


Ответы (1)


Верхние фреймы с символами должны быть в порядке, но фреймам в dll без символов нельзя доверять. Однако мой опыт показывает, что дальше, когда символы снова в порядке, коляски правильные. Некоторые мысли: во фрейме 8 первый параметр 04010000 должен быть дескриптором кучи, следующий — свободным адресом.

!heap –p –a 01001002  and see if it is a valid handle & address.

Адрес выглядит неправильно, так как он не выровнен. Попробуйте также

!address 1e456fd0 from frame 9 first param.

Этот параметр, вероятно, неверен, так как он должен соответствовать второму параметру кадра 8.

person Kjell Gunnar    schedule 05.02.2016
comment
Пытался вставить весь вывод windbg, но ТАК мешает мне... !heap ничего не вернул. Пробовал !address и мало что увидел. Сделал dc 1e456fd0-20 и увидел ожидаемое: 1e456fb0 abcdaaa9 84011000 00020008 00020030 ............0... 1e456fc0 002808a0 1b3cbde8 005f4748 dcbaaaa9 ..(...<.HG_..... 1e456fd0 f0f0f0f0 f0f0f0f0 f0f0f0f0 f0f0f0f0 ................ Показывая, что это освобожденный блок. Запустил dds 005f4748 (чтобы увидеть стек вызовов свободного действия) и увидел тот же стек вызовов, что и показанный выше. Хм? - person printf fan; 05.02.2016
comment
Другой вопрос: если я запускаю dds для значения стека вызовов в этом блоке кучи страниц, я часто получаю несколько стеков вызовов. Какой правильный? Почему я получаю несколько? Обычно первое имеет больше смысла, но другие сбивают с толку. - person printf fan; 05.02.2016