2009-04-27 22 views
19

En an answer en su propio controversial question, Mash ha demostrado que no necesita la palabra clave "inseguro" para leer y escribir directamente en los bytes de cualquier instancia de objeto .NET. Puede declarar los siguientes tipos:¿Por qué este código funciona sin la palabra clave insegura?

[StructLayout(LayoutKind.Explicit)] 
    struct MemoryAccess 
    { 

     [FieldOffset(0)] 
     public object Object; 

     [FieldOffset(0)] 
     public TopBytes Bytes; 
    } 

    class TopBytes 
    { 
     public byte b0; 
     public byte b1; 
     public byte b2; 
     public byte b3; 
     public byte b4; 
     public byte b5; 
     public byte b6; 
     public byte b7; 
     public byte b8; 
     public byte b9; 
     public byte b10; 
     public byte b11; 
     public byte b12; 
     public byte b13; 
     public byte b14; 
     public byte b15; 
    } 

Y luego puede hacer cosas como cambiar una cadena "inmutable". Los siguientes código imprime "bar" en mi máquina:

string foo = "foo"; 
MemoryAccess mem = new MemoryAccess(); 
mem.Object = foo; 
mem.Bytes.b8 = (byte)'b'; 
mem.Bytes.b10 = (byte)'a'; 
mem.Bytes.b12 = (byte)'r'; 
Console.WriteLine(foo); 

También pueden desencadenar un AccessViolationException corrompiendo las referencias a objetos con la misma técnica.

Pregunta: Pensé que (en el código C# administrado puro) la palabra clave unsafe era necesaria para hacer cosas como esta. ¿Por qué no es necesario aquí? ¿Esto significa que el código "seguro" gestionado puro no es realmente seguro en absoluto?

+0

Gracias por cambiar la forma de hacer la misma pregunta. El hilo anterior estaba demasiado inflamado. – Mash

+0

@Mash: No hay problema. Con suerte, esto dirigirá una atención más positiva a su pregunta original. –

+0

@wcoenen: No es importante, realmente, incluso si estaba pensando en ello, mi pregunta es contenido de la comunidad y no estoy ganando nada de eso. Entonces, lo único importante es una discusión positiva. Y parece que tu pregunta se ve mejor :) – Mash

Respuesta

12

OK, eso es desagradable ... los peligros de usar una unión. Eso puede funcionar, pero no es una muy buena idea, supongo que lo compararía con la reflexión (donde puedes hacer la mayoría de las cosas). Yo estaría interesado en ver si esto funciona en un entorno de acceso restringido - si es así, puede representar un problema más grande ...


yo sólo lo he probado sin la bandera "plena confianza", y el tiempo de ejecución lo rechaza:

no se pudo cargar el tipo 'MemoryAccess' del ensamblado 'ConsoleApplication4, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' porque los objetos solapadas en la posición 0 y la el ensamblaje debe ser verificable.

Y para tener esta bandera, ya necesita una gran confianza, por lo que ya puede hacer cosas más desagradables. Las cadenas son un caso ligeramente diferente, porque no son objetos .NET normales, pero hay otros ejemplos de formas de mutarlas, sin embargo, el enfoque de "unión" es interesante. Por otra manera hacky (con suficiente confianza):

string orig = "abc ", copy = orig; 
typeof(string).GetMethod("AppendInPlace", 
    BindingFlags.NonPublic | BindingFlags.Instance, 
    null, new Type[] { typeof(string), typeof(int) }, null) 
    .Invoke(orig, new object[] { "def", 3 }); 
Console.WriteLine(copy); // note we didn't touch "copy", so we have 
         // mutated the same reference 
+0

@Marc: ¿podrías elaborar sobre los otros métodos de mutación de cuerdas que tienes en mente, por favor? – Brann

+0

Este código funciona bien sin bandera insegura. Pero requiere confianza total al principio y el uso de CAS para establecer permisos no ayuda (al negar cosas que parezcan relacionadas). ¿Podría dar un ejemplo que cambia el contenido de la cadena y no utiliza bloques inseguros? – Mash

+0

Editado si se trata de la marca insegura o la confianza total - Cambié varias cosas, por lo que puede haber perdido la pista ... Re "cambia el contenido de la cadena" - no fuera de mi cabeza. Muy sucio ;-p –

0

Usted todavía está optando escape del elemento de 'gestionado'. Existe la suposición subyacente de que si puedes hacer eso, entonces sabes lo que estás haciendo.

5

Vaya, he embrollado unsafe con fixed. Aquí hay una versión corregida:

La razón de que el código de ejemplo no requiere etiquetado con la palabra clave unsafe es que no contiene punteros (véase más adelante la cita de por qué esto es considerado como inseguro). Estás en lo cierto: "seguro" podría denominarse mejor "amigable en tiempo de ejecución". Para obtener más información sobre este tema remito a Don Box y Chris Sells .NET esencial

Para citar a MSDN,

En el tiempo de ejecución de lenguaje común (CLR), código no seguro se conoce como código no verificable.El código inseguro en C# no es necesariamente peligroso; es solo el código cuya seguridad no puede ser verificado por el CLR. El CLR será , por lo tanto, solo ejecutar código inseguro si se encuentra en un ensamblaje totalmente confiable. Si utiliza un código inseguro, es su responsabilidad asegurarse de que su código no introduzca riesgos de seguridad o errores de puntero.

La diferencia entre fijo e inseguro es la que fija detiene el CLR de mover las cosas en la memoria, por lo que las cosas fuera del tiempo de ejecución pueden acceder a ellos de forma segura, mientras insegura se trata exactamente el problema contrario: mientras que el CLR puede garantizar la resolución correcta para una referencia dotnet, no puede hacerlo para un puntero. Puede recordar que varios Microsofties están hablando sobre cómo una referencia no es un puntero, y es por eso que hacen tanto alboroto sobre una distinción sutil.

+0

+1 Ah, eso tiene sentido. Esto aclara lo que significa "seguro". –

+0

Este código de hecho le permite salir de los límites del objeto y cambiar cualquier parte de la memoria accesible al proceso. Puede reconstruir bloques de sincronización y escribir el descriptor de cualquier objeto, entre dentro de los datos del GC. ¿Qué realmente inseguro da más de? – Mash

+0

De eso es de lo que estamos hablando. Este código no tiene signos de que CLR no pueda verificar su seguridad (en realidad no puede). Todos los objetos en .NET usan internamente código inseguro, pero eso no conduce a la capacidad de construir código declarado como seguro que puede acceder y cambiar los datos como inseguros. – Mash

Cuestiones relacionadas