¿Es la dirección 0000000C una dirección especial?

32

Cuando se programa a veces las cosas se rompen. Cometiste un error y tu programa intenta leer desde una dirección incorrecta.

Una cosa que me llama la atención es que a menudo esas excepciones son:

Access violation at address 012D37BC in module 'myprog.exe'. Read of address 0000000C.

Ahora veo muchos registros de errores y lo que más me llama la atención es: 0000000C. ¿Es esta una dirección "especial"? Veo otras violaciones de acceso con malas lecturas, pero las direcciones parecen aleatorias, pero esta sigue volviendo en situaciones totalmente diferentes.

Pieter B
fuente
1
También he notado que 0000000Ces mucho más común que eso 00000008, pero ninguna de las respuestas parece abordar eso en absoluto: /
Mooing Duck
2
Quizás esa System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringDatasea ​​la 12=0x0Crazón por la cual este desplazamiento es más común.
Mark Hurd
1
@ MarkHurd Eso da miedo. ¿Realmente cree que hay tantas aplicaciones no administradas que a propósito leen / escriben cadenas .NET que esta sería una fuente importante de violaciones de acceso?
Luaan

Respuestas:

57

00000000es una dirección especial (el puntero nulo). 0000000Ces justo lo que obtienes cuando agregas un desplazamiento de 12 al puntero nulo, muy probablemente porque alguien intentó obtener el zmiembro de una estructura como la siguiente a través de un puntero que en realidad era nulo.

struct Foo {
    int w, x, y; // or anything else that takes 12 bytes including padding
    // such as: uint64_t w; char x;
    // or: void *w; char padding[8];
    // all assuming an ordinary 32 bit x86 system
    int z;
}

fuente
29
O tal vez porque algún valor integral pequeño fue desreferenciado por error como si fuera un puntero. Los valores pequeños son mucho más comunes que los valores enormes, por lo que esto tiende a producir direcciones ilegales como 0X0000000C en lugar de, por ejemplo, 0x43FCC893.
Kilian Foth
3
La razón por la que hice esta pregunta es porque 0000000C regresa tan a menudo en comparación con otras direcciones. ¿Por qué el desplazamiento 12 es una magnitud más común que el desplazamiento 4, 8 o 16?
Pieter B
55
Después de una mayor investigación, esta respuesta es totalmente correcta. En mi fuente, la propiedad "etiqueta" de las clases se usa ampliamente (ya sea bueno o malo, tengo que lidiar con ella). La propiedad etiqueta en mi caso es parte de una clase base de bajo nivel y siempre se creó en ese desplazamiento.
Pieter B
1
Excelente punto Quizás el caso de puntero nulo estaba cubierto, pero el puntero nulo ++ es solo una dirección normal (y en este caso inválida), por lo tanto, falla solo al acceder a ella.
Neil
8
@Leushenko Sí, la protección de memoria generalmente funciona en páginas enteras, e incluso si fuera posible capturar solo 0, es preferible proteger también las siguientes direcciones porque es probable que se acceda a ellas si se produce una aritmética de puntero con un puntero nulo (como en Caso de OP).
11

En Windows es ilegal eliminar referencia a la totalidad de primera y la última página , en otras palabras, la primera o la última 64 KiB de la memoria del proceso (los intervalos 0x00000000a 0x0000ffffy 0xffff0000a 0xffffffffen una aplicación de 32 bits).

Esto es para atrapar el comportamiento indefinido de desreferenciar un puntero o índice nulo en una matriz nula. Y el tamaño de la página es de 64 KiB, por lo que Windows solo tiene que evitar que la primera o la última página tengan asignado un rango válido.

Esto no protegerá contra punteros no inicializados que podrían tener algún valor (incluidas las direcciones válidas).

monstruo de trinquete
fuente
77
Windows realmente no puede hacer eso. La tabla de páginas es una estructura definida y requerida por x86, y las páginas pequeñas se fijan en 4KB. Está en piedra (más precisamente, en silicio). El 64KB es probablemente por conveniencia.
ElderBug
12
Prefiero escribir 64 KiB en lugar de 65 kB en este caso, ya que el tamaño de la potencia de dos es relevante.
CodesInChaos
44
El rango de 64 KB es un sobrante de la versión Aplha de NT. Y no es el tamaño de la página, sino la granularidad de la asignación. blogs.msdn.com/b/oldnewthing/archive/2003/10/08/55239.aspx
shf301
3
@CodesInChaos: Si bien las mayúsculas "M", "G" y "T" son ambiguas, no veo ninguna razón para desaprobar el uso de "k" para 10 ^ 3 y "K" para 2 ^ 10.
supercat
2
@MooingDuck Sí, por eso precisé páginas pequeñas. La mayoría de las CPU x64 también admiten las páginas de 1GiB. Hasta donde sé, Windows siempre páginas con páginas 4KB, a menos que se asignen con API especiales.
ElderBug
2

En cuanto a por qué 0x0Cparece más común que 0x08(¿es realmente? No lo sé; y ¿en qué tipo de aplicaciones?), Esto podría tener que ver con punteros de tabla de métodos virtuales. Esto es realmente más un comentario (adivinanzas masivas :), pero es algo más grande, así que aquí va ... Si tienes una clase con métodos virtuales, sus propios campos se cambiarán 0x04. Por ejemplo, una clase que hereda de otra clase virtual podría tener un diseño de memoria como este:

0x00 - VMT pointer for parent
0x04 - Field 1 in parent
0x08 - VMT pointer for child
0x0C - Field 1 in child

¿Es este un escenario común, o incluso cercano? No estoy seguro. Sin embargo, tenga en cuenta que en una aplicación de 64 bits, esto podría cambiar aún más interesantemente hacia el 0x0Cvalor:

0x00 - VMT parent
0x08 - Field 1 parent
0x0C - VMT child
0x14 - Field 2 child

Entonces, en realidad, hay muchos casos en los que las aplicaciones pueden tener una superposición significativa en las compensaciones de puntero nulo. Puede ser el primer campo en una clase secundaria, o su puntero de tabla de método virtual, necesario siempre que llame a cualquier método virtual en una instancia, por lo que si llama a un método virtual en un nullpuntero, obtendrá una infracción de acceso en su VMT offset. La prevalencia de este valor particular podría tener algo que ver con alguna API común que proporciona una clase que tiene un patrón de herencia similar, o más probablemente, una interfaz particular (bastante posible para algunas clases de aplicaciones, como los juegos de DirectX). Es posible rastrear alguna causa común simple como esta, pero tiendo a deshacerme de las aplicaciones que anulan la referencia nula con bastante rapidez, así que ...

Luaan
fuente
1
Si revisa los comentarios, puede reducir las suposiciones considerablemente.
Deduplicador
@Deduplicator Bueno, creo que la idea de que las cadenas .NET administradas se usan en código inseguro con operaciones manuales de puntero da miedo, y la idea de que esta sería la causa principal de las violaciones de acceso aún más. "Sí, esto es totalmente seguro para la memoria, no se preocupe, usamos C #. Simplemente modificamos manualmente la memoria de C ++, pero es seguro en C #".
Luaan