2010-05-11 25 views
7

Estoy escribiendo una máquina virtual en C solo por diversión. Lame, lo sé, pero afortunadamente estoy en SO así que espero que nadie se burle :)Escribir una máquina virtual - ¿código de bytes bien formado?

Escribí una VM realmente rápida que lee líneas de (mi) ASM y hace cosas. En este momento, sólo tengo 3 Instrucciones: add, jmp, end. Todo está bien y es realmente muy bien ser capaz de alimentar a las líneas (hacer algo así como write_line(&prog[1], "jmp", regA, regB, 0); y después de ejecutar el programa:

while (machine.code_pointer <= BOUNDS && DONE != true) 
{ 
    run_line(&prog[machine.cp]); 
} 

estoy usando una tabla de búsqueda de código de operación (que puede no ser eficiente pero es elegante) en C y todo parece estar funcionando bien

Mi pregunta es más una pregunta de "mejores prácticas" pero creo que hay una respuesta correcta. Estoy haciendo que la VM pueda leer archivos binarios (almacenamiento de bytes) en unsigned char[]) y ejecutar bytecode. Mi pregunta es: ¿es el trabajo de la VM asegurarse de que el bytecode es bien formado o es solo el trabajo del compilador para asegurarse de que el archivo binario que escupe está bien formado?

Solo pregunto esto porque, ¿qué pasaría si alguien editara un archivo binario y atornillara cosas (borre partes arbitrarias de él, etc.)? Claramente, el programa sería defectuoso y probablemente no funcional. ¿Es esto incluso el problema de la máquina virtual? Estoy seguro de que personas mucho más inteligentes que yo han descubierto soluciones a estos problemas, ¡solo tengo curiosidad por saber qué son!

Respuesta

14

El trabajo de la VM es asegurarse de que el bytecode esté bien formado. ¿o es solo el trabajo del compilador asegurarse de que el archivo binario que escupe esté bien formado?

tiene que decidir.

La mejor práctica es hacer que el VM haga una sola comprobación antes de la ejecución, es proporcional al tamaño del programa, lo cual es lo suficientemente sofisticado como para garantizar que nada pueda ocurrir durante la ejecución. Luego, durante la ejecución real del bytecode, se ejecuta sin controles. Sin embargo, la idea de facturación antes de marcha puede requerir un análisis muy sofisticado, e incluso las máquinas virtuales más rendimiento consciente menudo tienen algunos controles en tiempo de ejecución (ejemplo: límites de la matriz).

Para un proyecto de pasatiempo, me gustaría mantener las cosas simples y tienen la comprobación de validez VM cada vez que se ejecute una instrucción. La sobrecarga para la mayoría de las instrucciones no será demasiado grande.

+0

respuesta perfecta, gracias! –

0

Tiene sentido que el compilador haga la mayor comprobación de cordura posible (ya que solo tiene que hacerlo una vez), pero siempre habrá problemas que no se pueden detectar mediante análisis estáticos, como [tos] desbordamiento de pila, errores de rango de matriz y similares.

1

El mismo problema se presenta en Java, y según recuerdo, en ese caso la máquina virtual tiene que hacer algunas comprobaciones para asegurarse de que el bytecode está bien formado. En esa situación, en realidad es un problema grave debido a la posibilidad de problemas de seguridad: si alguien puede alterar un archivo de código de bytes de Java para contener algo que el compilador nunca salida (como el acceso a una variable private de otra clase), que podría potencialmente exponer datos confidenciales retenidos en la memoria de la aplicación, o podrían permitir que la aplicación acceda a un sitio web que no debería permitirse, o algo así. La máquina virtual de Java incluye un verificador de código de bytes para asegurarse, en la medida de lo posible, de que este tipo de cosas no ocurra.

Ahora, en su caso, a menos que su lenguaje casero despegue y se vuelva popular, el aspecto de seguridad es algo de lo que no tiene que preocuparse tanto; después de todo, ¿quién va a piratear tus programas, aparte de ti? Aún así, diría que es una buena idea asegurarse de que su VM al menos tenga una estrategia de falla razonable para cuando el bytecode no sea válido. Como mínimo, si encuentra algo que no comprende y no puede procesar, debe detectarlo y fallar con un mensaje de error, lo que facilitará la depuración de su parte.

+0

Nunca tuve la impresión de que hacer miembros privados sea una herramienta de seguridad. Mi impresión es que no es más que una herramienta de diseño para proporcionar buenas abstracciones, que oculta los detalles de implementación, pero que no se considera segura. –

+0

@Chris, el acceso a miembros 'privados' puede no ser un problema de seguridad en sí mismo, pero imagine una optimización del compilador basada en la suposición de que se conocen todas las referencias y asignaciones a una variable privada. Por ejemplo, el compilador puede probar que un acceso a la matriz nunca está fuera de límites debido a una comparación previa con una variable privada que se sabe que es menor que la longitud de la matriz, por lo que elimina la verificación de límites. Si el bytecode "ilegal" invalida esa suposición, esa verificación de límites faltantes podría ocasionar un problema de seguridad. –

+1

usando 'Reflection' puede acceder a cualquier miembro de' private' o de otra manera y por lo tanto 'private'ness es meramente superficial y un buen compilador no podría confiar en él –

1

Las máquinas virtuales que interpretan bytecode generalmente tienen alguna forma de validar su entrada; por ejemplo, Java arrojará un VerifyError si el archivo de clase está en un estado incoherente

Sin embargo, parece que está implementando un procesador, y dado que tienden a ser de nivel inferior, hay menos formas de lograrlo cosas en un estado inválido detectable, dándole un código de operación indefinido es una manera obvia.Los procesadores reales indicarán que el proceso intentó ejecutar una instrucción ilegal, y el sistema operativo lo resolverá (Linux lo mata con SIGILL, por ejemplo)

0

Yo diría que es legítimo para su máquina virtual para que el fuego captura procesador emulado, siempre y cuando la propia aplicación VM no se desplome. Como el implementador de VM, puedes establecer las reglas. Pero si quiere que las empresas virtuales de hardware compren virtualmente su chip virtual, tendrá que hacer algo un poco más tolerante con los errores: buenas opciones podrían ser lanzar una excepción (más difícil de implementar) o reiniciar el procesador (mucho más fácil). O tal vez solo defina cada opcode para que sea válido, excepto que algunos son "no documentados": hacen algo no especificado, además de bloquear su implementación. Justificación: si (!) La implementación de su VM va a ejecutar varias instancias del invitado simultáneamente, sería muy malo que un invitado pudiese causar que otros fallaran.

1

Si usted está preocupado por alguien después de haber editado el archivo binario, entonces no es sólo una respuesta a su pregunta: la máquina virtual debe hacer el cheque. Es la única forma en que tienes la oportunidad de detectar la manipulación. El compilador solo crea el binario. No tiene forma de detectar la manipulación posterior.

Cuestiones relacionadas