c# .net .net-4.0 windbg heap-corruption

c# - Cómo depurar la corrupción en el montón administrado



.net .net-4.0 (2)

Mi programa lanza un error que no puede manejar un bloque catch(Exception e) y luego se bloquea:

Infracción de acceso Excepción de estado corrupto.

Esto es lo extraño, porque, como sé, las excepciones de estado corrupto se generan desde código no administrado, mientras que aquí obtengo esta excepción al llamar a un método de StringBuilder .

El código se ejecuta en un subproceso en segundo plano y se bloquea de vez en cuando que no se puede reproducir fácilmente. Así que WinDbg al proceso y tengo la siguiente pila de la excepción:

000000001dabd8c8 000007feea129a1d [HelperMethodFrame: 000000001dabd8c8] 000000001dabda00 000007fee90cfce8 System.Text.StringBuilder.ExpandByABlock(Int32) 000000001dabda40 000007fee90cfba4 System.Text.StringBuilder.Append(Char*, Int32) 000000001dabdaa0 000007fee9102955 System.Text.StringBuilder.Append(System.String, Int32, Int32) 000000001dabdaf0 000007ff00bf5ce3 MineUtils.Common.Strings.Strings.Replace(System.String, System.String, System.String, Boolean, Boolean) 000000001dabdb90 000007ff00bf5a59 MineUtils.Common.Strings.Strings.RemoveSubstrings(System.String, System.String, System.String, Boolean) [D:/Programs/Visual Studio 2005 Projects/MineUtils.Common/Strings/Strings.Common-Main.cs @ 1481

WinDbg muestra que ocurrió esta excepción:

EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 000007feea129a1d (clr!WKS::gc_heap::find_first_object+0x0000000000000092) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 0000000000003d80 Attempt to read from address 0000000000003d80

Leí que dichas excepciones se pueden manejar con un atributo de método [HandleProcessCorruptedStateExceptions], pero ¿por qué se produce esta excepción si solo uso StringBuilder?

Este es el análisis WinDbg anterior ( StringBuilder.ToString() provoca la excepción):

******************************************************************************* * * * Exception Analysis * * * ******************************************************************************* FAULTING_IP: clr!WKS::gc_heap::find_first_object+92 000007fe`ea129a1d f70100000080 test dword ptr [rcx],80000000h EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 000007feea129a1d (clr!WKS::gc_heap::find_first_object+0x0000000000000092) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000001 NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 0000000000001c98 Attempt to read from address 0000000000001c98 ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. EXCEPTION_PARAMETER1: 0000000000000000 EXCEPTION_PARAMETER2: 0000000000001c98 READ_ADDRESS: 0000000000001c98 FOLLOWUP_IP: clr!WKS::gc_heap::find_first_object+92 000007fe`ea129a1d f70100000080 test dword ptr [rcx],80000000h MOD_LIST: <ANALYSIS/> NTGLOBALFLAG: 0 APPLICATION_VERIFIER_FLAGS: 0 MANAGED_STACK: (TransitionMU) 000000001AB7DFC0 000007FEE90CFE07 mscorlib_ni!System.Text.StringBuilder.ToString()+0x27 000000001AB7E010 000007FF00C750A9 SgmlReaderDll!Sgml.Entity.ScanToken(System.Text.StringBuilder, System.String, Boolean)+0x169 000000001AB7E080 000007FF00C760E6 SgmlReaderDll!Sgml.SgmlDtd.ParseParameterEntity(System.String)+0xc6 000000001AB7E0F0 000007FF00C76FD8 SgmlReaderDll!Sgml.SgmlDtd.ParseModel(Char, Sgml.ContentModel)+0x298 000000001AB7E160 000007FF00C7701C SgmlReaderDll!Sgml.SgmlDtd.ParseModel(Char, Sgml.ContentModel)+0x2dc 000000001AB7E1D0 000007FF00C7701C SgmlReaderDll!Sgml.SgmlDtd.ParseModel(Char, Sgml.ContentModel)+0x2dc 000000001AB7E240 000007FF00C76BA5 SgmlReaderDll!Sgml.SgmlDtd.ParseContentModel(Char)+0x65 000000001AB7E290 000007FF00C763D7 SgmlReaderDll!Sgml.SgmlDtd.ParseElementDecl()+0xe7 000000001AB7E320 000007FF00C747A1 SgmlReaderDll!Sgml.SgmlDtd.Parse()+0xc1 000000001AB7E370 000007FF00C73EF5 SgmlReaderDll!Sgml.SgmlDtd.Parse(System.Uri, System.String, System.IO.TextReader, System.String, System.String, System.Xml.XmlNameTable)+0x175 000000001AB7E410 000007FF00C73B33 SgmlReaderDll!Sgml.SgmlReader.LazyLoadDtd(System.Uri)+0x163 000000001AB7E480 000007FF00C737B9 SgmlReaderDll!Sgml.SgmlReader.OpenInput()+0x19 000000001AB7E4E0 000007FF00C7334C SgmlReaderDll!Sgml.SgmlReader.Read()+0x1c 000000001AB7E530 000007FEE5983C4C System_Xml_ni!System.Xml.XmlLoader.Load(System.Xml.XmlDocument, System.Xml.XmlReader, Boolean)+0xac 000000001AB7E590 000007FEE5983730 System_Xml_ni!System.Xml.XmlDocument.Load(System.Xml.XmlReader)+0x90 ... 000000001AB7F0A0 000007FEE97ED792 mscorlib_ni!System.Threading.Tasks.Task.Execute()+0x82 000000001AB7F100 000007FEE90A181C mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0xdc 000000001AB7F160 000007FEE97E7F95 mscorlib_ni!System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef)+0x1b5 000000001AB7F1E0 000007FEE97E7D90 mscorlib_ni!System.Threading.Tasks.Task.ExecuteEntry(Boolean)+0xb0 000000001AB7F220 000007FEE90EBA83 mscorlib_ni!System.Threading.ThreadPoolWorkQueue.Dispatch()+0x193 000000001AB7F2C0 000007FEE90EB8D5 mscorlib_ni!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()+0x35 (TransitionUM) EXCEPTION_OBJECT: !pe 2a61228 Exception object: 0000000002a61228 Exception type: System.ExecutionEngineException Message: <none> InnerException: <none> StackTrace (generated): <none> StackTraceString: <none> HResult: 80131506 MANAGED_OBJECT_NAME: System.ExecutionEngineException MANAGED_STACK_COMMAND: _EFN_StackTrace LAST_CONTROL_TRANSFER: from 000007feea12bce4 to 000007feea129a1d ADDITIONAL_DEBUG_TEXT: Followup set based on attribute [Is_ChosenCrashFollowupThread] from Frame:[0] on thread:[PSEUDO_THREAD] FAULTING_THREAD: ffffffffffffffff DEFAULT_BUCKET_ID: INVALID_POINTER_READ_CALL PRIMARY_PROBLEM_CLASS: INVALID_POINTER_READ_CALL BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ_WRONG_SYMBOLS_CALL__SYSTEM.EXECUTIONENGINEEXCEPTION

ACTUALIZADO DE NUEVO

Aquí está la pila WinDbg de la excepción después de que habilité el montón paginado :

(1480.e84): Access violation - code c0000005 (first chance) ntdll!ZwTerminateProcess+0xa: 00000000`77c415da c3 ret 0:023> !clrstack OS Thread Id: 0xe84 (23) Child SP IP Call Site 0000000037ded848 0000000077c415da [HelperMethodFrame: 0000000037ded848] 0000000037dedab0 000007fee9effd17 System.Text.StringBuilder.ToString()*** WARNING: Unable to verify checksum for C:/Windows/assembly/NativeImages_v4.0.30319_64/mscorlib/8f7f691aa155c11216387cf3420d9d1b/mscorlib.ni.dll 0000000037dedb00 000007ff00cceae9 Sgml.Entity.ScanToken(System.Text.StringBuilder, System.String, Boolean) 0000000037dedb70 000007ff00cd19b2 Sgml.SgmlDtd.ParseAttDefault(Char, Sgml.AttDef) 0000000037dedbc0 000007ff00cd120b Sgml.SgmlDtd.ParseAttDef(Char) 0000000037dedc00 000007ff00cd1057 Sgml.SgmlDtd.ParseAttList(System.Collections.Generic.Dictionary`2<System.String,Sgml.AttDef>, Char) 0000000037dedc50 000007ff00cd10cd Sgml.SgmlDtd.ParseAttList(System.Collections.Generic.Dictionary`2<System.String,Sgml.AttDef>, Char) 0000000037dedca0 000007ff00cd0e9a Sgml.SgmlDtd.ParseAttList() 0000000037dedd10 000007ff00cce1f1 Sgml.SgmlDtd.Parse() 0000000037dedd60 000007ff00ccd945 Sgml.SgmlDtd.Parse(System.Uri, System.String, System.IO.TextReader, System.String, System.String, System.Xml.XmlNameTable) 0000000037dede00 000007ff00ccd582 Sgml.SgmlReader.LazyLoadDtd(System.Uri) 0000000037dede70 000007ff00ccd1f9 Sgml.SgmlReader.OpenInput() 0000000037deded0 000007ff00cccd8c Sgml.SgmlReader.Read() 0000000037dedf20 000007fee67b3bfc System.Xml.XmlLoader.Load(System.Xml.XmlDocument, System.Xml.XmlReader, Boolean)*** WARNING: Unable to verify checksum for C:/Windows/assembly/NativeImages_v4.0.30319_64/System.Xml/8e4323f5bfb90be4621456033d8b404b/System.Xml.ni.dll *** ERROR: Module load completed but symbols could not be loaded for C:/Windows/assembly/NativeImages_v4.0.30319_64/System.Xml/8e4323f5bfb90be4621456033d8b404b/System.Xml.ni.dll 0000000037dedf80 000007fee67b36e0 System.Xml.XmlDocument.Load(System.Xml.XmlReader) [deleted] 0000000037deea90 000007feea61d432 System.Threading.Tasks.Task.Execute() 0000000037deeaf0 000007fee9ed17ec System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 0000000037deeb50 000007feea617c35 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) 0000000037deebd0 000007feea617a30 System.Threading.Tasks.Task.ExecuteEntry(Boolean) 0000000037deec10 000007fee9f1b953 System.Threading.ThreadPoolWorkQueue.Dispatch() 0000000037deecb0 000007fee9f1b7a5 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() 0000000037def310 000007feeae4dc54 [DebuggerU2MCatchHandlerFrame: 0000000037def310] 0:023> !verifyheap -verify will only produce output if there are errors in the heap The garbage collector data structures are not in a valid state for traversal. It is either in the "plan phase," where objects are being moved around, or we are at the initialization or shutdown of the gc heap. Commands related to displaying, finding or traversing objects as well as gc heap segments may not work properly. !dumpheap and !verifyheap may incorrectly complain of heap consistency errors. object 000000000e34caf8: bad member 000000001024b9a0 at 000000000e34cb08 curr_object: 000000000e34caf8 Last good object: 000000000e34cab0 ---------------- 0:023> !analyze Last event: 1480.e84: Exit process 0:1480, code 80131506 debugger time: Sun Sep 18 14:22:42.592 2011 (UTC + 1:00) 0:023> !analyze -v Last event: 1480.e84: Exit process 0:1480, code 80131506 debugger time: Sun Sep 18 14:22:42.592 2011 (UTC + 1:00) 0:023> .do e34cab0 ^ Syntax error in ''.do e34cab0'' 0:023> !do e34cab0 Name: System.String MethodTable: 000007feea026870 EEClass: 000007fee9baed58 Size: 72(0x48) bytes File: C:/Windows/Microsoft.Net/assembly/GAC_64/mscorlib/v4.0_4.0.0.0__b77a5c561934e089/mscorlib.dll String: appliedFiltersContainer Fields: MT Field Offset Type VT Attr Value Name 000007feea02c758 4000103 8 System.Int32 1 instance 23 m_stringLength 000007feea02b298 4000104 c System.Char 1 instance 61 m_firstChar 000007feea026870 4000105 10 System.String 0 shared static Empty >> Domain:Value 00000000021343a0:000000000db21420 << 0:023> !do e34caf8 <Note: this object has an invalid CLASS field> Name: System.Reflection.RuntimeAssembly MethodTable: 000007feea02a128 EEClass: 000007fee9baf968 Size: 48(0x30) bytes File: C:/Windows/Microsoft.Net/assembly/GAC_64/mscorlib/v4.0_4.0.0.0__b77a5c561934e089/mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 000007feea9ef7f0 4000e14 8 ...solveEventHandler 0 instance 0000000000000000 _ModuleResolve 000007feea036338 4000e15 10 ...che.InternalCache 0 instance 000000001024b9a0 m_cachedData 000007feea0259c8 4000e16 18 System.Object 0 instance 000000000e3abd18 m_syncRoot 000007feea033450 4000e17 20 System.IntPtr 1 instance 37a95f10 m_assembly

¿Qué puede ser?


Recientemente, me enfrenté a una corrupción de montón gestionada que era algo nuevo para mí. Estaba muy frustrado con eso y tuve que aprender muchas cosas para poder depurarlo. Quiero agradecer a Seva Titov que me dio la dirección correcta para comenzar. Su respuesta fue concisa y muy útil. Quiero registrar las acciones que he tomado para depurar el problema para mi propia referencia. Probablemente esto será útil para otros que son nuevos en esto.

Depuración de la corrupción del montón en .NET 4:

¿Cómo sospechar la corrupción del montón?

Brevemente:

  1. La aplicación se bloquea de forma aleatoria sin tener en cuenta la captura de excepción aplicada e incluso pasa por mantas como la catch(Exception) que se supone que captura todas las excepciones.

  2. Al examinar la pila de CLR en los volcados de caída de la aplicación, se muestra el recolector de basura en la parte superior de la pila:

    000000001dabd8c8 000007feea129a1d [**HelperMethodFrame**: 000000001dabd8c8] 000000001dabda00 000007fee90cfce8 System.Text.StringBuilder.ExpandByABlock(Int32) 000000001dabda40 000007fee90cfba4 System.Text.StringBuilder.Append(Char*, Int32) ... EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 000007feea129a1d (**clr!WKS::gc_heap**::find_first_object+0x0000000000000092) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 0000000000003d80 ...

  3. La pila CLR siempre muestra diferentes puntos. Si el bloqueo ocurrió o el código que se muestra es claramente irrelevante, como el método de StringBuilder que se muestra causa de la excepción.

Para obtener más información, consulte .NET Crash: Managed Heap Corruption llamando a código no administrado .

Yendo paso a paso. Cada paso siguiente se usa si el anterior no ayuda.

Paso 1. Comprueba el código.

Verifique el código para usos de código no seguros o nativos:

  1. Revise el código para unsafe declaraciones unsafe , DllImport .
  2. Descargue .NET Reflector y utilícelo para analizar los conjuntos de aplicaciones para PInvoke . De la misma manera, analice los ensamblajes de terceros que utiliza la aplicación.

Si se encuentra un uso inseguro o de código nativo, dirija atención adicional a esos. La causa más común de la corrupción del montón en tales casos es un desbordamiento de búfer o una falta de coincidencia de tipo de argumento. Asegúrese de que el búfer suministrado al código nativo para rellenar sea lo suficientemente grande y que todos los argumentos pasados ​​al código nativo sean del tipo esperado.

Paso 2. Verificar si esta excepción de estado corrupto puede ser capturada.

Para manejar tales excepciones, uno necesita decorar el método que contiene la declaración catch(Exception) con el atributo [HandleProcessCorruptedStateExceptions] o aplicar lo siguiente en el archivo app.config :

<configuration> <runtime> <legacyCorruptedStateExceptionsPolicy enabled="true" /> </runtime> </configuration>

En el caso de que la excepción se haya capturado correctamente, puede iniciar sesión y examinarla. Esto significa que esto no es un problema de montón dañado.

Las excepciones de montón dañadas no se pueden manejar en absoluto: HandleProcessCorruptedStateExceptions no parece funcionar .

Para obtener más información sobre las excepciones de estado dañado, consulte Todo sobre excepciones de estado dañado en .NET4 .

Paso 3. Depuración en vivo.

En este paso, depuramos la aplicación de fallas en vivo en el entorno de producción (o donde podemos reproducir la falla).

Descargue las herramientas de depuración para Windows del SDK de Microsoft Windows para Windows 7 y .NET Framework 4 (se descargará un instalador web que permitirá seleccionar los componentes necesarios para la instalación, marque todos los componentes). Instalará versiones de 32 y 64 bits (si su sistema es x64) de las herramientas de depuración requeridas.

Aquí, uno necesita saber cómo adjuntar WinDbg a un proceso en vivo, cómo tomar volcados y examinarlos, cómo cargar la extensión SOS en WinDbg (google para más detalles).

Habilitar ayudantes de depuración:

  1. Inicie Application Verifier ( C:/Program Files/Application Verifier : use la edición requerida, ya sea x86 o x64, dependiendo de su modo de compilación ejecutable), agregue su ejecutable allí en el panel izquierdo y en el panel derecho, marque un nodo "Conceptos básicos / Muchísimo". Guarda los cambios.

  2. Inicie el ayudante de Global Flags ( C:/Program Files/Debugging Tools for Windows/gflags.exe - nuevamente seleccione la edición correcta, x86 o x64). Una vez que se inicien los indicadores globales , vaya a la pestaña "Archivo de imagen" y en el cuadro de texto superior ingrese el nombre de su archivo ejecutable sin ninguna ruta (por ejemplo, "MiPrograma.exe"). Luego presione la tecla Tab y configure los siguientes cuadros:

    • Habilitar la comprobación de la cola del montón
    • Habilitar la comprobación gratuita del montón
    • Habilitar la comprobación de parámetros del montón
    • Habilitar la validación del montón en la llamada
    • Desactivar la fusión del montón en libre
    • Habilitar montón de página
    • Habilitar el etiquetado del montón
    • Habilitar el verificador de la aplicación
    • Depurador (escriba la ruta al WinDbg instalado en el cuadro de texto a la derecha, por ejemplo, C:/Program Files/Debugging Tools for Windows (x64)/windbg.exe -g ).

    Para obtener más detalles, consulte la corrupción del montón, Parte 2 .

  3. Vaya a "Panel de control / Sistema y seguridad / Sistema" (o haga clic con el botón derecho en "Computadora" en el menú Inicio y seleccione "Propiedades". En el cuadro de diálogo que aparece, haga clic en "Configuración avanzada del sistema", vaya a la pestaña "Avanzadas" y haga clic en el botón "Variables de entorno". En el cuadro de diálogo que se muestra, agregue una nueva variable del sistema (si usted es un administrador del sistema; de lo contrario, una variable de usuario, deberá cerrar la sesión / iniciar sesión en este caso). La variable requerida es "COMPLUS_HeapVerify" con un valor de "1". Se pueden encontrar más detalles en la pregunta de Desbordamiento de pila .NET / C #: ¿Cómo establecer la variable de entorno de depuración COMPLUS_HeapVerify?.

Ahora estamos listos para comenzar a depurar. Iniciar la aplicación. WinDbg debería iniciarse automáticamente para ello. Deje la aplicación en ejecución hasta que se bloquee en WinDgb y luego examine el volcado.

SUGERENCIA : para eliminar rápidamente la configuración de los indicadores globales , el verificador de la aplicación y el adjunto del depurador, elimine la siguiente clave en el registro: x64 - HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432Node/Microsoft/Windows NT/CurrentVersion/Image File Execution Options/*YourAppName*

Paso 4. Habilitar MDAs.

Trate de usar los asistentes de depuración administrados. Los detalles están en la pregunta de desbordamiento de pila. ¿ Qué MDA son útiles para rastrear la corrupción de un montón? .

Los MDA deben usarse junto con WinDbg. Los utilicé incluso junto con Global Flags y Application Verifier .

Paso 5. Habilitar GCStress.

Usar GCStress es una opción extrema, porque la aplicación se vuelve casi inutilizable, pero aún es un camino por recorrer. Más detalles están en GCStress: ¿Cómo activar en Windows 7? .

Paso 6. Compilar para x86.

Si su aplicación se está compilando actualmente para la plataforma "Cualquier CPU" o "x64", intente compilarla para "x86" si no hay ninguna diferencia para usted qué plataforma usar. Vi esto reportado para resolver el problema para alguien.

Paso 7. Deshabilitar GC concurrente - esto es lo que funcionó para mí

Hay un problema conocido informado en .NET 4 informado en el subproceso Violación de acceso en .NET 4 Runtime en gc_heap :: garbage_collect sin módulos no administrados . El problema se puede resolver desactivando el GC concurrente en el archivo app.config :

<?xml version="1.0"?> <configuration> <runtime> <gcConcurrent enabled="false" /> </runtime> </configuration>


Usted ha logrado la corrupción del montón. No es fácil encontrar la causa raíz del problema para la corrupción del montón administrado, ya que el problema generalmente se demuestra mucho después de que el montón está dañado. En su caso, el StringBuilder es una pista falsa. La corrupción sucedió en algún momento antes.

Lo que yo haría es lo siguiente:

  1. Compruebe si tiene algún código C # inseguro. Si tiene alguno, vuelva a comprobar la lógica allí.
  2. Habilite el montón paginado para su aplicación. Ejecutarlo con el montón paginado ayudará a descubrir problemas con el código no administrado, en caso de que el código no administrado esté corrompiendo el montón administrado.
  3. Ejecutar !VerifyHeap en diferentes lugares. De esta manera, es posible que pueda localizar el lugar en su código donde ocurren los daños.
  4. Si tiene el tipo de servidor de recolección de basura habilitado para su aplicación, cámbielo temporalmente a la recolección de basura de la estación de trabajo - obtendrá un comportamiento más predecible de esta manera.
  5. Lea la publicación del blog de Tesses .NET Crash: Managed Heap Corruption llamando a código no administrado . Demuestra algunos ejemplos de corrupción de montón gestionada.

Tenga en cuenta que cuando ejecute su código bajo WinDbg, se encontrará con una primera oportunidad AV ocasional. Es seguro ingresar eso, simplemente escriba sxd av una vez que adjunte WinDbg al proceso, e investigue solo los AV de segunda oportunidad.